Compare commits

...

7 Commits

121 changed files with 1223 additions and 153 deletions

232
AGENTS.md
View File

@@ -2,22 +2,44 @@
## 项目概述
基于若依 v3.9.1 的纪检初核系统,Java 21 + Spring Boot 3 + Vue 2
本仓库是纪检初核系统主仓库,基于若依 `v3.9.1`,当前技术栈以 `Java 21 + Spring Boot 3 + Vue 2` 为主,并包含独立的流水分析 Mock 服务、Docker 部署文件、SQL 脚本、实施文档与测试文档。
仓库同时承载以下内容:
- Java 多模块后端工程
- `ruoyi-ui` 前端工程
- `lsfx-mock-server` Python FastAPI Mock 服务
- `docs/` 正式文档目录
- `assets/` 历史设计、测试、接口和实施材料
- `sql/` 初始化与增量脚本
---
## Build / Lint / Test Commands
## 协作约定
- 使用简体中文进行思考和对话
- Git 提交说明使用中文
- 根据设计文档产出实施计划时,默认输出两份文档:
- 后端实施计划放 `docs/plans/backend/`
- 前端实施计划放 `docs/plans/frontend/`
- 前端开发直接在当前分支进行,不需要额外创建 git worktree
- 测试结束后,自动关闭测试过程中启动的前后端进程
- 遇到 MCP 数据库操作时,使用项目配置文件中的数据库连接信息
---
## Build / Run / Test Commands
### 后端 (Maven)
```bash
# 编译项目
# 根目录编译全部 Java 模块
mvn clean compile
# 运行应用
mvn spring-boot:run
# 启动主应用
mvn -pl ruoyi-admin spring-boot:run
# 打包部署
# 打包全部模块
mvn clean package
# 运行单个测试类
@@ -26,7 +48,7 @@ mvn test -Dtest=ClassName
# 运行单个测试方法
mvn test -Dtest=ClassName#methodName
# 跳过测试
# 跳过测试打包
mvn clean package -DskipTests
```
@@ -38,21 +60,45 @@ cd ruoyi-ui
# 安装依赖
npm install --registry=https://registry.npmmirror.com
# 开发服务器
# 本地开发
npm run dev
# 生产构建
npm run build:prod
# 预览构建结果
npm run preview
```
### API 测试
### 流水分析 Mock 服务 (Python / FastAPI)
```bash
# 获取 Token (测试账号: admin/admin123)
POST http://localhost:8080/login/test?username=admin&password=admin123
cd lsfx-mock-server
# Swagger 文档
http://localhost:8080/swagger-ui/index.html
# 安装依赖
pip install -r requirements.txt
# 启动服务
python main.py
# 热重载启动
uvicorn main:app --reload --host 0.0.0.0 --port 8000
# 运行测试
pytest tests/ -v
```
### API 调试
```bash
# 测试登录(默认测试账号)
POST http://localhost:62318/login/test?username=admin&password=admin123
# 主系统 Swagger
http://localhost:62318/swagger-ui.html
# 流水分析 Mock Swagger
http://localhost:8000/docs
```
---
@@ -61,10 +107,10 @@ http://localhost:8080/swagger-ui/index.html
### Java 代码风格
- **注解**: 使用 Lombok `@Data` 简化实体类
- **依赖注入**: 使用 `@Resource` 而非 `@Autowired`
- **实体类**: 不继承 BaseEntity,单独添加审计字段
- **禁止**: 禁止使用全限定类名 (如 `java.util.List`),必须 import
- 实体类优先使用 Lombok `@Data`
- 依赖注入使用 `@Resource`,不要使用 `@Autowired`
- 实体类不继承 `BaseEntity`,审计字段单独声明
- 禁止使用全限定类名,必须通过 `import` 引入
```java
@Data
@@ -85,10 +131,13 @@ private ICcdiBaseStaffService baseStaffService;
### 分层规范
- **Controller**: 添加 Swagger 注释,分页使用 MyBatis Plus Page
- **Service**: 简单 CRUD 用 MyBatis Plus复杂操作在 XML 写 SQL
- **DTO/VO**: 接口传参用独立 DTO返回用独立 VO禁止与 entity 混用
- **禁止**: 禁止 `extends ServiceImpl<>`
- `Controller` 添加 Swagger 注释
- 分页查询优先使用 MyBatis Plus `Page`
- 简单 CRUD 优先使用 MyBatis Plus
- 复杂查询或批量逻辑放到 XML SQL
- 接口入参使用独立 DTO返回使用独立 VO
- 禁止 DTO、VO 与 entity 混用
- 禁止 `extends ServiceImpl<>`
### API 响应格式
@@ -107,43 +156,87 @@ return AjaxResult.success(result);
### 数据库规范
- 表名: `ccdi_` 前缀 (如 `ccdi_base_staff`)
- 非业务字段 (create_by, create_time 等) 由后端自动处理,前端表单不显示
- 业务表统一使用 `ccdi_` 前缀
- 非业务字段`create_by``create_time` 由后端自动维护
- 前端表单不要暴露通用审计字段
- 新增菜单、字典、初始化数据时,同步补充 SQL 脚本
### 前端规范
- **目录结构**: `views/` 按功能模块组织,`api/` 对应后端 Controller
- **API 调用**: 使用 `@/utils/request` 封装
- **菜单联动**: 添加页面后需同步修改数据库 `sys_menu`
- 页面放在 `ruoyi-ui/src/views/` 下,按业务域组织
- API 文件放在 `ruoyi-ui/src/api/` 下,与后端 Controller 对应
- 请求统一使用 `@/utils/request`
- 新增页面或功能入口时,同步检查 `sys_menu`、路由、权限标识
- 优先延续现有 `ccdi*` 业务目录与命名方式,不随意新造平行目录
### 导入功能规范
- 批量操作提高性能
- 返回结果展示失败数据,不展示成功数据
- 使用 EasyExcel + 异步处理大数据量导入
- 大批量导入优先考虑批量写入
- 返回结果展示失败数据
- 大数据量导入优先采用 EasyExcel + 异步处理
---
## 模块架
## 当前仓库结
```
```text
ccdi/
├── ruoyi-admin/ # 启动入口
├── ruoyi-framework/ # 安全配置
├── ruoyi-system/ # 系统模块
├── ruoyi-common/ # 通用工具
├── ccdi-info-collection/ # 信息采集 (员工、中介、黑名单)
├── ccdi-project/ # 项目管理
├── ccdi-lsfx/ # 流水分析对接
── ruoyi-ui/ # 前端
├── ruoyi-admin/ # Spring Boot 启动入口与 Web 层装配
├── ruoyi-framework/ # 安全、权限、配置等基础框架
├── ruoyi-system/ # 系统管理模块
├── ruoyi-common/ # 通用组件、工具类、基础能力
├── ruoyi-quartz/ # 定时任务模块
├── ruoyi-generator/ # 代码生成模块
├── ccdi-info-collection/ # 信息采集模块(员工、中介、黑名单等)
── ccdi-project/ # 项目管理与项目业务模块
├── ccdi-lsfx/ # 流水分析对接模块
├── ruoyi-ui/ # Vue 2 前端
├── lsfx-mock-server/ # 流水分析平台 Mock 服务FastAPI
├── docs/ # 正式设计、计划、测试、报告
├── assets/ # 历史设计稿、接口材料、测试资料、实施材料
├── sql/ # 建表、菜单、字典、修复、迁移脚本
├── docker/ # 后端、前端、Mock 服务容器文件
├── deploy/ # 部署相关文件
├── bin/ # 仓库级辅助脚本目录
├── scripts/ # 预留脚本目录(当前基本为空)
├── logs/ # 运行日志输出目录
└── .worktrees/ # 本地工作树目录
```
### 添加新模块
### Maven 模块
1. 根 pom.xml 添加 `<module>`
2. pom.xml 添加 `ruoyi-common` 依赖
3. `ruoyi-admin/pom.xml` 添加模块依赖
4. 按分层创建 controller/service/mapper/domain 包
`pom.xml` 当前包含以下 Java 模块:
1. `ruoyi-admin`
2. `ruoyi-framework`
3. `ruoyi-system`
4. `ruoyi-quartz`
5. `ruoyi-generator`
6. `ruoyi-common`
7. `ccdi-info-collection`
8. `ccdi-project`
9. `ccdi-lsfx`
### 主要业务代码分布
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/`
-`controller``domain``mapper``service``annotation``validation` 等目录
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/`
-`config``controller``domain``mapper``service`
- `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/`
-`client``config``constants``controller``domain/request``domain/response`
- `ruoyi-ui/src/views/`
- 当前包含 `ccdi``ccdiBaseStaff``ccdiProject``ccdiPurchaseTransaction``ccdiIntermediary`、亲属关系、员工调动、招聘等业务页面
- `ruoyi-ui/src/api/ccdi/`
- 放置纪检初核业务 API 封装
### 添加新后端模块时
1. 在根 `pom.xml` 增加 `<module>`
2. 在新模块 `pom.xml` 中声明公共依赖,如 `ruoyi-common`
3.`ruoyi-admin/pom.xml` 中引入该业务模块
4.`controller/service/mapper/domain` 分层创建代码
5. 补充对应 SQL、菜单、权限和前端 API/页面
---
@@ -154,12 +247,55 @@ ccdi/
| 应用入口 | `ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java` |
| 信息采集 Controller | `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/` |
| 项目管理 Controller | `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/` |
| 前端 API | `ruoyi-ui/src/api/` |
| 流水分析对接 Controller | `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/` |
| 前端业务 API | `ruoyi-ui/src/api/ccdi/` |
| 前端页面 | `ruoyi-ui/src/views/` |
| Vue 路由 | `ruoyi-ui/src/router/index.js` |
| Vuex | `ruoyi-ui/src/store/` |
| Mock 服务入口 | `lsfx-mock-server/main.py` |
| Mock 路由 | `lsfx-mock-server/routers/api.py` |
| Docker 文件 | `docker/` |
| SQL 脚本 | `sql/` |
---
## 沟通规范
## 文档与资产规范
- 使用简体中文进行思考和对话
- 遇到 MCP 数据库操作时,使用项目配置文件中的数据库
### 正式文档目录
- `docs/design/`:设计文档与设计附属文件
- `docs/plans/backend/`:后端实施计划
- `docs/plans/frontend/`:前端实施计划
- `docs/plans/fullstack/`:综合实施计划、联调计划、通用实施文档
- `docs/plans/misc/`:其他计划类文档
- `docs/tests/plans/`:测试计划
- `docs/tests/records/`:测试记录
- `docs/tests/scripts/`:测试脚本与脚本说明
- `docs/reports/implementation/`:实施报告
- `docs/reports/optimization/`:优化记录
- `docs/reports/code-review/`:代码评审报告
### 历史资料目录
- `assets/design/`:历史设计材料
- `assets/interface-doc/``assets/api-docs/`:接口资料
- `assets/database/``assets/database-docs/`:数据库相关资料
- `assets/test-reports/``assets/test-scripts/``assets/测试文档/`:测试资料
- `assets/plans/``assets/implementation/``assets/实施文档/`:历史实施材料
### 文档维护要求
- 新增文档优先落到 `docs/` 下的规范目录,不要继续堆放到 `docs/plans/` 根目录
- 只有历史资料或外部原始材料才放入 `assets/`
- 如果移动了文档,需同步修正文档内引用路径
- 若需求来自设计文档,默认同时沉淀后端与前端两份实施计划
---
## 开发与测试提醒
- `target/``node_modules/`、运行日志等目录默认视为构建产物,阅读时注意与源码区分
- `lsfx-mock-server` 是独立子项目,除非任务涉及流水分析联调,否则不要把其实现规则混入主 Java 工程
- `docker/backend``docker/frontend``docker/mock` 分别对应三类运行时镜像
- `sql/migration/` 用于增量迁移脚本,新增修复脚本优先按日期或功能命名
- 启动前后端或 Mock 服务做验证后,结束测试时要主动停止进程,避免残留占用端口

View File

@@ -502,4 +502,4 @@ head -20 doc/database/backup/ccdi_structure.sql
- 表结构文件: `doc/database/backup/ccdi_structure.sql`
- 数据文件: `doc/database/backup/ccdi_data.sql`
- 排序规则修改脚本: `doc/database/alter_collation_to_general_ci.sql`
- 设计文档: `docs/plans/2026-02-28-database-migration-design.md`
- 设计文档: `docs/design/2026-02-28-database-migration-design.md`

View File

@@ -266,7 +266,7 @@ d122e52 config(lsfx): 删除接口5、6配置更新接口7路径
## 参考资料
- **新版接口文档**: `doc/对接流水分析/兰溪-流水分析对接-新版.md`
- **实施计划**: `docs/plans/2026-03-02-lsfx-update-plan.md`
- **实施计划**: `docs/plans/fullstack/2026-03-02-lsfx-update-plan.md`
- **项目规范**: `CLAUDE.md`
---

View File

@@ -8,6 +8,7 @@ import com.ruoyi.common.utils.SecurityUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.RestController;
* 项目流水标签控制器
*/
@Tag(name = "项目流水标签")
@Slf4j
@RestController
@RequestMapping("/ccdi/project/tags")
public class CcdiBankTagController extends BaseController {
@@ -32,6 +34,8 @@ public class CcdiBankTagController extends BaseController {
@PostMapping("/rebuild")
public AjaxResult rebuild(@Validated @RequestBody CcdiBankTagRebuildDTO dto) {
String operator = SecurityUtils.getUsername();
log.info("【流水标签】收到手动重算请求: projectId={}, modelCode={}, operator={}",
dto.getProjectId(), dto.getModelCode(), operator);
return success(bankTagService.submitRebuild(dto, operator));
}
}

View File

@@ -14,10 +14,13 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
/**
* 流水标签规则执行参数解析器
*/
@Slf4j
@Component
public class BankTagRuleConfigResolver {
@@ -61,11 +64,23 @@ public class BankTagRuleConfigResolver {
Map<String, String> thresholdValues = new LinkedHashMap<>();
Set<String> requiredParamCodes = RULE_PARAM_MAPPING.getOrDefault(ruleMeta.getRuleCode(), Set.of());
log.info("【流水标签】解析规则参数: projectId={}, effectiveProjectId={}, ruleCode={}, requiredParams={}",
projectId, effectiveProjectId, ruleMeta.getRuleCode(), requiredParamCodes);
for (CcdiModelParam param : params) {
if (requiredParamCodes.contains(param.getParamCode())) {
thresholdValues.put(param.getParamCode(), param.getParamValue());
}
}
log.debug("【流水标签】规则参数解析结果: projectId={}, ruleCode={}, thresholdValues={}",
projectId, ruleMeta.getRuleCode(), thresholdValues);
Set<String> missingParamCodes = requiredParamCodes.stream()
.filter(code -> !thresholdValues.containsKey(code))
.collect(Collectors.toSet());
if (!missingParamCodes.isEmpty()) {
log.warn("【流水标签】规则参数缺失: projectId={}, ruleCode={}, missingParams={}",
projectId, ruleMeta.getRuleCode(), missingParamCodes);
}
BankTagRuleExecutionConfig config = new BankTagRuleExecutionConfig();
config.setProjectId(projectId);

View File

@@ -24,10 +24,12 @@ import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import lombok.extern.slf4j.Slf4j;
/**
* 流水标签服务实现
*/
@Slf4j
@Service
public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
@@ -85,16 +87,23 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
* @param triggerType 触发方式
*/
public Long rebuildProject(Long projectId, String modelCode, String operator, TriggerType triggerType) {
long taskStartTime = System.currentTimeMillis();
CcdiBankTagTask task = buildRunningTask(projectId, modelCode, operator, triggerType);
taskMapper.insertTask(task);
log.info("【流水标签】任务创建成功: taskId={}, projectId={}, modelCode={}, triggerType={}, operator={}",
task.getId(), projectId, modelCode, triggerType, operator);
try {
List<CcdiBankTagRule> rules = ruleMapper.selectEnabledRules(modelCode);
log.info("【流水标签】加载启用规则完成: taskId={}, projectId={}, modelCode={}, ruleCount={}",
task.getId(), projectId, modelCode, rules.size());
log.info("【流水标签】开始清理历史结果: taskId={}, projectId={}, modelCode={}",
task.getId(), projectId, modelCode);
resultMapper.deleteByProjectAndModel(projectId, modelCode);
List<CompletableFuture<List<CcdiBankTagResult>>> futures = rules.stream()
.map(rule -> CompletableFuture.supplyAsync(
() -> executeRule(projectId, rule, operator),
() -> executeRule(task.getId(), projectId, rule, operator),
tagRuleExecutor
))
.toList();
@@ -105,6 +114,8 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
.toList();
if (!allResults.isEmpty()) {
log.info("【流水标签】批量写入标签结果: taskId={}, projectId={}, resultCount={}",
task.getId(), projectId, allResults.size());
resultMapper.insertBatch(allResults);
}
@@ -117,6 +128,9 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
task.setUpdateBy(operator);
task.setUpdateTime(new Date());
taskMapper.updateTask(task);
log.info("【流水标签】任务执行成功: taskId={}, projectId={}, modelCode={}, triggerType={}, ruleCount={}, hitCount={}, costMs={}",
task.getId(), projectId, modelCode, triggerType, rules.size(), allResults.size(),
System.currentTimeMillis() - taskStartTime);
return task.getId();
} catch (Exception ex) {
task.setStatus(STATUS_FAILED);
@@ -126,6 +140,8 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
task.setUpdateBy(operator);
task.setUpdateTime(new Date());
taskMapper.updateTask(task);
log.error("【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}",
task.getId(), projectId, modelCode, triggerType, ex.getMessage(), ex);
throw ex;
}
}
@@ -149,15 +165,35 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
return task;
}
private List<CcdiBankTagResult> executeRule(Long projectId, CcdiBankTagRule rule, String operator) {
private List<CcdiBankTagResult> executeRule(Long taskId, Long projectId, CcdiBankTagRule rule, String operator) {
long startTime = System.currentTimeMillis();
log.info("【流水标签】规则开始执行: taskId={}, projectId={}, ruleCode={}, resultType={}",
taskId, projectId, rule.getRuleCode(), rule.getResultType());
BankTagRuleExecutionConfig config = configResolver.resolve(projectId, rule);
log.debug("【流水标签】规则执行参数: taskId={}, ruleCode={}, thresholds={}",
taskId, rule.getRuleCode(), config.getThresholdValues());
if (RESULT_TYPE_STATEMENT.equals(rule.getResultType())) {
List<BankTagStatementHitVO> hits = executeStatementRule(projectId, rule, config);
return buildStatementResults(projectId, rule, hits, operator);
List<CcdiBankTagResult> results = buildStatementResults(projectId, rule, hits, operator);
logRuleCompletion(taskId, projectId, rule.getRuleCode(), results.size(), startTime);
return results;
}
List<BankTagObjectHitVO> hits = executeObjectRule(projectId, rule, config);
return buildObjectResults(projectId, rule, hits, operator);
List<CcdiBankTagResult> results = buildObjectResults(projectId, rule, hits, operator);
logRuleCompletion(taskId, projectId, rule.getRuleCode(), results.size(), startTime);
return results;
}
private void logRuleCompletion(Long taskId, Long projectId, String ruleCode, int hitCount, long startTime) {
long costMs = System.currentTimeMillis() - startTime;
if (hitCount == 0) {
log.warn("【流水标签】规则无命中: taskId={}, projectId={}, ruleCode={}, costMs={}",
taskId, projectId, ruleCode, costMs);
return;
}
log.info("【流水标签】规则执行完成: taskId={}, projectId={}, ruleCode={}, hitCount={}, costMs={}",
taskId, projectId, ruleCode, hitCount, costMs);
}
private List<BankTagStatementHitVO> executeStatementRule(Long projectId,

View File

@@ -679,7 +679,11 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
}
private void handleTagRebuildAfterBatchCompletion(Long projectId, TriggerType triggerType, Boolean anySuccess) {
log.info("【流水标签】批处理完成,准备触发自动重算: projectId={}, triggerType={}, anySuccess={}",
projectId, triggerType, anySuccess);
if (!Boolean.TRUE.equals(anySuccess)) {
log.warn("【流水标签】跳过自动重算: projectId={}, triggerType={}, reason=all_records_failed",
projectId, triggerType);
return;
}
bankTagService.submitAutoRebuild(projectId, triggerType);

View File

@@ -9,10 +9,12 @@ import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
/**
* 项目级流水标签重算协调器
*/
@Slf4j
@Component
public class ProjectBankTagRebuildCoordinator {
@@ -33,7 +35,11 @@ public class ProjectBankTagRebuildCoordinator {
* @param operator 操作人
*/
public void submitManual(Long projectId, String modelCode, String operator) {
log.info("【流水标签】手动重算开始排队: projectId={}, modelCode={}, operator={}",
projectId, modelCode, operator);
if (isProjectRunning(projectId)) {
log.warn("【流水标签】项目已有运行中任务,拒绝手动重算: projectId={}, modelCode={}, operator={}",
projectId, modelCode, operator);
throw new ServiceException("当前项目标签正在重算中,请稍后再试");
}
@@ -49,6 +55,8 @@ public class ProjectBankTagRebuildCoordinator {
public void submitAuto(Long projectId, TriggerType triggerType) {
CcdiBankTagTask runningTask = taskMapper.selectRunningTaskByProjectId(projectId);
if (runningTask != null || runningProjects.containsKey(projectId)) {
log.warn("【流水标签】项目正在重算,已标记完成后补跑: projectId={}, runningTaskId={}, triggerType={}",
projectId, runningTask != null ? runningTask.getId() : null, triggerType);
markNeedRerun(runningTask);
return;
}
@@ -64,12 +72,15 @@ public class ProjectBankTagRebuildCoordinator {
private void executeWithLock(Long projectId, Runnable action) {
if (runningProjects.putIfAbsent(projectId, Boolean.TRUE) != null) {
log.warn("【流水标签】项目已有运行中任务,拒绝获取项目锁: projectId={}", projectId);
throw new ServiceException("当前项目标签正在重算中,请稍后再试");
}
log.info("【流水标签】获取项目重算锁成功: projectId={}", projectId);
try {
action.run();
} finally {
runningProjects.remove(projectId);
log.info("【流水标签】释放项目重算锁: projectId={}", projectId);
}
}

View File

@@ -1,5 +1,8 @@
package com.ruoyi.ccdi.project.controller;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.ruoyi.ccdi.project.domain.dto.CcdiBankTagRebuildDTO;
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
import com.ruoyi.common.core.domain.AjaxResult;
@@ -10,8 +13,10 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.LoggerFactory;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,4 +47,32 @@ class CcdiBankTagControllerTest {
verify(bankTagService).submitRebuild(dto, "admin");
}
}
@Test
void rebuild_shouldLogManualRebuildRequest() {
CcdiBankTagRebuildDTO dto = new CcdiBankTagRebuildDTO();
dto.setProjectId(40L);
dto.setModelCode("LARGE_TRANSACTION");
Logger logger = (Logger) LoggerFactory.getLogger(CcdiBankTagController.class);
ListAppender<ILoggingEvent> appender = new ListAppender<>();
appender.start();
logger.addAppender(appender);
when(bankTagService.submitRebuild(dto, "admin")).thenReturn("标签重算任务已提交");
try (MockedStatic<SecurityUtils> mocked = mockStatic(SecurityUtils.class)) {
mocked.when(SecurityUtils::getUsername).thenReturn("admin");
controller.rebuild(dto);
assertTrue(appender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("收到手动重算请求")
&& message.contains("projectId=40")
&& message.contains("modelCode=LARGE_TRANSACTION")
&& message.contains("operator=admin")));
} finally {
logger.detachAppender(appender);
}
}
}

View File

@@ -1,5 +1,8 @@
package com.ruoyi.ccdi.project.service.impl;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.ruoyi.ccdi.project.domain.CcdiModelParam;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule;
@@ -11,10 +14,12 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.LoggerFactory;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@@ -49,6 +54,42 @@ class BankTagRuleConfigResolverTest {
assertEquals("1111", config.getThresholdValue("SINGLE_TRANSACTION_AMOUNT"));
}
@Test
void resolve_shouldLogThresholdSourceAndMissingParams() {
CcdiProject project = new CcdiProject();
project.setProjectId(40L);
project.setConfigType("default");
when(projectMapper.selectById(40L)).thenReturn(project);
when(modelParamMapper.selectByProjectAndModel(0L, "LARGE_TRANSACTION")).thenReturn(List.of(
buildParam("LARGE_CASH_DEPOSIT", "50000")
));
CcdiBankTagRule ruleMeta = new CcdiBankTagRule();
ruleMeta.setModelCode("LARGE_TRANSACTION");
ruleMeta.setRuleCode("FREQUENT_CASH_DEPOSIT");
Logger logger = (Logger) LoggerFactory.getLogger(BankTagRuleConfigResolver.class);
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
logAppender.start();
logger.addAppender(logAppender);
try {
resolver.resolve(40L, ruleMeta);
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("解析规则参数")
&& message.contains("projectId=40")
&& message.contains("effectiveProjectId=0")
&& message.contains("FREQUENT_CASH_DEPOSIT")));
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("规则参数缺失")
&& message.contains("projectId=40")
&& message.contains("FREQUENT_CASH_DEPOSIT")));
} finally {
logger.detachAppender(logAppender);
}
}
private CcdiModelParam buildParam(String paramCode, String paramValue) {
CcdiModelParam param = new CcdiModelParam();
param.setProjectId(0L);

View File

@@ -1,5 +1,8 @@
package com.ruoyi.ccdi.project.service.impl;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
@@ -15,13 +18,17 @@ import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.LoggerFactory;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import java.util.concurrent.Executor;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -81,4 +88,91 @@ class CcdiBankTagServiceImplTest {
inOrder.verify(resultMapper).insertBatch(anyList());
verify(taskMapper).insertTask(any(CcdiBankTagTask.class));
}
@Test
void rebuildProject_shouldLogTaskLifecycleAndRuleSummary() {
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
CcdiBankTagRule rule = new CcdiBankTagRule();
rule.setModelCode("LARGE_TRANSACTION");
rule.setModelName("大额交易");
rule.setRuleCode("HOUSE_OR_CAR_EXPENSE");
rule.setRuleName("房车消费支出交易");
rule.setResultType("STATEMENT");
BankTagRuleExecutionConfig config = new BankTagRuleExecutionConfig();
config.setProjectId(40L);
config.setRuleMeta(rule);
BankTagStatementHitVO hit = new BankTagStatementHitVO();
hit.setBankStatementId(10L);
hit.setGroupId(40);
hit.setLogId(40001);
hit.setReasonDetail("命中房车消费支出");
doAnswer(invocation -> {
CcdiBankTagTask task = invocation.getArgument(0);
task.setId(88L);
return 1;
}).when(taskMapper).insertTask(any(CcdiBankTagTask.class));
when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule));
when(configResolver.resolve(40L, rule)).thenReturn(config);
when(analysisMapper.selectHouseOrCarExpenseStatements(40L)).thenReturn(List.of(hit));
Logger logger = (Logger) LoggerFactory.getLogger(CcdiBankTagServiceImpl.class);
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
logAppender.start();
logger.addAppender(logAppender);
try {
service.rebuildProject(40L, null, "admin", TriggerType.MANUAL);
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("任务创建成功")
&& message.contains("taskId=88")
&& message.contains("projectId=40")));
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("规则执行完成")
&& message.contains("ruleCode=HOUSE_OR_CAR_EXPENSE")
&& message.contains("hitCount=1")));
} finally {
logger.detachAppender(logAppender);
}
}
@Test
void rebuildProject_shouldLogFailureSummaryWhenRuleExecutionFails() {
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
CcdiBankTagRule rule = new CcdiBankTagRule();
rule.setModelCode("LARGE_TRANSACTION");
rule.setRuleCode("HOUSE_OR_CAR_EXPENSE");
rule.setResultType("STATEMENT");
doAnswer(invocation -> {
CcdiBankTagTask task = invocation.getArgument(0);
task.setId(89L);
return 1;
}).when(taskMapper).insertTask(any(CcdiBankTagTask.class));
when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule));
when(configResolver.resolve(40L, rule)).thenThrow(new RuntimeException("threshold missing"));
Logger logger = (Logger) LoggerFactory.getLogger(CcdiBankTagServiceImpl.class);
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
logAppender.start();
logger.addAppender(logAppender);
try {
assertThrows(RuntimeException.class,
() -> service.rebuildProject(40L, null, "admin", TriggerType.MANUAL));
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("任务执行失败")
&& message.contains("taskId=89")
&& message.contains("threshold missing")));
} finally {
logger.detachAppender(logAppender);
}
}
}

View File

@@ -54,8 +54,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -167,6 +167,59 @@ class CcdiFileUploadServiceImplTest {
assertFalse(Files.exists(batchLogDir));
}
@Test
void handleTagRebuildAfterBatchCompletion_shouldLogSkipWhenAllRecordsFailed() {
Logger logger = (Logger) LoggerFactory.getLogger(CcdiFileUploadServiceImpl.class);
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
logAppender.start();
logger.addAppender(logAppender);
try {
ReflectionTestUtils.invokeMethod(
service,
"handleTagRebuildAfterBatchCompletion",
PROJECT_ID,
TriggerType.AUTO_BATCH_UPLOAD,
Boolean.FALSE
);
verify(bankTagService, never()).submitAutoRebuild(any(), any());
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("跳过自动重算")
&& message.contains("projectId=100")
&& message.contains("AUTO_BATCH_UPLOAD")));
} finally {
logger.detachAppender(logAppender);
}
}
@Test
void handleTagRebuildAfterBatchCompletion_shouldLogSubmissionWhenAnyRecordSucceeded() {
Logger logger = (Logger) LoggerFactory.getLogger(CcdiFileUploadServiceImpl.class);
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
logAppender.start();
logger.addAppender(logAppender);
try {
ReflectionTestUtils.invokeMethod(
service,
"handleTagRebuildAfterBatchCompletion",
PROJECT_ID,
TriggerType.AUTO_PULL_BANK_INFO,
Boolean.TRUE
);
verify(bankTagService).submitAutoRebuild(PROJECT_ID, TriggerType.AUTO_PULL_BANK_INFO);
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("准备触发自动重算")
&& message.contains("projectId=100")
&& message.contains("AUTO_PULL_BANK_INFO")
&& message.contains("anySuccess=true")));
} finally {
logger.detachAppender(logAppender);
}
}
@Test
void processFileAsync_shouldKeepParsingUntilBankStatementsSaved() throws IOException {
List<String> events = new ArrayList<>();

View File

@@ -1,5 +1,8 @@
package com.ruoyi.ccdi.project.service.impl;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
@@ -9,8 +12,10 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.LoggerFactory;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -54,4 +59,54 @@ class ProjectBankTagRebuildCoordinatorTest {
verify(taskMapper).updateTask(any(CcdiBankTagTask.class));
verify(bankTagService, never()).rebuildProject(40L, null, "system", TriggerType.AUTO_BATCH_UPLOAD);
}
@Test
void submitManual_shouldLogRejectWhenProjectAlreadyRunning() {
CcdiBankTagTask runningTask = new CcdiBankTagTask();
runningTask.setId(1L);
runningTask.setProjectId(40L);
runningTask.setStatus("RUNNING");
when(taskMapper.selectRunningTaskByProjectId(40L)).thenReturn(runningTask);
Logger logger = (Logger) LoggerFactory.getLogger(ProjectBankTagRebuildCoordinator.class);
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
logAppender.start();
logger.addAppender(logAppender);
try {
assertThrows(ServiceException.class, () -> coordinator.submitManual(40L, null, "admin"));
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("拒绝手动重算")
&& message.contains("projectId=40")
&& message.contains("operator=admin")));
} finally {
logger.detachAppender(logAppender);
}
}
@Test
void submitAuto_shouldLogNeedRerunWhenProjectAlreadyRunning() {
CcdiBankTagTask runningTask = new CcdiBankTagTask();
runningTask.setId(1L);
runningTask.setProjectId(40L);
runningTask.setStatus("RUNNING");
runningTask.setNeedRerun(0);
when(taskMapper.selectRunningTaskByProjectId(40L)).thenReturn(runningTask);
Logger logger = (Logger) LoggerFactory.getLogger(ProjectBankTagRebuildCoordinator.class);
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
logAppender.start();
logger.addAppender(logAppender);
try {
coordinator.submitAuto(40L, TriggerType.AUTO_BATCH_UPLOAD);
assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage)
.anyMatch(message -> message.contains("已标记完成后补跑")
&& message.contains("projectId=40")
&& message.contains("runningTaskId=1")
&& message.contains("AUTO_BATCH_UPLOAD")));
} finally {
logger.detachAppender(logAppender);
}
}
}

25
docs/README.md Normal file
View File

@@ -0,0 +1,25 @@
# docs 目录说明
本目录按文档类型整理,优先解决历史文档集中堆放在 `docs/plans/` 根目录的问题。
## 目录结构
- `design/`: 设计文档与设计附属文件
- `plans/backend/`: 后端实施计划
- `plans/frontend/`: 前端实施计划
- `plans/fullstack/`: 综合实施计划、集成计划与通用实施记录
- `plans/misc/`: 计划类杂项文档
- `tests/plans/`: 测试计划
- `tests/records/`: 测试记录
- `tests/scripts/`: 测试脚本与脚本说明
- `reports/implementation/`: 实施报告
- `reports/optimization/`: 优化记录
- `reports/code-review/`: 代码评审报告
## 归类规则
- 设计类文档统一放入 `design/`
- 明确标注前端或后端的实施计划分别放入 `plans/frontend/``plans/backend/`
- 不区分前后端的综合方案、集成计划和通用实施文档放入 `plans/fullstack/`
- 测试相关文档统一放入 `tests/`
- 报告与复盘类文档统一放入 `reports/`

View File

@@ -249,7 +249,7 @@ SHOW CREATE TABLE sys_user;
3. **表结构文件**: `doc/database/backup/ccdi_structure.sql`
4. **数据文件**: `doc/database/backup/ccdi_data.sql`
5. **操作指南**: `doc/database/backup/export_guide.md`
6. **设计文档**: `docs/plans/2026-02-28-database-migration-design.md`
6. **设计文档**: `docs/design/2026-02-28-database-migration-design.md`
## 时间估算

View File

@@ -145,11 +145,11 @@ public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
### 5. 测试脚本 ✅
**文件**:
- `docs/test-scripts/test-project-creation.sh` (Bash)
- `docs/test-scripts/test-project-creation.ps1` (PowerShell)
- `docs/test-scripts/test-project-creation.bat` (批处理)
- `docs/test-scripts/test-simple.sh` (简化版)
- `docs/test-scripts/README.md` (文档)
- `docs/tests/scripts/test-project-creation.sh` (Bash)
- `docs/tests/scripts/test-project-creation.ps1` (PowerShell)
- `docs/tests/scripts/test-project-creation.bat` (批处理)
- `docs/tests/scripts/test-simple.sh` (简化版)
- `docs/tests/scripts/README.md` (文档)
**Commit**: `206754a` - "test: 添加项目创建功能测试脚本和文档"

View File

@@ -839,7 +839,7 @@ public void saveParams(ModelParamSaveDTO saveDTO) {
**测试文件:**
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiModelParamServiceImplTest.java` - 单元测试
- `docs/test-scripts/test-param-config.sh` - 集成测试脚本
- `docs/tests/scripts/test-param-config.sh` - 集成测试脚本
### 8.2 参考文档

View File

@@ -265,7 +265,7 @@ ON DUPLICATE KEY UPDATE
本次实现后应同步更新以下文档,避免数据库说明继续失真:
- `assets/对接流水分析/ccdi_bank_statement.md`
- 如有必要,同步 `docs/plans/2026-03-04-bank-statement-entity-design.md` 中的表结构补充说明
- 如有必要,同步 `docs/design/2026-03-04-bank-statement-entity-design.md` 中的表结构补充说明
## 影响范围

View File

@@ -4,7 +4,7 @@
现有员工亲属关系维护页面 `http://localhost/maintain/staffFmyRelation` 已支持员工亲属关系的新增、编辑、删除、详情、导入导出,但尚不支持维护亲属名下资产信息。
当前仓库中已有员工资产维护设计文档 [2026-03-12-employee-asset-maintenance-design.md](/D:/ccdi/ccdi/docs/plans/2026-03-12-employee-asset-maintenance-design.md),其核心约束是通过 `family_id` 表示归属员工,通过 `person_id` 表示资产实际持有人。本次需求需要将该能力调整到“员工亲属关系维护页面”中,并明确仅维护亲属资产,不包含员工本人资产。
当前仓库中已有员工资产维护设计文档 [2026-03-12-employee-asset-maintenance-design.md](/D:/ccdi/ccdi/docs/design/2026-03-12-employee-asset-maintenance-design.md),其核心约束是通过 `family_id` 表示归属员工,通过 `person_id` 表示资产实际持有人。本次需求需要将该能力调整到“员工亲属关系维护页面”中,并明确仅维护亲属资产,不包含员工本人资产。
本次设计于 2026-03-12 确认以下业务口径:

