# 流水分析平台对接实施计划

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**目标:** 实现与见知现金流尽调系统的对接，封装7个核心接口调用

**架构:** 创建ccdi-lsfx独立模块，使用RestTemplate发起HTTP请求，通过HttpUtil工具类统一处理各类请求，LsfxAnalysisClient封装7个接口调用逻辑

**技术栈:** Spring Boot 3.5.8, RestTemplate, Lombok, SpringDoc OpenAPI

---

## 前置准备

### Task 0: 更新根POM和Admin POM

**文件:**
- 修改: `pom.xml:221-230`
- 修改: `ruoyi-admin/pom.xml`

**步骤 1: 在根pom.xml中添加ccdi-lsfx模块**

在 `<modules>` 标签内添加新模块：

```xml
<modules>
    <module>ruoyi-admin</module>
    <module>ruoyi-framework</module>
    <module>ruoyi-system</module>
    <module>ruoyi-quartz</module>
    <module>ruoyi-generator</module>
    <module>ruoyi-common</module>
    <module>ccdi-info-collection</module>
    <module>ccdi-project</module>
    <module>ccdi-lsfx</module>
</modules>
```

**步骤 2: 在ruoyi-admin/pom.xml中添加ccdi-lsfx依赖**

在 `<dependencies>` 标签内添加：

```xml
<!-- 流水分析平台对接 -->
<dependency>
    <groupId>com.ruoyi</groupId>
    <artifactId>ccdi-lsfx</artifactId>
</dependency>
```

**步骤 3: 提交更改**

```bash
git add pom.xml ruoyi-admin/pom.xml
git commit -m "chore: 添加ccdi-lsfx模块依赖"
```

---

## 模块基础结构

### Task 1: 创建ccdi-lsfx模块目录和pom.xml

**文件:**
- 创建: `ccdi-lsfx/pom.xml`

**步骤 1: 创建pom.xml文件**

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi</artifactId>
        <version>3.9.1</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <artifactId>ccdi-lsfx</artifactId>

    <description>流水分析平台对接模块</description>

    <dependencies>
        <!-- 通用工具 -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>

        <!-- Spring Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- SpringDoc OpenAPI (Swagger) -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        </dependency>
    </dependencies>
</project>
```

**步骤 2: 创建基础目录结构**

```bash
mkdir -p ccdi-lsfx/src/main/java/com/ruoyi/lsfx/{config,constants,client,domain/{request,response},exception,util,controller}
mkdir -p ccdi-lsfx/src/main/resources
```

**步骤 3: 提交更改**

```bash
git add ccdi-lsfx/
git commit -m "feat: 创建ccdi-lsfx模块基础结构"
```

---

## 配置层

### Task 2: 添加配置到application-dev.yml

**文件:**
- 修改: `ruoyi-admin/src/main/resources/application-dev.yml`

**步骤 1: 在文件末尾添加lsfx配置**

```yaml
# 流水分析平台配置
lsfx:
  api:
    # 测试环境
    base-url: http://158.234.196.5:82/c4c3
    # 生产环境（注释掉测试环境后启用）
    # base-url: http://64.202.32.176/c4c3

    # 认证配置
    app-id: remote_app
    app-secret: your_app_secret_here  # 从见知获取
    client-id: c2017e8d105c435a96f86373635b6a09  # 测试环境固定值

    # 接口路径配置
    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
      generate-report: /watson/api/project/confirmStageUploadLogs
      check-report-status: /watson/api/project/upload/getallpendings
      get-bank-statement: /watson/api/project/upload/getBankStatement

    # RestTemplate配置
    connection-timeout: 30000  # 连接超时30秒
    read-timeout: 60000        # 读取超时60秒
```

**步骤 2: 提交更改**

```bash
git add ruoyi-admin/src/main/resources/application-dev.yml
git commit -m "config: 添加流水分析平台配置"
```

---

### Task 3: 创建RestTemplate配置类

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/config/RestTemplateConfig.java`

**步骤 1: 创建配置类**

