外观
记录写入与状态维护
在已有报表、文件导出或资源上传流程中注入 DownloadCenterService,在任务开始时登记记录,在执行过程中写入进度,成功后保存下载地址,失败时保存失败状态。首次装配、版本管理和建表见快速开始。
1. 引入服务
已有下载中心 App 或管理端 Controller 时,不必重复声明依赖;只需要后台任务维护记录的应用可单独引入:
xml
<dependency>
<groupId>com.own.business</groupId>
<artifactId>springboot-business-download-center-business</artifactId>
</dependency>服务没有专用创建任务方法,使用继承自基础业务服务的 addByDto、addByPo、getPoById、updateByPo、updateByDto 等能力。它们操作数据库,不会提交文件生成任务,也不会自动绑定当前用户或工作空间。
2. 随任务维护记录
下面的类放入应用扫描范围,通过构造器注入服务。示例方法应由可信的应用任务编排调用;userId 从发起任务时的可信身份取得,id 从创建结果取得,不能直接信任客户端指定的任意记录 ID。
java
package com.example.export;
import com.own.business.download.center.business.service.DownloadCenterService;
import com.own.business.download.center.common.entity.dto.DownloadCenterDto;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class ExportDownloadRecords {
private final DownloadCenterService downloadCenterService;
public ExportDownloadRecords(DownloadCenterService downloadCenterService) {
this.downloadCenterService = downloadCenterService;
}
public Long start(Long userId, String fileName, String fileType) {
var dto = new DownloadCenterDto()
.setUserId(userId)
.setFileName(fileName)
.setFileType(fileType)
.setStatus(0)
.setProgress(0);
return downloadCenterService.addByDto(dto).getId();
}
public void progress(Long id, int percent) {
if (percent < 0 || percent > 100) {
throw new IllegalArgumentException("进度必须在 0 到 100 之间");
}
var po = downloadCenterService.getPoById(id);
po.setProgress(percent);
po.setModifiedTime(LocalDateTime.now());
downloadCenterService.updateByPo(po);
}
public void complete(Long id, String filePath, long fileSize) {
var po = downloadCenterService.getPoById(id);
po.setFilePath(filePath);
po.setFileSize(fileSize);
po.setStatus(1);
po.setProgress(100);
po.setModifiedTime(LocalDateTime.now());
downloadCenterService.updateByPo(po);
}
public void fail(Long id) {
var po = downloadCenterService.getPoById(id);
po.setStatus(2);
po.setModifiedTime(LocalDateTime.now());
downloadCenterService.updateByPo(po);
}
}调用顺序为 start → 应用生成并保存文件 → complete。生成过程中可调用 progress;捕获任务异常时调用 fail,并由原任务系统保存异常原因。上述代码只维护记录,实际生成器、异步执行和重试由应用提供。
任务开始时显式传递归属用户,异步线程不应假定存在请求线程的登录上下文。若需要审计信息,在应用中补充可信的操作人;addByDto 本身不会调用 po.init(loginUser)。使用数据库默认创建时间时,新增返回的内存 VO 不保证已经包含数据库生成字段,需要时重新查询。
3. 约定状态和字段
| 字段 | 语义及写入约定 |
|---|---|
fileName | 展示名称,数据库长度 255 |
fileType | 自定义业务类型或扩展名,数据库长度 50;应用统一取值,例如 csv、xlsx |
filePath | 可供调用方使用的下载地址,数据库长度 500;模块不验证地址、生成签名或检查文件存在 |
fileSize | Long,文件大小,单位字节 |
userId | Long,DTO 转 PO 时写入 fkUserId,数据库列为 fk_user_id |
status | 0 上传中、1 上传完成、2 上传失败;用于导出任务时可对应生成中、已完成、失败 |
progress | 整数百分比,应用建议限定为 0~100 |
SQL 中 status、progress 默认值均为 0,但 DTO 和 PO 没有 Java 字段默认值;新增时应像示例一样显式设置。当前模型没有专用必填、状态取值或进度范围校验,应用应检查用户归属、字段长度及文件大小等业务约束。
状态只是普通字段,没有自动转换规则。设置 status=1 不会自动设置 progress=100,失败也不会自动清空地址或进度。模块没有失败原因、过期时间、任务标识和重试次数字段,相关信息由原任务系统管理。
更新、并发与失败处理
示例通过 getPoById 读取完整记录,再修改需要的字段。updateByDto(id, dto) 会把 DTO 的全部业务字段赋给旧 PO,包括空值;实际 SQL 是否更新空列受 MyBatis-Plus 字段策略影响,因此不要将只填写 status 的 DTO 当成有保证的局部更新协议。
通用新增、更新方法不自动检查归属,getPoById 和 updateByPo 也不限定当前用户。需要向客户端暴露更新入口时,由应用先验证记录所属用户;现有 App Controller 仅公开查询与删除。
version 继承自基础 PO,并带 @Version;乐观锁行为依赖应用是否配置相应 MyBatis-Plus 拦截器。通用 updateByPo 不根据受影响行数抛出业务失败,不能仅凭返回 VO 判定并发更新成功。建议单个任务串行维护一条记录,需要严格状态转换时由应用实现条件更新及结果检查。
数据库写入与文件存储之间没有分布式事务。文件已保存但记录更新失败时,由应用安排补偿;异步任务应在创建记录的事务提交后再启动。记录被删除后,后续 getPoById 会查询失败,任务侧应约定停止、忽略或补偿行为。
文件清理和地址失效也由应用处理。删除下载记录不会触发资源删除 Hook;存储签名过期后,模块仍原样返回旧地址。界面查询和删除确认见查询与删除。
使用 Resource 资源上传时,上传结果是独立的 FileRecordVo,应用可根据 position 与 path 构造访问地址,再写入下载中心 filePath。私有对象需遵循私有文件访问流程。Resource 删除物理移除文件记录,下载中心删除采用逻辑删除;两种操作都不会清理实际文件,也不会相互同步。