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

40 KiB
Raw Blame History

流水分析接口功能更新实施计划

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: 确认当前配置

cd ccdi-lsfx
cat pom.xml | grep -A 5 "artifactId"

Expected: 确认模块依赖正确

Step 2: 确认Mock Server配置

grep -A 10 "lsfx:" ../ruoyi-admin/src/main/resources/application-dev.yml

Expected:

Step 3: 确认项目可启动

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

在文件末尾的 } 之前添加:

    // 新增:固定值常量(根据文档)
    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: 验证编译

cd ccdi-lsfx
mvn compile

Expected: BUILD SUCCESS

Step 3: 提交

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

package com.ruoyi.lsfx.domain.request;

import lombok.Data;

/**
 * 获取单个文件上传状态请求(接口5)
 */
@Data
public class GetFileUploadStatusRequest {

    /** 项目ID (必填) */
    private Integer groupId;

    /** 文件ID (可选,不传则查询所有) */
    private Integer logId;
}

Step 2: 验证文件创建

cat src/main/java/com/ruoyi/lsfx/domain/request/GetFileUploadStatusRequest.java

Expected: 文件内容正确显示

Step 3: 提交

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

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: 验证文件创建

cat src/main/java/com/ruoyi/lsfx/domain/request/DeleteFilesRequest.java

Expected: 文件内容正确显示

Step 3: 提交

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

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: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 3: 提交

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

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: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 3: 提交

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字段

在类的字段区域添加:

    /** 校验码 (固定值"ZJRCU") */
    private String dataChannelCode;

Step 2: 验证修改

cat src/main/java/com/ruoyi/lsfx/domain/request/FetchInnerFlowRequest.java

Expected: 包含dataChannelCode字段

Step 3: 提交

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请求方法

在类的末尾 } 之前添加:

    /**
     * 发送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

在文件顶部添加:

import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.http.HttpMethod;

Step 3: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 4: 提交

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 部分添加:

    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: 验证修改

cat ../ruoyi-admin/src/main/resources/application-dev.yml | grep -A 10 "endpoints:"

Expected: 包含新添加的2个endpoint

Step 3: 提交

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配置注入

在类的字段区域添加:

    @Value("${lsfx.api.endpoints.get-file-upload-status}")
    private String getFileUploadStatusEndpoint;

    @Value("${lsfx.api.endpoints.delete-files}")
    private String deleteFilesEndpoint;

Step 2: 验证修改

cat src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java | grep -A 2 "get-file-upload-status"

Expected: 显示新添加的配置

Step 3: 提交

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方法

在类的末尾 } 之前添加:

    /**
     * 获取单个文件上传状态(接口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

在文件顶部添加:

import com.ruoyi.lsfx.domain.request.GetFileUploadStatusRequest;
import com.ruoyi.lsfx.domain.response.GetFileUploadStatusResponse;

Step 3: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 4: 提交

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方法之后添加:

    /**
     * 删除文件/主体(接口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

在文件顶部添加:

import com.ruoyi.lsfx.domain.request.DeleteFilesRequest;
import com.ruoyi.lsfx.domain.response.DeleteFilesResponse;
import java.util.Arrays;

Step 3: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 4: 提交

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构建部分,更新为:

            // 构建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

在文件顶部添加:

import com.ruoyi.common.utils.StringUtils;

Step 3: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 4: 提交

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构建部分,更新为:

            // 构建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: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 3: 提交

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方法,更新注释为:

    /**
     * 检查文件解析状态(接口4)
     *
     * 使用说明:
     * - 文件上传后需要轮询此接口,直到 parsing=false
     * - 建议轮询间隔: 1秒
     * - status=-5 且 uploadStatusDesc="data.wait.confirm.newaccount" 表示解析成功
     *
     * @param groupId 项目ID
     * @param inprogressList 文件ID列表(逗号分隔)
     * @return 解析状态
     */

Step 2: 验证修改

cat src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java | grep -A 10 "检查文件解析状态"

Expected: 显示更新的注释

Step 3: 提交

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接口

在类的末尾 } 之前添加:

    @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

在文件顶部添加:

import com.ruoyi.lsfx.domain.request.GetFileUploadStatusRequest;
import com.ruoyi.lsfx.domain.response.GetFileUploadStatusResponse;

Step 3: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 4: 提交

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方法之后添加:

    @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

在文件顶部添加:

import com.ruoyi.lsfx.domain.request.DeleteFilesRequest;
import com.ruoyi.lsfx.domain.response.DeleteFilesResponse;

Step 3: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 4: 提交

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方法,在参数校验之后添加:

        // 必填字段设置默认值
        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

确认文件顶部有:

import com.ruoyi.lsfx.constants.LsfxConstants;

Step 3: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 4: 提交

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方法,在参数校验之后添加:

        // 设置默认值
        if (StringUtils.isBlank(request.getDataChannelCode())) {
            request.setDataChannelCode(LsfxConstants.DEFAULT_DATA_CHANNEL_CODE);
        }

Step 2: 验证编译

mvn compile

Expected: BUILD SUCCESS

