feat(models,utils): implement data models and utility classes
This commit is contained in:
1
lsfx-mock-server/models/__init__.py
Normal file
1
lsfx-mock-server/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Models package
|
||||||
50
lsfx-mock-server/models/request.py
Normal file
50
lsfx-mock-server/models/request.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
|
||||||
|
class GetTokenRequest(BaseModel):
|
||||||
|
"""获取Token请求模型"""
|
||||||
|
projectNo: str = Field(..., description="项目编号,格式:902000_当前时间戳")
|
||||||
|
entityName: str = Field(..., description="项目名称")
|
||||||
|
userId: str = Field(..., description="操作人员编号,固定值")
|
||||||
|
userName: str = Field(..., description="操作人员姓名,固定值")
|
||||||
|
orgCode: str = Field(..., description="行社机构号,固定值")
|
||||||
|
entityId: Optional[str] = Field(None, description="企业统信码或个人身份证号")
|
||||||
|
xdRelatedPersons: Optional[str] = Field(None, description="信贷关联人信息")
|
||||||
|
jzDataDateId: Optional[str] = Field("0", description="拉取指定日期推送过来的金综链流水")
|
||||||
|
innerBSStartDateId: Optional[str] = Field("0", description="拉取行内流水开始日期")
|
||||||
|
innerBSEndDateId: Optional[str] = Field("0", description="拉取行内流水结束日期")
|
||||||
|
analysisType: Optional[int] = Field(-1, description="分析类型")
|
||||||
|
departmentCode: Optional[str] = Field(None, description="客户经理所属营业部/分理处的机构编码")
|
||||||
|
|
||||||
|
|
||||||
|
class FetchInnerFlowRequest(BaseModel):
|
||||||
|
"""拉取行内流水请求模型"""
|
||||||
|
groupId: int = Field(..., description="项目id")
|
||||||
|
customerNo: str = Field(..., description="客户身份证号")
|
||||||
|
dataChannelCode: str = Field(..., description="校验码")
|
||||||
|
requestDateId: int = Field(..., description="发起请求的时间")
|
||||||
|
dataStartDateId: int = Field(..., description="拉取开始日期")
|
||||||
|
dataEndDateId: int = Field(..., description="拉取结束日期")
|
||||||
|
uploadUserId: int = Field(..., description="柜员号")
|
||||||
|
|
||||||
|
|
||||||
|
class CheckParseStatusRequest(BaseModel):
|
||||||
|
"""检查文件解析状态请求模型"""
|
||||||
|
groupId: int = Field(..., description="项目id")
|
||||||
|
inprogressList: str = Field(..., description="文件id列表,逗号分隔")
|
||||||
|
|
||||||
|
|
||||||
|
class GetBankStatementRequest(BaseModel):
|
||||||
|
"""获取银行流水请求模型"""
|
||||||
|
groupId: int = Field(..., description="项目id")
|
||||||
|
logId: int = Field(..., description="文件id")
|
||||||
|
pageNow: int = Field(..., description="当前页码")
|
||||||
|
pageSize: int = Field(..., description="查询条数")
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteFilesRequest(BaseModel):
|
||||||
|
"""删除文件请求模型"""
|
||||||
|
groupId: int = Field(..., description="项目id")
|
||||||
|
logIds: List[int] = Field(..., description="文件id数组")
|
||||||
|
userId: int = Field(..., description="用户柜员号")
|
||||||
187
lsfx-mock-server/models/response.py
Normal file
187
lsfx-mock-server/models/response.py
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import Optional, List, Dict, Any
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== Token相关模型 ====================
|
||||||
|
|
||||||
|
class TokenData(BaseModel):
|
||||||
|
"""Token数据"""
|
||||||
|
token: str = Field(..., description="token")
|
||||||
|
projectId: int = Field(..., description="见知项目Id")
|
||||||
|
projectNo: str = Field(..., description="项目编号")
|
||||||
|
entityName: str = Field(..., description="项目名称")
|
||||||
|
analysisType: int = Field(0, description="分析类型")
|
||||||
|
|
||||||
|
|
||||||
|
class GetTokenResponse(BaseModel):
|
||||||
|
"""获取Token响应"""
|
||||||
|
code: str = Field("200", description="返回码")
|
||||||
|
data: Optional[TokenData] = Field(None, description="返回数据")
|
||||||
|
message: str = Field("create.token.success", description="返回消息")
|
||||||
|
status: str = Field("200", description="状态")
|
||||||
|
successResponse: bool = Field(True, description="是否成功响应")
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 文件上传相关模型 ====================
|
||||||
|
|
||||||
|
class AccountInfo(BaseModel):
|
||||||
|
"""账户信息"""
|
||||||
|
bank: str = Field(..., description="银行")
|
||||||
|
accountName: str = Field(..., description="账户名称")
|
||||||
|
accountNo: str = Field(..., description="账号")
|
||||||
|
currency: str = Field(..., description="币种")
|
||||||
|
|
||||||
|
|
||||||
|
class UploadLogItem(BaseModel):
|
||||||
|
"""上传日志项"""
|
||||||
|
accountNoList: List[str] = Field(default=[], description="账号列表")
|
||||||
|
bankName: str = Field(..., description="银行名称")
|
||||||
|
dataTypeInfo: List[str] = Field(default=[], description="数据类型信息")
|
||||||
|
downloadFileName: str = Field(..., description="下载文件名")
|
||||||
|
enterpriseNameList: List[str] = Field(default=[], description="企业名称列表")
|
||||||
|
filePackageId: str = Field(..., description="文件包ID")
|
||||||
|
fileSize: int = Field(..., description="文件大小")
|
||||||
|
fileUploadBy: int = Field(..., description="上传者ID")
|
||||||
|
fileUploadByUserName: str = Field(..., description="上传者用户名")
|
||||||
|
fileUploadTime: str = Field(..., description="上传时间")
|
||||||
|
leId: int = Field(..., description="企业ID")
|
||||||
|
logId: int = Field(..., description="日志ID")
|
||||||
|
logMeta: str = Field(..., description="日志元数据")
|
||||||
|
logType: str = Field(..., description="日志类型")
|
||||||
|
loginLeId: int = Field(..., description="登录企业ID")
|
||||||
|
realBankName: str = Field(..., description="真实银行名称")
|
||||||
|
rows: int = Field(0, description="行数")
|
||||||
|
source: str = Field(..., description="来源")
|
||||||
|
status: int = Field(-5, description="状态值")
|
||||||
|
templateName: str = Field(..., description="模板名称")
|
||||||
|
totalRecords: int = Field(0, description="总记录数")
|
||||||
|
trxDateEndId: int = Field(..., description="交易结束日期ID")
|
||||||
|
trxDateStartId: int = Field(..., description="交易开始日期ID")
|
||||||
|
uploadFileName: str = Field(..., description="上传文件名")
|
||||||
|
uploadStatusDesc: str = Field(..., description="上传状态描述")
|
||||||
|
|
||||||
|
|
||||||
|
class UploadFileResponse(BaseModel):
|
||||||
|
"""上传文件响应"""
|
||||||
|
code: str = Field("200", description="返回码")
|
||||||
|
data: Optional[Dict[str, Any]] = Field(None, description="返回数据")
|
||||||
|
status: str = Field("200", description="状态")
|
||||||
|
successResponse: bool = Field(True, description="是否成功响应")
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 检查解析状态相关模型 ====================
|
||||||
|
|
||||||
|
class PendingItem(BaseModel):
|
||||||
|
"""待处理项"""
|
||||||
|
accountNoList: List[str] = Field(default=[], description="账号列表")
|
||||||
|
bankName: str = Field(..., description="银行名称")
|
||||||
|
dataTypeInfo: List[str] = Field(default=[], description="数据类型信息")
|
||||||
|
downloadFileName: str = Field(..., description="下载文件名")
|
||||||
|
enterpriseNameList: List[str] = Field(default=[], description="企业名称列表")
|
||||||
|
filePackageId: str = Field(..., description="文件包ID")
|
||||||
|
fileSize: int = Field(..., description="文件大小")
|
||||||
|
fileUploadBy: int = Field(..., description="上传者ID")
|
||||||
|
fileUploadByUserName: str = Field(..., description="上传者用户名")
|
||||||
|
fileUploadTime: str = Field(..., description="上传时间")
|
||||||
|
isSplit: int = Field(0, description="是否分割")
|
||||||
|
leId: int = Field(..., description="企业ID")
|
||||||
|
logId: int = Field(..., description="日志ID")
|
||||||
|
logMeta: str = Field(..., description="日志元数据")
|
||||||
|
logType: str = Field(..., description="日志类型")
|
||||||
|
loginLeId: int = Field(..., description="登录企业ID")
|
||||||
|
lostHeader: List[str] = Field(default=[], description="丢失的头部")
|
||||||
|
realBankName: str = Field(..., description="真实银行名称")
|
||||||
|
rows: int = Field(0, description="行数")
|
||||||
|
source: str = Field(..., description="来源")
|
||||||
|
status: int = Field(-5, description="状态值")
|
||||||
|
templateName: str = Field(..., description="模板名称")
|
||||||
|
totalRecords: int = Field(0, description="总记录数")
|
||||||
|
trxDateEndId: int = Field(..., description="交易结束日期ID")
|
||||||
|
trxDateStartId: int = Field(..., description="交易开始日期ID")
|
||||||
|
uploadFileName: str = Field(..., description="上传文件名")
|
||||||
|
uploadStatusDesc: str = Field(..., description="上传状态描述")
|
||||||
|
|
||||||
|
|
||||||
|
class CheckParseStatusResponse(BaseModel):
|
||||||
|
"""检查解析状态响应"""
|
||||||
|
code: str = Field("200", description="返回码")
|
||||||
|
data: Optional[Dict[str, Any]] = Field(None, description="返回数据,包含parsing和pendingList")
|
||||||
|
status: str = Field("200", description="状态")
|
||||||
|
successResponse: bool = Field(True, description="是否成功响应")
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 银行流水相关模型 ====================
|
||||||
|
|
||||||
|
class BankStatementItem(BaseModel):
|
||||||
|
"""银行流水项"""
|
||||||
|
accountId: int = Field(0, description="账号ID")
|
||||||
|
accountMaskNo: str = Field(..., description="账号")
|
||||||
|
accountingDate: str = Field(..., description="记账日期")
|
||||||
|
accountingDateId: int = Field(..., description="记账日期ID")
|
||||||
|
archivingFlag: int = Field(0, description="归档标志")
|
||||||
|
attachments: int = Field(0, description="附件数")
|
||||||
|
balanceAmount: float = Field(..., description="余额")
|
||||||
|
bank: str = Field(..., description="银行")
|
||||||
|
bankComments: str = Field("", description="银行注释")
|
||||||
|
bankStatementId: int = Field(..., description="流水ID")
|
||||||
|
bankTrxNumber: str = Field(..., description="银行交易号")
|
||||||
|
batchId: int = Field(..., description="批次ID")
|
||||||
|
cashType: str = Field("1", description="现金类型")
|
||||||
|
commentsNum: int = Field(0, description="评论数")
|
||||||
|
crAmount: float = Field(0, description="贷方金额")
|
||||||
|
cretNo: str = Field(..., description="证件号")
|
||||||
|
currency: str = Field("CNY", description="币种")
|
||||||
|
customerAccountMaskNo: str = Field(..., description="客户账号")
|
||||||
|
customerBank: str = Field("", description="客户银行")
|
||||||
|
customerId: int = Field(-1, description="客户ID")
|
||||||
|
customerName: str = Field(..., description="客户名称")
|
||||||
|
customerReference: str = Field("", description="客户参考")
|
||||||
|
downPaymentFlag: int = Field(0, description="首付标志")
|
||||||
|
drAmount: float = Field(0, description="借方金额")
|
||||||
|
exceptionType: str = Field("", description="异常类型")
|
||||||
|
groupId: int = Field(0, description="项目ID")
|
||||||
|
internalFlag: int = Field(0, description="内部标志")
|
||||||
|
leId: int = Field(..., description="企业ID")
|
||||||
|
leName: str = Field(..., description="企业名称")
|
||||||
|
overrideBsId: int = Field(0, description="覆盖流水ID")
|
||||||
|
paymentMethod: str = Field("", description="支付方式")
|
||||||
|
sourceCatalogId: int = Field(0, description="来源目录ID")
|
||||||
|
split: int = Field(0, description="分割")
|
||||||
|
subBankstatementId: int = Field(0, description="子流水ID")
|
||||||
|
toDoFlag: int = Field(0, description="待办标志")
|
||||||
|
transAmount: float = Field(..., description="交易金额")
|
||||||
|
transFlag: str = Field("P", description="交易标志")
|
||||||
|
transTypeId: int = Field(0, description="交易类型ID")
|
||||||
|
transformAmount: int = Field(0, description="转换金额")
|
||||||
|
transformCrAmount: int = Field(0, description="转换贷方金额")
|
||||||
|
transformDrAmount: int = Field(0, description="转换借方金额")
|
||||||
|
transfromBalanceAmount: int = Field(0, description="转换余额")
|
||||||
|
trxBalance: int = Field(0, description="交易余额")
|
||||||
|
trxDate: str = Field(..., description="交易日期")
|
||||||
|
userMemo: str = Field(..., description="用户备注")
|
||||||
|
|
||||||
|
|
||||||
|
class GetBankStatementResponse(BaseModel):
|
||||||
|
"""获取银行流水响应"""
|
||||||
|
code: str = Field("200", description="返回码")
|
||||||
|
data: Optional[Dict[str, Any]] = Field(None, description="返回数据,包含bankStatementList和totalCount")
|
||||||
|
status: str = Field("200", description="状态")
|
||||||
|
successResponse: bool = Field(True, description="是否成功响应")
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 其他响应模型 ====================
|
||||||
|
|
||||||
|
class FetchInnerFlowResponse(BaseModel):
|
||||||
|
"""拉取行内流水响应"""
|
||||||
|
code: str = Field("200", description="返回码")
|
||||||
|
data: Optional[Dict[str, Any]] = Field(None, description="返回数据")
|
||||||
|
status: str = Field("200", description="状态")
|
||||||
|
successResponse: bool = Field(True, description="是否成功响应")
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteFilesResponse(BaseModel):
|
||||||
|
"""删除文件响应"""
|
||||||
|
code: str = Field("200", description="返回码")
|
||||||
|
data: Optional[Dict[str, str]] = Field(None, description="返回数据")
|
||||||
|
status: str = Field("200", description="状态")
|
||||||
|
successResponse: bool = Field(True, description="是否成功响应")
|
||||||
1
lsfx-mock-server/utils/__init__.py
Normal file
1
lsfx-mock-server/utils/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Utils package
|
||||||
BIN
lsfx-mock-server/utils/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
lsfx-mock-server/utils/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
49
lsfx-mock-server/utils/error_simulator.py
Normal file
49
lsfx-mock-server/utils/error_simulator.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
from typing import Dict, Optional
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorSimulator:
|
||||||
|
"""错误场景模拟器"""
|
||||||
|
|
||||||
|
# 错误码映射表
|
||||||
|
ERROR_CODES = {
|
||||||
|
"40101": {"code": "40101", "message": "appId错误"},
|
||||||
|
"40102": {"code": "40102", "message": "appSecretCode错误"},
|
||||||
|
"40104": {"code": "40104", "message": "可使用项目次数为0,无法创建项目"},
|
||||||
|
"40105": {"code": "40105", "message": "只读模式下无法新建项目"},
|
||||||
|
"40106": {"code": "40106", "message": "错误的分析类型,不在规定的取值范围内"},
|
||||||
|
"40107": {"code": "40107", "message": "当前系统不支持的分析类型"},
|
||||||
|
"40108": {"code": "40108", "message": "当前用户所属行社无权限"},
|
||||||
|
"501014": {"code": "501014", "message": "无行内流水文件"},
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def detect_error_marker(value: str) -> Optional[str]:
|
||||||
|
"""检测字符串中的错误标记
|
||||||
|
|
||||||
|
规则:如果字符串包含 error_XXXX,则返回 XXXX
|
||||||
|
例如:
|
||||||
|
- "project_error_40101" -> "40101"
|
||||||
|
- "test_error_501014" -> "501014"
|
||||||
|
"""
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
|
||||||
|
pattern = r'error_(\d+)'
|
||||||
|
match = re.search(pattern, value)
|
||||||
|
if match:
|
||||||
|
return match.group(1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_error_response(error_code: str) -> Optional[Dict]:
|
||||||
|
"""构建错误响应"""
|
||||||
|
if error_code in ErrorSimulator.ERROR_CODES:
|
||||||
|
error_info = ErrorSimulator.ERROR_CODES[error_code]
|
||||||
|
return {
|
||||||
|
"code": error_info["code"],
|
||||||
|
"message": error_info["message"],
|
||||||
|
"status": error_info["code"],
|
||||||
|
"successResponse": False
|
||||||
|
}
|
||||||
|
return None
|
||||||
69
lsfx-mock-server/utils/response_builder.py
Normal file
69
lsfx-mock-server/utils/response_builder.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Any
|
||||||
|
import copy
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseBuilder:
|
||||||
|
"""响应构建器"""
|
||||||
|
|
||||||
|
TEMPLATE_DIR = Path(__file__).parent.parent / "config" / "responses"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_template(template_name: str) -> Dict:
|
||||||
|
"""加载 JSON 模板
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template_name: 模板名称(不含.json扩展名)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
模板字典
|
||||||
|
"""
|
||||||
|
file_path = ResponseBuilder.TEMPLATE_DIR / f"{template_name}.json"
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def replace_placeholders(template: Dict, **kwargs) -> Dict:
|
||||||
|
"""递归替换占位符
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template: 模板字典
|
||||||
|
**kwargs: 占位符键值对
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
替换后的字典
|
||||||
|
"""
|
||||||
|
def replace_value(value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
result = value
|
||||||
|
for key, val in kwargs.items():
|
||||||
|
placeholder = f"{{{key}}}"
|
||||||
|
if placeholder in result:
|
||||||
|
result = result.replace(placeholder, str(val))
|
||||||
|
return result
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
return {k: replace_value(v) for k, v in value.items()}
|
||||||
|
elif isinstance(value, list):
|
||||||
|
return [replace_value(item) for item in value]
|
||||||
|
return value
|
||||||
|
|
||||||
|
# 深拷贝模板,避免修改原始数据
|
||||||
|
return replace_value(copy.deepcopy(template))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_success_response(template_name: str, **kwargs) -> Dict:
|
||||||
|
"""构建成功响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template_name: 模板名称
|
||||||
|
**kwargs: 占位符键值对
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
响应字典
|
||||||
|
"""
|
||||||
|
template = ResponseBuilder.load_template(template_name)
|
||||||
|
return ResponseBuilder.replace_placeholders(
|
||||||
|
template["success_response"],
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user