Merge branch 'feature/bank-statement-entity' into dev

实现银行流水实体类和转换功能:
- 添加 ccdi_bank_statement 表的 project_id 字段
- 创建 CcdiBankStatement 实体类(39个字段)
- 实现 fromResponse() 转换方法(支持9个字段映射)
- 创建 Mapper 接口和 XML 映射文件
- 完整的单元测试覆盖
This commit is contained in:
wkc
2026-03-04 16:21:47 +08:00
6 changed files with 440 additions and 1 deletions

7
.gitignore vendored
View File

@@ -47,7 +47,12 @@ nul
# Git Worktrees
.worktrees/
test/
# Test output directories (not source code)
**/target/test-classes/
**/target/surefire-reports/
# Test data files (keep test source code)
*.test.log
!*/build/*.java
!*/build/*.html

View File

@@ -0,0 +1,213 @@
package com.ruoyi.ccdi.project.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.lsfx.domain.response.GetBankStatementResponse.BankStatementItem;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 银行流水对象 ccdi_bank_statement
*
* @author ruoyi
* @date 2026-03-04
*/
@Data
@TableName("ccdi_bank_statement")
public class CcdiBankStatement implements Serializable {
private static final Logger log = LoggerFactory.getLogger(CcdiBankStatement.class);
@Serial
private static final long serialVersionUID = 1L;
// ===== 主键和关联字段 =====
/** 流水ID */
@TableId(type = IdType.AUTO)
private Long bankStatementId;
/** 关联项目ID业务字段 */
private Long projectId;
/** 企业ID */
private Integer leId;
/** 账号ID */
private Long accountId;
/** 项目id保留原有字段 */
private Integer groupId;
// ===== 账号信息 =====
/** 企业账号名称 */
private String leAccountName;
/** 企业银行账号 */
private String leAccountNo;
/** 账号日期ID */
private Integer accountingDateId;
/** 账号日期 */
private String accountingDate;
/** 交易日期 */
private String trxDate;
/** 币种 */
private String currency;
// ===== 交易金额 =====
/** 付款金额 */
private BigDecimal amountDr;
/** 收款金额 */
private BigDecimal amountCr;
/** 余额 */
private BigDecimal amountBalance;
// ===== 交易类型和标志 =====
/** 交易类型 */
private String cashType;
/** 交易标志位 */
private String trxFlag;
/** 分类ID */
private Integer trxType;
/** 异常类型 */
private String exceptionType;
/** 是否为内部交易 */
private Integer internalFlag;
// ===== 对手方信息 =====
/** 对手方企业ID */
private Integer customerLeId;
/** 对手方企业名称 */
private String customerAccountName;
/** 对手方账号 */
private String customerAccountNo;
/** 对手方银行 */
private String customerBank;
/** 对手方备注 */
private String customerReference;
// ===== 摘要和备注 =====
/** 用户交易摘要 */
private String userMemo;
/** 银行交易摘要 */
private String bankComments;
/** 银行交易号 */
private String bankTrxNumber;
// ===== 银行信息 =====
/** 所属银行缩写 */
private String bank;
// ===== 批次和上传信息 =====
/** 上传logId */
private Integer batchId;
/** 每次上传在文件中的line */
private Integer batchSequence;
// ===== 附加字段 =====
/** meta json固定为null */
private String metaJson;
/** 是否包含余额 */
private Integer noBalance;
/** 初始余额 */
private Integer beginBalance;
/** 结束余额 */
private Integer endBalance;
/** 覆盖标识 */
private Long overrideBsId;
/** 交易方式 */
private String paymentMethod;
/** 身份证号 */
private String cretNo;
// ===== 审计字段 =====
/** 创建时间 */
private Date createDate;
/** 创建者 */
private Long createdBy;
/**
* 从流水分析接口响应转换为实体
*
* @param item 流水分析接口返回的流水项
* @return 流水实体,如果 item 为 null 则返回 null
*/
public static CcdiBankStatement fromResponse(BankStatementItem item) {
// 1. 空值检查
if (item == null) {
log.warn("流水项为空,无法转换");
return null;
}
try {
// 2. 创建实体对象
CcdiBankStatement entity = new CcdiBankStatement();
// 3. 使用 BeanUtils 复制同名字段
BeanUtils.copyProperties(item, entity);
// 4. 手动映射字段名不一致的情况
entity.setLeAccountNo(item.getAccountMaskNo());
entity.setCustomerAccountNo(item.getCustomerAccountMaskNo());
entity.setLeAccountName(item.getLeName());
entity.setAmountDr(item.getDrAmount());
entity.setAmountCr(item.getCrAmount());
entity.setAmountBalance(item.getBalanceAmount());
entity.setTrxFlag(item.getTransFlag());
entity.setTrxType(item.getTransTypeId());
entity.setCustomerLeId(item.getCustomerId());
entity.setCustomerAccountName(item.getCustomerName());
// 5. 特殊字段处理
entity.setMetaJson(null); // 根据文档要求强制设为 null
// 注意project_id 需要在 Service 层根据业务逻辑设置
return entity;
} catch (Exception e) {
log.error("流水数据转换失败, bankStatementId={}", item.getBankStatementId(), e);
throw new RuntimeException("流水数据转换失败", e);
}
}
}