View File

@@ -698,8 +698,8 @@ mvn spring-boot:run
**步骤 6: 提交测试记录**
```bash
mkdir -p docs/test-records
git add docs/test-records/
mkdir -p docs/tests/records
git add docs/tests/records/
git commit -m "test(ccdi-project): 记录后端接口测试结果"
```

View File

@@ -15,7 +15,7 @@
**Files:**
- Create: `sql/2026-03-12_ccdi_asset_info.sql`
- Review: `assets/资产信息表.csv`
- Review: `docs/plans/2026-03-12-employee-asset-maintenance-design.md`
- Review: `docs/design/2026-03-12-employee-asset-maintenance-design.md`
**Step 1: Write the SQL script**
@@ -40,7 +40,7 @@ Confirm the script:
**Step 3: Commit**
```bash
git add sql/2026-03-12_ccdi_asset_info.sql docs/plans/2026-03-12-employee-asset-maintenance-design.md
git add sql/2026-03-12_ccdi_asset_info.sql docs/design/2026-03-12-employee-asset-maintenance-design.md
git commit -m "新增员工资产信息设计与建表脚本"
```
@@ -349,6 +349,6 @@ Expected: compile succeeds without Java or mapper XML errors.
**Step 3: Commit**
```bash
git add sql/2026-03-12_ccdi_asset_info.sql ccdi-info-collection/src/main/java/com/ruoyi/info/collection ccdi-info-collection/src/main/resources/mapper/info/collection docs/plans/2026-03-12-employee-asset-maintenance-backend-implementation.md
git add sql/2026-03-12_ccdi_asset_info.sql ccdi-info-collection/src/main/java/com/ruoyi/info/collection ccdi-info-collection/src/main/resources/mapper/info/collection docs/plans/backend/2026-03-12-employee-asset-maintenance-backend-implementation.md
git commit -m "新增员工资产信息后端实施计划"
```

