本部分内容主要来源于鱼皮智能协图云图库部分,并在笔者个人项目学习的基础上进行扩展衍生。由于项目开发文档已经足够详细,因此这里只记录要点。
该部分内容较为基础,大多为简单的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);
}
}
}