接通第一期对象规则真实分发
This commit is contained in:
@@ -252,9 +252,11 @@ public interface CcdiBankTagAnalysisMapper {
|
|||||||
* 微信支付宝频繁提现
|
* 微信支付宝频繁提现
|
||||||
*
|
*
|
||||||
* @param projectId 项目ID
|
* @param projectId 项目ID
|
||||||
|
* @param frequencyThreshold 提现频次阈值
|
||||||
* @return 对象命中结果
|
* @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 "FIXED_COUNTERPARTY_TRANSFER" -> analysisMapper.selectFixedCounterpartyTransferObjects(projectId);
|
||||||
case "INTEREST_PAYMENT_BY_OTHERS" -> analysisMapper.selectInterestPaymentByOthersObjects(projectId);
|
case "INTEREST_PAYMENT_BY_OTHERS" -> analysisMapper.selectInterestPaymentByOthersObjects(projectId);
|
||||||
case "SUPPLIER_CONCENTRATION" -> analysisMapper.selectSupplierConcentrationObjects(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 "WITHDRAW_AMT" -> analysisMapper.selectWithdrawAmtObjects(projectId);
|
||||||
case "SALARY_QUICK_TRANSFER" -> analysisMapper.selectSalaryQuickTransferObjects(projectId);
|
case "SALARY_QUICK_TRANSFER" -> analysisMapper.selectSalaryQuickTransferObjects(projectId);
|
||||||
case "SALARY_UNUSED" -> analysisMapper.selectSalaryUnusedObjects(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 id="selectWithdrawCntObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
select
|
select
|
||||||
'STAFF_ID_CARD' AS objectType,
|
'STAFF_ID_CARD' AS objectType,
|
||||||
'' AS objectKey,
|
t.objectKey AS objectKey,
|
||||||
'占位SQL,待补充真实规则' AS reasonDetail
|
CONCAT(
|
||||||
from ccdi_bank_statement bs
|
'单日微信/支付宝提现 ', CAST(t.withdrawCount AS CHAR),
|
||||||
where 1 = 0
|
' 次,超过阈值 ', 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>
|
||||||
|
|
||||||
<select id="selectWithdrawAmtObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectWithdrawAmtObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
|||||||
void placeholderRules_shouldUseEmptyResultSqlTemplate() throws Exception {
|
void placeholderRules_shouldUseEmptyResultSqlTemplate() throws Exception {
|
||||||
String xml = readXml(RESOURCE);
|
String xml = readXml(RESOURCE);
|
||||||
assertTrue(xml.contains("占位SQL,待补充真实规则"));
|
assertTrue(xml.contains("占位SQL,待补充真实规则"));
|
||||||
assertEquals(17, countMatches(xml, "where 1 = 0"));
|
assertEquals(16, countMatches(xml, "where 1 = 0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
@Test
|
||||||
void analysisMapperXml_shouldBeWellFormed() throws Exception {
|
void analysisMapperXml_shouldBeWellFormed() throws Exception {
|
||||||
String xml = readXml(RESOURCE);
|
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.CcdiBankTagRule;
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
||||||
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
|
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.BankTagRuleExecutionConfig;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.BankTagStatementHitVO;
|
import com.ruoyi.ccdi.project.domain.vo.BankTagStatementHitVO;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagAnalysisMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagAnalysisMapper;
|
||||||
@@ -24,6 +25,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
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));
|
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
|
@Test
|
||||||
void shouldMarkProjectTaggingBeforeExecutingAndCompletedAfterSuccess() {
|
void shouldMarkProjectTaggingBeforeExecutingAndCompletedAfterSuccess() {
|
||||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||||
|
|||||||
Reference in New Issue
Block a user