From b948c846b11c9aec641199ad307355ab75a57219 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Mon, 16 Mar 2026 18:23:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A1=A5=E9=BD=90=E6=B5=81=E6=B0=B4?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E8=A7=84=E5=88=99=E5=88=86=E6=9E=90SQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/domain/vo/BankTagObjectHitVO.java | 19 + .../domain/vo/BankTagStatementHitVO.java | 22 ++ .../mapper/CcdiBankTagAnalysisMapper.java | 92 +++++ .../project/CcdiBankTagAnalysisMapper.xml | 366 ++++++++++++++++++ .../CcdiBankTagAnalysisMapperXmlTest.java | 47 +++ 5 files changed, 546 insertions(+) create mode 100644 ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagObjectHitVO.java create mode 100644 ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagStatementHitVO.java create mode 100644 ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java create mode 100644 ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml create mode 100644 ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagObjectHitVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagObjectHitVO.java new file mode 100644 index 00000000..5b3cdada --- /dev/null +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagObjectHitVO.java @@ -0,0 +1,19 @@ +package com.ruoyi.ccdi.project.domain.vo; + +import lombok.Data; + +/** + * 对象级标签命中结果 + */ +@Data +public class BankTagObjectHitVO { + + /** 对象类型 */ + private String objectType; + + /** 对象主键 */ + private String objectKey; + + /** 异常原因摘要 */ + private String reasonDetail; +} diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagStatementHitVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagStatementHitVO.java new file mode 100644 index 00000000..bd7033d2 --- /dev/null +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagStatementHitVO.java @@ -0,0 +1,22 @@ +package com.ruoyi.ccdi.project.domain.vo; + +import lombok.Data; + +/** + * 流水级标签命中结果 + */ +@Data +public class BankTagStatementHitVO { + + /** 流水ID */ + private Long bankStatementId; + + /** 项目分组ID */ + private Integer groupId; + + /** 上传日志ID */ + private Integer logId; + + /** 异常原因摘要 */ + private String reasonDetail; +} diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java new file mode 100644 index 00000000..23087beb --- /dev/null +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java @@ -0,0 +1,92 @@ +package com.ruoyi.ccdi.project.mapper; + +import com.ruoyi.ccdi.project.domain.vo.BankTagStatementHitVO; +import com.ruoyi.ccdi.project.domain.vo.BankTagObjectHitVO; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 流水标签分析 Mapper + */ +public interface CcdiBankTagAnalysisMapper { + + /** + * 房车消费支出交易 + * + * @param projectId 项目ID + * @return 流水命中结果 + */ + List selectHouseOrCarExpenseStatements(@Param("projectId") Long projectId); + + /** + * 税务支出交易 + * + * @param projectId 项目ID + * @return 流水命中结果 + */ + List selectTaxExpenseStatements(@Param("projectId") Long projectId); + + /** + * 大额单笔收入 + * + * @param projectId 项目ID + * @param threshold 单笔阈值 + * @return 流水命中结果 + */ + List selectSingleLargeIncomeStatements(@Param("projectId") Long projectId, + @Param("threshold") BigDecimal threshold); + + /** + * 累计收入超限对象 + * + * @param projectId 项目ID + * @param threshold 累计阈值 + * @return 对象命中结果 + */ + List selectCumulativeIncomeObjects(@Param("projectId") Long projectId, + @Param("threshold") BigDecimal threshold); + + /** + * 年流水交易额超限对象 + * + * @param projectId 项目ID + * @param threshold 年交易额阈值 + * @return 对象命中结果 + */ + List selectAnnualTurnoverObjects(@Param("projectId") Long projectId, + @Param("threshold") BigDecimal threshold); + + /** + * 大额存现交易 + * + * @param projectId 项目ID + * @param threshold 存现阈值 + * @return 流水命中结果 + */ + List selectLargeCashDepositStatements(@Param("projectId") Long projectId, + @Param("threshold") BigDecimal threshold); + + /** + * 短时间多次存现对象 + * + * @param projectId 项目ID + * @param amountThreshold 单笔存现阈值 + * @param frequencyThreshold 频次阈值 + * @return 对象命中结果 + */ + List selectFrequentCashDepositObjects(@Param("projectId") Long projectId, + @Param("amountThreshold") BigDecimal amountThreshold, + @Param("frequencyThreshold") Integer frequencyThreshold); + + /** + * 大额转账交易 + * + * @param projectId 项目ID + * @param threshold 转账阈值 + * @return 流水命中结果 + */ + List selectLargeTransferStatements(@Param("projectId") Long projectId, + @Param("threshold") BigDecimal threshold); +} diff --git a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml new file mode 100644 index 00000000..aeb3aeaa --- /dev/null +++ b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + NULL AS bankStatementId, + NULL AS groupId, + NULL AS logId, + NULL AS reasonDetail + + + + NULL AS objectType, + NULL AS objectKey, + NULL AS reasonDetail + + + + ( + ( + ( + ( + IFNULL(bs.USER_MEMO, '') LIKE '%现金%' + and IFNULL(bs.USER_MEMO, '') NOT LIKE '%金管理%' + and IFNULL(bs.USER_MEMO, '') NOT LIKE '%金添利%' + and IFNULL(bs.USER_MEMO, '') NOT LIKE '%现金利%' + and IFNULL(bs.USER_MEMO, '') NOT LIKE '%现金宝%' + and IFNULL(bs.USER_MEMO, '') NOT LIKE '%金分析%' + ) + or IFNULL(bs.USER_MEMO, '') LIKE '%存现%' + or IFNULL(bs.USER_MEMO, '') LIKE '%现存%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%现金%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%存现%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%现存%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%金存入%' + or IFNULL(bs.USER_MEMO, '') LIKE '%金存入%' + or ( + IFNULL(bs.USER_MEMO, '') LIKE '%ATM%' + and ( + IFNULL(bs.USER_MEMO, '') LIKE '%存款%' + or IFNULL(bs.USER_MEMO, '') LIKE '%转入%' + ) + ) + or ( + IFNULL(bs.CASH_TYPE, '') LIKE '%ATM%' + and ( + IFNULL(bs.CASH_TYPE, '') LIKE '%存款%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%转入%' + ) + ) + ) + and ( + IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') = '' + or bs.CUSTOMER_ACCOUNT_NAME = '无' + or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') LIKE '%存现%' + ) + ) + or IFNULL(bs.USER_MEMO, '') LIKE '%DEPOSIT%' + or ( + bs.CUSTOMER_ACCOUNT_NAME = '库存现金' + or ( + ( + IFNULL(bs.USER_MEMO, '') LIKE '%现金存款%' + or IFNULL(bs.USER_MEMO, '') LIKE '%自助存款%' + or IFNULL(bs.USER_MEMO, '') LIKE '%CRS存款%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%现金存款%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%自助存款%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%本行CRS存款%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%柜面%' + or IFNULL(bs.USER_MEMO, '') LIKE '%柜面%' + ) + and IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') = '' + ) + or (bs.CUSTOMER_ACCOUNT_NAME = '现金' and IFNULL(bs.USER_MEMO, '') NOT LIKE '%借款%') + or IFNULL(bs.USER_MEMO, '') LIKE '%本行ATM%' + ) + ) + + + + not ( + bs.CUSTOMER_ACCOUNT_NAME = '浙江兰溪农村商业银行股份有限公司' + and ( + IFNULL(bs.USER_MEMO, '') LIKE '%代发%' + or IFNULL(bs.USER_MEMO, '') LIKE '%工资%' + or IFNULL(bs.USER_MEMO, '') LIKE '%奖金%' + or IFNULL(bs.USER_MEMO, '') LIKE '%薪酬%' + or IFNULL(bs.USER_MEMO, '') LIKE '%薪金%' + or IFNULL(bs.USER_MEMO, '') LIKE '%补贴%' + or IFNULL(bs.USER_MEMO, '') LIKE '%薪%' + or IFNULL(bs.USER_MEMO, '') LIKE '%年终奖%' + or IFNULL(bs.USER_MEMO, '') LIKE '%年金%' + or IFNULL(bs.USER_MEMO, '') LIKE '%加班费%' + or IFNULL(bs.USER_MEMO, '') LIKE '%劳务费%' + or IFNULL(bs.USER_MEMO, '') LIKE '%劳务外包%' + or IFNULL(bs.USER_MEMO, '') LIKE '%提成%' + or IFNULL(bs.USER_MEMO, '') LIKE '%劳务派遣%' + or IFNULL(bs.USER_MEMO, '') LIKE '%绩效%' + or IFNULL(bs.USER_MEMO, '') LIKE '%酬劳%' + or IFNULL(bs.USER_MEMO, '') LIKE '%PAYROLL%' + or IFNULL(bs.USER_MEMO, '') LIKE '%SALA%' + or IFNULL(bs.USER_MEMO, '') LIKE '%CPF%' + or IFNULL(bs.USER_MEMO, '') LIKE '%directors%fees%' + or IFNULL(bs.USER_MEMO, '') LIKE '%批量代付%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%代发%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%工资%' + or IFNULL(bs.CASH_TYPE, '') LIKE '%劳务费%' + ) + ) + + + + + + + + + + + + + + + + + + + diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java new file mode 100644 index 00000000..a4171363 --- /dev/null +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java @@ -0,0 +1,47 @@ +package com.ruoyi.ccdi.project.mapper; + +import org.junit.jupiter.api.Test; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CcdiBankTagAnalysisMapperXmlTest { + + private static final String RESOURCE = "mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml"; + + @Test + void statementRuleSql_shouldSelectGroupIdAndLogId() throws Exception { + String xml = readXml(RESOURCE); + assertTrue(xml.contains("AS groupId")); + assertTrue(xml.contains("AS logId")); + } + + @Test + void houseOrCarExpenseRule_shouldJoinBankStatementAndReturnStatementHitFields() throws Exception { + String xml = readXml(RESOURCE); + assertTrue(xml.contains("selectHouseOrCarExpenseStatements")); + assertTrue(xml.contains("bs.bank_statement_id AS bankStatementId")); + assertTrue(xml.contains("bs.group_id AS groupId")); + assertTrue(xml.contains("bs.batch_id AS logId")); + } + + @Test + void allLargeTransactionRules_shouldExistInAnalysisMapperXml() throws Exception { + String xml = readXml(RESOURCE); + assertTrue(xml.contains("selectTaxExpenseStatements")); + assertTrue(xml.contains("selectSingleLargeIncomeStatements")); + assertTrue(xml.contains("selectCumulativeIncomeObjects")); + assertTrue(xml.contains("selectAnnualTurnoverObjects")); + assertTrue(xml.contains("selectLargeCashDepositStatements")); + assertTrue(xml.contains("selectFrequentCashDepositObjects")); + assertTrue(xml.contains("selectLargeTransferStatements")); + } + + private String readXml(String resource) throws Exception { + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resource)) { + return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + } + } +}