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

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

该部分内容较为基础,大多为简单的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);
        }
    }
}
相关推荐
点光8 小时前
使用Sentinel作为Spring Boot应用限流组件
后端
不要秃头啊9 小时前
别再谈提效了:AI 时代的开发范式本质变了
前端·后端·程序员
有志9 小时前
Java 项目添加慢 SQL 查询工具实践
后端
山佳的山9 小时前
KingbaseES 共享锁(SHARE)与排他锁(EXCLUSIVE)详解及测试复现
后端
Leo8999 小时前
rust 从零单排 之 一战到底
后端
程序员清风10 小时前
程序员兼职必看:靠谱软件外包平台挑选指南与避坑清单!
java·后端·面试
鱼人11 小时前
MySQL 实战入门:从“增删改查”到“高效查询”的核心指南
后端
大鹏198811 小时前
告别 Session:Spring Boot 实现 JWT 无状态登录认证全攻略
后端
Java编程爱好者11 小时前
从 AQS 到 ReentrantLock:搞懂同步队列与条件队列,这一篇就够了
后端
鱼人11 小时前
Nginx 全能指南:从反向代理到负载均衡,一篇打通任督二脉
后端