View File

@@ -13,7 +13,7 @@
### Task 1: Verify backend impact is zero
**Files:**
- Review: `docs/plans/2026-03-12-pull-bank-info-date-limit-design.md`
- Review: `docs/design/2026-03-12-pull-bank-info-date-limit-design.md`
- Review: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/`
- Review: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/`

View File

@@ -13,7 +13,7 @@
### Task 1: Verify backend impact is zero
**Files:**
- Review: `docs/plans/2026-03-12-pull-bank-info-upload-button-hit-area-design.md`
- Review: `docs/design/2026-03-12-pull-bank-info-upload-button-hit-area-design.md`
- Review: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/`
- Review: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/`

View File

@@ -14,7 +14,7 @@
**Files:**
- Create: `lsfx-mock-server/**`
- Modify: `docs/plans/2026-03-13-ccdi-docker-deployment-design.md`
- Modify: `docs/design/2026-03-13-ccdi-docker-deployment-design.md`
- Test: `lsfx-mock-server/tests/test_api.py`
**Step 1: 复制并清理运行文件**
@@ -110,7 +110,7 @@ Expected: Compose 文件能正常展开且无语法错误
### Task 5: 构建与联调验证
**Files:**
- Modify: `docs/plans/2026-03-13-ccdi-docker-deployment-design.md`
- Modify: `docs/design/2026-03-13-ccdi-docker-deployment-design.md`
**Step 1: 本地构建后端**
@@ -131,6 +131,6 @@ Expected: 无错误
**Step 4: 提交**
```bash
git add lsfx-mock-server docker docker-compose.yml .env.example deploy docs/plans/2026-03-13-ccdi-docker-deployment-*.md
git add lsfx-mock-server docker docker-compose.yml .env.example deploy docs/design/2026-03-13-ccdi-docker-deployment-design.md docs/plans/backend/2026-03-13-ccdi-docker-deployment-backend-implementation.md docs/plans/frontend/2026-03-13-ccdi-docker-deployment-frontend-implementation.md
git commit -m "新增Docker后端部署方案"
```

