完成银行流水打标规则大写编码与后端落地
This commit is contained in:
@@ -89,4 +89,204 @@ public interface CcdiBankTagAnalysisMapper {
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectLargeTransferStatements(@Param("projectId") Long projectId,
|
||||
@Param("threshold") BigDecimal threshold);
|
||||
|
||||
/**
|
||||
* 与客户之间非正常资金往来
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectAbnormalCustomerTransactionStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 低收入亲属大额交易
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectLowIncomeRelativeLargeTransactionObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 疑似赌博交易
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectMultiPartyGamblingTransferObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 疑似敏感交易
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectGamblingSensitiveKeywordStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 特殊金额交易
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectSpecialAmountTransactionStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 月度固定收入疑似兼职
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectMonthlyFixedIncomeObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 固定交易对手转入疑似兼职
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectFixedCounterpartyTransferObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 摘要收入疑似兼职
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectSuspiciousIncomeKeywordStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 购房交易与房产登记不匹配
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectHouseRegistrationMismatchStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 物业缴费与房产登记不匹配
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectPropertyFeeRegistrationMismatchStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 大额纳税与资产登记不匹配
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectTaxAssetRegistrationMismatchStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 收入资产不符
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectIncomeAssetMismatchStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 单笔购汇金额超限
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectForexBuyAmtStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 单笔结汇金额超限
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectForexSellAmtStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 单笔跨境汇款金额超限
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectCrossBorderAmtStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 可疑付息
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectInterestPaymentByOthersObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 单笔采购金额超过10万元
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectLargePurchaseTransactionStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 供应商集中采购
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectSupplierConcentrationObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 可疑银证大额转账
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectStockTfrLargeStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 微信支付宝频繁提现
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectWithdrawCntObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 微信支付宝提现超额
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectWithdrawAmtObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 工资快速转出
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectSalaryQuickTransferObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 工资无使用记录
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectSalaryUnusedObjects(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 大额炒股
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 流水命中结果
|
||||
*/
|
||||
List<BankTagStatementHitVO> selectLargeStockTradingStatements(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 疑似代理他人账户
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectProxyAccountOperationObjects(@Param("projectId") Long projectId);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class BankTagRuleConfigResolver {
|
||||
private static final Map<String, Set<String>> RULE_PARAM_MAPPING = Map.of(
|
||||
"SINGLE_LARGE_INCOME", Set.of("SINGLE_TRANSACTION_AMOUNT"),
|
||||
"CUMULATIVE_INCOME", Set.of("CUMULATIVE_TRANSACTION_AMOUNT"),
|
||||
"ANNUAL_TURNOVER", Set.of("annual_turnover"),
|
||||
"ANNUAL_TURNOVER", Set.of("ANNUAL_TURNOVER"),
|
||||
"LARGE_CASH_DEPOSIT", Set.of("LARGE_CASH_DEPOSIT"),
|
||||
"FREQUENT_CASH_DEPOSIT", Set.of("LARGE_CASH_DEPOSIT", "FREQUENT_CASH_DEPOSIT"),
|
||||
"LARGE_TRANSFER", Set.of("FREQUENT_TRANSFER")
|
||||
|
||||
@@ -211,6 +211,20 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
||||
case "LARGE_TRANSFER" -> analysisMapper.selectLargeTransferStatements(
|
||||
projectId, toBigDecimal(config.getThresholdValue("FREQUENT_TRANSFER"))
|
||||
);
|
||||
case "ABNORMAL_CUSTOMER_TRANSACTION" -> analysisMapper.selectAbnormalCustomerTransactionStatements(projectId);
|
||||
case "GAMBLING_SENSITIVE_KEYWORD" -> analysisMapper.selectGamblingSensitiveKeywordStatements(projectId);
|
||||
case "SPECIAL_AMOUNT_TRANSACTION" -> analysisMapper.selectSpecialAmountTransactionStatements(projectId);
|
||||
case "SUSPICIOUS_INCOME_KEYWORD" -> analysisMapper.selectSuspiciousIncomeKeywordStatements(projectId);
|
||||
case "HOUSE_REGISTRATION_MISMATCH" -> analysisMapper.selectHouseRegistrationMismatchStatements(projectId);
|
||||
case "PROPERTY_FEE_REGISTRATION_MISMATCH" -> analysisMapper.selectPropertyFeeRegistrationMismatchStatements(projectId);
|
||||
case "TAX_ASSET_REGISTRATION_MISMATCH" -> analysisMapper.selectTaxAssetRegistrationMismatchStatements(projectId);
|
||||
case "INCOME_ASSET_MISMATCH" -> analysisMapper.selectIncomeAssetMismatchStatements(projectId);
|
||||
case "FOREX_BUY_AMT" -> analysisMapper.selectForexBuyAmtStatements(projectId);
|
||||
case "FOREX_SELL_AMT" -> analysisMapper.selectForexSellAmtStatements(projectId);
|
||||
case "CROSS_BORDER_AMT" -> analysisMapper.selectCrossBorderAmtStatements(projectId);
|
||||
case "LARGE_PURCHASE_TRANSACTION" -> analysisMapper.selectLargePurchaseTransactionStatements(projectId);
|
||||
case "STOCK_TFR_LARGE" -> analysisMapper.selectStockTfrLargeStatements(projectId);
|
||||
case "LARGE_STOCK_TRADING" -> analysisMapper.selectLargeStockTradingStatements(projectId);
|
||||
default -> List.of();
|
||||
};
|
||||
}
|
||||
@@ -223,13 +237,24 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
||||
projectId, toBigDecimal(config.getThresholdValue("CUMULATIVE_TRANSACTION_AMOUNT"))
|
||||
);
|
||||
case "ANNUAL_TURNOVER" -> analysisMapper.selectAnnualTurnoverObjects(
|
||||
projectId, toBigDecimal(config.getThresholdValue("annual_turnover"))
|
||||
projectId, toBigDecimal(config.getThresholdValue("ANNUAL_TURNOVER"))
|
||||
);
|
||||
case "FREQUENT_CASH_DEPOSIT" -> analysisMapper.selectFrequentCashDepositObjects(
|
||||
projectId,
|
||||
toBigDecimal(config.getThresholdValue("LARGE_CASH_DEPOSIT")),
|
||||
toInteger(config.getThresholdValue("FREQUENT_CASH_DEPOSIT"))
|
||||
);
|
||||
case "LOW_INCOME_RELATIVE_LARGE_TRANSACTION" -> analysisMapper.selectLowIncomeRelativeLargeTransactionObjects(projectId);
|
||||
case "MULTI_PARTY_GAMBLING_TRANSFER" -> analysisMapper.selectMultiPartyGamblingTransferObjects(projectId);
|
||||
case "MONTHLY_FIXED_INCOME" -> analysisMapper.selectMonthlyFixedIncomeObjects(projectId);
|
||||
case "FIXED_COUNTERPARTY_TRANSFER" -> analysisMapper.selectFixedCounterpartyTransferObjects(projectId);
|
||||
case "INTEREST_PAYMENT_BY_OTHERS" -> analysisMapper.selectInterestPaymentByOthersObjects(projectId);
|
||||
case "SUPPLIER_CONCENTRATION" -> analysisMapper.selectSupplierConcentrationObjects(projectId);
|
||||
case "WITHDRAW_CNT" -> analysisMapper.selectWithdrawCntObjects(projectId);
|
||||
case "WITHDRAW_AMT" -> analysisMapper.selectWithdrawAmtObjects(projectId);
|
||||
case "SALARY_QUICK_TRANSFER" -> analysisMapper.selectSalaryQuickTransferObjects(projectId);
|
||||
case "SALARY_UNUSED" -> analysisMapper.selectSalaryUnusedObjects(projectId);
|
||||
case "PROXY_ACCOUNT_OPERATION" -> analysisMapper.selectProxyAccountOperationObjects(projectId);
|
||||
default -> List.of();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -363,4 +363,243 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
)
|
||||
</select>
|
||||
|
||||
<select id="selectAbnormalCustomerTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectLowIncomeRelativeLargeTransactionObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectMultiPartyGamblingTransferObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectGamblingSensitiveKeywordStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectSpecialAmountTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectMonthlyFixedIncomeObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectFixedCounterpartyTransferObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectSuspiciousIncomeKeywordStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectHouseRegistrationMismatchStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectPropertyFeeRegistrationMismatchStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectTaxAssetRegistrationMismatchStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectIncomeAssetMismatchStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectForexBuyAmtStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectForexSellAmtStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectCrossBorderAmtStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectInterestPaymentByOthersObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectLargePurchaseTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectSupplierConcentrationObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectStockTfrLargeStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectWithdrawCntObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectWithdrawAmtObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectSalaryQuickTransferObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectSalaryUnusedObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectLargeStockTradingStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
<select id="selectProxyAccountOperationObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -7,12 +7,41 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.StringReader;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class CcdiBankTagAnalysisMapperXmlTest {
|
||||
|
||||
private static final String RESOURCE = "mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml";
|
||||
private static final List<String> PLACEHOLDER_SELECT_IDS = List.of(
|
||||
"selectAbnormalCustomerTransactionStatements",
|
||||
"selectLowIncomeRelativeLargeTransactionObjects",
|
||||
"selectMultiPartyGamblingTransferObjects",
|
||||
"selectGamblingSensitiveKeywordStatements",
|
||||
"selectSpecialAmountTransactionStatements",
|
||||
"selectMonthlyFixedIncomeObjects",
|
||||
"selectFixedCounterpartyTransferObjects",
|
||||
"selectSuspiciousIncomeKeywordStatements",
|
||||
"selectHouseRegistrationMismatchStatements",
|
||||
"selectPropertyFeeRegistrationMismatchStatements",
|
||||
"selectTaxAssetRegistrationMismatchStatements",
|
||||
"selectIncomeAssetMismatchStatements",
|
||||
"selectForexBuyAmtStatements",
|
||||
"selectForexSellAmtStatements",
|
||||
"selectCrossBorderAmtStatements",
|
||||
"selectInterestPaymentByOthersObjects",
|
||||
"selectLargePurchaseTransactionStatements",
|
||||
"selectSupplierConcentrationObjects",
|
||||
"selectStockTfrLargeStatements",
|
||||
"selectWithdrawCntObjects",
|
||||
"selectWithdrawAmtObjects",
|
||||
"selectSalaryQuickTransferObjects",
|
||||
"selectSalaryUnusedObjects",
|
||||
"selectLargeStockTradingStatements",
|
||||
"selectProxyAccountOperationObjects"
|
||||
);
|
||||
|
||||
@Test
|
||||
void statementRuleSql_shouldSelectGroupIdAndLogId() throws Exception {
|
||||
@@ -42,6 +71,21 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
||||
assertTrue(xml.contains("selectLargeTransferStatements"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void allPlaceholderRules_shouldExistInAnalysisMapperXml() throws Exception {
|
||||
String xml = readXml(RESOURCE);
|
||||
for (String selectId : PLACEHOLDER_SELECT_IDS) {
|
||||
assertTrue(xml.contains(selectId), () -> "缺少占位规则 SQL: " + selectId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeholderRules_shouldUseEmptyResultSqlTemplate() throws Exception {
|
||||
String xml = readXml(RESOURCE);
|
||||
assertTrue(xml.contains("占位SQL,待补充真实规则"));
|
||||
assertEquals(25, countMatches(xml, "where 1 = 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void analysisMapperXml_shouldBeWellFormed() throws Exception {
|
||||
String xml = readXml(RESOURCE);
|
||||
@@ -56,4 +100,14 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
||||
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private int countMatches(String text, String target) {
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
while ((index = text.indexOf(target, index)) >= 0) {
|
||||
count++;
|
||||
index += target.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,46 @@ class BankTagRuleConfigResolverTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolve_shouldReturnEmptyThresholdsForPlaceholderRulesWithoutIndicatorCode() {
|
||||
CcdiProject project = new CcdiProject();
|
||||
project.setProjectId(40L);
|
||||
project.setConfigType("default");
|
||||
when(projectMapper.selectById(40L)).thenReturn(project);
|
||||
when(modelParamMapper.selectByProjectAndModel(0L, "ABNORMAL_TRANSACTION")).thenReturn(List.of(
|
||||
buildParam("IGNORED_PARAM", "999")
|
||||
));
|
||||
|
||||
CcdiBankTagRule ruleMeta = new CcdiBankTagRule();
|
||||
ruleMeta.setModelCode("ABNORMAL_TRANSACTION");
|
||||
ruleMeta.setRuleCode("ABNORMAL_CUSTOMER_TRANSACTION");
|
||||
ruleMeta.setIndicatorCode(null);
|
||||
|
||||
BankTagRuleExecutionConfig config = resolver.resolve(40L, ruleMeta);
|
||||
|
||||
assertTrue(config.getThresholdValues().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolve_shouldReadUppercaseAnnualTurnoverParamCode() {
|
||||
CcdiProject project = new CcdiProject();
|
||||
project.setProjectId(40L);
|
||||
project.setConfigType("default");
|
||||
when(projectMapper.selectById(40L)).thenReturn(project);
|
||||
when(modelParamMapper.selectByProjectAndModel(0L, "LARGE_TRANSACTION")).thenReturn(List.of(
|
||||
buildParam("ANNUAL_TURNOVER", "8888")
|
||||
));
|
||||
|
||||
CcdiBankTagRule ruleMeta = new CcdiBankTagRule();
|
||||
ruleMeta.setModelCode("LARGE_TRANSACTION");
|
||||
ruleMeta.setRuleCode("ANNUAL_TURNOVER");
|
||||
ruleMeta.setIndicatorCode("ANNUAL_TURNOVER");
|
||||
|
||||
BankTagRuleExecutionConfig config = resolver.resolve(40L, ruleMeta);
|
||||
|
||||
assertEquals("8888", config.getThresholdValue("ANNUAL_TURNOVER"));
|
||||
}
|
||||
|
||||
private CcdiModelParam buildParam(String paramCode, String paramValue) {
|
||||
CcdiModelParam param = new CcdiModelParam();
|
||||
param.setProjectId(0L);
|
||||
|
||||
@@ -28,8 +28,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -175,4 +177,61 @@ class CcdiBankTagServiceImplTest {
|
||||
logger.detachAppender(logAppender);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void rebuildProject_shouldDispatchPlaceholderStatementRuleAndFinishWithoutResults() {
|
||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||
|
||||
CcdiBankTagRule rule = buildRule("ABNORMAL_TRANSACTION", "异常交易",
|
||||
"ABNORMAL_CUSTOMER_TRANSACTION", "与客户之间非正常资金往来", "STATEMENT");
|
||||
BankTagRuleExecutionConfig config = buildConfig(40L, rule);
|
||||
|
||||
when(ruleMapper.selectEnabledRules("ABNORMAL_TRANSACTION")).thenReturn(List.of(rule));
|
||||
when(configResolver.resolve(40L, rule)).thenReturn(config);
|
||||
when(analysisMapper.selectAbnormalCustomerTransactionStatements(40L)).thenReturn(List.of());
|
||||
|
||||
service.rebuildProject(40L, "ABNORMAL_TRANSACTION", "admin", TriggerType.MANUAL);
|
||||
|
||||
verify(resultMapper).deleteByProjectAndModel(40L, "ABNORMAL_TRANSACTION");
|
||||
verify(analysisMapper).selectAbnormalCustomerTransactionStatements(40L);
|
||||
verify(resultMapper, never()).insertBatch(anyList());
|
||||
verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus()) && task.getHitCount() == 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rebuildProject_shouldDispatchPlaceholderObjectRuleAndFinishWithoutResults() {
|
||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||
|
||||
CcdiBankTagRule rule = buildRule("SUSPICIOUS_PURCHASE", "可疑采购",
|
||||
"SUPPLIER_CONCENTRATION", "可疑采购", "OBJECT");
|
||||
BankTagRuleExecutionConfig config = buildConfig(40L, rule);
|
||||
|
||||
when(ruleMapper.selectEnabledRules("SUSPICIOUS_PURCHASE")).thenReturn(List.of(rule));
|
||||
when(configResolver.resolve(40L, rule)).thenReturn(config);
|
||||
when(analysisMapper.selectSupplierConcentrationObjects(40L)).thenReturn(List.of());
|
||||
|
||||
service.rebuildProject(40L, "SUSPICIOUS_PURCHASE", "admin", TriggerType.MANUAL);
|
||||
|
||||
verify(analysisMapper).selectSupplierConcentrationObjects(40L);
|
||||
verify(resultMapper).deleteByProjectAndModel(40L, "SUSPICIOUS_PURCHASE");
|
||||
verify(resultMapper, never()).insertBatch(anyList());
|
||||
verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus()) && task.getFailedRuleCount() == 0));
|
||||
}
|
||||
|
||||
private CcdiBankTagRule buildRule(String modelCode, String modelName, String ruleCode, String ruleName, String resultType) {
|
||||
CcdiBankTagRule rule = new CcdiBankTagRule();
|
||||
rule.setModelCode(modelCode);
|
||||
rule.setModelName(modelName);
|
||||
rule.setRuleCode(ruleCode);
|
||||
rule.setRuleName(ruleName);
|
||||
rule.setResultType(resultType);
|
||||
return rule;
|
||||
}
|
||||
|
||||
private BankTagRuleExecutionConfig buildConfig(Long projectId, CcdiBankTagRule rule) {
|
||||
BankTagRuleExecutionConfig config = new BankTagRuleExecutionConfig();
|
||||
config.setProjectId(projectId);
|
||||
config.setRuleMeta(rule);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class CcdiModelParamServiceImplTest {
|
||||
when(projectMapper.selectById(123L)).thenReturn(project);
|
||||
when(modelParamMapper.selectByProjectId(0L)).thenReturn(List.of(
|
||||
buildParam(1L, 0L, "LARGE_TRANSACTION", "大额交易模型", "SINGLE_TRANSACTION_AMOUNT", "1111"),
|
||||
buildParam(2L, 0L, "SUSPICIOUS_GAMBLING", "疑似赌博交易模型", "multi_party_amt_min", "500")
|
||||
buildParam(2L, 0L, "SUSPICIOUS_GAMBLING", "疑似赌博交易模型", "MULTI_PARTY_AMT_MIN", "500")
|
||||
));
|
||||
when(modelParamMapper.insertBatch(anyList())).thenReturn(2);
|
||||
when(modelParamMapper.updateParamValue(123L, "LARGE_TRANSACTION", "SINGLE_TRANSACTION_AMOUNT", "2222", "admin"))
|
||||
|
||||
@@ -22,6 +22,15 @@ class CcdiModelParamSqlDefaultsTest {
|
||||
assertQuarterlyStableIncomeRangeConfig(updateSql);
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultSql_shouldUseUppercaseParamCodesForBankTagModels() throws IOException {
|
||||
String initSql = readProjectFile("sql", "ccdi_model_param.sql");
|
||||
String updateSql = readProjectFile("sql", "2026-03-16-update-ccdi-model-param-defaults.sql");
|
||||
|
||||
assertUppercaseBankTagParamCodes(initSql);
|
||||
assertUppercaseBankTagParamCodes(updateSql);
|
||||
}
|
||||
|
||||
private void assertQuarterlyStableIncomeRangeConfig(String sqlContent) {
|
||||
assertAll(
|
||||
() -> assertTrue(sqlContent.contains("FIXED_COUNTERPARTY_TRANSFER_MIN"),
|
||||
@@ -41,6 +50,35 @@ class CcdiModelParamSqlDefaultsTest {
|
||||
);
|
||||
}
|
||||
|
||||
private void assertUppercaseBankTagParamCodes(String sqlContent) {
|
||||
assertAll(
|
||||
() -> assertTrue(sqlContent.contains("ANNUAL_TURNOVER"),
|
||||
"应包含大写参数编码 ANNUAL_TURNOVER"),
|
||||
() -> assertTrue(sqlContent.contains("STOCK_TFR_LARGE"),
|
||||
"应包含大写参数编码 STOCK_TFR_LARGE"),
|
||||
() -> assertTrue(sqlContent.contains("WITHDRAW_CNT"),
|
||||
"应包含大写参数编码 WITHDRAW_CNT"),
|
||||
() -> assertTrue(sqlContent.contains("WITHDRAW_AMT"),
|
||||
"应包含大写参数编码 WITHDRAW_AMT"),
|
||||
() -> assertTrue(sqlContent.contains("MULTI_PARTY_AMT_MIN"),
|
||||
"应包含大写参数编码 MULTI_PARTY_AMT_MIN"),
|
||||
() -> assertTrue(sqlContent.contains("MULTI_PARTY_AMT_MAX"),
|
||||
"应包含大写参数编码 MULTI_PARTY_AMT_MAX"),
|
||||
() -> assertFalse(sqlContent.contains("'annual_turnover'"),
|
||||
"不应继续保留小写参数编码 annual_turnover"),
|
||||
() -> assertFalse(sqlContent.contains("'stock_tfr_large'"),
|
||||
"不应继续保留小写参数编码 stock_tfr_large"),
|
||||
() -> assertFalse(sqlContent.contains("'withdraw_cnt'"),
|
||||
"不应继续保留小写参数编码 withdraw_cnt"),
|
||||
() -> assertFalse(sqlContent.contains("'withdraw_amt'"),
|
||||
"不应继续保留小写参数编码 withdraw_amt"),
|
||||
() -> assertFalse(sqlContent.contains("'multi_party_amt_min'"),
|
||||
"不应继续保留小写参数编码 multi_party_amt_min"),
|
||||
() -> assertFalse(sqlContent.contains("'multi_party_amt_max'"),
|
||||
"不应继续保留小写参数编码 multi_party_amt_max")
|
||||
);
|
||||
}
|
||||
|
||||
private String readProjectFile(String... parts) throws IOException {
|
||||
Path path = Path.of("..", parts);
|
||||
return Files.readString(path, StandardCharsets.UTF_8);
|
||||
|
||||
Reference in New Issue
Block a user