View File

@@ -0,0 +1,24 @@
package com.ruoyi.ccdi.project.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 银行流水Mapper接口
*
* @author ruoyi
* @date 2026-03-04
*/
public interface CcdiBankStatementMapper extends BaseMapper<CcdiBankStatement> {
/**
* 批量插入银行流水
*
* @param list 银行流水列表
* @return 插入记录数
*/
int insertBatch(@Param("list") List<CcdiBankStatement> list);
}

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper">
<resultMap type="com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement" id="CcdiBankStatementResult">
<id property="bankStatementId" column="bank_statement_id" />
<result property="projectId" column="project_id" />
<result property="leId" column="LE_ID" />
<result property="accountId" column="ACCOUNT_ID" />
<result property="groupId" column="group_id" />
<result property="leAccountName" column="LE_ACCOUNT_NAME" />
<result property="leAccountNo" column="LE_ACCOUNT_NO" />
<result property="accountingDateId" column="ACCOUNTING_DATE_ID" />
<result property="accountingDate" column="ACCOUNTING_DATE" />
<result property="trxDate" column="TRX_DATE" />
<result property="currency" column="CURRENCY" />
<result property="amountDr" column="AMOUNT_DR" />
<result property="amountCr" column="AMOUNT_CR" />
<result property="amountBalance" column="AMOUNT_BALANCE" />
<result property="cashType" column="CASH_TYPE" />
<result property="customerLeId" column="CUSTOMER_LE_ID" />
<result property="customerAccountName" column="CUSTOMER_ACCOUNT_NAME" />
<result property="customerAccountNo" column="CUSTOMER_ACCOUNT_NO" />
<result property="customerBank" column="customer_bank" />
<result property="customerReference" column="customer_reference" />
<result property="userMemo" column="USER_MEMO" />
<result property="bankComments" column="BANK_COMMENTS" />
<result property="bankTrxNumber" column="BANK_TRX_NUMBER" />
<result property="bank" column="BANK" />
<result property="trxFlag" column="TRX_FLAG" />
<result property="trxType" column="TRX_TYPE" />
<result property="exceptionType" column="EXCEPTION_TYPE" />
<result property="internalFlag" column="internal_flag" />
<result property="batchId" column="batch_id" />
<result property="batchSequence" column="batch_sequence" />
<result property="createDate" column="CREATE_DATE" />
<result property="createdBy" column="created_by" />
<result property="metaJson" column="meta_json" />
<result property="noBalance" column="no_balance" />
<result property="beginBalance" column="begin_balance" />
<result property="endBalance" column="end_balance" />
<result property="overrideBsId" column="override_bs_id" />
<result property="paymentMethod" column="payment_method" />
<result property="cretNo" column="cret_no" />
</resultMap>
<sql id="selectCcdiBankStatementVo">
select bank_statement_id, project_id, LE_ID, ACCOUNT_ID, group_id,
LE_ACCOUNT_NAME, LE_ACCOUNT_NO, ACCOUNTING_DATE_ID, ACCOUNTING_DATE,
TRX_DATE, CURRENCY, AMOUNT_DR, AMOUNT_CR, AMOUNT_BALANCE,
CASH_TYPE, CUSTOMER_LE_ID, CUSTOMER_ACCOUNT_NAME, CUSTOMER_ACCOUNT_NO,
customer_bank, customer_reference, USER_MEMO, BANK_COMMENTS,
BANK_TRX_NUMBER, BANK, TRX_FLAG, TRX_TYPE, EXCEPTION_TYPE,
internal_flag, batch_id, batch_sequence, CREATE_DATE, created_by,
meta_json, no_balance, begin_balance, end_balance,
override_bs_id, payment_method, cret_no
from ccdi_bank_statement
</sql>
<insert id="insertBatch" parameterType="java.util.List">
insert into ccdi_bank_statement (
project_id, LE_ID, ACCOUNT_ID, group_id,
LE_ACCOUNT_NAME, LE_ACCOUNT_NO, ACCOUNTING_DATE_ID, ACCOUNTING_DATE,
TRX_DATE, CURRENCY, AMOUNT_DR, AMOUNT_CR, AMOUNT_BALANCE,
CASH_TYPE, CUSTOMER_LE_ID, CUSTOMER_ACCOUNT_NAME, CUSTOMER_ACCOUNT_NO,
customer_bank, customer_reference, USER_MEMO, BANK_COMMENTS,
BANK_TRX_NUMBER, BANK, TRX_FLAG, TRX_TYPE, EXCEPTION_TYPE,
internal_flag, batch_id, batch_sequence, CREATE_DATE, created_by,
meta_json, no_balance, begin_balance, end_balance,
override_bs_id, payment_method, cret_no
) values
<foreach collection="list" item="item" separator=",">
(
#{item.projectId}, #{item.leId}, #{item.accountId}, #{item.groupId},
#{item.leAccountName}, #{item.leAccountNo}, #{item.accountingDateId}, #{item.accountingDate},
#{item.trxDate}, #{item.currency}, #{item.amountDr}, #{item.amountCr}, #{item.amountBalance},
#{item.cashType}, #{item.customerLeId}, #{item.customerAccountName}, #{item.customerAccountNo},
#{item.customerBank}, #{item.customerReference}, #{item.userMemo}, #{item.bankComments},
#{item.bankTrxNumber}, #{item.bank}, #{item.trxFlag}, #{item.trxType}, #{item.exceptionType},
#{item.internalFlag}, #{item.batchId}, #{item.batchSequence}, #{item.createDate}, #{item.createdBy},
#{item.metaJson}, #{item.noBalance}, #{item.beginBalance}, #{item.endBalance},
#{item.overrideBsId}, #{item.paymentMethod}, #{item.cretNo}
)
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,94 @@
package com.ruoyi.ccdi.project.domain.entity;
import com.ruoyi.lsfx.domain.response.GetBankStatementResponse.BankStatementItem;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.*;
/**
* 银行流水实体类测试
*
* @author ruoyi
* @date 2026-03-04
*/
class CcdiBankStatementTest {
@Test
void testFromResponse_Success() {
// 准备测试数据
BankStatementItem item = new BankStatementItem();
item.setBankStatementId(123456L);
item.setLeId(100);
item.setAccountId(200L);
item.setLeName("测试企业");
item.setAccountMaskNo("6222****1234");
item.setDrAmount(new BigDecimal("1000.00"));
item.setCrAmount(new BigDecimal("500.00"));
item.setBalanceAmount(new BigDecimal("5000.00"));
item.setTrxDate("2026-03-04");
item.setCustomerAccountMaskNo("6228****5678");
// 执行转换
CcdiBankStatement entity = CcdiBankStatement.fromResponse(item);
// 验证结果
assertNotNull(entity, "转换结果不应为null");
assertEquals(123456L, entity.getBankStatementId(), "流水ID应该匹配");
assertEquals(100, entity.getLeId(), "企业ID应该匹配");
assertEquals(200L, entity.getAccountId(), "账号ID应该匹配");
assertEquals("测试企业", entity.getLeAccountName(), "企业名称应该匹配");
// 验证手动映射的字段
assertEquals("6222****1234", entity.getLeAccountNo(), "企业账号应该从 accountMaskNo 映射");
assertEquals("6228****5678", entity.getCustomerAccountNo(), "对手方账号应该从 customerAccountMaskNo 映射");
// 验证金额字段
assertEquals(new BigDecimal("1000.00"), entity.getAmountDr(), "付款金额应该匹配");
assertEquals(new BigDecimal("500.00"), entity.getAmountCr(), "收款金额应该匹配");
assertEquals(new BigDecimal("5000.00"), entity.getAmountBalance(), "余额应该匹配");
// 验证特殊字段
assertNull(entity.getMetaJson(), "metaJson 应该强制为 null");
assertNull(entity.getProjectId(), "projectId 应该为 null需要 Service 层设置)");
}
@Test
void testFromResponse_Null() {
// 测试空值处理
CcdiBankStatement entity = CcdiBankStatement.fromResponse(null);
// 验证返回 null
assertNull(entity, "传入 null 应该返回 null");
}
@Test
void testFromResponse_EmptyObject() {
// 测试空对象转换
BankStatementItem item = new BankStatementItem();
// 执行转换
CcdiBankStatement entity = CcdiBankStatement.fromResponse(item);
// 验证不会抛出异常
assertNotNull(entity, "空对象转换结果不应为 null");
assertNull(entity.getMetaJson(), "metaJson 应该为 null");
}
@Test
void testFromResponse_FieldTypeCompatibility() {
// 测试字段类型兼容性
BankStatementItem item = new BankStatementItem();
item.setInternalFlag(1); // Integer 类型
item.setTransTypeId(100); // Integer 类型
// 执行转换
CcdiBankStatement entity = CcdiBankStatement.fromResponse(item);
// 验证类型转换正确
assertNotNull(entity, "转换结果不应为 null");
assertEquals(1, entity.getInternalFlag(), "Integer 类型应该正确复制");
assertEquals(100, entity.getTrxType(), "Integer 类型应该正确复制");
}
}

View File

@@ -0,0 +1,14 @@
-- 为 ccdi_bank_statement 表添加 project_id 字段
-- 用途关联项目ID实现流水数据与项目的业务关联
-- 作者:系统自动生成
-- 日期2026-03-04
USE ccdi;
-- 添加 project_id 字段
ALTER TABLE `ccdi_bank_statement`
ADD COLUMN `project_id` bigint(20) DEFAULT NULL COMMENT '关联项目ID' AFTER `bank_statement_id`;
-- 添加索引以提升查询性能
ALTER TABLE `ccdi_bank_statement`
ADD INDEX `idx_project_id` (`project_id`);