View File

@@ -214,7 +214,7 @@ git commit -m "实现员工资产导入归属匹配"
### Task 5: 执行回归验证
**Files:**
- Modify: `docs/plans/2026-03-13-employee-family-asset-import-split-design.md`
- Modify: `docs/design/2026-03-13-employee-family-asset-import-split-design.md`
**Step 1: 运行后端定向测试**
@@ -248,6 +248,6 @@ Expected:
**Step 4: 提交**
```bash
git add docs/plans/2026-03-13-employee-family-asset-import-split-design.md
git add docs/design/2026-03-13-employee-family-asset-import-split-design.md
git commit -m "完成资产导入拆分后端验证"
```

View File

@@ -31,7 +31,7 @@
**Step 4: Commit**
```bash
git add docs/plans/2026-03-16-large-transaction-project40-design.md assets/database/2026-03-16-project40-large-transaction-seed.sql
git add docs/design/2026-03-16-large-transaction-project40-design.md assets/database/2026-03-16-project40-large-transaction-seed.sql
git commit -m "文档: 补充项目40大额交易测试数据设计"
```
@@ -114,7 +114,7 @@ Expected: 每个指标返回至少 1 条命中记录或 1 个命中分组。
**Step 4: Commit**
```bash
git add assets/database/2026-03-16-project40-large-transaction-seed.sql docs/implementation-reports/2026-03-16-project40-large-transaction-report.md
git add assets/database/2026-03-16-project40-large-transaction-seed.sql docs/reports/implementation2026-03-16-project40-large-transaction-report.md
git commit -m "验证: 完成项目40大额交易测试流水校验"
```
@@ -144,6 +144,6 @@ Expected: `project_id=40` 存在稳定数量的测试流水。
**Step 4: Commit**
```bash
git add docs/implementation-reports/2026-03-16-project40-large-transaction-report.md
git add docs/reports/implementation2026-03-16-project40-large-transaction-report.md
git commit -m "文档: 完善项目40大额交易测试流水报告"
```

