接通第一期对象规则真实分发
This commit is contained in:
@@ -252,9 +252,11 @@ public interface CcdiBankTagAnalysisMapper {
|
||||
* 微信支付宝频繁提现
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @param frequencyThreshold 提现频次阈值
|
||||
* @return 对象命中结果
|
||||
*/
|
||||
List<BankTagObjectHitVO> selectWithdrawCntObjects(@Param("projectId") Long projectId);
|
||||
List<BankTagObjectHitVO> selectWithdrawCntObjects(@Param("projectId") Long projectId,
|
||||
@Param("frequencyThreshold") Integer frequencyThreshold);
|
||||
|
||||
/**
|
||||
* 微信支付宝提现超额
|
||||
|
||||
@@ -272,7 +272,9 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
||||
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_CNT" -> analysisMapper.selectWithdrawCntObjects(
|
||||
projectId, toInteger(config.getThresholdValue("WITHDRAW_CNT"))
|
||||
);
|
||||
case "WITHDRAW_AMT" -> analysisMapper.selectWithdrawAmtObjects(projectId);
|
||||
case "SALARY_QUICK_TRANSFER" -> analysisMapper.selectSalaryQuickTransferObjects(projectId);
|
||||
case "SALARY_UNUSED" -> analysisMapper.selectSalaryUnusedObjects(projectId);
|
||||
|
||||
@@ -644,10 +644,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<select id="selectWithdrawCntObjects" resultMap="BankTagObjectHitResultMap">
|
||||
select
|
||||
'STAFF_ID_CARD' AS objectType,
|
||||
'' AS objectKey,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
t.objectKey AS objectKey,
|
||||
CONCAT(
|
||||
'单日微信/支付宝提现 ', CAST(t.withdrawCount AS CHAR),
|
||||
' 次,超过阈值 ', CAST(#{frequencyThreshold} AS CHAR),
|
||||
' 次,交易日:', t.transDate
|
||||
) AS reasonDetail
|
||||
from (
|
||||
select
|
||||
staff.id_card AS objectKey,
|
||||
LEFT(TRIM(bs.TRX_DATE), 10) AS transDate,
|
||||
COUNT(1) AS withdrawCount
|
||||
from ccdi_bank_statement bs
|
||||
inner join ccdi_base_staff staff on staff.id_card = bs.cret_no
|
||||
where bs.project_id = #{projectId}
|
||||
and IFNULL(bs.AMOUNT_CR, 0) >= 0
|
||||
and (
|
||||
IFNULL(bs.USER_MEMO, '') REGEXP '财付通|微信零钱|微信|wechat|WeChat|Tenpay|支付宝|Alipay|提现'
|
||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '财付通|微信零钱|微信|wechat|WeChat|Tenpay|支付宝|Alipay|提现'
|
||||
)
|
||||
group by staff.id_card, LEFT(TRIM(bs.TRX_DATE), 10)
|
||||
having COUNT(1) > #{frequencyThreshold}
|
||||
) t
|
||||
</select>
|
||||
|
||||
<select id="selectWithdrawAmtObjects" resultMap="BankTagObjectHitResultMap">
|
||||
|
||||
@@ -90,7 +90,7 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
||||
void placeholderRules_shouldUseEmptyResultSqlTemplate() throws Exception {
|
||||
String xml = readXml(RESOURCE);
|
||||
assertTrue(xml.contains("占位SQL,待补充真实规则"));
|
||||
assertEquals(17, countMatches(xml, "where 1 = 0"));
|
||||
assertEquals(16, countMatches(xml, "where 1 = 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -106,6 +106,16 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void withdrawCntObjectRule_shouldUseRealSqlAndKeepObjectHitFields() throws Exception {
|
||||
String xml = readXml(RESOURCE);
|
||||
String selectSql = extractSelectSql(xml, "selectWithdrawCntObjects");
|
||||
assertTrue(selectSql.contains("'STAFF_ID_CARD' AS objectType"));
|
||||
assertTrue(selectSql.contains("AS objectKey"));
|
||||
assertTrue(selectSql.contains("reasonDetail"));
|
||||
assertTrue(!selectSql.contains("where 1 = 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void analysisMapperXml_shouldBeWellFormed() throws Exception {
|
||||
String xml = readXml(RESOURCE);
|
||||
|
||||
@@ -6,6 +6,7 @@ import ch.qos.logback.core.read.ListAppender;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
||||
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
|
||||
import com.ruoyi.ccdi.project.domain.vo.BankTagObjectHitVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.BankTagRuleExecutionConfig;
|
||||
import com.ruoyi.ccdi.project.domain.vo.BankTagStatementHitVO;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagAnalysisMapper;
|
||||
@@ -24,6 +25,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -243,6 +245,52 @@ class CcdiBankTagServiceImplTest {
|
||||
verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus()) && task.getFailedRuleCount() == 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rebuildProject_shouldDispatchWithdrawCntObjectRuleWithResolvedThreshold() {
|
||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||
|
||||
CcdiBankTagRule rule = buildRule("ABNORMAL_BEHAVIOR", "异常行为",
|
||||
"WITHDRAW_CNT", "微信支付宝频繁提现", "OBJECT");
|
||||
BankTagRuleExecutionConfig config = buildConfig(40L, rule);
|
||||
config.setThresholdValues(Map.of("WITHDRAW_CNT", "3"));
|
||||
|
||||
BankTagObjectHitVO hit = new BankTagObjectHitVO();
|
||||
hit.setObjectType("STAFF_ID_CARD");
|
||||
hit.setObjectKey("330101199001011234");
|
||||
hit.setReasonDetail("单日微信提现 4 次,超过阈值 3 次");
|
||||
|
||||
when(ruleMapper.selectEnabledRules("ABNORMAL_BEHAVIOR")).thenReturn(List.of(rule));
|
||||
when(configResolver.resolve(40L, rule)).thenReturn(config);
|
||||
when(analysisMapper.selectWithdrawCntObjects(40L, 3)).thenReturn(List.of(hit));
|
||||
|
||||
service.rebuildProject(40L, "ABNORMAL_BEHAVIOR", "admin", TriggerType.MANUAL);
|
||||
|
||||
verify(analysisMapper).selectWithdrawCntObjects(40L, 3);
|
||||
verify(resultMapper).insertBatch(argThat(results -> results.size() == 1
|
||||
&& "STAFF_ID_CARD".equals(results.get(0).getObjectType())
|
||||
&& "330101199001011234".equals(results.get(0).getObjectKey())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rebuildProject_shouldTreatEmptyWithdrawCntHitsAsSuccess() {
|
||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||
|
||||
CcdiBankTagRule rule = buildRule("ABNORMAL_BEHAVIOR", "异常行为",
|
||||
"WITHDRAW_CNT", "微信支付宝频繁提现", "OBJECT");
|
||||
BankTagRuleExecutionConfig config = buildConfig(40L, rule);
|
||||
config.setThresholdValues(Map.of("WITHDRAW_CNT", "3"));
|
||||
|
||||
when(ruleMapper.selectEnabledRules("ABNORMAL_BEHAVIOR")).thenReturn(List.of(rule));
|
||||
when(configResolver.resolve(40L, rule)).thenReturn(config);
|
||||
when(analysisMapper.selectWithdrawCntObjects(40L, 3)).thenReturn(List.of());
|
||||
|
||||
service.rebuildProject(40L, "ABNORMAL_BEHAVIOR", "admin", TriggerType.MANUAL);
|
||||
|
||||
verify(analysisMapper).selectWithdrawCntObjects(40L, 3);
|
||||
verify(resultMapper, never()).insertBatch(anyList());
|
||||
verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus()) && task.getHitCount() == 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMarkProjectTaggingBeforeExecutingAndCompletedAfterSuccess() {
|
||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||
|
||||
Reference in New Issue
Block a user