```java
package com.ruoyi.lsfx.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplate配置
 */
@Configuration
public class RestTemplateConfig {

    @Value("${lsfx.api.connection-timeout:30000}")
    private int connectionTimeout;

    @Value("${lsfx.api.read-timeout:60000}")
    private int readTimeout;

    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(connectionTimeout);
        factory.setReadTimeout(readTimeout);
        return new RestTemplate(factory);
    }
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/config/RestTemplateConfig.java
git commit -m "feat: 添加RestTemplate配置类"
```

---

### Task 4: 创建常量类

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/constants/LsfxConstants.java`

**步骤 1: 创建常量类**

```java
package com.ruoyi.lsfx.constants;

/**
 * 流水分析平台常量
 */
public class LsfxConstants {

    /** 基础URL配置键 */
    public static final String BASE_URL_KEY = "lsfx.api.base-url";

    /** 成功状态码 */
    public static final String SUCCESS_CODE = "200";

    /** 文件解析成功状态 */
    public static final int PARSE_SUCCESS_STATUS = -5;
    public static final String PARSE_SUCCESS_DESC = "data.wait.confirm.newaccount";

    /** 数据渠道编码 */
    public static final String DATA_CHANNEL_ZJRCU = "ZJRCU";

    /** 分析类型 */
    public static final String ANALYSIS_TYPE = "-1";

    /** 请求头 */
    public static final String HEADER_CLIENT_ID = "X-Xencio-Client-Id";
    public static final String HEADER_CONTENT_TYPE = "Content-Type";

    /** 默认角色 */
    public static final String DEFAULT_ROLE = "VIEWER";
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/constants/LsfxConstants.java
git commit -m "feat: 添加流水分析常量类"
```

---

## 工具类层

### Task 5: 创建MD5工具类

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/MD5Util.java`

**步骤 1: 创建MD5工具类**

```java
package com.ruoyi.lsfx.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * MD5加密工具类
 */
public class MD5Util {

    /**
     * MD5加密
     * @param input 待加密字符串
     * @return MD5加密后的32位小写字符串
     */
    public static String encrypt(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes());
            StringBuilder hexString = new StringBuilder();
            for (byte b : messageDigest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5加密失败", e);
        }
    }

    /**
     * 生成安全码
     * @param projectNo 项目编号
     * @param entityName 项目名称
     * @param appSecret 应用密钥
     * @return MD5安全码
     */
    public static String generateSecretCode(String projectNo, String entityName, String appSecret) {
        String raw = projectNo + "_" + entityName + "_" + appSecret;
        return encrypt(raw);
    }
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/MD5Util.java
git commit -m "feat: 添加MD5加密工具类"
```

---

### Task 6: 创建HttpUtil工具类

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/HttpUtil.java`

**步骤 1: 创建HttpUtil工具类**

```java
package com.ruoyi.lsfx.util;

import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Map;

/**
 * HTTP请求工具类
 */
@Component
public class HttpUtil {

    @Resource
    private RestTemplate restTemplate;

    /**
     * 发送GET请求（带请求头）
     * @param url 请求URL
     * @param headers 请求头
     * @param responseType 响应类型
     * @return 响应对象
     */
    public <T> T get(String url, Map<String, String> headers, Class<T> responseType) {
        HttpHeaders httpHeaders = createHeaders(headers);
        HttpEntity<Void> requestEntity = new HttpEntity<>(httpHeaders);

        ResponseEntity<T> response = restTemplate.exchange(
            url, HttpMethod.GET, requestEntity, responseType
        );
        return response.getBody();
    }

    /**
     * 发送POST请求（JSON格式，带请求头）
     * @param url 请求URL
     * @param request 请求对象
     * @param headers 请求头
     * @param responseType 响应类型
     * @return 响应对象
     */
    public <T> T postJson(String url, Object request, Map<String, String> headers, Class<T> responseType) {
        HttpHeaders httpHeaders = createHeaders(headers);
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<Object> requestEntity = new HttpEntity<>(request, httpHeaders);

        ResponseEntity<T> response = restTemplate.postForEntity(url, requestEntity, responseType);
        return response.getBody();
    }