View File

@@ -214,7 +214,7 @@ git commit -m "refactor: 收敛模型参数服务对齐逻辑"
**Files:**
- Reference: `sql/ccdi_model_param.sql`
- Reference: `sql/2026-03-16-update-ccdi-model-param-defaults.sql`
- Optional Record: `docs/test-records/model-param-backend-alignment-test.md`
- Optional Record: `docs/tests/records/model-param-backend-alignment-test.md`
**Step 1: 准备校验项**
@@ -262,13 +262,13 @@ ORDER BY model_code, sort_order, id;
将验证过程写入:
```text
docs/test-records/model-param-backend-alignment-test.md
docs/tests/records/model-param-backend-alignment-test.md
```
**Step 5: 提交**
```bash
git add docs/test-records/model-param-backend-alignment-test.md
git add docs/tests/records/model-param-backend-alignment-test.md
git commit -m "test: 记录模型默认参数后端对齐验证"
```
@@ -348,10 +348,10 @@ POST /ccdi/modelParam/saveAll
测试结束后关闭 `mvn spring-boot:run` 启动的进程,再提交测试记录:
```bash
git add docs/test-records/model-param-backend-alignment-test.md
git add docs/tests/records/model-param-backend-alignment-test.md
git commit -m "test: 完成模型参数后端接口回归验证"
```
---
Plan complete and saved to `docs/plans/2026-03-16-model-param-csv-alignment-backend-implementation.md`.
Plan complete and saved to `docs/plans/backend/2026-03-16-model-param-csv-alignment-backend-implementation.md`.

View File

@@ -13,7 +13,7 @@
### Task 1: 确认需求边界
**Files:**
- Review: `docs/plans/2026-03-16-param-save-bar-fixed-bottom-design.md`
- Review: `docs/design/2026-03-16-param-save-bar-fixed-bottom-design.md`
- Review: `ccdi-project`
- Review: `ccdi-info-collection`
@@ -32,7 +32,7 @@
### Task 2: 回归验证清单
**Files:**
- Review: `docs/plans/2026-03-16-param-save-bar-fixed-bottom-design.md`
- Review: `docs/design/2026-03-16-param-save-bar-fixed-bottom-design.md`
**Step 1: 验证参数查询接口**

View File

@@ -688,7 +688,7 @@ git commit -m "feat: 接入拉取本行信息完成后的自动流水打标"
### Task 12: 完成全量验证
**Files:**
- Modify: `docs/plans/2026-03-16-project-bank-statement-tagging-backend-implementation.md`
- Modify: `docs/plans/backend/2026-03-16-project-bank-statement-tagging-backend-implementation.md`
**Step 1: Run focused backend tests**
@@ -721,7 +721,7 @@ Expected:
**Step 4: Commit**
```bash
git add ccdi-project sql docs/plans/2026-03-16-project-bank-statement-tagging-backend-implementation.md
git add ccdi-project sql docs/plans/backend/2026-03-16-project-bank-statement-tagging-backend-implementation.md
git commit -m "feat: 完成项目流水标签后端实现"
```

View File

@@ -428,9 +428,9 @@ npm run dev
**步骤 6: 提交测试记录**
```bash
mkdir -p docs/test-records
echo "## 全局配置页面测试结果\n\n测试时间$(date)\n\n- [x] 页面显示正确\n- [x] 修改功能正常\n- [x] 保存功能正常\n- [x] 错误处理正常" > docs/test-records/global-config-test.md
git add docs/test-records/
mkdir -p docs/tests/records
echo "## 全局配置页面测试结果\n\n测试时间$(date)\n\n- [x] 页面显示正确\n- [x] 修改功能正常\n- [x] 保存功能正常\n- [x] 错误处理正常" > docs/tests/records/global-config-test.md
git add docs/tests/records/
git commit -m "test(ui): 记录全局配置页面测试结果"
```
@@ -745,8 +745,8 @@ git commit -m "feat(ui): 重构项目内模型参数配置页面"
**步骤 7: 提交测试记录**
```bash
echo "## 项目配置页面测试结果\n\n测试时间$(date)\n\n- [x] 页面显示正确\n- [x] 使用默认配置项目测试通过\n- [x] 自定义配置项目测试通过\n- [x] 多模型修改测试通过" > docs/test-records/project-config-test.md
git add docs/test-records/
echo "## 项目配置页面测试结果\n\n测试时间$(date)\n\n- [x] 页面显示正确\n- [x] 使用默认配置项目测试通过\n- [x] 自定义配置项目测试通过\n- [x] 多模型修改测试通过" > docs/tests/records/project-config-test.md
git add docs/tests/records/
git commit -m "test(ui): 记录项目配置页面测试结果"
```
@@ -791,8 +791,8 @@ git commit -m "test(ui): 记录项目配置页面测试结果"
**步骤 5: 提交测试报告**
```bash
echo "## 端到端集成测试结果\n\n测试时间$(date)\n\n### 功能测试\n- [x] 全局配置影响项目配置\n- [x] 项目配置不影响全局配置\n- [x] 并发操作正常\n\n### 性能测试\n- [x] listAll接口响应时间 < 200ms\n- [x] saveAll接口响应时间 < 500ms\n\n### 结论\n前后端集成测试通过功能正常性能符合要求。" > docs/test-records/e2e-test.md
git add docs/test-records/
echo "## 端到端集成测试结果\n\n测试时间$(date)\n\n### 功能测试\n- [x] 全局配置影响项目配置\n- [x] 项目配置不影响全局配置\n- [x] 并发操作正常\n\n### 性能测试\n- [x] listAll接口响应时间 < 200ms\n- [x] saveAll接口响应时间 < 500ms\n\n### 结论\n前后端集成测试通过功能正常性能符合要求。" > docs/tests/records/e2e-test.md
git add docs/tests/records/
git commit -m "test(ui): 完成端到端集成测试"
```

