Files
ccdi/lsfx-mock-server/services/statement_rule_samples.py
2026-03-20 14:45:49 +08:00

626 lines
22 KiB
Python

from datetime import datetime, timedelta
from typing import Dict, List, Optional
DEFAULT_LARGE_TRANSACTION_THRESHOLDS = {
"SINGLE_TRANSACTION_AMOUNT": 1111,
"CUMULATIVE_TRANSACTION_AMOUNT": 50000001,
"ANNUAL_TURNOVER": 50000001,
"LARGE_CASH_DEPOSIT": 2000001,
"FREQUENT_CASH_DEPOSIT": 5,
"FREQUENT_TRANSFER": 100001,
}
IDENTITY_POOL = {
"staff_primary": {
"name": "模型测试员工",
"id_card": "330101198801010011",
"account": "6222024000000001",
},
"family_primary": {
"name": "模型测试家属",
"id_card": "330101199001010022",
"account": "6222024000000002",
},
"staff_secondary": {
"name": "模型二测试员工",
"id_card": "330101198802020033",
"account": "6222024000000003",
},
"family_secondary": {
"name": "模型二测试家属",
"id_card": "330101199202020044",
"account": "6222024000000004",
},
}
IDENTITY_SCOPES = {
"primary": {
"staff": IDENTITY_POOL["staff_primary"],
"family": IDENTITY_POOL["family_primary"],
},
"secondary": {
"staff": IDENTITY_POOL["staff_secondary"],
"family": IDENTITY_POOL["family_secondary"],
},
}
IDENTITY_CARD_POOL = tuple(identity["id_card"] for identity in IDENTITY_POOL.values())
REFERENCE_NOW = datetime(2026, 3, 18, 9, 0, 0)
def resolve_identity_scope(log_id: int) -> Dict[str, Dict[str, str]]:
"""按 logId 稳定选择单个员工域。"""
return IDENTITY_SCOPES["primary"] if log_id % 2 == 1 else IDENTITY_SCOPES["secondary"]
def resolve_identity_cards(log_id: int) -> tuple:
"""返回指定 logId 允许出现的证件号集合(员工本人及家属)。"""
identity_scope = resolve_identity_scope(log_id)
return tuple(identity["id_card"] for identity in identity_scope.values())
def _format_datetime(value: datetime) -> str:
return value.strftime("%Y-%m-%d %H:%M:%S")
def _format_date(value: datetime) -> str:
return value.strftime("%Y-%m-%d")
def _build_statement(
group_id: int,
log_id: int,
*,
trx_datetime: datetime,
cret_no: str,
customer_name: str,
user_memo: str,
cash_type: str,
dr_amount: float = 0.0,
cr_amount: float = 0.0,
le_name: str = "模型测试主体",
account_mask_no: str = "6222024999999999",
customer_account_mask_no: str = "9558800000000001",
bank_comments: str = "",
customer_bank: str = "",
) -> Dict:
trans_amount = round(dr_amount if dr_amount > 0 else cr_amount, 2)
balance_amount = round(80000000 + cr_amount - dr_amount, 2)
return {
"accountId": 0,
"accountMaskNo": account_mask_no,
"accountingDate": _format_date(trx_datetime),
"accountingDateId": int(trx_datetime.strftime("%Y%m%d")),
"archivingFlag": 0,
"attachments": 0,
"balanceAmount": balance_amount,
"bank": "ZJRCU",
"bankComments": bank_comments,
"bankStatementId": 0,
"bankTrxNumber": "",
"batchId": log_id,
"cashType": cash_type,
"commentsNum": 0,
"crAmount": round(cr_amount, 2),
"createDate": _format_datetime(REFERENCE_NOW),
"createdBy": "902001",
"cretNo": cret_no,
"currency": "CNY",
"customerAccountMaskNo": customer_account_mask_no,
"customerBank": customer_bank,
"customerId": -1,
"customerName": customer_name,
"customerReference": "",
"downPaymentFlag": 0,
"drAmount": round(dr_amount, 2),
"exceptionType": "",
"groupId": group_id,
"internalFlag": 0,
"leId": 16308,
"leName": le_name,
"overrideBsId": 0,
"paymentMethod": "",
"sourceCatalogId": 0,
"split": 0,
"subBankstatementId": 0,
"toDoFlag": 0,
"transAmount": trans_amount,
"transFlag": "P" if dr_amount > 0 else "R",
"transTypeId": 0,
"transformAmount": 0,
"transformCrAmount": 0,
"transformDrAmount": 0,
"transfromBalanceAmount": 0,
"trxBalance": 0,
"trxDate": _format_datetime(trx_datetime),
"uploadSequnceNumber": 0,
"userMemo": user_memo,
}
def _build_sample_context(
log_id: int,
primary_enterprise_name: Optional[str] = None,
primary_account_no: Optional[str] = None,
staff_id_card: Optional[str] = None,
family_id_cards: Optional[List[str]] = None,
) -> Dict[str, str]:
identity_scope = resolve_identity_scope(log_id)
staff_identity = identity_scope["staff"]
family_identity = identity_scope["family"]
selected_staff_id_card = staff_id_card or staff_identity["id_card"]
selected_family_id_cards = list(family_id_cards or [family_identity["id_card"]])
return {
"le_name": primary_enterprise_name or "模型测试主体",
"account_no": primary_account_no or "6222024999999999",
"staff_id_card": selected_staff_id_card,
"family_id_card": (
selected_family_id_cards[0] if selected_family_id_cards else selected_staff_id_card
),
}
def build_house_or_car_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=9, hours=1),
cret_no=context["staff_id_card"],
customer_name="杭州贝壳房地产经纪有限公司",
user_memo="购买房产首付款",
cash_type="对公转账",
dr_amount=680000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024555500001",
),
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=8, hours=2),
cret_no=context["family_id_card"],
customer_name="兰溪星耀汽车销售服务有限公司",
user_memo="购车首付款",
cash_type="对公转账",
dr_amount=380000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024555500002",
),
]
def build_tax_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=7, hours=1),
cret_no=context["staff_id_card"],
customer_name="国家金库兰溪市中心支库",
user_memo="个人所得税税款",
cash_type="税务缴款",
dr_amount=126000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024555500003",
),
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=6, hours=3),
cret_no=context["family_id_card"],
customer_name="兰溪市税务局",
user_memo="房产税务缴税",
cash_type="税务缴款",
dr_amount=88000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024555500004",
),
]
def build_single_large_income_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=5, hours=2),
cret_no=context["staff_id_card"],
customer_name="浙江远望贸易有限公司",
user_memo="经营往来收入",
cash_type="对公转账",
cr_amount=18800000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024666600001",
)
]
def build_cumulative_income_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=5, hours=2),
cret_no=context["staff_id_card"],
customer_name="浙江远望贸易有限公司",
user_memo="经营往来收入",
cash_type="对公转账",
cr_amount=18800000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024666600001",
),
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=5, hours=1),
cret_no=context["staff_id_card"],
customer_name="浙江远望贸易有限公司",
user_memo="项目回款收入",
cash_type="对公转账",
cr_amount=20800000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024666600001",
),
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=4, hours=4),
cret_no=context["staff_id_card"],
customer_name="浙江远望贸易有限公司",
user_memo="业务合作收入",
cash_type="对公转账",
cr_amount=20700000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024666600001",
),
]
def build_annual_turnover_supporting_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=15, hours=2),
cret_no=context["staff_id_card"],
customer_name="浙江金穗供应链有限公司",
user_memo="年度经营回款",
cash_type="对公转账",
cr_amount=17200000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024666600002",
),
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=11, hours=3),
cret_no=context["staff_id_card"],
customer_name="浙江金穗供应链有限公司",
user_memo="年度项目回款",
cash_type="对公转账",
cr_amount=17600000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024666600002",
),
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=9, hours=4),
cret_no=context["staff_id_card"],
customer_name="浙江金穗供应链有限公司",
user_memo="年度合作收入",
cash_type="对公转账",
cr_amount=17800000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024666600002",
),
]
def build_large_cash_deposit_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=datetime(2026, 3, 10, 9, 0, 0),
cret_no=context["staff_id_card"],
customer_name="",
user_memo="现金存款",
cash_type="现金存款",
cr_amount=3000000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
)
]
def build_frequent_cash_deposit_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
deposit_specs = [
(datetime(2026, 3, 10, 9, 0, 0), "现金存款", 3000000.0),
(datetime(2026, 3, 10, 9, 30, 0), "ATM现金存款", 3100000.0),
(datetime(2026, 3, 10, 10, 0, 0), "自助存款现金存入", 3200000.0),
(datetime(2026, 3, 10, 10, 30, 0), "CRS存款", 3300000.0),
(datetime(2026, 3, 10, 11, 0, 0), "本行ATM存款", 3400000.0),
(datetime(2026, 3, 10, 11, 30, 0), "柜面现金存款", 3500000.0),
]
return [
_build_statement(
group_id,
log_id,
trx_datetime=trx_datetime,
cret_no=context["staff_id_card"],
customer_name="",
user_memo=user_memo,
cash_type="现金存款",
cr_amount=cr_amount,
le_name=context["le_name"],
account_mask_no=context["account_no"],
)
for trx_datetime, user_memo, cr_amount in deposit_specs
]
def build_large_transfer_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=3, hours=1),
cret_no=context["staff_id_card"],
customer_name="异地转账平台",
user_memo="手机银行转账",
cash_type="转账支出",
dr_amount=12000000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024777700001",
)
]
def build_gambling_sensitive_keyword_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=4, hours=2),
cret_no=context["staff_id_card"],
customer_name="欢乐游戏科技有限公司",
user_memo="游戏充值",
cash_type="快捷支付",
dr_amount=6888.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024888800001",
)
]
def build_special_amount_transaction_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=4, hours=1),
cret_no=context["staff_id_card"],
customer_name="兰溪特别金额结算中心",
user_memo="特殊金额转账",
cash_type="转账支出",
dr_amount=88888.88,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024888800002",
)
]
def build_suspicious_income_keyword_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=3, hours=5),
cret_no=context["staff_id_card"],
customer_name="灰度信息咨询有限公司",
user_memo="咨询返现收入",
cash_type="对公转账",
cr_amount=166666.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024888800003",
)
]
def build_forex_buy_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=2, hours=6),
cret_no=context["staff_id_card"],
customer_name="中国银行外汇业务中心",
user_memo="个人购汇",
cash_type="购汇支出",
dr_amount=126000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024999900001",
)
]
def build_forex_sell_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=2, hours=4),
cret_no=context["staff_id_card"],
customer_name="中国银行外汇业务中心",
user_memo="个人结汇",
cash_type="结汇收入",
cr_amount=132000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024999900002",
)
]
def build_stock_transfer_large_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=2, hours=2),
cret_no=context["staff_id_card"],
customer_name="国信证券资金账户",
user_memo="证券大额转托管转出",
cash_type="转账支出",
dr_amount=560000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024999900003",
)
]
def build_large_stock_trading_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
return [
_build_statement(
group_id,
log_id,
trx_datetime=REFERENCE_NOW - timedelta(days=1, hours=3),
cret_no=context["staff_id_card"],
customer_name="华泰证券资金账户",
user_memo="证券大额交易买入",
cash_type="证券交易",
dr_amount=880000.0,
le_name=context["le_name"],
account_mask_no=context["account_no"],
customer_account_mask_no="6222024999900004",
)
]
def build_withdraw_cnt_samples(group_id: int, log_id: int, **kwargs) -> List[Dict]:
context = _build_sample_context(log_id, **kwargs)
withdraw_specs = [
(datetime(2026, 3, 12, 9, 0, 0), "微信提现", 8000.0),
(datetime(2026, 3, 12, 10, 0, 0), "支付宝提现", 9000.0),
(datetime(2026, 3, 12, 11, 0, 0), "微信提现", 8500.0),
(datetime(2026, 3, 12, 12, 0, 0), "支付宝提现", 9200.0),
]
return [
_build_statement(
group_id,
log_id,
trx_datetime=trx_datetime,
cret_no=context["staff_id_card"],
customer_name="财付通结算账户" if "微信" in user_memo else "支付宝结算账户",
user_memo=user_memo,
cash_type="提现支出",
dr_amount=dr_amount,
le_name=context["le_name"],
account_mask_no=context["account_no"],
)
for trx_datetime, user_memo, dr_amount in withdraw_specs
]
LARGE_TRANSACTION_BUILDERS = {
"HOUSE_OR_CAR_EXPENSE": build_house_or_car_samples,
"TAX_EXPENSE": build_tax_samples,
"SINGLE_LARGE_INCOME": build_single_large_income_samples,
"CUMULATIVE_INCOME": build_cumulative_income_samples,
"ANNUAL_TURNOVER": build_annual_turnover_supporting_samples,
"LARGE_CASH_DEPOSIT": build_large_cash_deposit_samples,
"FREQUENT_CASH_DEPOSIT": build_frequent_cash_deposit_samples,
"LARGE_TRANSFER": build_large_transfer_samples,
}
PHASE1_RULE_BUILDERS = {
"GAMBLING_SENSITIVE_KEYWORD": build_gambling_sensitive_keyword_samples,
"SPECIAL_AMOUNT_TRANSACTION": build_special_amount_transaction_samples,
"SUSPICIOUS_INCOME_KEYWORD": build_suspicious_income_keyword_samples,
"FOREX_BUY_AMT": build_forex_buy_samples,
"FOREX_SELL_AMT": build_forex_sell_samples,
"STOCK_TFR_LARGE": build_stock_transfer_large_samples,
"LARGE_STOCK_TRADING": build_large_stock_trading_samples,
"WITHDRAW_CNT": build_withdraw_cnt_samples,
}
def build_seed_statements_for_rule_plan(
group_id: int,
log_id: int,
rule_plan: Dict,
**kwargs,
) -> List[Dict]:
statements: List[Dict] = []
for rule_code in rule_plan.get("large_transaction_hit_rules", []):
builder = LARGE_TRANSACTION_BUILDERS.get(rule_code)
if builder is not None:
statements.extend(builder(group_id, log_id, **kwargs))
for rule_code in rule_plan.get("phase1_hit_rules", []):
builder = PHASE1_RULE_BUILDERS.get(rule_code)
if builder is not None:
statements.extend(builder(group_id, log_id, **kwargs))
return statements
def build_large_transaction_seed_statements(
group_id: int,
log_id: int,
primary_enterprise_name: Optional[str] = None,
primary_account_no: Optional[str] = None,
staff_id_card: Optional[str] = None,
family_id_cards: Optional[List[str]] = None,
) -> List[Dict]:
return build_seed_statements_for_rule_plan(
group_id=group_id,
log_id=log_id,
rule_plan={
"large_transaction_hit_rules": list(LARGE_TRANSACTION_BUILDERS.keys()),
"phase1_hit_rules": [],
},
primary_enterprise_name=primary_enterprise_name,
primary_account_no=primary_account_no,
staff_id_card=staff_id_card,
family_id_cards=family_id_cards,
)