484 lines
13 KiB
Markdown
484 lines
13 KiB
Markdown
|
|
# 项目异步文件上传功能 - 子计划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层核心实现
|