跳转到正文

记录写入与状态维护

在已有报表、文件导出或资源上传流程中注入 DownloadCenterService,在任务开始时登记记录,在执行过程中写入进度,成功后保存下载地址,失败时保存失败状态。首次装配、版本管理和建表见快速开始

1. 引入服务

已有下载中心 App 或管理端 Controller 时,不必重复声明依赖;只需要后台任务维护记录的应用可单独引入:

xml
<dependency>
    <groupId>com.own.business</groupId>
    <artifactId>springboot-business-download-center-business</artifactId>
</dependency>

服务没有专用创建任务方法,使用继承自基础业务服务的 addByDtoaddByPogetPoByIdupdateByPoupdateByDto 等能力。它们操作数据库,不会提交文件生成任务,也不会自动绑定当前用户或工作空间。

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;应用统一取值,例如 csvxlsx
filePath可供调用方使用的下载地址,数据库长度 500;模块不验证地址、生成签名或检查文件存在
fileSizeLong,文件大小,单位字节
userIdLong,DTO 转 PO 时写入 fkUserId,数据库列为 fk_user_id
status0 上传中、1 上传完成、2 上传失败;用于导出任务时可对应生成中、已完成、失败
progress整数百分比,应用建议限定为 0100

SQL 中 statusprogress 默认值均为 0,但 DTO 和 PO 没有 Java 字段默认值;新增时应像示例一样显式设置。当前模型没有专用必填、状态取值或进度范围校验,应用应检查用户归属、字段长度及文件大小等业务约束。

状态只是普通字段,没有自动转换规则。设置 status=1 不会自动设置 progress=100,失败也不会自动清空地址或进度。模块没有失败原因、过期时间、任务标识和重试次数字段,相关信息由原任务系统管理。

更新、并发与失败处理

示例通过 getPoById 读取完整记录,再修改需要的字段。updateByDto(id, dto) 会把 DTO 的全部业务字段赋给旧 PO,包括空值;实际 SQL 是否更新空列受 MyBatis-Plus 字段策略影响,因此不要将只填写 status 的 DTO 当成有保证的局部更新协议。

通用新增、更新方法不自动检查归属,getPoByIdupdateByPo 也不限定当前用户。需要向客户端暴露更新入口时,由应用先验证记录所属用户;现有 App Controller 仅公开查询与删除。

version 继承自基础 PO,并带 @Version;乐观锁行为依赖应用是否配置相应 MyBatis-Plus 拦截器。通用 updateByPo 不根据受影响行数抛出业务失败,不能仅凭返回 VO 判定并发更新成功。建议单个任务串行维护一条记录,需要严格状态转换时由应用实现条件更新及结果检查。

数据库写入与文件存储之间没有分布式事务。文件已保存但记录更新失败时,由应用安排补偿;异步任务应在创建记录的事务提交后再启动。记录被删除后,后续 getPoById 会查询失败,任务侧应约定停止、忽略或补偿行为。

文件清理和地址失效也由应用处理。删除下载记录不会触发资源删除 Hook;存储签名过期后,模块仍原样返回旧地址。界面查询和删除确认见查询与删除

使用 Resource 资源上传时,上传结果是独立的 FileRecordVo,应用可根据 positionpath 构造访问地址,再写入下载中心 filePath。私有对象需遵循私有文件访问流程。Resource 删除物理移除文件记录,下载中心删除采用逻辑删除;两种操作都不会清理实际文件,也不会相互同步。

示例涉及的源码类型

DownloadCenterDto