Files
ccdi/docs/plans/2026-03-04-lsfx-interface-update-plan.md

1714 lines
40 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 流水分析接口功能更新实施计划
> **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<LogItem> logs;
/** 状态 */
private String status;
/** 账号ID */
private Integer accountId;
/** 币种 */
private String currency;
}
@Data
public static class LogItem {
/** 账号列表 */
private List<String> accountNoList;
/** 银行名称 */
private String bankName;
/** 数据类型信息 [格式, 分隔符] */
private List<String> dataTypeInfo;
/** 下载文件名 */
private String downloadFileName;
/** 主体名称列表(重要:用于判断是否需要生成主体) */
private List<String> 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<String> 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> T get(String url, Map<String, Object> params, Map<String, String> headers,
Class<T> 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<String> entity = new HttpEntity<>(httpHeaders);
// 执行GET请求
ResponseEntity<String> 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<String, Object> params = new HashMap<>();
params.put("groupId", request.getGroupId());
if (request.getLogId() != null) {
params.put("logId", request.getLogId());
}
Map<String, String> 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<String, Object> params = new HashMap<>();
params.put("groupId", request.getGroupId());
params.put("logIds", request.getLogIds()); // 数组
params.put("userId", request.getUserId());
Map<String, String> 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<String, Object> 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<String, Object> 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中定义的编码规范
---
**计划结束**