实现lsfx-mock全命中SQL对齐
This commit is contained in:
@@ -49,6 +49,9 @@ PHASE2_BASELINE_RULE_CODES = [
|
||||
]
|
||||
|
||||
RULE_CONFLICT_GROUPS = []
|
||||
ALL_MODE_STATEMENT_BASELINE_RULE_CODES = {
|
||||
"LOW_INCOME_RELATIVE_LARGE_TRANSACTION",
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -300,10 +303,19 @@ class FileService:
|
||||
|
||||
def _apply_phase2_baselines(self, file_record: FileRecord) -> None:
|
||||
"""按当前记录命中的第二期基线规则幂等补齐外部事实。"""
|
||||
baseline_rule_codes = list(file_record.phase2_baseline_hit_rules)
|
||||
if settings.RULE_HIT_MODE == "all":
|
||||
for rule_code in file_record.phase2_statement_hit_rules:
|
||||
if (
|
||||
rule_code in ALL_MODE_STATEMENT_BASELINE_RULE_CODES
|
||||
and rule_code not in baseline_rule_codes
|
||||
):
|
||||
baseline_rule_codes.append(rule_code)
|
||||
|
||||
self.phase2_baseline_service.apply(
|
||||
staff_id_card=file_record.staff_id_card,
|
||||
family_id_cards=file_record.family_id_cards,
|
||||
baseline_rule_codes=file_record.phase2_baseline_hit_rules,
|
||||
baseline_rule_codes=baseline_rule_codes,
|
||||
)
|
||||
|
||||
async def upload_file(
|
||||
|
||||
@@ -10,6 +10,7 @@ class Phase2BaselineService:
|
||||
|
||||
SUPPLIER_PURCHASE_ID = "LSFXMOCKP2PUR001"
|
||||
SUPPLIER_NAME = "兰溪市联调供应链有限公司"
|
||||
LOW_INCOME_RELATIVE_RULE_CODE = "LOW_INCOME_RELATIVE_LARGE_TRANSACTION"
|
||||
ASSET_IDENTIFIERS = {
|
||||
"HOUSE_REGISTRATION_MISMATCH": "LSFX Mock P2 HOUSE_REGISTRATION_MISMATCH",
|
||||
"PROPERTY_FEE_REGISTRATION_MISMATCH": "LSFX Mock P2 PROPERTY_FEE_REGISTRATION_MISMATCH",
|
||||
@@ -207,6 +208,63 @@ class Phase2BaselineService:
|
||||
).strip(),
|
||||
]
|
||||
|
||||
def _build_low_income_family_baseline_sql(
|
||||
self,
|
||||
staff_id_card: str,
|
||||
family_id_cards: List[str],
|
||||
) -> List[str]:
|
||||
target_family_id_card = next((card for card in family_id_cards if card), None)
|
||||
if not target_family_id_card:
|
||||
return []
|
||||
|
||||
return [
|
||||
dedent(
|
||||
f"""
|
||||
INSERT INTO ccdi_staff_fmy_relation (
|
||||
person_id,
|
||||
relation_type,
|
||||
relation_name,
|
||||
gender,
|
||||
relation_cert_type,
|
||||
relation_cert_no,
|
||||
relation_desc,
|
||||
status,
|
||||
effective_date,
|
||||
remark,
|
||||
data_source,
|
||||
is_emp_family,
|
||||
is_cust_family,
|
||||
created_by,
|
||||
updated_by,
|
||||
annual_income
|
||||
)
|
||||
VALUES (
|
||||
{self._sql_quote(staff_id_card)},
|
||||
'父亲',
|
||||
'LSFX低收入亲属',
|
||||
'M',
|
||||
'身份证',
|
||||
{self._sql_quote(target_family_id_card)},
|
||||
'用于命中 LOW_INCOME_RELATIVE_LARGE_TRANSACTION 真实规则',
|
||||
1,
|
||||
NOW(),
|
||||
'LSFX Mock 低收入亲属基线',
|
||||
'SYSTEM',
|
||||
1,
|
||||
0,
|
||||
'admin',
|
||||
'admin',
|
||||
0.00
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
annual_income = 0.00,
|
||||
status = 1,
|
||||
updated_by = 'admin',
|
||||
update_time = CURRENT_TIMESTAMP;
|
||||
"""
|
||||
).strip()
|
||||
]
|
||||
|
||||
def build_sql_plan(
|
||||
self,
|
||||
staff_id_card: str,
|
||||
@@ -228,6 +286,13 @@ class Phase2BaselineService:
|
||||
for rule_code in selected_rule_codes:
|
||||
if rule_code == "SUPPLIER_CONCENTRATION":
|
||||
sql_plan.extend(self._build_supplier_concentration_sql(staff_id_card))
|
||||
elif rule_code == self.LOW_INCOME_RELATIVE_RULE_CODE:
|
||||
sql_plan.extend(
|
||||
self._build_low_income_family_baseline_sql(
|
||||
staff_id_card=staff_id_card,
|
||||
family_id_cards=family_id_cards or [],
|
||||
)
|
||||
)
|
||||
elif rule_code in self.ASSET_IDENTIFIERS:
|
||||
family_id, person_id = asset_owner_ids[rule_code]
|
||||
sql_plan.extend(
|
||||
|
||||
@@ -460,7 +460,7 @@ def build_special_amount_transaction_samples(group_id: int, log_id: int, **kwarg
|
||||
customer_name="兰溪特别金额结算中心",
|
||||
user_memo="特殊金额转账",
|
||||
cash_type="转账支出",
|
||||
dr_amount=88888.88,
|
||||
dr_amount=1314.0,
|
||||
le_name=context["le_name"],
|
||||
account_mask_no=context["account_no"],
|
||||
customer_account_mask_no="6222024888800002",
|
||||
@@ -477,8 +477,8 @@ def build_suspicious_income_keyword_samples(group_id: int, log_id: int, **kwargs
|
||||
trx_datetime=REFERENCE_NOW - timedelta(days=3, hours=5),
|
||||
cret_no=context["staff_id_card"],
|
||||
customer_name="灰度信息咨询有限公司",
|
||||
user_memo="咨询返现收入",
|
||||
cash_type="对公转账",
|
||||
user_memo="劳务费发放",
|
||||
cash_type="劳务费入账",
|
||||
cr_amount=166666.0,
|
||||
le_name=context["le_name"],
|
||||
account_mask_no=context["account_no"],
|
||||
@@ -671,6 +671,8 @@ def build_monthly_fixed_income_samples(group_id: int, log_id: int, **kwargs) ->
|
||||
family_id_cards=kwargs.get("family_id_cards"),
|
||||
)
|
||||
income_months = [
|
||||
datetime(2025, 10, 5, 9, 0, 0),
|
||||
datetime(2025, 11, 5, 9, 0, 0),
|
||||
datetime(2025, 12, 5, 9, 0, 0),
|
||||
datetime(2026, 1, 5, 9, 0, 0),
|
||||
datetime(2026, 2, 5, 9, 0, 0),
|
||||
@@ -716,7 +718,7 @@ def build_fixed_counterparty_transfer_samples(
|
||||
group_id,
|
||||
log_id,
|
||||
trx_datetime=trx_datetime,
|
||||
cret_no=subjects["secondary"],
|
||||
cret_no=subjects["primary"],
|
||||
customer_name="兰溪零工服务有限公司",
|
||||
user_memo="季度稳定兼职收入",
|
||||
cash_type="对私转账",
|
||||
|
||||
@@ -4,6 +4,8 @@ import random
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from config.settings import settings
|
||||
|
||||
from services.statement_rule_samples import (
|
||||
build_seed_statements_for_rule_plan,
|
||||
resolve_identity_cards,
|
||||
@@ -43,34 +45,44 @@ class StatementService:
|
||||
primary_account_no: str,
|
||||
allowed_identity_cards: tuple,
|
||||
rng: random.Random,
|
||||
noise_index: int = 0,
|
||||
safe_all_mode_noise: bool = False,
|
||||
) -> Dict:
|
||||
"""生成单条随机噪声流水记录。"""
|
||||
reference_now = datetime(2026, 3, 18, 9, 0, 0)
|
||||
days_ago = rng.randint(0, 365)
|
||||
trx_datetime = reference_now - timedelta(days=days_ago, minutes=rng.randint(0, 1439))
|
||||
trans_amount = round(rng.uniform(10, 10000), 2)
|
||||
|
||||
if rng.random() > 0.5:
|
||||
if safe_all_mode_noise:
|
||||
trans_amount = round(rng.uniform(10, 200), 2)
|
||||
dr_amount = trans_amount
|
||||
cr_amount = 0.0
|
||||
trans_flag = "P"
|
||||
customer_name = f"日常消费商户{noise_index}"
|
||||
user_memo = f"日常消费_{noise_index}"
|
||||
else:
|
||||
cr_amount = trans_amount
|
||||
dr_amount = 0.0
|
||||
trans_flag = "R"
|
||||
trans_amount = round(rng.uniform(10, 10000), 2)
|
||||
|
||||
customer_name = rng.choice(
|
||||
["小店", "支付宝", "微信支付", "财付通", "美团", "京东", "淘宝", "银行转账"]
|
||||
)
|
||||
user_memo = rng.choice(
|
||||
[
|
||||
f"消费_{customer_name}",
|
||||
f"转账_{customer_name}",
|
||||
f"收款_{customer_name}",
|
||||
f"支付_{customer_name}",
|
||||
f"退款_{customer_name}",
|
||||
]
|
||||
)
|
||||
if rng.random() > 0.5:
|
||||
dr_amount = trans_amount
|
||||
cr_amount = 0.0
|
||||
trans_flag = "P"
|
||||
else:
|
||||
cr_amount = trans_amount
|
||||
dr_amount = 0.0
|
||||
trans_flag = "R"
|
||||
|
||||
customer_name = rng.choice(
|
||||
["小店", "支付宝", "微信支付", "财付通", "美团", "京东", "淘宝", "银行转账"]
|
||||
)
|
||||
user_memo = rng.choice(
|
||||
[
|
||||
f"消费_{customer_name}",
|
||||
f"转账_{customer_name}",
|
||||
f"收款_{customer_name}",
|
||||
f"支付_{customer_name}",
|
||||
f"退款_{customer_name}",
|
||||
]
|
||||
)
|
||||
|
||||
return {
|
||||
"accountId": 0,
|
||||
@@ -167,10 +179,11 @@ class StatementService:
|
||||
staff_id_card=record.staff_id_card if record is not None else None,
|
||||
family_id_cards=record.family_id_cards if record is not None else None,
|
||||
)
|
||||
safe_all_mode_noise = settings.RULE_HIT_MODE == "all" and record is not None
|
||||
|
||||
total_count = max(count, len(seeded_statements))
|
||||
statements = list(seeded_statements)
|
||||
for _ in range(total_count - len(seeded_statements)):
|
||||
for noise_index in range(total_count - len(seeded_statements)):
|
||||
statements.append(
|
||||
self._generate_random_statement(
|
||||
group_id,
|
||||
@@ -179,6 +192,8 @@ class StatementService:
|
||||
primary_account_no,
|
||||
allowed_identity_cards,
|
||||
rng,
|
||||
noise_index=noise_index,
|
||||
safe_all_mode_noise=safe_all_mode_noise,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user