feat: 修复接口参数并改为form-data格式

- 添加缺失的认证参数:appId, appSecretCode, role
- 修复 analysisType 和 departmentCode 参数
- 将所有接口改为使用 Form 参数(form-data 格式)
- 更新服务层支持字典参数
- 更新所有测试代码
- 所有测试通过(7/7)
This commit is contained in:
wkc
2026-03-03 13:40:56 +08:00
parent a1f062d09d
commit 626f7d566b
21 changed files with 2527 additions and 52 deletions

View File

@@ -51,7 +51,11 @@ response = requests.post(
"entityName": "测试企业",
"userId": "902001",
"userName": "902001",
"orgCode": "902000"
"appId": "remote_app",
"appSecretCode": "test_secret_code_12345",
"role": "VIEWER",
"orgCode": "902000",
"departmentCode": "902000"
}
)
token_data = response.json()
@@ -102,7 +106,11 @@ response = requests.post(
"entityName": "测试企业",
"userId": "902001",
"userName": "902001",
"orgCode": "902000"
"appId": "remote_app",
"appSecretCode": "test_secret_code_12345",
"role": "VIEWER",
"orgCode": "902000",
"departmentCode": "902000"
}
)
# 返回: {"code": "40101", "message": "appId错误", ...}

View File

@@ -8,14 +8,17 @@ class GetTokenRequest(BaseModel):
entityName: str = Field(..., description="项目名称")
userId: str = Field(..., description="操作人员编号,固定值")
userName: str = Field(..., description="操作人员姓名,固定值")
appId: str = Field("remote_app", description="应用ID固定值")
appSecretCode: str = Field(..., description="安全码md5(projectNo + '_' + entityName + '_' + dXj6eHRmPv)")
role: str = Field("VIEWER", 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="客户经理所属营业部/分理处的机构编码")
analysisType: str = Field("-1", description="分析类型,固定值")
departmentCode: str = Field(..., description="客户经理所属营业部/分理处的机构编码")
class FetchInnerFlowRequest(BaseModel):

View File

