diff --git a/.gitignore b/.gitignore index 01893db..ac26973 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java new file mode 100644 index 0000000..6fdb0bf --- /dev/null +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java @@ -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); + } + } +} diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java new file mode 100644 index 0000000..2f2262c --- /dev/null +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java @@ -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 { + + /** + * 批量插入银行流水 + * + * @param list 银行流水列表 + * @return 插入记录数 + */ + int insertBatch(@Param("list") List list); +} diff --git a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml new file mode 100644 index 0000000..c8c22dd --- /dev/null +++ b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + 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 + + ( + #{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} + ) + + + + diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java new file mode 100644 index 0000000..4be7e86 --- /dev/null +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java @@ -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 类型应该正确复制"); + } +} diff --git a/sql/ccdi_bank_statement_add_project_id.sql b/sql/ccdi_bank_statement_add_project_id.sql new file mode 100644 index 0000000..db42cfd --- /dev/null +++ b/sql/ccdi_bank_statement_add_project_id.sql @@ -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`);