    /**
     * 上传文件（Multipart格式）
     * @param url 请求URL
     * @param params 参数（包含文件）
     * @param headers 请求头
     * @param responseType 响应类型
     * @return 响应对象
     */
    public <T> T uploadFile(String url, Map<String, Object> params, Map<String, String> headers, Class<T> responseType) {
        HttpHeaders httpHeaders = createHeaders(headers);
        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);

        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        if (params != null) {
            params.forEach(body::add);
        }

        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, httpHeaders);

        ResponseEntity<T> response = restTemplate.postForEntity(url, requestEntity, responseType);
        return response.getBody();
    }

    /**
     * 创建请求头
     * @param headers 请求头Map
     * @return HttpHeaders对象
     */
    private HttpHeaders createHeaders(Map<String, String> headers) {
        HttpHeaders httpHeaders = new HttpHeaders();
        if (headers != null && !headers.isEmpty()) {
            headers.forEach(httpHeaders::set);
        }
        return httpHeaders;
    }
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/HttpUtil.java
git commit -m "feat: 添加HTTP请求工具类"
```

---

## 异常处理层

### Task 7: 创建异常类

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/exception/LsfxApiException.java`

**步骤 1: 创建异常类**

```java
package com.ruoyi.lsfx.exception;

/**
 * 流水分析平台API异常
 */
public class LsfxApiException extends RuntimeException {

    private String errorCode;

    public LsfxApiException(String message) {
        super(message);
    }

    public LsfxApiException(String message, Throwable cause) {
        super(message, cause);
    }

    public LsfxApiException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public String getErrorCode() {
        return errorCode;
    }
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/exception/LsfxApiException.java
git commit -m "feat: 添加流水分析API异常类"
```

---

## DTO层 - 接口1（获取Token）

### Task 8: 创建GetTokenRequest

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/request/GetTokenRequest.java`

**步骤 1: 创建请求DTO**

```java
package com.ruoyi.lsfx.domain.request;

import lombok.Data;

/**
 * 获取Token请求参数
 */
@Data
public class GetTokenRequest {

    /** 项目编号 */
    private String projectNo;

    /** 项目名称 */
    private String entityName;

    /** 操作人员编号 */
    private String userId;

    /** 操作人员姓名 */
    private String userName;

    /** 见知提供appId */
    private String appId;

    /** 安全码 md5(projectNo + "_" + entityName + "_" + appSecret) */
    private String appSecretCode;

    /** 人员角色 */
    private String role;

    /** 行社机构号 */
    private String orgCode;

    /** 企业统信码或个人身份证号 */
    private String entityId;

    /** 信贷关联人信息 */
    private String xdRelatedPersons;

    /** 金综链流水日期ID */
    private String jzDataDateId;

    /** 行内流水开始日期 */
    private String innerBSStartDateId;

    /** 行内流水结束日期 */
    private String innerBSEndDateId;

    /** 分析类型 */
    private String analysisType;

