Files
ccdi/doc/plans/2026-03-05-async-file-upload-part1-database.md

484 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目异步文件上传功能 - 子计划1数据库和基础组件
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 创建文件上传功能的数据库表、实体类、Mapper接口和基础配置
**Architecture:** 使用 MyBatis Plus 进行数据持久化配置容量100的异步线程池
**Tech Stack:** MySQL 8.0, MyBatis Plus 3.5.10, Spring Boot 3.5.8
---
## Task 1: 数据库表创建
**Files:**
- Create: `sql/ccdi_file_upload_record.sql`
**Step 1: 创建SQL脚本文件**
创建文件 `sql/ccdi_file_upload_record.sql`:
```sql
-- 项目文件上传记录表
-- 用途:记录项目下所有文件的上传记录和处理状态
-- 作者:系统
-- 日期2026-03-05
USE ccdi;
-- 创建文件上传记录表
CREATE TABLE `ccdi_file_upload_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`project_id` bigint(20) NOT NULL COMMENT '项目ID',
`lsfx_project_id` int(11) DEFAULT NULL COMMENT '流水分析平台项目ID',
`log_id` int(11) DEFAULT NULL COMMENT '流水分析平台返回的logId',
`file_name` varchar(255) NOT NULL COMMENT '文件名称',
`file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(字节)',
`file_status` varchar(20) NOT NULL COMMENT '文件状态uploading-上传中parsing-解析中parsed_success-解析成功parsed_failed-解析失败',
`enterprise_names` text COMMENT '主体名称(多个用逗号分隔)',
`account_nos` text COMMENT '主体账号(多个用逗号分隔)',
`error_message` text COMMENT '错误信息(解析失败时记录)',
`upload_time` datetime NOT NULL COMMENT '上传时间',
`upload_user` varchar(64) NOT NULL COMMENT '上传人',
PRIMARY KEY (`id`),
KEY `idx_project_id` (`project_id`),
KEY `idx_log_id` (`log_id`),
KEY `idx_file_status` (`file_status`),
KEY `idx_upload_time` (`upload_time`),
KEY `idx_project_status` (`project_id`, `file_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目文件上传记录表';
```
**Step 2: 执行SQL脚本**
```bash
mysql -h 116.62.17.81 -u root -pKfcx@1234 ccdi < sql/ccdi_file_upload_record.sql
```
**Step 3: 验证表创建成功**
```bash
mysql -h 116.62.17.81 -u root -pKfcx@1234 ccdi -e "SHOW CREATE TABLE ccdi_file_upload_record\G"
```
Expected: 输出表结构,包含所有字段和索引
**Step 4: 提交SQL脚本**
```bash
git add sql/ccdi_file_upload_record.sql
git commit -m "feat: 添加文件上传记录表SQL脚本"
```
---
## Task 2: 实体类创建
**Files:**
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiFileUploadRecord.java`
**Step 1: 创建实体类**
创建文件 `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiFileUploadRecord.java`:
```java
package com.ruoyi.ccdi.project.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 文件上传记录实体
*
* @author ruoyi
* @date 2026-03-05
*/
@Data
@TableName("ccdi_file_upload_record")
public class CcdiFileUploadRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 主键ID */
@TableId(type = IdType.AUTO)
private Long id;
/** 项目ID */
private Long projectId;
/** 流水分析平台项目ID */
private Integer lsfxProjectId;
/** 流水分析平台返回的logId */
private Integer logId;
/** 文件名称 */
private String fileName;
/** 文件大小(字节) */
private Long fileSize;
/** 文件状态uploading-上传中parsing-解析中parsed_success-解析成功parsed_failed-解析失败 */
private String fileStatus;
/** 主体名称(多个用逗号分隔) */
private String enterpriseNames;
/** 主体账号(多个用逗号分隔) */
private String accountNos;
/** 错误信息(解析失败时记录) */
private String errorMessage;
/** 上传时间 */
private Date uploadTime;
/** 上传人 */
private String uploadUser;
}
```
**Step 2: 编译验证**
```bash
cd ccdi-project
mvn clean compile
```
Expected: BUILD SUCCESS
**Step 3: 提交**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiFileUploadRecord.java
git commit -m "feat: 添加文件上传记录实体类"
```
---
## Task 3: Mapper 接口和 XML
**Files:**
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiFileUploadRecordMapper.java`
- Create: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiFileUploadRecordMapper.xml`
**Step 1: 创建 Mapper 接口**
创建文件 `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiFileUploadRecordMapper.java`:
```java
package com.ruoyi.ccdi.project.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 文件上传记录 Mapper 接口
*
* @author ruoyi
* @date 2026-03-05
*/
@Mapper
public interface CcdiFileUploadRecordMapper extends BaseMapper<CcdiFileUploadRecord> {
/**
* 批量插入文件上传记录
*
* @param records 记录列表
* @return 插入条数
*/
int insertBatch(@Param("list") List<CcdiFileUploadRecord> records);
/**
* 统计各状态文件数量
*
* @param projectId 项目ID
* @return 统计结果Map形式key为状态value为数量
*/
List<java.util.Map<String, Object>> countByStatus(@Param("projectId") Long projectId);
}
```
**Step 2: 创建 Mapper XML**
创建文件 `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiFileUploadRecordMapper.xml`:
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper">
<resultMap type="com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord" id="CcdiFileUploadRecordResult">
<id property="id" column="id" />
<result property="projectId" column="project_id" />
<result property="lsfxProjectId" column="lsfx_project_id" />
<result property="logId" column="log_id" />
<result property="fileName" column="file_name" />
<result property="fileSize" column="file_size" />
<result property="fileStatus" column="file_status" />
<result property="enterpriseNames" column="enterprise_names" />
<result property="accountNos" column="account_nos" />
<result property="errorMessage" column="error_message" />
<result property="uploadTime" column="upload_time" />
<result property="uploadUser" column="upload_user" />
</resultMap>
<sql id="selectCcdiFileUploadRecordVo">
select id, project_id, lsfx_project_id, log_id, file_name, file_size,
file_status, enterprise_names, account_nos, error_message,
upload_time, upload_user
from ccdi_file_upload_record
</sql>
<!-- 批量插入 -->
<insert id="insertBatch" parameterType="java.util.List">
insert into ccdi_file_upload_record (
project_id, lsfx_project_id, file_name, file_size, file_status,
upload_time, upload_user
) values
<foreach collection="list" item="item" separator=",">
(
#{item.projectId}, #{item.lsfxProjectId}, #{item.fileName},
#{item.fileSize}, #{item.fileStatus}, #{item.uploadTime},
#{item.uploadUser}
)
</foreach>
</insert>
<!-- 统计各状态文件数量 -->
<select id="countByStatus" resultType="java.util.Map">
select file_status as `status`, count(*) as count
from ccdi_file_upload_record
where project_id = #{projectId}
group by file_status
</select>
</mapper>
```
**Step 3: 编译验证**
```bash
cd ccdi-project
mvn clean compile
```
Expected: BUILD SUCCESS
**Step 4: 提交**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiFileUploadRecordMapper.java
git add ccdi-project/src/main/resources/mapper/ccdi/project/CcdiFileUploadRecordMapper.xml
git commit -m "feat: 添加文件上传记录Mapper接口和XML映射"
```
---
## Task 4: DTO 和 VO 类
**Files:**
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiFileUploadQueryDTO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiFileUploadStatisticsVO.java`
**Step 1: 创建查询 DTO**
创建文件 `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiFileUploadQueryDTO.java`:
```java
package com.ruoyi.ccdi.project.domain.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 文件上传记录查询 DTO
*
* @author ruoyi
* @date 2026-03-05
*/
@Data
public class CcdiFileUploadQueryDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 项目ID */
private Long projectId;
/** 文件状态 */
private String fileStatus;
/** 文件名称(模糊查询) */
private String fileName;
/** 上传人 */
private String uploadUser;
}
```
**Step 2: 创建统计 VO**
创建文件 `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiFileUploadStatisticsVO.java`:
```java
package com.ruoyi.ccdi.project.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 文件上传统计 VO
*
* @author ruoyi
* @date 2026-03-05
*/
@Data
public class CcdiFileUploadStatisticsVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 上传中数量 */
private Long uploading;
/** 解析中数量 */
private Long parsing;
/** 解析成功数量 */
private Long parsedSuccess;
/** 解析失败数量 */
private Long parsedFailed;
/** 总数量 */
private Long total;
}
```
**Step 3: 编译验证**
```bash
cd ccdi-project
mvn clean compile
```
Expected: BUILD SUCCESS
**Step 4: 提交**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiFileUploadQueryDTO.java
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiFileUploadStatisticsVO.java
git commit -m "feat: 添加文件上传查询DTO和统计VO"
```
---
## Task 5: 线程池配置
**Files:**
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/config/AsyncThreadPoolConfig.java`
**Step 1: 创建线程池配置类**
创建文件 `ccdi-project/src/main/java/com/ruoyi/ccdi/project/config/AsyncThreadPoolConfig.java`:
```java
package com.ruoyi.ccdi.project.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 异步线程池配置
*
* @author ruoyi
* @date 2026-03-05
*/
@Configuration
@EnableAsync
public class AsyncThreadPoolConfig {
/**
* 文件上传专用线程池
* 容量100个线程
* 拒绝策略AbortPolicy直接拒绝由调度线程捕获并重试
*/
@Bean("fileUploadExecutor")
public Executor fileUploadExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(100);
// 最大线程数
executor.setMaxPoolSize(100);
// 队列容量设为0不使用队列直接走拒绝策略
executor.setQueueCapacity(0);
// 线程名称前缀
executor.setThreadNamePrefix("file-upload-");
// 拒绝策略AbortPolicy抛出 RejectedExecutionException
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// 线程空闲时间(秒)
executor.setKeepAliveSeconds(60);
// 等待所有任务完成后再关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
// 最长等待时间
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
```
**Step 2: 编译验证**
```bash
cd ccdi-project
mvn clean compile
```
Expected: BUILD SUCCESS
**Step 3: 提交**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/config/AsyncThreadPoolConfig.java
git commit -m "feat: 添加异步线程池配置"
```
---
## 子计划1完成检查清单
- [ ] 数据库表创建成功
- [ ] 实体类编译通过
- [ ] Mapper接口和XML映射正确
- [ ] DTO和VO类创建完成
- [ ] 线程池配置完成
- [ ] 所有代码已提交到git
**下一步:** 执行子计划2 - Service层核心实现