【智能协图云图库|第七天】 空间模块初始化

本部分内容主要来源于鱼皮智能协图云图库部分,并在笔者个人项目学习的基础上进行扩展衍生。由于项目开发文档已经足够详细,因此这里只记录要点。

该部分内容较为基础,大多为简单的CRUD。因此熟悉业务流程才是重点。

需求分析

设想:创建用户独享的空间服务(可作为云盘)------>衍生具体功能------>衍生问题思考

功能------问题:

1)用户能创建空间------>创建空间的条件,私有空间的权限,空间的额度等

2)管理员管理空间------>对于用户空间是不是能随意修改,管理员权限范围等

理解空间:

和公共空间一样本质就是张表。在我们没有设计公共空间的时候,picture表本身就代表着公共空间。这里也可以采用在picture表添加字段的形式将picture划分为公共空间与私有空间。然而由于这里对公共空间与私有空间的逻辑处理有较大差别,因此专门设计单独的空间表。

在有公共空间与私有空间的划分后,图片归属于空间,因此也要对图片加字段来分辨图片处于公共空间or私有空间。

sql 复制代码
ALTER TABLE picture
    ADD COLUMN spaceId  bigint  null comment '空间 id(为空表示公共空间)';


CREATE INDEX idx_spaceId ON picture (spaceId);

后端开发

空间模块的初步建立,增删改查操作流程------>有空间模块后对图片模块的增删改查加入条件控制

------>完善空间模块级别限额等细节控制

这里的新知识点主要是用户创建私有空间

java 复制代码
@Resource
private TransactionTemplate transactionTemplate;

@Override
public long addSpace(SpaceAddRequest spaceAddRequest, User loginUser) {
    
    Space space = new Space();
    BeanUtils.copyProperties(spaceAddRequest, space);
    
    if (StrUtil.isBlank(spaceAddRequest.getSpaceName())) {
        space.setSpaceName("默认空间");
    }
    if (spaceAddRequest.getSpaceLevel() == null) {
        space.setSpaceLevel(SpaceLevelEnum.COMMON.getValue());
    }
    
    this.fillSpaceBySpaceLevel(space);
    
    this.validSpace(space, true);
    Long userId = loginUser.getId();
    space.setUserId(userId);
    
    if (SpaceLevelEnum.COMMON.getValue() != spaceAddRequest.getSpaceLevel() && !userService.isAdmin(loginUser)) {
        throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限创建指定级别的空间");
    }
    
    String lock = String.valueOf(userId).intern();
    synchronized (lock) {
        Long newSpaceId = transactionTemplate.execute(status -> {
            boolean exists = this.lambdaQuery().eq(Space::getUserId, userId).exists();
            ThrowUtils.throwIf(exists, ErrorCode.OPERATION_ERROR, "每个用户仅能有一个私有空间");
            
            boolean result = this.save(space);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
            
            return space.getId();
        });
        
        return Optional.ofNullable(newSpaceId).orElse(-1L);
    }
}

要点:

1.锁粒度为用户,为确保锁住的是同一个对象使用intern()

2.缩小事务粒度,避免释放锁后事务还未提交transactionTemplate.execute()

为避免intern不能及时释放数据,占用大量内存还可以使用ConcurrentHashMap

java 复制代码
Map<Long, Object> lockMap = new ConcurrentHashMap<>();

public long addSpace(SpaceAddRequest spaceAddRequest, User user) {
    Long userId = user.getId();
    Object lock = lockMap.computeIfAbsent(userId, key -> new Object());
    synchronized (lock) {
        try {
            
        } finally {
            
            lockMap.remove(userId);
        }
    }
}
相关推荐
Lee川2 小时前
mini-cursor 揭秘:从 Tool 定义到 Agent 循环的完整实现
前端·人工智能·后端
weelinking2 小时前
【产品】00_产品经理用Claude实现产品系列介绍
数据库·人工智能·sql·数据挖掘·github·产品经理
一直不明飞行3 小时前
Java的equals(),hashCode()应该在什么时候重写
java·开发语言·jvm
REDcker3 小时前
有限状态机与状态模式详解 FSM建模Java状态模式与C++表驱动模板实践
java·c++·状态模式
2301_803934613 小时前
Go语言如何做网络爬虫_Go语言爬虫开发教程【指南】
jvm·数据库·python
你的保护色3 小时前
【无标题】
java·服务器·网络
basketball6164 小时前
C++ 构造函数完全指南:从入门到进阶
java·开发语言·c++
秋94 小时前
windows中安装redis
数据库·redis·缓存
淘矿人4 小时前
Claude辅助DevOps实践
java·大数据·运维·人工智能·算法·bug·devops
Cosolar4 小时前
万字详解:RAG 向量索引算法与向量数据库架构及实战
数据库·人工智能·算法·数据库架构·milvus