核心业务流程分析

功能模块设计
-
用户模块
用户登录、注册、注销、用户权限管理、管理用户(管理员权限)
-
图片模块
上传图片、图片信息编辑(标签、分类,管理图片、查看(page和搜索图片、图片详情、图片下载、用户上传图片、管理员审核图片、通过Url导入图片、管理员批量抓取图片
图片模块扩展:
图片搜索:基础属性搜索,图片搜索,颜色搜索
图片分享:链接分享、扫码分享
图片批量管理:批量修改信息,批量重命名
图片编辑:基础图片编辑,ai图片编辑
-
空间模块
管理空间(admin权限、用户开通私有空间、私有空间权限控制、空间级别和限额控制、空间图库分析
空间模块扩展:
创建团队空间
空间成员管理(CRUD,成员邀请、设置权限
空间成员权限控制
技术选型
Spring Boot
Mysql数据库
Mybatis-Plus
Mybatis-X
Redi分布式缓存、Caffeine本地缓存
Jsoup数据抓取
Sa-Token权限控制
AI大模型接入
JUC并发和异步编程
Vue3、Vite、Ant Design Vue组件库、Axios、Pinia
架构设计

功能开发
一些没有必要重复编写的代码
-
自定义异常(错误类枚举,见项目代码 enum ErrorCode
-
注意和主流的错误码保持一致,但是要有一定的区分度
-
不要完全连续,预留一些间隔
-
-
自定义业务异常,便于定制化输出异常信息,见项目代码 class BussinessException
-
封装一个ThrowUtils,类似断言,简化异常代码 见项目代码 ThrowUtils
-
统一响应包装类,便于前端获取结果信息 class BaseResponse<T>
-
统一响应包装类的工具类,简化相应类实例化 class ResultUtils
-
全局异常处理器 class GlobalExceptionHandler
-
请求包装类(对于分页,删除
-
class PageResult
-
class DeleteRequest
-
-
全局跨域配置
-
CorsConfig
-
前端项目搭建
-
创建项目 npm create vue@latest

-
自动生成请求代码
npm i --save-dev @umijs/openapi
在根目录创建openapi.config.js
import { generateService } from '@umijs/openapi'
generateService({
requestLibPath: "import request from '@/request'",
schemaPath: 'http://localhost:8123/api/v2/api-docs',// 这里的路径改为后端Swagger接口文档的
serversPath: './src',
})
在package.json 的 script 中添加:
"openapi":"node openapi.config.js"
Java8的函数式编程语法
// map是一个中间操作,调用getPictureVO方法,将每一个picture转化为pictureVO
// collect是一个结束操作,将Stream中的元素收集到List中
List<PictureVO> pictureVOList = records.stream().map(picture -> getPictureVO(picture, request)).collect(Collectors.toList());
//从List收集userId到Set集中
Set<Long> userIdSet = records.stream().map(Picture::getUserId).collect(Collectors.toSet());
//批量查询userIds,将结果按照userId 将结果收集为Map
Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream().collect(Collectors.groupingBy(User::getId));
// 从Map中取出对应的 User
if(userIdUserListMap.containsKey(userId)){
List<User> userList = userIdUserListMap.get(userId);
user = userList.get(0);
}
项目中涉及到的设计模式
-
模板方法:针对同一个方法中写了两种不同的文件上传实现,这两种不同的实现包含了大部分相同的代码,考虑将不同的代码抽离出来,保留相同的代码。
-
实现: 新建一个上传模板抽象类,将不同的实现封装为抽象方法,需要实现实现不同抽象方法就去继承模板抽象类,然后具体实现
-
功能开发part-1-用户模块
-
需求分析
-
用户注册
-
提交账号、密码、确认密码进行注册
-
-
用户登录
-
输入账号、密码
-
-
获取当前登录用户
-
得到当前登录用户的信息(不用重复登录
-
-
用户注销
-
退出登录
-
-
管理员-管理用户
-
管理员权限,对整个系统的用户进行管理,例如:删除用户、搜索用户
-
-
-
方案设计
-
库表设计、用户登录流程、如何对用户进行权限控制
-

-
-
用户登录流程

-
权限控制分类
-
未登录可用
-
登录可用
-
未登录限制可用(登录可以有更多的操作
-
仅管理员可用
-
传统的权限校验方法,是在每个接口内单独编写逻辑:先获取到当前登录用户的信息,然后判断用户的权限是否符合,虽然灵活但是繁琐,而且无法一眼看出接口所需要的权限。
使用Spring AOP + 自定义权限校验注解(流程如下

功能模块part-2-图片模块
-
需求分析
-
管理员功能
-
图片上传和创建(支持本地上传,并填写相关信息,如名称、简介、便签、分类。系统自动解析图片基础信息
-
图片管理(CRUD
-
-
用户功能
-
查看与搜索图片(主页、按照关键词检索图片、并支持按照分类标签条件筛选分页查看的图片列表
-
查看图片详情
-
图片下载
-
-
-
图片上传(流程图-fileManager公共上传方法

-
图片上传Service开发


功能模块-part3-用户传图
-
需求分析
-
支持用户上传图片和审核功能
-
通过RUL导入图片
-
批量抓取和创建图片
-
-
审核流程

-
通过URL上传图片(区别file文件校验、通过HttpUtil.downloadFile下载图片

-
批量抓取和创建图片

part4-传图优化
-
图片查询优化
-
redis分布式缓存
-
缓存设计:key、value、过期时间
-
key:针对不同的查询条件,对应的数据不同,所以要把查询条件作为key的一部分,(实现:将查询条件对象作为转换为JSON,利用MD5来压缩key,此外因为使用分布式缓存,需要在key前拼接项目名称前缀:
-
value: 将Page对象转为JSON,或二进制,对应的Redis结构都是String
-
缓存过期时间:合适即可
-
接口: class listPictureVOByPageWithCache
-
-
流程设计
-
-

2. Caffenine本地缓存
1. 构造本地缓存,流程相同
2. private final Cache<String, String> LOCAL_CACHE =
Caffeine.newBuilder().initialCapacity(1024)
.maximumSize(10000L)
// 缓存 5 分钟移除
.expireAfterWrite(5L, TimeUnit.MINUTES)
.build();
3. 测试用例
@SpringBootTest
public class RedisStringTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void testRedisStringOperations() {
// 获取操作对象
ValueOperations<String, String> valueOps = stringRedisTemplate.opsForValue();
// Key 和 Value
String key = "testKey";
String value = "testValue";
// 1. 测试新增或更新操作
valueOps.set(key, value);
String storedValue = valueOps.get(key);
assertEquals(value, storedValue, "存储的值与预期不一致");
// 2. 测试修改操作
String updatedValue = "updatedValue";
valueOps.set(key, updatedValue);
storedValue = valueOps.get(key);
assertEquals(updatedValue, storedValue, "更新后的值与预期不一致");
// 3. 测试查询操作
storedValue = valueOps.get(key);
assertNotNull(storedValue, "查询的值为空");
assertEquals(updatedValue, storedValue, "查询的值与预期不一致");
// 4. 测试删除操作
stringRedisTemplate.delete(key);
storedValue = valueOps.get(key);
assertNull(storedValue, "删除后的值不为空");
}
}
-
构建多级缓存

-
缓存常见问题
-
缓存击穿:某些热点信息在缓存过期后,大量请求直接打到数据库上(解决:设置超长过期时间,获取使用互斥锁控制缓存刷新
-
缓存穿透:用户请求频繁请求不存在的数据,导致大量请求直接触发数据库查询(解决:对无效查询结果也进行缓存,如火村空值,或者使用布隆过滤器
-
缓存雪崩:大量缓存同时过期,导致请求达到数据库(解决:设置不同的过期时间,使用多级缓存
-
-
扩展
-
缓存击穿(Cache Breakdown) 命名原因:“击穿”这个词形象地描述了当某个热点数据的缓存失效时,大量请求像“击穿”了缓存层一样,直接打到数据库上,导致数据库压力骤增。就像一层保护屏障被“击穿”了,失去了保护作用。
-
问题本质:热点数据缓存失效,导致请求直接访问数据库。
解决思路:通过设置超长过期时间或使用互斥锁(如分布式锁)来控制缓存刷新,避免大量请求同时打到数据库。
2. 缓存穿透(Cache Penetration)
命名原因:“穿透”这个词形象地描述了请求直接“穿透”缓存层,到达数据库的场景。通常是因为请求的数据在缓存和数据库中都不存在,导致缓存无法发挥作用,请求每次都直接访问数据库。
问题本质:频繁请求不存在的数据,导致缓存失效,数据库压力增加。
解决思路:对无效查询结果也进行缓存(如缓存空值),或者使用布隆过滤器(Bloom Filter)来快速判断数据是否存在,避免无效查询。
3. 缓存雪崩(Cache Avalanche)
命名原因:“雪崩”这个词形象地描述了大量缓存同时失效的场景,就像雪山上的积雪突然崩塌一样,导致大量请求直接打到数据库,造成数据库压力瞬间激增,甚至可能引发系统崩溃。
问题本质:大量缓存同时过期,导致请求直接访问数据库。
解决思路:通过设置不同的过期时间(如添加随机值),或者使用多级缓存(如本地缓存 + 分布式缓存)来分散缓存失效的压力。
part4-图片上传优化
主要就是阅读COS对象存储、万象存储文档
功能模块-part5-空间模块
-
用户创建私有空间

-
RBAC权限管理
-
登录认证(设计思路
-

- 权限设计(设计思路
* 核心逻辑就是判断一个账号是否拥有指定的权限
+ 有:通过
+ 无:禁止访问


4.空间模块
- 从请求冲获取上下文参数 - 私人空间创建

- 空间成员管理(CURD - 空间成员权限控制

1867

被折叠的 条评论
为什么被折叠?