View File

@@ -574,6 +574,6 @@ Expected: 本地员工信息维护页面可访问
**Step 6: 最终提交**
```bash
git add ruoyi-ui/src/api/ccdiBaseStaff.js ruoyi-ui/src/api/ccdiAssetInfo.js ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit docs/plans/2026-03-12-employee-asset-maintenance-frontend-implementation.md
git add ruoyi-ui/src/api/ccdiBaseStaff.js ruoyi-ui/src/api/ccdiAssetInfo.js ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit docs/plans/frontend/2026-03-12-employee-asset-maintenance-frontend-implementation.md
git commit -m "新增员工资产信息前端实施计划"
```

View File

@@ -15,7 +15,7 @@
**Files:**
- Create: `docker/frontend/Dockerfile`
- Create: `docker/frontend/nginx.conf`
- Modify: `docs/plans/2026-03-13-ccdi-docker-deployment-design.md`
- Modify: `docs/design/2026-03-13-ccdi-docker-deployment-design.md`
**Step 1: 创建前端镜像定义**
@@ -69,7 +69,7 @@ Expected: 前端服务、依赖与端口映射正确
### Task 4: 联调验证
**Files:**
- Modify: `docs/plans/2026-03-13-ccdi-docker-deployment-design.md`
- Modify: `docs/design/2026-03-13-ccdi-docker-deployment-design.md`
**Step 1: 检查前端生产产物**
@@ -84,6 +84,6 @@ Expected: 前端服务、依赖与端口映射正确
**Step 3: 提交**
```bash
git add docker/frontend deploy/deploy.ps1 docker-compose.yml .env.example docs/plans/2026-03-13-ccdi-docker-deployment-*.md
git add docker/frontend deploy/deploy.ps1 docker-compose.yml .env.example docs/design/2026-03-13-ccdi-docker-deployment-design.md docs/plans/backend/2026-03-13-ccdi-docker-deployment-backend-implementation.md docs/plans/frontend/2026-03-13-ccdi-docker-deployment-frontend-implementation.md
git commit -m "新增Docker前端部署方案"
```

View File

@@ -188,7 +188,7 @@ git commit -m "保护亲属页资产导入交互不回归"
### Task 5: 执行前端回归验证
**Files:**
- Modify: `docs/plans/2026-03-13-employee-family-asset-import-split-design.md`
- Modify: `docs/design/2026-03-13-employee-family-asset-import-split-design.md`
**Step 1: 运行全部相关静态测试**
@@ -225,6 +225,6 @@ Expected:
**Step 4: 提交**
```bash
git add docs/plans/2026-03-13-employee-family-asset-import-split-design.md
git add docs/design/2026-03-13-employee-family-asset-import-split-design.md
git commit -m "完成资产导入拆分前端验证"
```

View File

@@ -28,7 +28,7 @@
**Step 3: Commit**
```bash
git add docs/implementation-reports/2026-03-16-project40-large-transaction-report.md
git add docs/reports/implementation2026-03-16-project40-large-transaction-report.md
git commit -m "文档: 补充项目40流水前端验证说明"
```
@@ -53,6 +53,6 @@ git commit -m "文档: 补充项目40流水前端验证说明"
**Step 3: Commit**
```bash
git add docs/implementation-reports/2026-03-16-project40-large-transaction-report.md
git add docs/reports/implementation2026-03-16-project40-large-transaction-report.md
git commit -m "文档: 完成项目40流水前端验证清单"
```

View File

@@ -233,7 +233,7 @@ git commit -m "refactor: 收敛模型参数页修改状态管理"
### Task 5: 验证“无千分位设计”和“接口驱动展示”
**Files:**
- Optional Record: `docs/test-records/model-param-frontend-alignment-test.md`
- Optional Record: `docs/tests/records/model-param-frontend-alignment-test.md`
**Step 1: 启动前端开发服务**
@@ -273,13 +273,13 @@ npm run dev
测试结束后关闭 `npm run dev` 启动的进程,并把结果写入:
```text
docs/test-records/model-param-frontend-alignment-test.md
docs/tests/records/model-param-frontend-alignment-test.md
```
然后提交:
```bash
git add docs/test-records/model-param-frontend-alignment-test.md
git add docs/tests/records/model-param-frontend-alignment-test.md
git commit -m "test: 记录模型参数前端动态展示验证"
```
@@ -326,10 +326,10 @@ npm run dev
**Step 5: 提交联调记录**
```bash
git add docs/test-records/model-param-frontend-alignment-test.md docs/test-records/model-param-backend-alignment-test.md
git add docs/tests/records/model-param-frontend-alignment-test.md docs/tests/records/model-param-backend-alignment-test.md
git commit -m "test: 完成模型参数前后端联调验收"
```
---
Plan complete and saved to `docs/plans/2026-03-16-model-param-csv-alignment-frontend-implementation.md`.
Plan complete and saved to `docs/plans/frontend/2026-03-16-model-param-csv-alignment-frontend-implementation.md`.

View File

@@ -13,7 +13,7 @@
### Task 1: 明确本期前端范围为零代码接入
**Files:**
- Modify: `docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
- Modify: `docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
**Step 1: Write the acceptance checklist**
@@ -44,14 +44,14 @@ Expected:
**Step 4: Commit**
```bash
git add docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git add docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git commit -m "docs: 明确流水标签前端一期范围"
```
### Task 2: 记录未来 API 契约占位
**Files:**
- Modify: `docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
- Modify: `docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
**Step 1: Define the future API contract**
@@ -70,7 +70,7 @@ git commit -m "docs: 明确流水标签前端一期范围"
Run:
```bash
Get-Content docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md | Select-String "/ccdi/project/tags/rebuild"
Get-Content docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md | Select-String "/ccdi/project/tags/rebuild"
```
Expected:
@@ -84,14 +84,14 @@ Expected:
**Step 4: Commit**
```bash
git add docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git add docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git commit -m "docs: 补充流水标签前端后续接口契约"
```
### Task 3: 约束后续页面接入位置
**Files:**
- Modify: `docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
- Modify: `docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
**Step 1: Write the page integration checklist**
@@ -125,14 +125,14 @@ Expected:
**Step 4: Commit**
```bash
git add docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git add docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git commit -m "docs: 约束流水标签前端二期接入位置"
```
### Task 4: 完成本期前端回归检查
**Files:**
- Modify: `docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
- Modify: `docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
**Step 1: Run frontend build smoke check**
@@ -165,7 +165,7 @@ Expected:
**Step 4: Commit**
```bash
git add docs/plans/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git add docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git commit -m "docs: 完成流水标签前端一期兼容性核对"
```

View File

