startFormat()逻辑
startFormat:参数校验与初始化 -- 防重复提交控制 -- 清理旧排版结果 -- 设置任务状态 -- 保存初始状态 -- 异步执行排版任务 -- 日志记录与返回
1. FormatService中:startFormat()
java
public void startFormat(StartFormatRequest request) {
String nodeId = request.getNodeId();
Integer styleId = request.getStyleId();
Long userId = SecurityUtils.getCurrentUserId();
// 1. 参数校验与初始化: 校验排版样式(fromCode会在styleId无效时抛出异常)
LayoutStyle.fromCode(styleId);
// 2.防重复提交控制:检查是否正在处理中.
// 通过 Redis 检查当前用户是否正在处理相同文件的排版任务,避免重复提交.
String taskKey = FORMAT_TASK_KEY_PREFIX + userId + ":" + styleId + ":" + nodeId;
String currentStatus = stringRedisTemplate.opsForValue().get(taskKey);//检查是否正在处理中
if (TASK_PROCESSING.equals(currentStatus)) {
log.info("FormatService##startFormat-1,文件正在排版处理中,请勿重复提交,nodeId={},styleId={}", nodeId, styleId);
throw new BizException(ErrorCode.BUSINESS_ERROR, "文件正在排版处理中,请勿重复提交");
}
// ....
// 3. 清理该文件的旧排版结果(同一userId + sourceNodeId 只保留最新一次)
// 调用 cleanupOldFormatResults 方法删除用户历史排版结果,确保只保留最新一次排版数据。
cleanupOldFormatResults(userId, nodeId);
// 4.设置任务状态为处理中(24小时过期)
// 在 Redis 中设置任务状态为"处理中"(PROCESSING),并设置 24 小时过期时间
stringRedisTemplate.opsForValue().set(taskKey, TASK_PROCESSING, 24, TimeUnit.HOURS);
// 5. 保存初始状态:立即保存"排版中"状态的结果项,让前端可以立即查询到
// 调用 saveFormattingResultItem 方法立即保存"排版中"状态的结果项,供前端实时查询
saveFormattingResultItem(nodeId, fileName, userId);
// 6. 异步执行排版任务:通过ApplicationContext获取代理对象,调用doFormatAsync方法异步执行排版逻辑
// 通过 ApplicationContext 获取代理对象调用,触发 @Async 异步执行
// 直接调用 doFormatAsync() 不会走 AOP 代理,@Async 不生效
// 注意:Spring 使用 JDK 动态代理,需要通过接口类型获取 Bean
FormatService proxy = applicationContext.getBean(FormatService.class);
proxy.doFormatAsync(styleId, null, nodeId, fileId, fileName, userId, taskKey);
// 7.日志记录与返回:方法立即返回,前端通过轮询 query 接口查询排版状态
// 记录关键操作日志,方法执行后立即返回,前端通过轮询接口查询排版状态
log.info("FormatService##startFormat-6,排版任务已提交异步执行,nodeId={}", nodeId);
}
(1)设置任务状态 :①目的:防止重复提交任务。②实现方式:通过 Redis 的 stringRedisTemplate.opsForValue().set(taskKey, TASK_PROCESSING, 24, TimeUnit.HOURS) 设置一个键值对;taskKey 是基于用户 ID、样式 ID 和文件节点 ID 生成的唯一标识;值为 TASK_PROCESSING("PROCESSING"),表示当前任务正在进行中;过期时间为 24 小时,确保任务状态不会永久占用 Redis。③作用:在方法开头检查该键是否存在且值为 TASK_PROCESSING,如果存在则抛出异常,提示用户"文件正在排版处理中,请勿重复提交";避免同一用户对同一文件重复发起排版任务。
(2) 保存初始状态 :①目的:记录排版任务的初始信息,供前端实时查询。②实现方式:调用 saveFormattingResultItem 方法,将"排版中"状态的结果项保存到 Redis;该方法会创建一个 FormatResultItem 对象,状态为 FORMATTING,并将其序列化后存储到 Redis 中;同时更新用户与文件的映射关系(通过 FORMAT_USER_KEY_PREFIX 和 FORMAT_SOURCE_KEY_PREFIX)。③作用:提供给前端查询接口使用,前端可以通过轮询获取当前任务的状态(如"排版中");即使异步任务尚未完成,前端也能立即感知到任务已启动。

(3)清理旧版结果:为了清除与当前用户和原文件相关的历史排版任务数据。包括以下几类历史数据:①旧排版结果项详情:清理 Redis 中存储的旧排版结果项(FormatResultItem),这些数据以 format:item:{resultNodeId} 为键,包含排版任务的状态、文件名等信息;②用户与原文件的映射关系:清理 format:source:{userId}:{sourceNodeId} 键对应的 Set 数据,该 Set 存储了原文件的所有排版结果 resultNodeId 列表;③从 format:user:{userId} 键对应的 Set 中移除原文件的 sourceNodeId,表示该用户不再关联此原文件的排版记录。