Step 3: 提交

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注解:

    @Operation(summary = "检查文件解析状态",
               description = "轮询检查上传文件的解析状态。建议间隔1秒轮询直到parsing=false。" +
                            "status=-5且uploadStatusDesc='data.wait.confirm.newaccount'表示解析成功")

Step 2: 验证修改

cat src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java | grep -A 5 "检查文件解析状态"

Expected: 显示更新的描述

Step 3: 提交

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接口

在文件中添加新的路由:

@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接口

@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启动

cd ../lsfx-mock-server
python app.py &

Expected: 服务启动在 http://localhost:8000

Step 4: 提交

git add ../lsfx-mock-server/app.py
git commit -m "feat(lsfx-mock): 添加获取文件状态和删除文件Mock接口"

阶段九: 测试验证

Task 21: 启动后端服务

Files:

  • 无文件修改

Step 1: 停止可能运行的旧服务

pkill -f "spring-boot:run" || true

Step 2: 启动Mock Server

cd lsfx-mock-server
python app.py > mock.log 2>&1 &
echo $! > mock.pid
cd ..

Expected: Mock Server启动成功

Step 3: 启动后端服务

cd ruoyi-admin
mvn spring-boot:run > ../backend.log 2>&1 &
echo $! > ../backend.pid
cd ..

Expected: 后端服务启动成功

Step 4: 等待服务启动

sleep 30
tail -20 backend.log

Expected: 看到"Started RuoYiApplication"日志


Task 22: 测试获取Token接口

Files:

  • 无文件修改

Step 1: 测试getToken接口(带默认值)

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保存到变量:

PROJECT_ID=16238  # 替换为实际返回的值

Task 23: 测试获取文件上传状态接口(新接口)

Files:

  • 无文件修改

Step 1: 测试查询单个文件状态

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: 测试查询所有文件状态

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: 测试删除单个文件

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默认值

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默认值

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: 验证日志

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: 提交文档

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: 提交测试报告

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模块部分添加新接口说明:

### ccdi-lsfx 业务模块 (核心)

**核心功能:**
- 获取访问令牌 (Token)
- 上传流水文件并解析
- 拉取行内流水数据
- 查询解析状态和结果
- 获取单个文件上传状态 (新增)
- 删除流水文件 (新增)
- 获取银行流水明细

**主要组件:**
- LsfxAnalysisClient: 流水分析平台客户端(7个接口方法)
- LsfxTestController: 测试接口(7个接口)

Step 2: 提交文档更新

git add CLAUDE.md
git commit -m "docs: 更新CLAUDE.md流水分析模块说明"

阶段十一: 最终验证

Task 30: 代码质量检查

Files:

  • 无文件修改

Step 1: 检查代码规范

# 检查是否有编译警告
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: 检查日志完整性

grep -r "log.info" ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java | wc -l

Expected: 每个方法至少有开始和成功日志

Step 3: 检查注释完整性

grep -r "@Operation" ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java | wc -l

Expected: 7个接口都有Swagger注解


Task 31: 停止测试服务

Files:

  • 无文件修改

Step 1: 停止后端服务

if [ -f backend.pid ]; then
  kill $(cat backend.pid) || true
  rm backend.pid
fi

Step 2: 停止Mock Server

if [ -f mock.pid ]; then
  kill $(cat mock.pid) || true
  rm mock.pid
fi

Step 3: 清理日志文件

rm -f backend.log mock.log

Task 32: 最终提交和总结

Files:

  • 无文件修改

Step 1: 查看所有提交

git log --oneline --all -20

Expected: 看到本次更新的所有提交

Step 2: 推送到远程仓库(如需要)

git push origin dev

Step 3: 生成变更摘要

git diff master --stat > doc/implementation/lsfx-update-20260304-summary.txt

验收标准

功能验收

  • 所有7个接口都能正常调用
  • 参数校验正确(必填、格式、范围)
  • 响应格式符合文档定义
  • 错误处理完善(异常捕获、日志记录)

代码质量

  • 遵循项目编码规范
  • 代码注释完整清晰
  • 日志记录完善
  • 无编译警告

文档完整性

  • API文档完整(lsfx-api-v3.md)
  • 测试报告完整(lsfx-test-report-20260304.md)
  • 设计文档已保存(2026-03-04-lsfx-interface-update-design.md)
  • CLAUDE.md已更新

故障排查

问题1: 编译失败

可能原因: import语句缺失或类路径错误

解决方案:

mvn clean compile
# 查看具体错误信息
mvn compile -X

问题2: 接口调用404

可能原因: 接口路径配置错误或服务未启动

解决方案:

# 检查服务是否启动
curl http://localhost:8080/actuator/health

# 检查接口路径
cat ruoyi-admin/src/main/resources/application-dev.yml | grep endpoints -A 10

问题3: Mock Server无法启动

可能原因: 端口8000被占用或Python依赖缺失

解决方案:

# 检查端口
lsof -i :8000

# 安装依赖
pip install flask

问题4: 参数校验不生效

可能原因: 未正确设置@RequestParam或@RequestBody

解决方案:

# 检查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中定义的编码规范

计划结束