    /** 客户经理所属营业部机构编码 */
    private String departmentCode;
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/request/GetTokenRequest.java
git commit -m "feat: 添加获取Token请求DTO"
```

---

### Task 9: 创建GetTokenResponse

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetTokenResponse.java`

**步骤 1: 创建响应DTO**

```java
package com.ruoyi.lsfx.domain.response;

import lombok.Data;

/**
 * 获取Token响应
 */
@Data
public class GetTokenResponse {

    /** 返回码 */
    private String code;

    /** 响应状态 */
    private String status;

    /** 消息 */
    private String message;

    /** 成功标识 */
    private Boolean successResponse;

    /** 响应数据 */
    private TokenData data;

    @Data
    public static class TokenData {
        /** token */
        private String token;

        /** 见知项目Id */
        private Integer projectId;

        /** 项目编号 */
        private String projectNo;

        /** 项目名称 */
        private String entityName;

        /** 分析类型 */
        private Integer analysisType;
    }
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetTokenResponse.java
git commit -m "feat: 添加获取Token响应DTO"
```

---

## DTO层 - 接口2-7（简化示例）

### Task 10: 创建其他接口DTO（批量创建）

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/request/FetchInnerFlowRequest.java`
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/request/GenerateReportRequest.java`
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/request/GetBankStatementRequest.java`
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/UploadFileResponse.java`
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/FetchInnerFlowResponse.java`
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/CheckParseStatusResponse.java`
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GenerateReportResponse.java`
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/CheckReportStatusResponse.java`
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetBankStatementResponse.java`

**步骤 1: 参考设计文档创建各个DTO类**

根据 `docs/design/2026-03-02-lsfx-integration-design.md` 中的DTO设计，创建剩余的请求和响应对象。每个DTO都使用 `@Data` 注解，字段根据接口文档定义。

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/
git commit -m "feat: 添加其他接口的DTO对象"
```

---

## 客户端层

### Task 11: 创建LsfxAnalysisClient（接口1-3）

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java`

**步骤 1: 创建客户端类（第一部分）**

```java
package com.ruoyi.lsfx.client;

import com.ruoyi.lsfx.constants.LsfxConstants;
import com.ruoyi.lsfx.domain.request.*;
import com.ruoyi.lsfx.domain.response.*;
import com.ruoyi.lsfx.util.HttpUtil;
import com.ruoyi.lsfx.util.MD5Util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * 流水分析平台客户端
 */
@Component
public class LsfxAnalysisClient {

    @Resource
    private HttpUtil httpUtil;

    @Value("${lsfx.api.base-url}")
    private String baseUrl;

    @Value("${lsfx.api.app-id}")
    private String appId;

    @Value("${lsfx.api.app-secret}")
    private String appSecret;

    @Value("${lsfx.api.client-id}")
    private String clientId;

    /**
     * 获取Token
     */
    public GetTokenResponse getToken(GetTokenRequest request) {
        String secretCode = MD5Util.generateSecretCode(
            request.getProjectNo(),
            request.getEntityName(),
            appSecret
        );
        request.setAppSecretCode(secretCode);
        request.setAppId(appId);

        if (request.getAnalysisType() == null) {
            request.setAnalysisType(LsfxConstants.ANALYSIS_TYPE);
        }
        if (request.getRole() == null) {
            request.setRole(LsfxConstants.DEFAULT_ROLE);
        }

        String url = baseUrl + "/account/common/getToken";
        return httpUtil.postJson(url, request, GetTokenResponse.class);
    }

    /**
     * 上传文件
     */
    public UploadFileResponse uploadFile(Integer groupId, Resource file) {
        String url = baseUrl + "/watson/api/project/remoteUploadSplitFile";

        Map<String, Object> params = new HashMap<>();
        params.put("groupId", groupId);
        params.put("files", file);

        Map<String, String> headers = new HashMap<>();
        headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);

        return httpUtil.uploadFile(url, params, headers, UploadFileResponse.class);
    }

    /**
     * 拉取行内流水
     */
    public FetchInnerFlowResponse fetchInnerFlow(FetchInnerFlowRequest request) {
        String url = baseUrl + "/watson/api/project/getJZFileOrZjrcuFile";

        Map<String, String> headers = new HashMap<>();
        headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);

        return httpUtil.postJson(url, request, headers, FetchInnerFlowResponse.class);
    }
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java
git commit -m "feat: 添加流水分析客户端（接口1-3）"
```

---

### Task 12: 完善LsfxAnalysisClient（接口4-7）

**文件:**
- 修改: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java`

**步骤 1: 添加剩余接口方法**

在LsfxAnalysisClient类中添加：

```java
    /**
     * 检查文件解析状态
     */
    public CheckParseStatusResponse checkParseStatus(Integer groupId, String inprogressList) {
        String url = baseUrl + "/watson/api/project/upload/getpendings";

        Map<String, Object> params = new HashMap<>();
        params.put("groupId", groupId);
        params.put("inprogressList", inprogressList);

        Map<String, String> headers = new HashMap<>();
        headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);

        return httpUtil.postJson(url, params, headers, CheckParseStatusResponse.class);
    }

    /**
     * 生成尽调报告
     */
    public GenerateReportResponse generateReport(GenerateReportRequest request) {
        String url = baseUrl + "/watson/api/project/confirmStageUploadLogs";

        Map<String, String> headers = new HashMap<>();
        headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);

        return httpUtil.postJson(url, request, headers, GenerateReportResponse.class);
    }

    /**
     * 检查报告生成状态
     */
    public CheckReportStatusResponse checkReportStatus(Integer groupId) {
        String url = baseUrl + "/watson/api/project/upload/getallpendings?groupId=" + groupId;

        Map<String, String> headers = new HashMap<>();
        headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);

        return httpUtil.get(url, headers, CheckReportStatusResponse.class);
    }

    /**
     * 获取银行流水
     */
    public GetBankStatementResponse getBankStatement(GetBankStatementRequest request) {
        String url = baseUrl + "/watson/api/project/upload/getBankStatement";

        Map<String, String> headers = new HashMap<>();
        headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);

        return httpUtil.postJson(url, request, headers, GetBankStatementResponse.class);
    }
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java
git commit -m "feat: 完善流水分析客户端（接口4-7）"
```

---

## 控制器层

### Task 13: 创建测试Controller

**文件:**
- 创建: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java`

**步骤 1: 创建测试Controller**

```java
package com.ruoyi.lsfx.controller;

import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.domain.request.*;
import com.ruoyi.lsfx.domain.response.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.Resource;

import javax.annotation.Resource;

/**
 * 流水分析平台接口测试控制器
 */
@Tag(name = "流水分析平台接口测试", description = "用于测试流水分析平台的7个接口")
@RestController
@RequestMapping("/lsfx/test")
public class LsfxTestController {

    @Resource
    private LsfxAnalysisClient lsfxAnalysisClient;

    @Operation(summary = "获取Token", description = "创建项目并获取访问Token")
    @PostMapping("/getToken")
    public AjaxResult getToken(@RequestBody GetTokenRequest request) {
        GetTokenResponse response = lsfxAnalysisClient.getToken(request);
        return AjaxResult.success(response);
    }

    @Operation(summary = "上传流水文件", description = "上传银行流水文件到流水分析平台")
    @PostMapping("/uploadFile")
    public AjaxResult uploadFile(
        @Parameter(description = "项目ID") @RequestParam Integer groupId,
        @Parameter(description = "流水文件") @RequestParam("file") MultipartFile file
    ) {
        Resource fileResource = file.getResource();
        UploadFileResponse response = lsfxAnalysisClient.uploadFile(groupId, fileResource);
        return AjaxResult.success(response);
    }

    @Operation(summary = "拉取行内流水", description = "从数仓拉取行内流水数据")
    @PostMapping("/fetchInnerFlow")
    public AjaxResult fetchInnerFlow(@RequestBody FetchInnerFlowRequest request) {
        FetchInnerFlowResponse response = lsfxAnalysisClient.fetchInnerFlow(request);
        return AjaxResult.success(response);
    }

    @Operation(summary = "检查文件解析状态", description = "轮询检查上传文件的解析状态")
    @PostMapping("/checkParseStatus")
    public AjaxResult checkParseStatus(
        @Parameter(description = "项目ID") @RequestParam Integer groupId,
        @Parameter(description = "文件ID列表") @RequestParam String inprogressList
    ) {
        CheckParseStatusResponse response = lsfxAnalysisClient.checkParseStatus(groupId, inprogressList);
        return AjaxResult.success(response);
    }

    @Operation(summary = "生成尽调报告", description = "确认文件后生成尽调报告")
    @PostMapping("/generateReport")
    public AjaxResult generateReport(@RequestBody GenerateReportRequest request) {
        GenerateReportResponse response = lsfxAnalysisClient.generateReport(request);
        return AjaxResult.success(response);
    }