@@ -1,16 +1,9 @@
from fastapi import APIRouter, BackgroundTasks, UploadFile, File, Form
from models.request import (
GetTokenRequest,
FetchInnerFlowRequest,
CheckParseStatusRequest,
GetBankStatementRequest,
DeleteFilesRequest,
)
from services.token_service import TokenService
from services.file_service import FileService
from services.statement_service import StatementService
from utils.error_simulator import ErrorSimulator
from typing import List
from typing import List, Optional
# 创建路由器
router = APIRouter()
@@ -23,18 +16,53 @@ statement_service = StatementService()
# ==================== 接口1获取Token ====================
@router.post("/account/common/getToken")
async def get_token(request: GetTokenRequest):
async def get_token(
projectNo: str = Form(..., description="项目编号格式902000_当前时间戳"),
entityName: str = Form(..., description="项目名称"),
userId: str = Form(..., description="操作人员编号,固定值"),
userName: str = Form(..., description="操作人员姓名,固定值"),
appId: str = Form("remote_app", description="应用ID固定值"),
appSecretCode: str = Form(..., description="安全码"),
role: str = Form("VIEWER", description="角色,固定值"),
orgCode: str = Form(..., description="行社机构号,固定值"),
entityId: Optional[str] = Form(None, description="企业统信码或个人身份证号"),
xdRelatedPersons: Optional[str] = Form(None, description="信贷关联人信息"),
jzDataDateId: str = Form("0", description="拉取指定日期推送过来的金综链流水"),
innerBSStartDateId: str = Form("0", description="拉取行内流水开始日期"),
innerBSEndDateId: str = Form("0", description="拉取行内流水结束日期"),
analysisType: str = Form("-1", description="分析类型,固定值"),
departmentCode: str = Form(..., description="客户经理所属营业部/分理处的机构编码"),
):
"""创建项目并获取访问Token
如果 projectNo 包含 error_XXXX 标记,将返回对应的错误响应
"""
# 检测错误标记
error_code = ErrorSimulator.detect_error_marker(request.projectNo)
error_code = ErrorSimulator.detect_error_marker(projectNo)
if error_code:
return ErrorSimulator.build_error_response(error_code)
# 构建请求数据字典
request_data = {
"projectNo": projectNo,
"entityName": entityName,
"userId": userId,
"userName": userName,
"appId": appId,
"appSecretCode": appSecretCode,
"role": role,
"orgCode": orgCode,
"entityId": entityId,
"xdRelatedPersons": xdRelatedPersons,
"jzDataDateId": jzDataDateId,
"innerBSStartDateId": innerBSStartDateId,
"innerBSEndDateId": innerBSEndDateId,
"analysisType": analysisType,
"departmentCode": departmentCode,
}
# 正常流程
return token_service.create_token(request)
return token_service.create_token(request_data)
# ==================== 接口2上传文件 ====================
@@ -53,47 +81,85 @@ async def upload_file(
# ==================== 接口3拉取行内流水 ====================
@router.post("/watson/api/project/getJZFileOrZjrcuFile")
async def fetch_inner_flow(request: FetchInnerFlowRequest):
async def fetch_inner_flow(
groupId: int = Form(..., description="项目id"),
customerNo: str = Form(..., description="客户身份证号"),
dataChannelCode: str = Form(..., description="校验码"),
requestDateId: int = Form(..., description="发起请求的时间"),
dataStartDateId: int = Form(..., description="拉取开始日期"),
dataEndDateId: int = Form(..., description="拉取结束日期"),
uploadUserId: int = Form(..., description="柜员号"),
):
"""拉取行内流水
如果 customerNo 包含 error_XXXX 标记,将返回对应的错误响应
"""
# 检测错误标记
error_code = ErrorSimulator.detect_error_marker(request.customerNo)
error_code = ErrorSimulator.detect_error_marker(customerNo)
if error_code:
return ErrorSimulator.build_error_response(error_code)
# 构建请求字典
request_data = {
"groupId": groupId,
"customerNo": customerNo,
"dataChannelCode": dataChannelCode,
"requestDateId": requestDateId,
"dataStartDateId": dataStartDateId,
"dataEndDateId": dataEndDateId,
"uploadUserId": uploadUserId,
}
# 正常流程
return file_service.fetch_inner_flow(request)
return file_service.fetch_inner_flow(request_data)
# ==================== 接口4检查文件解析状态 ====================
@router.post("/watson/api/project/upload/getpendings")
async def check_parse_status(request: CheckParseStatusRequest):
async def check_parse_status(
groupId: int = Form(..., description="项目id"),
inprogressList: str = Form(..., description="文件id列表逗号分隔"),
):
"""检查文件解析状态
返回文件是否还在解析中parsing字段
"""
return file_service.check_parse_status(
request.groupId, request.inprogressList
)
return file_service.check_parse_status(groupId, inprogressList)
# ==================== 接口5删除文件 ====================
@router.post("/watson/api/project/batchDeleteUploadFile")
async def delete_files(request: DeleteFilesRequest):
async def delete_files(
groupId: int = Form(..., description="项目id"),
logIds: str = Form(..., description="文件id数组逗号分隔如: 10001,10002"),
userId: int = Form(..., description="用户柜员号"),
):
"""批量删除上传的文件
根据logIds列表删除对应的文件记录
"""
return file_service.delete_files(request.groupId, request.logIds, request.userId)
# 将逗号分隔的字符串转换为整数列表
log_id_list = [int(id.strip()) for id in logIds.split(",")]
return file_service.delete_files(groupId, log_id_list, userId)
# ==================== 接口6获取银行流水 ====================
@router.post("/watson/api/project/getBSByLogId")
async def get_bank_statement(request: GetBankStatementRequest):
async def get_bank_statement(
groupId: int = Form(..., description="项目id"),
logId: int = Form(..., description="文件id"),
pageNow: int = Form(..., description="当前页码"),
pageSize: int = Form(..., description="查询条数"),
):
"""获取银行流水列表
支持分页查询pageNow, pageSize
"""
return statement_service.get_bank_statement(request)
# 构建请求字典
request_data = {
"groupId": groupId,
"logId": logId,
"pageNow": pageNow,
"pageSize": pageSize,
}
return statement_service.get_bank_statement(request_data)

View File

@@ -1,8 +1,7 @@
from fastapi import BackgroundTasks, UploadFile
from models.request import FetchInnerFlowRequest
from utils.response_builder import ResponseBuilder
from config.settings import settings
from typing import Dict, List
from typing import Dict, List, Union
import time
from datetime import datetime
@@ -133,11 +132,11 @@ class FileService:
"successResponse": True,
}
def fetch_inner_flow(self, request: FetchInnerFlowRequest) -> Dict:
def fetch_inner_flow(self, request: Union[Dict, object]) -> Dict:
"""拉取行内流水(模拟无数据场景)
Args:
request: 拉取流水请求
request: 拉取流水请求(可以是字典或对象)
Returns:
流水响应字典

View File

@@ -1,28 +1,35 @@
from models.request import GetBankStatementRequest
from utils.response_builder import ResponseBuilder
from typing import Dict
from typing import Dict, Union
class StatementService:
"""流水数据服务"""
def get_bank_statement(self, request: GetBankStatementRequest) -> Dict:
def get_bank_statement(self, request: Union[Dict, object]) -> Dict:
"""获取银行流水列表
Args:
request: 获取银行流水请求
request: 获取银行流水请求(可以是字典或对象)
Returns:
银行流水响应字典
"""
# 支持 dict 或对象
if isinstance(request, dict):
page_now = request.get("pageNow", 1)
page_size = request.get("pageSize", 10)
else:
page_now = request.pageNow
page_size = request.pageSize
# 加载模板
template = ResponseBuilder.load_template("bank_statement")
statements = template["success_response"]["data"]["bankStatementList"]
total_count = len(statements)
# 模拟分页
start = (request.pageNow - 1) * request.pageSize
end = start + request.pageSize
start = (page_now - 1) * page_size
end = start + page_size
page_data = statements[start:end]
return {

View File

@@ -1,7 +1,7 @@
from models.request import GetTokenRequest
from utils.response_builder import ResponseBuilder
from config.settings import settings
from typing import Dict
from typing import Dict, Union
class TokenService:
@@ -11,15 +11,23 @@ class TokenService:
self.project_counter = settings.INITIAL_PROJECT_ID
self.tokens = {} # projectId -> token_data
def create_token(self, request: GetTokenRequest) -> Dict:
def create_token(self, request: Union[GetTokenRequest, Dict]) -> Dict:
"""创建Token
Args:
request: 获取Token请求
request: 获取Token请求(可以是 GetTokenRequest 对象或字典)
Returns:
Token响应字典
"""
# 支持 dict 或 GetTokenRequest 对象
if isinstance(request, dict):
project_no = request.get("projectNo")
entity_name = request.get("entityName")
else:
project_no = request.projectNo
entity_name = request.entityName
# 生成唯一项目ID
self.project_counter += 1
project_id = self.project_counter
@@ -28,8 +36,8 @@ class TokenService:
response = ResponseBuilder.build_success_response(
"token",
project_id=project_id,
project_no=request.projectNo,
entity_name=request.entityName
project_no=project_no,
entity_name=entity_name
)
# 存储token信息

View File

@@ -20,11 +20,15 @@ def client():
@pytest.fixture
def sample_token_request():
"""示例 Token 请求"""
"""示例 Token 请求 - 返回 form-data 格式的数据"""
return {
"projectNo": "test_project_001",
"entityName": "测试企业",
"userId": "902001",
"userName": "902001",
"appId": "remote_app",
"appSecretCode": "test_secret_code_12345",
"role": "VIEWER",
"orgCode": "902000",
"departmentCode": "902000",
}

View File

@@ -10,12 +10,16 @@ def test_complete_workflow(client):
# 1. 获取 Token
response = client.post(
"/account/common/getToken",
json={
data={
"projectNo": "integration_test_001",
"entityName": "集成测试企业",
"userId": "902001",
"userName": "902001",
"appId": "remote_app",
"appSecretCode": "test_secret_code_12345",
"role": "VIEWER",
"orgCode": "902000",
"departmentCode": "902000",
},
)
assert response.status_code == 200
@@ -31,7 +35,7 @@ def test_complete_workflow(client):
# 3. 检查解析状态
response = client.post(
"/watson/api/project/upload/getpendings",
json={"groupId": project_id, "inprogressList": "10001"},
data={"groupId": project_id, "inprogressList": "10001"},
)
assert response.status_code == 200
status_data = response.json()
@@ -40,7 +44,7 @@ def test_complete_workflow(client):
# 4. 获取银行流水
response = client.post(
"/watson/api/project/getBSByLogId",
json={
data={
"groupId": project_id,
"logId": 10001,
"pageNow": 1,
@@ -61,12 +65,16 @@ def test_all_error_codes(client):
for error_code in error_codes:
response = client.post(
"/account/common/getToken",
json={
data={
"projectNo": f"test_error_{error_code}",
"entityName": "测试企业",
"userId": "902001",
"userName": "902001",
"appId": "remote_app",
"appSecretCode": "test_secret_code_12345",
"role": "VIEWER",
"orgCode": "902000",
"departmentCode": "902000",
},
)
assert response.status_code == 200
@@ -80,12 +88,16 @@ def test_pagination(client):
# 获取 Token
response = client.post(
"/account/common/getToken",
json={
data={
"projectNo": "pagination_test",
"entityName": "分页测试",
"userId": "902001",
"userName": "902001",
"appId": "remote_app",
"appSecretCode": "test_secret_code_12345",
"role": "VIEWER",
"orgCode": "902000",
"departmentCode": "902000",
},
)
project_id = response.json()["data"]["projectId"]
@@ -93,14 +105,14 @@ def test_pagination(client):
# 测试第一页
response = client.post(
"/watson/api/project/getBSByLogId",
json={"groupId": project_id, "logId": 10001, "pageNow": 1, "pageSize": 1},
data={"groupId": project_id, "logId": 10001, "pageNow": 1, "pageSize": 1},
)
page1 = response.json()
# 测试第二页
response = client.post(
"/watson/api/project/getBSByLogId",
json={"groupId": project_id, "logId": 10001, "pageNow": 2, "pageSize": 1},
data={"groupId": project_id, "logId": 10001, "pageNow": 2, "pageSize": 1},
)
page2 = response.json()

View File

@@ -22,7 +22,7 @@ def test_health_check(client):
def test_get_token_success(client, sample_token_request):
"""测试获取 Token - 成功场景"""
response = client.post("/account/common/getToken", json=sample_token_request)
response = client.post("/account/common/getToken", data=sample_token_request)
assert response.status_code == 200
data = response.json()
assert data["code"] == "200"
@@ -37,9 +37,13 @@ def test_get_token_error_40101(client):
"entityName": "测试企业",
"userId": "902001",
"userName": "902001",
"appId": "remote_app",
"appSecretCode": "test_secret_code_12345",
"role": "VIEWER",
"orgCode": "902000",
"departmentCode": "902000",
}
response = client.post("/account/common/getToken", json=request_data)
response = client.post("/account/common/getToken", data=request_data)
assert response.status_code == 200
data = response.json()
assert data["code"] == "40101"