@@ -804,12 +804,12 @@ Expected: 前端服务启动成功,访问 http://localhost/ccdiProject
```bash
# 打开浏览器访问 http://localhost/ccdiProject
# 使用截图工具拍摄完整页面截图
# 保存为 docs/plans/implementation-screenshot.png
# 保存为 docs/plans/fullstack/implementation-screenshot.png
```
**Step 5: 创建验证报告**
创建文件 `docs/plans/verification-report.md`,记录验证结果:
创建文件 `docs/plans/misc/verification-report.md`,记录验证结果:
```markdown
# 项目管理页面重构验证报告
@@ -846,7 +846,7 @@ Expected: 前端服务启动成功,访问 http://localhost/ccdiProject
Run:
```bash
git add docs/plans/verification-report.md
git add docs/plans/misc/verification-report.md
git commit -m "docs: 添加项目管理页面重构验证报告"
```
@@ -950,7 +950,7 @@ Expected: 代码已推送到远程仓库
## 相关文件
- 设计文档:`docs/plans/2026-02-27-project-management-page-redesign.md`
- 设计文档:`docs/plans/fullstack/2026-02-27-project-management-page-redesign.md`
- 原型图:`doc/创建项目功能/ScreenShot_2026-02-27_111611_994.png`
- 主组件:`ruoyi-ui/src/views/ccdiProject/index.vue`
- 搜索组件:`ruoyi-ui/src/views/ccdiProject/components/SearchBar.vue`

View File

@@ -482,7 +482,7 @@ Expected:
## Task 9: 最终提交和文档更新
**Files:**
- Modify: `docs/plans/2026-02-27-project-status-counts-fix-design.md`
- Modify: `docs/design/2026-02-27-project-status-counts-fix-design.md`
**Step 1: 更新设计文档状态**
@@ -511,7 +511,7 @@ Expected:
**Step 3: 提交文档更新**
```bash
git add docs/plans/2026-02-27-project-status-counts-fix-design.md
git add docs/design/2026-02-27-project-status-counts-fix-design.md
git commit -m "docs: 更新项目状态统计修复设计文档状态为已完成"
```
@@ -573,6 +573,6 @@ git push origin dev
## 相关文档
- 设计文档: `docs/plans/2026-02-27-project-status-counts-fix-design.md`
- 设计文档: `docs/design/2026-02-27-project-status-counts-fix-design.md`
- 若依框架文档: 项目根目录的 `CLAUDE.md`
- MyBatis Plus 文档: https://baomidou.com/

View File

@@ -1124,7 +1124,7 @@ cp db_config.conf.template db_config.conf
- 实际配置: `db_config.conf`
- 表结构文件: `doc/database/backup/ccdi_structure.sql`
- 数据文件: `doc/database/backup/ccdi_data.sql`
- 设计文档: `docs/plans/2026-02-28-database-migration-design.md`
- 设计文档: `docs/design/2026-02-28-database-migration-design.md`
```
**Step 2: 提交操作指南**

View File

@@ -648,7 +648,7 @@ git commit -m "feat: 添加获取Token响应DTO"
**步骤 1: 参考设计文档创建各个DTO类**
根据 `docs/plans/2026-03-02-lsfx-integration-design.md` 中的DTO设计创建剩余的请求和响应对象。每个DTO都使用 `@Data` 注解,字段根据接口文档定义。
根据 `docs/design/2026-03-02-lsfx-integration-design.md` 中的DTO设计创建剩余的请求和响应对象。每个DTO都使用 `@Data` 注解,字段根据接口文档定义。
**步骤 2: 提交更改**

View File

@@ -1041,7 +1041,7 @@ git log --oneline
## 参考资料
- 新版接口文档:`doc/对接流水分析/兰溪-流水分析对接-新版.md`
- 设计文档:`docs/plans/2026-03-02-lsfx-integration-design.md`
- 设计文档:`docs/design/2026-03-02-lsfx-integration-design.md`
- 若依框架规范:`CLAUDE.md`
---

View File

@@ -955,6 +955,6 @@ git push origin dev
## 文档参考
- 设计文档: `docs/plans/2026-03-04-project-detail-navigation-menu-design.md`
- 设计文档: `docs/design/2026-03-04-project-detail-navigation-menu-design.md`
- Element UI Menu 文档: https://element.eleme.cn/#/zh-CN/component/menu
- Vue 动态组件: https://cn.vuejs.org/v2/guide/components.html#动态组件

View File

@@ -314,7 +314,7 @@ git commit -m "feat(lsfx-mock): 添加银行流水审计字段到 mock 响应
## Task 4: 更新文档(可选)
**Files:**
- Update: `docs/plans/2026-03-05-bank-statement-audit-fields-design.md`(已存在)
- Update: `docs/design/2026-03-05-bank-statement-audit-fields-design.md`(已存在)
**Step 1: 验证设计文档完整性**
@@ -366,7 +366,7 @@ git commit -m "docs: 更新银行流水接口文档,补充审计字段说明"
## 参考资料
- 设计文档: `docs/plans/2026-03-05-bank-statement-audit-fields-design.md`
- 设计文档: `docs/design/2026-03-05-bank-statement-audit-fields-design.md`
- 实体类: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java`
- 项目规范: `CLAUDE.md`
- 外部平台接口文档 6.5 节

View File

@@ -252,6 +252,6 @@ git commit -m "fix: 补充银行流水接口 uploadSequnceNumber 字段接收和
## 参考资料
- 设计文档:`docs/plans/2026-03-05-bank-statement-field-design.md`
- 设计文档:`docs/design/2026-03-05-bank-statement-field-design.md`
- 字段映射文档:`assets/对接流水分析/ccdi_bank_statement.md`
- 接口文档:`assets/对接流水分析/兰溪-流水分析对接-新版.md`

View File

@@ -8,7 +8,7 @@
**技术栈:** Spring Boot 3.5.8 + MyBatis Plus 3.0.5 + Vue 2.6.12 + Element UI 2.15.14
**设计文档:** `docs/plans/2026-03-06-model-param-config-optimization-design.md`
**设计文档:** `docs/design/2026-03-06-model-param-config-optimization-design.md`
---
@@ -675,7 +675,7 @@ mvn spring-boot:run
**步骤 5: 提交测试记录**
```bash
git add docs/test-records/
git add docs/tests/records/
git commit -m "test: 记录后端接口测试结果"
```
@@ -1007,7 +1007,7 @@ npm run dev
**步骤 4: 提交测试记录**
```bash
git add docs/test-records/
git add docs/tests/records/
git commit -m "test: 记录全局配置页面测试结果"
```
@@ -1270,7 +1270,7 @@ git commit -m "feat: 重构项目内模型参数配置页面"
**步骤 4: 提交测试记录**
```bash
git add docs/test-records/
git add docs/tests/records/
git commit -m "test: 记录项目配置页面测试结果"
```
@@ -1295,7 +1295,7 @@ git commit -m "test: 记录项目配置页面测试结果"
**步骤 3: 提交测试记录**
```bash
git add docs/test-records/
git add docs/tests/records/
git commit -m "test: 完成端到端功能测试"
```
@@ -1319,7 +1319,7 @@ git commit -m "test: 完成端到端功能测试"
**步骤 4: 提交测试记录**
```bash
git add docs/test-records/
git add docs/tests/records/
git commit -m "test: 完成性能测试"
```

View File

@@ -8,7 +8,7 @@
**技术栈:** Spring Boot 3.5.8 + MyBatis Plus 3.0.5 + Vue 2.6.12 + Element UI 2.15.14
**设计文档:** `docs/plans/2026-03-06-model-param-config-optimization-design.md`
**设计文档:** `docs/design/2026-03-06-model-param-config-optimization-design.md`
---
@@ -683,7 +683,7 @@ mvn spring-boot:run
记录测试结果并提交(如果需要):
```bash
git add docs/test-records/
git add docs/tests/records/
git commit -m "test: 记录后端接口测试结果"
```

View File

@@ -301,4 +301,4 @@ npm run build:prod
- `ruoyi-ui/src/settings.js` - 默认配置文件(本次修改)
- `ruoyi-ui/src/store/modules/settings.js` - Vuex 状态管理(无需修改)
- `ruoyi-ui/src/layout/components/Settings/index.vue` - 设置界面(无需修改)
- `docs/plans/2026-03-06-theme-light-default-design.md` - 设计文档
- `docs/design/2026-03-06-theme-light-default-design.md` - 设计文档

View File

@@ -308,8 +308,8 @@ git commit -m "fix(ccdi-project): cleanup partial bank statements on upload fail
### Task 4: 回归验证并整理交付
**Files:**
- Modify: `docs/plans/2026-03-09-file-upload-parse-success-after-bank-statement-design.md`
- Modify: `docs/plans/2026-03-09-file-upload-parse-success-after-bank-statement.md`
- Modify: `docs/design/2026-03-09-file-upload-parse-success-after-bank-statement-design.md`
- Modify: `docs/plans/fullstack/2026-03-09-file-upload-parse-success-after-bank-statement.md`
**Step 1: Run final verification**
@@ -343,7 +343,7 @@ Expected:
**Step 4: Commit**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java docs/plans/2026-03-09-file-upload-parse-success-after-bank-statement-design.md docs/plans/2026-03-09-file-upload-parse-success-after-bank-statement.md
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java docs/design/2026-03-09-file-upload-parse-success-after-bank-statement-design.md docs/plans/fullstack/2026-03-09-file-upload-parse-success-after-bank-statement.md
git commit -m "docs: finalize file upload parse success timing plan"
```

Some files were not shown because too many files have changed in this diff Show More