    @Operation(summary = "检查报告生成状态", description = "轮询检查尽调报告生成状态")
    @GetMapping("/checkReportStatus")
    public AjaxResult checkReportStatus(
        @Parameter(description = "项目ID") @RequestParam Integer groupId
    ) {
        CheckReportStatusResponse response = lsfxAnalysisClient.checkReportStatus(groupId);
        return AjaxResult.success(response);
    }

    @Operation(summary = "获取银行流水列表", description = "分页获取银行流水数据")
    @PostMapping("/getBankStatement")
    public AjaxResult getBankStatement(@RequestBody GetBankStatementRequest request) {
        GetBankStatementResponse response = lsfxAnalysisClient.getBankStatement(request);
        return AjaxResult.success(response);
    }
}
```

**步骤 2: 提交更改**

```bash
git add ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java
git commit -m "feat: 添加流水分析测试控制器"
```

---

## 测试验证

### Task 14: 编译项目验证依赖

**文件:**
- 无文件修改

**步骤 1: 编译项目**

```bash
mvn clean compile
```

预期：编译成功，无错误

**步骤 2: 修复可能的编译错误**

如果出现编译错误，检查：
1. 所有import语句是否正确
2. Lombok是否正确配置
3. 依赖是否正确添加

---

### Task 15: 启动应用验证配置

**文件:**
- 无文件修改

**步骤 1: 启动应用**

提示用户手动启动应用：

```bash
# 方式1: 使用Maven
mvn spring-boot:run

# 方式2: 使用启动脚本（Windows）
ry.bat

# 方式3: 使用启动脚本（Linux/Mac）
./ry.sh start
```

**步骤 2: 验证启动成功**

检查日志中是否包含：
- RestTemplate bean创建成功
- ccdi-lsfx模块加载成功

访问：http://localhost:8080/swagger-ui/index.html

预期：在Swagger UI中能看到 "流水分析平台接口测试" 分组

---

### Task 16: 测试接口1（获取Token）

**文件:**
- 无文件修改

**步骤 1: 通过Swagger测试获取Token接口**

访问：http://localhost:8080/swagger-ui/index.html

找到 "流水分析平台接口测试" → "获取Token"

请求示例：
```json
{
  "projectNo": "test-project-001",
  "entityName": "测试项目001",
  "userId": "test001",
  "userName": "测试用户001",
  "orgCode": "800000",
  "departmentCode": "800111"
}
```

预期：返回200状态码，包含token和projectId

**步骤 2: 记录测试结果**

如果成功，记录返回的 token 和 projectId，后续测试需要使用。

---

## 完成标记

### Task 17: 标记实施完成

**文件:**
- 无文件修改

**步骤 1: 确认所有功能正常**

检查清单：
- [ ] 项目编译成功
- [ ] 应用启动成功
- [ ] Swagger UI可访问
- [ ] 获取Token接口测试通过
- [ ] 其他接口可正常调用（可选）

**步骤 2: 创建完成标记**

```bash
git tag -a v1.0.0-lsfx -m "完成流水分析平台对接模块"
git push origin v1.0.0-lsfx
```

---

## 备注

### 关键注意事项

1. **安全码生成**: Token接口需要MD5加密，格式为 `projectNo_entityName_appSecret`
2. **请求头设置**: 除Token接口外，其他接口都需要设置 `X-Xencio-Client-Id` 请求头
3. **文件上传**: 上传文件接口使用 `multipart/form-data` 格式
4. **配置管理**: 生产环境需要修改 `application-dev.yml` 中的 `base-url` 和 `client-id`
5. **app-secret**: 需要从见知获取真实的 `app-secret` 配置

### 后续优化建议

1. 添加接口调用日志记录
2. 实现接口调用失败重试机制
3. 添加接口响应数据缓存
4. 实现流水数据持久化到本地数据库
5. 添加接口调用的单元测试和集成测试
