收敛Mock文件记录主体账号绑定模型
This commit is contained in:
@@ -21,6 +21,8 @@ class FileRecord:
|
|||||||
parsing: bool = True # True表示正在解析
|
parsing: bool = True # True表示正在解析
|
||||||
|
|
||||||
# 新增字段 - 账号和主体信息
|
# 新增字段 - 账号和主体信息
|
||||||
|
primary_enterprise_name: str = ""
|
||||||
|
primary_account_no: str = ""
|
||||||
account_no_list: List[str] = field(default_factory=list)
|
account_no_list: List[str] = field(default_factory=list)
|
||||||
enterprise_name_list: List[str] = field(default_factory=list)
|
enterprise_name_list: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
@@ -74,6 +76,27 @@ class FileService:
|
|||||||
else:
|
else:
|
||||||
return "ZJRCU", "ZJRCU_T251114"
|
return "ZJRCU", "ZJRCU_T251114"
|
||||||
|
|
||||||
|
def _generate_primary_binding(self) -> tuple:
|
||||||
|
"""生成单一稳定的本方主体/本方账号绑定。"""
|
||||||
|
primary_account_no = f"{random.randint(10000000000, 99999999999)}"
|
||||||
|
primary_enterprise_name = "测试主体"
|
||||||
|
return primary_enterprise_name, primary_account_no
|
||||||
|
|
||||||
|
def _generate_primary_binding_from_rng(self, rng: random.Random) -> tuple:
|
||||||
|
"""使用局部随机源生成单一稳定的本方主体/本方账号绑定。"""
|
||||||
|
primary_account_no = f"{rng.randint(10000000000, 99999999999)}"
|
||||||
|
primary_enterprise_name = "测试主体"
|
||||||
|
return primary_enterprise_name, primary_account_no
|
||||||
|
|
||||||
|
def _build_primary_binding_lists(
|
||||||
|
self, primary_enterprise_name: str, primary_account_no: str
|
||||||
|
) -> dict:
|
||||||
|
"""基于主绑定事实源构建列表字段。"""
|
||||||
|
return {
|
||||||
|
"accountNoList": [primary_account_no],
|
||||||
|
"enterpriseNameList": [primary_enterprise_name],
|
||||||
|
}
|
||||||
|
|
||||||
async def upload_file(
|
async def upload_file(
|
||||||
self, group_id: int, file: UploadFile, background_tasks: BackgroundTasks
|
self, group_id: int, file: UploadFile, background_tasks: BackgroundTasks
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
@@ -100,9 +123,11 @@ class FileService:
|
|||||||
trx_date_start_id = int(start_date.strftime("%Y%m%d"))
|
trx_date_start_id = int(start_date.strftime("%Y%m%d"))
|
||||||
trx_date_end_id = int(end_date.strftime("%Y%m%d"))
|
trx_date_end_id = int(end_date.strftime("%Y%m%d"))
|
||||||
|
|
||||||
# 生成随机账号和主体
|
# 生成单一主绑定
|
||||||
account_no = f"{random.randint(10000000000, 99999999999)}"
|
primary_enterprise_name, primary_account_no = self._generate_primary_binding()
|
||||||
enterprise_names = ["测试主体"] if random.random() > 0.3 else [""]
|
binding_lists = self._build_primary_binding_lists(
|
||||||
|
primary_enterprise_name, primary_account_no
|
||||||
|
)
|
||||||
|
|
||||||
# 创建完整的文件记录
|
# 创建完整的文件记录
|
||||||
file_record = FileRecord(
|
file_record = FileRecord(
|
||||||
@@ -113,8 +138,10 @@ class FileService:
|
|||||||
bank_name=bank_name,
|
bank_name=bank_name,
|
||||||
real_bank_name=bank_name,
|
real_bank_name=bank_name,
|
||||||
template_name=template_name,
|
template_name=template_name,
|
||||||
account_no_list=[account_no],
|
primary_enterprise_name=primary_enterprise_name,
|
||||||
enterprise_name_list=enterprise_names,
|
primary_account_no=primary_account_no,
|
||||||
|
account_no_list=binding_lists["accountNoList"],
|
||||||
|
enterprise_name_list=binding_lists["enterpriseNameList"],
|
||||||
le_id=10000 + random.randint(0, 9999),
|
le_id=10000 + random.randint(0, 9999),
|
||||||
login_le_id=10000 + random.randint(0, 9999),
|
login_le_id=10000 + random.randint(0, 9999),
|
||||||
file_size=random.randint(10000, 100000),
|
file_size=random.randint(10000, 100000),
|
||||||
@@ -143,19 +170,21 @@ class FileService:
|
|||||||
str(file_record.log_id): [
|
str(file_record.log_id): [
|
||||||
{
|
{
|
||||||
"bank": file_record.bank_name,
|
"bank": file_record.bank_name,
|
||||||
"accountName": file_record.enterprise_name_list[0] if file_record.enterprise_name_list else "",
|
"accountName": file_record.primary_enterprise_name,
|
||||||
"accountNo": file_record.account_no_list[0] if file_record.account_no_list else "",
|
"accountNo": file_record.primary_account_no,
|
||||||
"currency": "CNY"
|
"currency": "CNY"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"uploadLogList": [
|
"uploadLogList": [
|
||||||
{
|
{
|
||||||
"accountNoList": file_record.account_no_list,
|
**self._build_primary_binding_lists(
|
||||||
|
file_record.primary_enterprise_name,
|
||||||
|
file_record.primary_account_no,
|
||||||
|
),
|
||||||
"bankName": file_record.bank_name,
|
"bankName": file_record.bank_name,
|
||||||
"dataTypeInfo": file_record.data_type_info,
|
"dataTypeInfo": file_record.data_type_info,
|
||||||
"downloadFileName": file_record.download_file_name,
|
"downloadFileName": file_record.download_file_name,
|
||||||
"enterpriseNameList": file_record.enterprise_name_list,
|
|
||||||
"filePackageId": file_record.file_package_id,
|
"filePackageId": file_record.file_package_id,
|
||||||
"fileSize": file_record.file_size,
|
"fileSize": file_record.file_size,
|
||||||
"fileUploadBy": file_record.file_upload_by,
|
"fileUploadBy": file_record.file_upload_by,
|
||||||
@@ -197,7 +226,9 @@ class FileService:
|
|||||||
if log_id in self.file_records:
|
if log_id in self.file_records:
|
||||||
self.file_records[log_id].parsing = False
|
self.file_records[log_id].parsing = False
|
||||||
|
|
||||||
def _generate_deterministic_record(self, log_id: int, group_id: int) -> dict:
|
def _generate_deterministic_record(
|
||||||
|
self, log_id: int, group_id: int, rng: random.Random
|
||||||
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
基于 logId 生成确定性的文件记录
|
基于 logId 生成确定性的文件记录
|
||||||
|
|
||||||
@@ -215,39 +246,40 @@ class FileService:
|
|||||||
("ZJRCU", "ZJRCU_T251114")
|
("ZJRCU", "ZJRCU_T251114")
|
||||||
]
|
]
|
||||||
|
|
||||||
bank_name, template_name = random.choice(bank_options)
|
bank_name, template_name = rng.choice(bank_options)
|
||||||
|
|
||||||
# 生成交易日期范围
|
# 生成交易日期范围
|
||||||
end_date = datetime.now()
|
end_date = datetime.now()
|
||||||
start_date = end_date - timedelta(days=random.randint(90, 365))
|
start_date = end_date - timedelta(days=rng.randint(90, 365))
|
||||||
|
|
||||||
# 生成账号和主体
|
# 生成账号和主体
|
||||||
account_no = f"{random.randint(10000000000, 99999999999)}"
|
primary_enterprise_name, primary_account_no = self._generate_primary_binding_from_rng(rng)
|
||||||
enterprise_names = ["测试主体"] if random.random() > 0.3 else [""]
|
binding_lists = self._build_primary_binding_lists(
|
||||||
|
primary_enterprise_name, primary_account_no
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"accountNoList": [account_no],
|
**binding_lists,
|
||||||
"bankName": bank_name,
|
"bankName": bank_name,
|
||||||
"dataTypeInfo": ["CSV", ","],
|
"dataTypeInfo": ["CSV", ","],
|
||||||
"downloadFileName": f"测试文件_{log_id}.csv",
|
"downloadFileName": f"测试文件_{log_id}.csv",
|
||||||
"enterpriseNameList": enterprise_names,
|
"fileSize": rng.randint(10000, 100000),
|
||||||
"fileSize": random.randint(10000, 100000),
|
|
||||||
"fileUploadBy": 448,
|
"fileUploadBy": 448,
|
||||||
"fileUploadByUserName": "admin@support.com",
|
"fileUploadByUserName": "admin@support.com",
|
||||||
"fileUploadTime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
"fileUploadTime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
"isSplit": 0,
|
"isSplit": 0,
|
||||||
"leId": 10000 + random.randint(0, 9999),
|
"leId": 10000 + rng.randint(0, 9999),
|
||||||
"logId": log_id,
|
"logId": log_id,
|
||||||
"logMeta": "{\"lostHeader\":[],\"balanceAmount\":\"-1\"}",
|
"logMeta": "{\"lostHeader\":[],\"balanceAmount\":\"-1\"}",
|
||||||
"logType": "bankstatement",
|
"logType": "bankstatement",
|
||||||
"loginLeId": 10000 + random.randint(0, 9999),
|
"loginLeId": 10000 + rng.randint(0, 9999),
|
||||||
"lostHeader": [],
|
"lostHeader": [],
|
||||||
"realBankName": bank_name,
|
"realBankName": bank_name,
|
||||||
"rows": 0,
|
"rows": 0,
|
||||||
"source": "http",
|
"source": "http",
|
||||||
"status": -5,
|
"status": -5,
|
||||||
"templateName": template_name,
|
"templateName": template_name,
|
||||||
"totalRecords": random.randint(100, 300),
|
"totalRecords": rng.randint(100, 300),
|
||||||
"trxDateEndId": int(end_date.strftime("%Y%m%d")),
|
"trxDateEndId": int(end_date.strftime("%Y%m%d")),
|
||||||
"trxDateStartId": int(start_date.strftime("%Y%m%d")),
|
"trxDateStartId": int(start_date.strftime("%Y%m%d")),
|
||||||
"uploadFileName": f"测试文件_{log_id}.pdf",
|
"uploadFileName": f"测试文件_{log_id}.pdf",
|
||||||
@@ -257,11 +289,13 @@ class FileService:
|
|||||||
def _build_log_detail(self, record: FileRecord) -> dict:
|
def _build_log_detail(self, record: FileRecord) -> dict:
|
||||||
"""构建日志详情对象"""
|
"""构建日志详情对象"""
|
||||||
return {
|
return {
|
||||||
"accountNoList": record.account_no_list,
|
**self._build_primary_binding_lists(
|
||||||
|
record.primary_enterprise_name,
|
||||||
|
record.primary_account_no,
|
||||||
|
),
|
||||||
"bankName": record.bank_name,
|
"bankName": record.bank_name,
|
||||||
"dataTypeInfo": record.data_type_info,
|
"dataTypeInfo": record.data_type_info,
|
||||||
"downloadFileName": record.download_file_name,
|
"downloadFileName": record.download_file_name,
|
||||||
"enterpriseNameList": record.enterprise_name_list,
|
|
||||||
"fileSize": record.file_size,
|
"fileSize": record.file_size,
|
||||||
"fileUploadBy": record.file_upload_by,
|
"fileUploadBy": record.file_upload_by,
|
||||||
"fileUploadByUserName": record.file_upload_by_user_name,
|
"fileUploadByUserName": record.file_upload_by_user_name,
|
||||||
@@ -333,11 +367,11 @@ class FileService:
|
|||||||
logs = []
|
logs = []
|
||||||
|
|
||||||
if log_id:
|
if log_id:
|
||||||
# 使用 logId 作为随机种子,确保相同 logId 返回相同数据
|
# 使用局部随机源,避免污染全局随机状态
|
||||||
random.seed(log_id)
|
rng = random.Random(log_id)
|
||||||
|
|
||||||
# 生成确定性的文件记录
|
# 生成确定性的文件记录
|
||||||
record = self._generate_deterministic_record(log_id, group_id)
|
record = self._generate_deterministic_record(log_id, group_id, rng)
|
||||||
logs.append(record)
|
logs.append(record)
|
||||||
|
|
||||||
# 返回响应
|
# 返回响应
|
||||||
|
|||||||
40
lsfx-mock-server/tests/test_file_service.py
Normal file
40
lsfx-mock-server/tests/test_file_service.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
"""
|
||||||
|
FileService 单一主绑定语义测试
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import io
|
||||||
|
|
||||||
|
from fastapi import BackgroundTasks
|
||||||
|
from fastapi.datastructures import UploadFile
|
||||||
|
|
||||||
|
from services.file_service import FileService
|
||||||
|
|
||||||
|
|
||||||
|
def test_upload_file_primary_binding_response(monkeypatch):
|
||||||
|
"""同一 logId 的主绑定必须稳定且只保留一组主体/账号信息。"""
|
||||||
|
service = FileService()
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
service,
|
||||||
|
"_generate_primary_binding",
|
||||||
|
lambda: ("测试主体A", "6222021234567890"),
|
||||||
|
)
|
||||||
|
|
||||||
|
background_tasks = BackgroundTasks()
|
||||||
|
file = UploadFile(filename="测试文件.csv", file=io.BytesIO(b"mock"))
|
||||||
|
|
||||||
|
response = asyncio.run(service.upload_file(1001, file, background_tasks))
|
||||||
|
|
||||||
|
log = response["data"]["uploadLogList"][0]
|
||||||
|
account_info = response["data"]["accountsOfLog"][str(log["logId"])][0]
|
||||||
|
record = service.file_records[log["logId"]]
|
||||||
|
|
||||||
|
assert log["enterpriseNameList"] == ["测试主体A"]
|
||||||
|
assert log["accountNoList"] == ["6222021234567890"]
|
||||||
|
assert account_info["accountName"] == "测试主体A"
|
||||||
|
assert account_info["accountNo"] == "6222021234567890"
|
||||||
|
assert record.primary_enterprise_name == "测试主体A"
|
||||||
|
assert record.primary_account_no == "6222021234567890"
|
||||||
|
assert record.enterprise_name_list == ["测试主体A"]
|
||||||
|
assert record.account_no_list == ["6222021234567890"]
|
||||||
Reference in New Issue
Block a user