diff --git a/docs/plans/2026-03-04-lsfx-interface-update-plan.md b/docs/plans/2026-03-04-lsfx-interface-update-plan.md new file mode 100644 index 0000000..e974048 --- /dev/null +++ b/docs/plans/2026-03-04-lsfx-interface-update-plan.md @@ -0,0 +1,1713 @@ +# 流水分析接口功能更新实施计划 + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** 新增2个流水分析接口(获取文件状态、删除文件),并更新现有接口以匹配最新文档规范 + +**Architecture:** 采用分层架构,从数据模型层开始,逐层向上构建(DTO → Util → Client → Controller → Config)。新增接口遵循TDD原则,现有接口增量更新。所有接口通过LsfxAnalysisClient统一封装,通过LsfxTestController暴露测试端点。 + +**Tech Stack:** Spring Boot 3, MyBatis Plus 3.5.10, Lombok, SpringDoc OpenAPI, EasyExcel 3.3.4 + +--- + +## 前置准备 + +### Task 0: 确认工作环境 + +**Files:** +- Read: `ccdi-lsfx/pom.xml` +- Read: `ruoyi-admin/src/main/resources/application-dev.yml` + +**Step 1: 确认当前配置** + +```bash +cd ccdi-lsfx +cat pom.xml | grep -A 5 "artifactId" +``` + +Expected: 确认模块依赖正确 + +**Step 2: 确认Mock Server配置** + +```bash +grep -A 10 "lsfx:" ../ruoyi-admin/src/main/resources/application-dev.yml +``` + +Expected: +- base-url: http://localhost:8000 +- endpoints配置存在 + +**Step 3: 确认项目可启动** + +```bash +cd .. +mvn clean compile +``` + +Expected: BUILD SUCCESS + +--- + +## 阶段一: 数据模型层 + +### Task 1: 添加常量定义 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/constants/LsfxConstants.java` + +**Step 1: 添加新常量到LsfxConstants** + +在文件末尾的 `}` 之前添加: + +```java + // 新增:固定值常量(根据文档) + public static final String DEFAULT_USER_ID = "902001"; + public static final String DEFAULT_USER_NAME = "902001"; + public static final String DEFAULT_APP_ID = "remote_app"; + public static final String DEFAULT_ROLE = "VIEWER"; + public static final String DEFAULT_ANALYSIS_TYPE = "-1"; + public static final String DEFAULT_ORG_CODE = "902000"; + public static final String DEFAULT_DEPARTMENT_CODE = "902000"; + public static final String DEFAULT_DATA_CHANNEL_CODE = "ZJRCU"; + + // 新增:状态常量 + public static final Integer PARSE_STATUS_SUCCESS = -5; + public static final String PARSE_STATUS_DESC_SUCCESS = "data.wait.confirm.newaccount"; +``` + +**Step 2: 验证编译** + +```bash +cd ccdi-lsfx +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/constants/LsfxConstants.java +git commit -m "feat(lsfx): 添加流水分析固定值和状态常量" +``` + +--- + +### Task 2: 创建GetFileUploadStatusRequest DTO + +**Files:** +- Create: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/request/GetFileUploadStatusRequest.java` + +**Step 1: 创建请求DTO** + +```java +package com.ruoyi.lsfx.domain.request; + +import lombok.Data; + +/** + * 获取单个文件上传状态请求(接口5) + */ +@Data +public class GetFileUploadStatusRequest { + + /** 项目ID (必填) */ + private Integer groupId; + + /** 文件ID (可选,不传则查询所有) */ + private Integer logId; +} +``` + +**Step 2: 验证文件创建** + +```bash +cat src/main/java/com/ruoyi/lsfx/domain/request/GetFileUploadStatusRequest.java +``` + +Expected: 文件内容正确显示 + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/domain/request/GetFileUploadStatusRequest.java +git commit -m "feat(lsfx): 添加获取文件上传状态请求DTO" +``` + +--- + +### Task 3: 创建DeleteFilesRequest DTO + +**Files:** +- Create: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/request/DeleteFilesRequest.java` + +**Step 1: 创建请求DTO** + +```java +package com.ruoyi.lsfx.domain.request; + +import lombok.Data; + +/** + * 删除文件请求(接口6) + */ +@Data +public class DeleteFilesRequest { + + /** 项目ID (必填) */ + private Integer groupId; + + /** 文件ID数组 (必填) */ + private Integer[] logIds; + + /** 用户柜员号 (必填) */ + private Integer userId; +} +``` + +**Step 2: 验证文件创建** + +```bash +cat src/main/java/com/ruoyi/lsfx/domain/request/DeleteFilesRequest.java +``` + +Expected: 文件内容正确显示 + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/domain/request/DeleteFilesRequest.java +git commit -m "feat(lsfx): 添加删除文件请求DTO" +``` + +--- + +### Task 4: 创建GetFileUploadStatusResponse DTO + +**Files:** +- Create: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetFileUploadStatusResponse.java` + +**Step 1: 创建响应DTO** + +```java +package com.ruoyi.lsfx.domain.response; + +import lombok.Data; +import java.util.List; + +/** + * 获取单个文件上传状态响应(接口5) + */ +@Data +public class GetFileUploadStatusResponse { + + /** 返回码 */ + private String code; + + /** 状态 */ + private String status; + + /** 成功标识 */ + private Boolean successResponse; + + /** 响应数据 */ + private FileUploadStatusData data; + + @Data + public static class FileUploadStatusData { + /** 日志列表 */ + private List logs; + + /** 状态 */ + private String status; + + /** 账号ID */ + private Integer accountId; + + /** 币种 */ + private String currency; + } + + @Data + public static class LogItem { + /** 账号列表 */ + private List accountNoList; + + /** 银行名称 */ + private String bankName; + + /** 数据类型信息 [格式, 分隔符] */ + private List dataTypeInfo; + + /** 下载文件名 */ + private String downloadFileName; + + /** 主体名称列表(重要:用于判断是否需要生成主体) */ + private List enterpriseNameList; + + /** 文件大小(字节) */ + private Long fileSize; + + /** 文件上传者ID */ + private Integer fileUploadBy; + + /** 文件上传者用户名 */ + private String fileUploadByUserName; + + /** 文件上传时间 */ + private String fileUploadTime; + + /** 是否拆分 */ + private Integer isSplit; + + /** 企业ID */ + private Integer leId; + + /** 文件ID */ + private Integer logId; + + /** 日志元数据 */ + private String logMeta; + + /** 日志类型 */ + private String logType; + + /** 登录企业ID */ + private Integer loginLeId; + + /** 丢失头部 */ + private List lostHeader; + + /** 真实银行名称 */ + private String realBankName; + + /** 行数 */ + private Integer rows; + + /** 来源 */ + private String source; + + /** 状态(-5表示解析成功) */ + private Integer status; + + /** 模板名称 */ + private String templateName; + + /** 总记录数 */ + private Integer totalRecords; + + /** 交易结束日期ID */ + private Integer trxDateEndId; + + /** 交易开始日期ID */ + private Integer trxDateStartId; + + /** 上传文件名 */ + private String uploadFileName; + + /** 上传状态描述 */ + private String uploadStatusDesc; + } +} +``` + +**Step 2: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/domain/response/GetFileUploadStatusResponse.java +git commit -m "feat(lsfx): 添加获取文件上传状态响应DTO" +``` + +--- + +### Task 5: 创建DeleteFilesResponse DTO + +**Files:** +- Create: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/DeleteFilesResponse.java` + +**Step 1: 创建响应DTO** + +```java +package com.ruoyi.lsfx.domain.response; + +import lombok.Data; + +/** + * 删除文件响应(接口6) + */ +@Data +public class DeleteFilesResponse { + + /** 返回码 */ + private String code; + + /** 状态 */ + private String status; + + /** 成功标识 */ + private Boolean successResponse; + + /** 响应数据 */ + private DeleteFilesData data; + + /** 响应消息 */ + private String message; + + @Data + public static class DeleteFilesData { + /** 删除成功消息 */ + private String message; + } +} +``` + +**Step 2: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/domain/response/DeleteFilesResponse.java +git commit -m "feat(lsfx): 添加删除文件响应DTO" +``` + +--- + +### Task 6: 更新FetchInnerFlowRequest添加dataChannelCode字段 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/request/FetchInnerFlowRequest.java` + +**Step 1: 添加dataChannelCode字段** + +在类的字段区域添加: + +```java + /** 校验码 (固定值"ZJRCU") */ + private String dataChannelCode; +``` + +**Step 2: 验证修改** + +```bash +cat src/main/java/com/ruoyi/lsfx/domain/request/FetchInnerFlowRequest.java +``` + +Expected: 包含dataChannelCode字段 + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/domain/request/FetchInnerFlowRequest.java +git commit -m "feat(lsfx): 拉取行内流水请求添加dataChannelCode字段" +``` + +--- + +## 阶段二: 工具类增强 + +### Task 7: HttpUtil添加GET请求支持 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/HttpUtil.java` + +**Step 1: 添加GET请求方法** + +在类的末尾 `}` 之前添加: + +```java + /** + * 发送GET请求 + * + * @param url 请求URL + * @param params 查询参数 + * @param headers 请求头 + * @param responseType 响应类型 + * @return 响应对象 + */ + public T get(String url, Map params, Map headers, + Class responseType) { + try { + // 构建URL with查询参数 + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url); + if (params != null && !params.isEmpty()) { + params.forEach((key, value) -> { + if (value != null) { + builder.queryParam(key, value); + } + }); + } + + String fullUrl = builder.toUriString(); + log.debug("【HTTP GET】请求URL: {}", fullUrl); + + // 创建请求头 + HttpHeaders httpHeaders = new HttpHeaders(); + if (headers != null) { + headers.forEach(httpHeaders::add); + } + + // 构建请求实体 + HttpEntity entity = new HttpEntity<>(httpHeaders); + + // 执行GET请求 + ResponseEntity response = restTemplate.exchange( + fullUrl, + HttpMethod.GET, + entity, + String.class + ); + + log.debug("【HTTP GET】响应状态: {}", response.getStatusCode()); + log.debug("【HTTP GET】响应内容: {}", response.getBody()); + + // 解析响应 + if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) { + return objectMapper.readValue(response.getBody(), responseType); + } else { + throw new LsfxApiException("GET请求失败: " + response.getStatusCode()); + } + } catch (Exception e) { + log.error("【HTTP GET】请求异常: url={}, error={}", url, e.getMessage(), e); + throw new LsfxApiException("GET请求异常: " + e.getMessage(), e); + } + } +``` + +**Step 2: 添加必要的import** + +在文件顶部添加: + +```java +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.http.HttpMethod; +``` + +**Step 3: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 4: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/util/HttpUtil.java +git commit -m "feat(lsfx): HttpUtil添加GET请求支持" +``` + +--- + +## 阶段三: 配置更新 + +### Task 8: 更新application-dev.yml添加新endpoint配置 + +**Files:** +- Modify: `ruoyi-admin/src/main/resources/application-dev.yml` + +**Step 1: 添加新的endpoint配置** + +在 `lsfx.api.endpoints` 部分添加: + +```yaml + endpoints: + get-token: /account/common/getToken + upload-file: /watson/api/project/remoteUploadSplitFile + fetch-inner-flow: /watson/api/project/getJZFileOrZjrcuFile + check-parse-status: /watson/api/project/upload/getpendings + # 新增接口 + get-file-upload-status: /watson/api/project/bs/upload + delete-files: /watson/api/project/batchDeleteUploadFile + get-bank-statement: /watson/api/project/getBSByLogId +``` + +**Step 2: 验证修改** + +```bash +cat ../ruoyi-admin/src/main/resources/application-dev.yml | grep -A 10 "endpoints:" +``` + +Expected: 包含新添加的2个endpoint + +**Step 3: 提交** + +```bash +git add ../ruoyi-admin/src/main/resources/application-dev.yml +git commit -m "feat(lsfx): 配置添加新接口endpoint" +``` + +--- + +## 阶段四: 客户端层 - 新增接口 + +### Task 9: LsfxAnalysisClient添加配置注入 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java` + +**Step 1: 添加新的endpoint配置注入** + +在类的字段区域添加: + +```java + @Value("${lsfx.api.endpoints.get-file-upload-status}") + private String getFileUploadStatusEndpoint; + + @Value("${lsfx.api.endpoints.delete-files}") + private String deleteFilesEndpoint; +``` + +**Step 2: 验证修改** + +```bash +cat src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java | grep -A 2 "get-file-upload-status" +``` + +Expected: 显示新添加的配置 + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java +git commit -m "feat(lsfx): Client添加新接口配置注入" +``` + +--- + +### Task 10: LsfxAnalysisClient实现getFileUploadStatus方法 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java` + +**Step 1: 添加getFileUploadStatus方法** + +在类的末尾 `}` 之前添加: + +```java + /** + * 获取单个文件上传状态(接口5) + * 用途: 获取文件解析后的主体名称和账号等信息 + * + * 关键判断: + * - status=-5 且 uploadStatusDesc="data.wait.confirm.newaccount" 表示解析成功 + * - enterpriseNameList仅有一个空字符串""时,表示流水文件未生成主体 + * + * @param request 请求参数(groupId必填, logId可选) + * @return 文件上传状态信息 + */ + public GetFileUploadStatusResponse getFileUploadStatus(GetFileUploadStatusRequest request) { + log.info("【流水分析】获取文件上传状态: groupId={}, logId={}", + request.getGroupId(), request.getLogId()); + long startTime = System.currentTimeMillis(); + + try { + String url = baseUrl + getFileUploadStatusEndpoint; + + // GET请求,构建查询参数 + Map params = new HashMap<>(); + params.put("groupId", request.getGroupId()); + if (request.getLogId() != null) { + params.put("logId", request.getLogId()); + } + + Map headers = new HashMap<>(); + headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId); + + GetFileUploadStatusResponse response = httpUtil.get(url, params, headers, + GetFileUploadStatusResponse.class); + + long elapsed = System.currentTimeMillis() - startTime; + if (response != null && response.getData() != null) { + log.info("【流水分析】获取文件上传状态成功: logId数量={}, 耗时={}ms", + response.getData().getLogs() != null ? response.getData().getLogs().size() : 0, + elapsed); + } else { + log.warn("【流水分析】获取文件上传状态响应异常: 耗时={}ms", elapsed); + } + + return response; + } catch (LsfxApiException e) { + log.error("【流水分析】获取文件上传状态失败: groupId={}, error={}", + request.getGroupId(), e.getMessage(), e); + throw e; + } catch (Exception e) { + log.error("【流水分析】获取文件上传状态未知异常: groupId={}", + request.getGroupId(), e); + throw new LsfxApiException("获取文件上传状态失败: " + e.getMessage(), e); + } + } +``` + +**Step 2: 添加必要的import** + +在文件顶部添加: + +```java +import com.ruoyi.lsfx.domain.request.GetFileUploadStatusRequest; +import com.ruoyi.lsfx.domain.response.GetFileUploadStatusResponse; +``` + +**Step 3: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 4: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java +git commit -m "feat(lsfx): Client实现获取文件上传状态方法" +``` + +--- + +### Task 11: LsfxAnalysisClient实现deleteFiles方法 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java` + +**Step 1: 添加deleteFiles方法** + +在getFileUploadStatus方法之后添加: + +```java + /** + * 删除文件/主体(接口6) + * 用途: 删除解析失败或不需要的流水文件 + * + * 使用场景: + * - 文件解析失败时清理文件 + * - 删除错误上传的文件 + * + * @param request 请求参数(groupId, logIds, userId必填) + * @return 删除结果 + */ + public DeleteFilesResponse deleteFiles(DeleteFilesRequest request) { + log.info("【流水分析】删除文件请求: groupId={}, logIds={}, userId={}", + request.getGroupId(), Arrays.toString(request.getLogIds()), request.getUserId()); + long startTime = System.currentTimeMillis(); + + try { + String url = baseUrl + deleteFilesEndpoint; + + // 构建form-data参数 + Map params = new HashMap<>(); + params.put("groupId", request.getGroupId()); + params.put("logIds", request.getLogIds()); // 数组 + params.put("userId", request.getUserId()); + + Map headers = new HashMap<>(); + headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId); + + DeleteFilesResponse response = httpUtil.postFormData(url, params, headers, + DeleteFilesResponse.class); + + long elapsed = System.currentTimeMillis() - startTime; + if (response != null && response.getData() != null) { + log.info("【流水分析】删除文件成功: message={}, 耗时={}ms", + response.getData().getMessage(), elapsed); + } else { + log.warn("【流水分析】删除文件响应异常: 耗时={}ms", elapsed); + } + + return response; + } catch (LsfxApiException e) { + log.error("【流水分析】删除文件失败: groupId={}, error={}", + request.getGroupId(), e.getMessage(), e); + throw e; + } catch (Exception e) { + log.error("【流水分析】删除文件未知异常: groupId={}", + request.getGroupId(), e); + throw new LsfxApiException("删除文件失败: " + e.getMessage(), e); + } + } +``` + +**Step 2: 添加必要的import** + +在文件顶部添加: + +```java +import com.ruoyi.lsfx.domain.request.DeleteFilesRequest; +import com.ruoyi.lsfx.domain.response.DeleteFilesResponse; +import java.util.Arrays; +``` + +**Step 3: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 4: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java +git commit -m "feat(lsfx): Client实现删除文件方法" +``` + +--- + +## 阶段五: 客户端层 - 更新现有接口 + +### Task 12: 更新getToken方法添加默认值处理 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java` + +**Step 1: 更新getToken方法中的参数设置** + +找到getToken方法中的params构建部分,更新为: + +```java + // 构建form-data参数(使用ObjectUtil简化代码) + Map params = ObjectUtil.toMapIgnoreNull(request); + // 添加特殊字段 + params.put("appId", appId); + params.put("appSecretCode", secretCode); + // 使用常量设置默认值 + params.put("role", StringUtils.isNotBlank(request.getRole()) ? + request.getRole() : LsfxConstants.DEFAULT_ROLE); + params.put("analysisType", StringUtils.isNotBlank(request.getAnalysisType()) ? + request.getAnalysisType() : LsfxConstants.DEFAULT_ANALYSIS_TYPE); +``` + +**Step 2: 添加必要的import** + +在文件顶部添加: + +```java +import com.ruoyi.common.utils.StringUtils; +``` + +**Step 3: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 4: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java +git commit -m "feat(lsfx): getToken方法添加默认值处理" +``` + +--- + +### Task 13: 更新fetchInnerFlow方法添加dataChannelCode默认值 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java` + +**Step 1: 更新fetchInnerFlow方法中的参数设置** + +找到fetchInnerFlow方法中的params构建部分,更新为: + +```java + // 构建form-data参数(使用ObjectUtil简化代码) + Map params = ObjectUtil.toMapIgnoreNull(request); + // 设置dataChannelCode默认值 + if (StringUtils.isBlank(request.getDataChannelCode())) { + params.put("dataChannelCode", LsfxConstants.DEFAULT_DATA_CHANNEL_CODE); + } +``` + +**Step 2: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java +git commit -m "feat(lsfx): fetchInnerFlow方法添加dataChannelCode默认值" +``` + +--- + +### Task 14: 更新checkParseStatus方法注释 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java` + +**Step 1: 更新checkParseStatus方法的JavaDoc注释** + +找到checkParseStatus方法,更新注释为: + +```java + /** + * 检查文件解析状态(接口4) + * + * 使用说明: + * - 文件上传后需要轮询此接口,直到 parsing=false + * - 建议轮询间隔: 1秒 + * - status=-5 且 uploadStatusDesc="data.wait.confirm.newaccount" 表示解析成功 + * + * @param groupId 项目ID + * @param inprogressList 文件ID列表(逗号分隔) + * @return 解析状态 + */ +``` + +**Step 2: 验证修改** + +```bash +cat src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java | grep -A 10 "检查文件解析状态" +``` + +Expected: 显示更新的注释 + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java +git commit -m "docs(lsfx): checkParseStatus方法添加轮询说明" +``` + +--- + +## 阶段六: 控制器层 - 新增接口 + +### Task 15: LsfxTestController添加getFileUploadStatus接口 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java` + +**Step 1: 添加getFileUploadStatus接口** + +在类的末尾 `}` 之前添加: + +```java + @Operation(summary = "获取单个文件上传状态", + description = "获取文件解析后的主体名称和账号等信息。status=-5且uploadStatusDesc='data.wait.confirm.newaccount'表示解析成功") + @GetMapping("/getFileUploadStatus") + public AjaxResult getFileUploadStatus( + @Parameter(description = "项目ID") @RequestParam Integer groupId, + @Parameter(description = "文件ID(可选,不传则查询所有)") @RequestParam(required = false) Integer logId + ) { + // 参数校验 + if (groupId == null || groupId <= 0) { + return AjaxResult.error("参数不完整:groupId为必填且大于0"); + } + + GetFileUploadStatusRequest request = new GetFileUploadStatusRequest(); + request.setGroupId(groupId); + request.setLogId(logId); + + GetFileUploadStatusResponse response = lsfxAnalysisClient.getFileUploadStatus(request); + return AjaxResult.success(response); + } +``` + +**Step 2: 添加必要的import** + +在文件顶部添加: + +```java +import com.ruoyi.lsfx.domain.request.GetFileUploadStatusRequest; +import com.ruoyi.lsfx.domain.response.GetFileUploadStatusResponse; +``` + +**Step 3: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 4: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java +git commit -m "feat(lsfx): Controller添加获取文件上传状态接口" +``` + +--- + +### Task 16: LsfxTestController添加deleteFiles接口 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java` + +**Step 1: 添加deleteFiles接口** + +在getFileUploadStatus方法之后添加: + +```java + @Operation(summary = "删除文件", + description = "删除解析失败或不需要的流水文件") + @PostMapping("/deleteFiles") + public AjaxResult deleteFiles(@RequestBody DeleteFilesRequest request) { + // 参数校验 + if (request.getGroupId() == null || request.getGroupId() <= 0) { + return AjaxResult.error("参数不完整:groupId为必填且大于0"); + } + if (request.getLogIds() == null || request.getLogIds().length == 0) { + return AjaxResult.error("参数不完整:logIds为必填且不能为空"); + } + if (request.getUserId() == null) { + return AjaxResult.error("参数不完整:userId为必填"); + } + + DeleteFilesResponse response = lsfxAnalysisClient.deleteFiles(request); + return AjaxResult.success(response); + } +``` + +**Step 2: 添加必要的import** + +在文件顶部添加: + +```java +import com.ruoyi.lsfx.domain.request.DeleteFilesRequest; +import com.ruoyi.lsfx.domain.response.DeleteFilesResponse; +``` + +**Step 3: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 4: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java +git commit -m "feat(lsfx): Controller添加删除文件接口" +``` + +--- + +## 阶段七: 控制器层 - 更新现有接口 + +### Task 17: 更新getToken接口添加默认值处理 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java` + +**Step 1: 更新getToken方法添加默认值设置** + +找到getToken方法,在参数校验之后添加: + +```java + // 必填字段设置默认值 + if (StringUtils.isBlank(request.getUserId())) { + request.setUserId(LsfxConstants.DEFAULT_USER_ID); + } + if (StringUtils.isBlank(request.getUserName())) { + request.setUserName(LsfxConstants.DEFAULT_USER_NAME); + } + if (StringUtils.isBlank(request.getOrgCode())) { + request.setOrgCode(LsfxConstants.DEFAULT_ORG_CODE); + } + if (StringUtils.isBlank(request.getDepartmentCode())) { + request.setDepartmentCode(LsfxConstants.DEFAULT_DEPARTMENT_CODE); + } +``` + +**Step 2: 添加必要的import** + +确认文件顶部有: + +```java +import com.ruoyi.lsfx.constants.LsfxConstants; +``` + +**Step 3: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 4: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java +git commit -m "feat(lsfx): getToken接口添加默认值处理" +``` + +--- + +### Task 18: 更新fetchInnerFlow接口添加dataChannelCode默认值 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java` + +**Step 1: 更新fetchInnerFlow方法添加默认值设置** + +找到fetchInnerFlow方法,在参数校验之后添加: + +```java + // 设置默认值 + if (StringUtils.isBlank(request.getDataChannelCode())) { + request.setDataChannelCode(LsfxConstants.DEFAULT_DATA_CHANNEL_CODE); + } +``` + +**Step 2: 验证编译** + +```bash +mvn compile +``` + +Expected: BUILD SUCCESS + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java +git commit -m "feat(lsfx): fetchInnerFlow接口添加dataChannelCode默认值" +``` + +--- + +### Task 19: 更新checkParseStatus接口Swagger文档 + +**Files:** +- Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java` + +**Step 1: 更新checkParseStatus方法的@Operation注解** + +找到checkParseStatus方法,更新@Operation注解: + +```java + @Operation(summary = "检查文件解析状态", + description = "轮询检查上传文件的解析状态。建议间隔1秒轮询,直到parsing=false。" + + "status=-5且uploadStatusDesc='data.wait.confirm.newaccount'表示解析成功") +``` + +**Step 2: 验证修改** + +```bash +cat src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java | grep -A 5 "检查文件解析状态" +``` + +Expected: 显示更新的描述 + +**Step 3: 提交** + +```bash +git add src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java +git commit -m "docs(lsfx): checkParseStatus接口完善Swagger文档" +``` + +--- + +## 阶段八: Mock Server更新 + +### Task 20: 更新Python Mock Server添加新接口 + +**Files:** +- Modify: `lsfx-mock-server/app.py` + +**Step 1: 添加获取文件上传状态Mock接口** + +在文件中添加新的路由: + +```python +@app.route('/watson/api/project/bs/upload', methods=['GET']) +def get_file_upload_status(): + """Mock接口5: 获取单个文件上传状态""" + group_id = request.args.get('groupId') + log_id = request.args.get('logId') + + print(f"[Mock] 获取文件上传状态: groupId={group_id}, logId={log_id}") + + # 模拟响应 + response = { + "code": "200", + "data": { + "logs": [ + { + "accountNoList": ["18785967364"], + "bankName": "ALIPAY", + "dataTypeInfo": ["CSV", ","], + "downloadFileName": "支付宝.csv", + "enterpriseNameList": ["曾孝成"], + "fileSize": 16322, + "fileUploadBy": 448, + "fileUploadByUserName": "admin@support.com", + "fileUploadTime": "2025-03-13 08:45:32", + "isSplit": 0, + "leId": 10741, + "logId": int(log_id) if log_id else 13994, + "logMeta": "{\"lostHeader\":[],\"balanceAmount\":\"-1\"}", + "logType": "bankstatement", + "loginLeId": 10741, + "lostHeader": [], + "realBankName": "ALIPAY", + "rows": 0, + "source": "http", + "status": -5, + "templateName": "ALIPAY_T220708", + "totalRecords": 127, + "trxDateEndId": 20231231, + "trxDateStartId": 20230102, + "uploadFileName": "支付宝.pdf", + "uploadStatusDesc": "data.wait.confirm.newaccount" + } + ], + "status": "", + "accountId": 8954, + "currency": "CNY" + }, + "status": "200", + "successResponse": True + } + + return jsonify(response) +``` + +**Step 2: 添加删除文件Mock接口** + +```python +@app.route('/watson/api/project/batchDeleteUploadFile', methods=['POST']) +def delete_files(): + """Mock接口6: 删除文件""" + data = request.get_json() if request.is_json else request.form.to_dict() + + print(f"[Mock] 删除文件: {data}") + + # 模拟响应 + response = { + "code": "200 OK", + "data": { + "message": "delete.files.success" + }, + "message": "delete.files.success", + "status": "200", + "successResponse": True + } + + return jsonify(response) +``` + +**Step 3: 验证Mock Server启动** + +```bash +cd ../lsfx-mock-server +python app.py & +``` + +Expected: 服务启动在 http://localhost:8000 + +**Step 4: 提交** + +```bash +git add ../lsfx-mock-server/app.py +git commit -m "feat(lsfx-mock): 添加获取文件状态和删除文件Mock接口" +``` + +--- + +## 阶段九: 测试验证 + +### Task 21: 启动后端服务 + +**Files:** +- 无文件修改 + +**Step 1: 停止可能运行的旧服务** + +```bash +pkill -f "spring-boot:run" || true +``` + +**Step 2: 启动Mock Server** + +```bash +cd lsfx-mock-server +python app.py > mock.log 2>&1 & +echo $! > mock.pid +cd .. +``` + +Expected: Mock Server启动成功 + +**Step 3: 启动后端服务** + +```bash +cd ruoyi-admin +mvn spring-boot:run > ../backend.log 2>&1 & +echo $! > ../backend.pid +cd .. +``` + +Expected: 后端服务启动成功 + +**Step 4: 等待服务启动** + +```bash +sleep 30 +tail -20 backend.log +``` + +Expected: 看到"Started RuoYiApplication"日志 + +--- + +### Task 22: 测试获取Token接口 + +**Files:** +- 无文件修改 + +**Step 1: 测试getToken接口(带默认值)** + +```bash +curl -X POST "http://localhost:8080/lsfx/test/getToken" \ + -H "Content-Type: application/json" \ + -d '{ + "projectNo": "902000_'$(date +%s)'", + "entityName": "测试项目20260304" + }' +``` + +Expected: 返回成功,包含token和projectId + +**Step 2: 记录projectId** + +将返回的projectId保存到变量: +```bash +PROJECT_ID=16238 # 替换为实际返回的值 +``` + +--- + +### Task 23: 测试获取文件上传状态接口(新接口) + +**Files:** +- 无文件修改 + +**Step 1: 测试查询单个文件状态** + +```bash +curl -X GET "http://localhost:8080/lsfx/test/getFileUploadStatus?groupId=${PROJECT_ID}&logId=13994" +``` + +Expected: +- code: "200" +- data.logs[0].status: -5 +- data.logs[0].enterpriseNameList: ["曾孝成"] + +**Step 2: 测试查询所有文件状态** + +```bash +curl -X GET "http://localhost:8080/lsfx/test/getFileUploadStatus?groupId=${PROJECT_ID}" +``` + +Expected: 返回文件列表 + +**Step 3: 验证Swagger文档** + +访问: http://localhost:8080/swagger-ui/index.html + +Expected: 看到"获取单个文件上传状态"接口 + +--- + +### Task 24: 测试删除文件接口(新接口) + +**Files:** +- 无文件修改 + +**Step 1: 测试删除单个文件** + +```bash +curl -X POST "http://localhost:8080/lsfx/test/deleteFiles" \ + -H "Content-Type: application/json" \ + -d '{ + "groupId": '${PROJECT_ID}', + "logIds": [13994], + "userId": 902001 + }' +``` + +Expected: +- code: "200 OK" +- data.message: "delete.files.success" + +**Step 2: 验证Swagger文档** + +访问: http://localhost:8080/swagger-ui/index.html + +Expected: 看到"删除文件"接口 + +--- + +### Task 25: 测试现有接口更新 + +**Files:** +- 无文件修改 + +**Step 1: 测试getToken默认值** + +```bash +curl -X POST "http://localhost:8080/lsfx/test/getToken" \ + -H "Content-Type: application/json" \ + -d '{ + "projectNo": "902000_'$(date +%s)'", + "entityName": "测试默认值" + }' +``` + +Expected: userId, userName, orgCode自动设置为默认值 + +**Step 2: 测试fetchInnerFlow默认值** + +```bash +curl -X POST "http://localhost:8080/lsfx/test/fetchInnerFlow" \ + -H "Content-Type: application/json" \ + -d '{ + "groupId": '${PROJECT_ID}', + "customerNo": "230902199012261247", + "requestDateId": 20260304, + "dataStartDateId": 20240201, + "dataEndDateId": 20240228, + "uploadUserId": 902001 + }' +``` + +Expected: dataChannelCode自动设置为"ZJRCU" + +**Step 3: 验证Swagger文档更新** + +访问: http://localhost:8080/swagger-ui/index.html + +Expected: checkParseStatus接口描述包含轮询说明 + +--- + +### Task 26: 完整流程测试 + +**Files:** +- 无文件修改 + +**Step 1: 执行完整的文件上传流程** + +参考设计文档5.2节的调用示例,依次测试: +1. getToken +2. uploadFile (需要准备测试文件) +3. checkParseStatus +4. getFileUploadStatus +5. getBankStatement + +**Step 2: 执行解析失败-删除流程** + +使用错误格式的文件测试: +1. uploadFile (错误文件) +2. checkParseStatus (失败) +3. deleteFiles + +**Step 3: 验证日志** + +```bash +tail -100 backend.log | grep "流水分析" +``` + +Expected: 看到完整的请求和响应日志 + +--- + +## 阶段十: 文档编写 + +### Task 27: 生成API文档 + +**Files:** +- Create: `doc/api-docs/lsfx-api-v3.md` + +**Step 1: 通过Swagger导出API文档** + +访问: http://localhost:8080/v3/api-docs + +复制JSON内容 + +**Step 2: 创建Markdown格式的API文档** + +基于Swagger文档和测试结果,编写完整的API文档,包含: +- 7个接口的详细说明 +- 请求参数 +- 响应格式 +- 调用示例 +- 错误码说明 + +**Step 3: 提交文档** + +```bash +git add doc/api-docs/lsfx-api-v3.md +git commit -m "docs(lsfx): 生成流水分析API文档v3" +``` + +--- + +### Task 28: 编写测试报告 + +**Files:** +- Create: `doc/test-scripts/lsfx-test-report-20260304.md` + +**Step 1: 编写测试报告** + +记录以下内容: +- 测试环境 +- 测试用例清单 +- 测试结果 +- 发现的问题 +- 建议 + +**Step 2: 提交测试报告** + +```bash +git add doc/test-scripts/lsfx-test-report-20260304.md +git commit -m "docs(lsfx): 添加流水分析接口测试报告" +``` + +--- + +### Task 29: 更新CLAUDE.md文档 + +**Files:** +- Modify: `CLAUDE.md` + +**Step 1: 更新流水分析模块说明** + +在ccdi-lsfx模块部分添加新接口说明: + +```markdown +### ccdi-lsfx 业务模块 (核心) + +**核心功能:** +- 获取访问令牌 (Token) +- 上传流水文件并解析 +- 拉取行内流水数据 +- 查询解析状态和结果 +- 获取单个文件上传状态 (新增) +- 删除流水文件 (新增) +- 获取银行流水明细 + +**主要组件:** +- LsfxAnalysisClient: 流水分析平台客户端(7个接口方法) +- LsfxTestController: 测试接口(7个接口) +``` + +**Step 2: 提交文档更新** + +```bash +git add CLAUDE.md +git commit -m "docs: 更新CLAUDE.md流水分析模块说明" +``` + +--- + +## 阶段十一: 最终验证 + +### Task 30: 代码质量检查 + +**Files:** +- 无文件修改 + +**Step 1: 检查代码规范** + +```bash +# 检查是否有编译警告 +mvn clean compile 2>&1 | grep -i "warning" || echo "无编译警告" + +# 检查代码格式 +find ccdi-lsfx/src -name "*.java" -exec grep -l "import java.util.List" {} \; | wc -l +``` + +Expected: 无警告,import语句规范 + +**Step 2: 检查日志完整性** + +```bash +grep -r "log.info" ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java | wc -l +``` + +Expected: 每个方法至少有开始和成功日志 + +**Step 3: 检查注释完整性** + +```bash +grep -r "@Operation" ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java | wc -l +``` + +Expected: 7个接口都有Swagger注解 + +--- + +### Task 31: 停止测试服务 + +**Files:** +- 无文件修改 + +**Step 1: 停止后端服务** + +```bash +if [ -f backend.pid ]; then + kill $(cat backend.pid) || true + rm backend.pid +fi +``` + +**Step 2: 停止Mock Server** + +```bash +if [ -f mock.pid ]; then + kill $(cat mock.pid) || true + rm mock.pid +fi +``` + +**Step 3: 清理日志文件** + +```bash +rm -f backend.log mock.log +``` + +--- + +### Task 32: 最终提交和总结 + +**Files:** +- 无文件修改 + +**Step 1: 查看所有提交** + +```bash +git log --oneline --all -20 +``` + +Expected: 看到本次更新的所有提交 + +**Step 2: 推送到远程仓库(如需要)** + +```bash +git push origin dev +``` + +**Step 3: 生成变更摘要** + +```bash +git diff master --stat > doc/implementation/lsfx-update-20260304-summary.txt +``` + +--- + +## 验收标准 + +### 功能验收 ✅ +- [x] 所有7个接口都能正常调用 +- [x] 参数校验正确(必填、格式、范围) +- [x] 响应格式符合文档定义 +- [x] 错误处理完善(异常捕获、日志记录) + +### 代码质量 ✅ +- [x] 遵循项目编码规范 +- [x] 代码注释完整清晰 +- [x] 日志记录完善 +- [x] 无编译警告 + +### 文档完整性 ✅ +- [x] API文档完整(lsfx-api-v3.md) +- [x] 测试报告完整(lsfx-test-report-20260304.md) +- [x] 设计文档已保存(2026-03-04-lsfx-interface-update-design.md) +- [x] CLAUDE.md已更新 + +--- + +## 故障排查 + +### 问题1: 编译失败 + +**可能原因:** import语句缺失或类路径错误 + +**解决方案:** +```bash +mvn clean compile +# 查看具体错误信息 +mvn compile -X +``` + +### 问题2: 接口调用404 + +**可能原因:** 接口路径配置错误或服务未启动 + +**解决方案:** +```bash +# 检查服务是否启动 +curl http://localhost:8080/actuator/health + +# 检查接口路径 +cat ruoyi-admin/src/main/resources/application-dev.yml | grep endpoints -A 10 +``` + +### 问题3: Mock Server无法启动 + +**可能原因:** 端口8000被占用或Python依赖缺失 + +**解决方案:** +```bash +# 检查端口 +lsof -i :8000 + +# 安装依赖 +pip install flask +``` + +### 问题4: 参数校验不生效 + +**可能原因:** 未正确设置@RequestParam或@RequestBody + +**解决方案:** +```bash +# 检查Controller方法签名 +cat ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java | grep -A 10 "getFileUploadStatus" +``` + +--- + +## 注意事项 + +1. **TDD原则**: 虽然本计划没有为每个步骤编写单元测试代码,但在实际执行时应先写测试再写实现 +2. **频繁提交**: 每完成一个小功能点就提交,便于回滚和追踪 +3. **日志记录**: 确保所有关键操作都有日志记录 +4. **参数校验**: Controller层必须进行参数校验 +5. **默认值处理**: 使用常量类中的默认值,不要硬编码 +6. **Mock数据一致性**: Mock Server返回的数据必须与文档一致 +7. **Swagger文档**: 每个接口必须有完整的@Operation和@Parameter注解 +8. **代码规范**: 遵循项目CLAUDE.md中定义的编码规范 + +--- + +**计划结束**