Files
zkxw/backend/app/api/market_mobile.py
T
zlwuandCursor 063727a7d8 feat: 完善协议缴费分期、样本编码搜索与鸿蒙附件选择
支持移动端样本编码远程关键字检索、分期起始日刷新与鸿蒙微信附件选取,并同步收款账户选择、角色页与相关设计文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 09:23:17 +08:00

1096 lines
38 KiB
Python

"""市场专员/地区经理移动端专用 API。"""
from __future__ import annotations
from typing import Literal, Optional
from fastapi import APIRouter, Depends, File, Query, UploadFile
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.mobile_menu_guard import (
require_market_menu_code,
user_has_tenant_menu_perm,
)
from app.core.response import error_response, success_response
from app.schemas.market_mobile import (
MarketAttachmentDownloadBody,
MarketFreightCreateBody,
MarketFreightItemsBody,
MarketFreightUpdateBody,
MarketPaymentTransferBody,
MarketPaymentTransferCreateBody,
MarketPresignOrderCreateBody,
MarketPresignOrderBody,
MarketPresignTransferBody,
MarketProtocolBody,
)
from app.services import (
market_freight,
market_payment,
market_presign,
market_protocol,
market_support,
)
from app.services.market_field_scope import MarketFieldContext, ScopeKind
from app.services.web_auth_roles import resolve_web_role_for_user
router = APIRouter(tags=["移动市场"])
def _context(user: dict, scope_kind: ScopeKind) -> MarketFieldContext:
return MarketFieldContext(
tenant_no=str(user.get("tenant_no") or ""),
user_id=str(user.get("user_id") or ""),
scope_kind=scope_kind,
)
def _has_menu(db: Session, user: dict, permission: str) -> bool:
tenant_no = str(user.get('tenant_no') or '').strip()
user_id = str(user.get('user_id') or '').strip()
return resolve_web_role_for_user(
db, user_id, tenant_no=tenant_no
) == 'super_admin' or user_has_tenant_menu_perm(
db,
user_id=user_id,
tenant_no=tenant_no,
perm_code=permission,
)
def _register_presign_routes(scope_kind: ScopeKind) -> None:
prefix = f"/mini/market/{scope_kind}/presign-orders"
permission = f"mini:market:{scope_kind}:presign"
permission_guard = require_market_menu_code(permission)
@router.get(prefix)
async def list_orders(
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100, alias="pageSize"),
keyword: Optional[str] = None,
order_status: Optional[str] = Query(None, alias="orderStatus"),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
ctx = _context(user, scope_kind)
items, total = market_presign.list_presign_orders(
db,
ctx=ctx,
user_info=user,
page=page,
page_size=page_size,
keyword=keyword,
order_status=order_status,
)
items = [
market_support.decorate_allowed_actions(
'presign', item, scope_kind=scope_kind
)
for item in items
]
return success_response(
{
"list": items,
"total": total,
"page": page,
"pageSize": page_size,
"allowedActions": market_support.allowed_actions(
'presign-list', status='', scope_kind=scope_kind
),
}
)
@router.get(f"{prefix}/form-options")
async def presign_form_options(
region_id: Optional[str] = Query(None, alias="regionId"),
business_id: Optional[str] = Query(None, alias="businessId"),
user_keyword: Optional[str] = Query(None, alias='userKeyword', max_length=64),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_support.get_form_options(
db,
module="presign",
ctx=_context(user, scope_kind),
user_info=user,
region_id=region_id,
business_id=business_id,
user_keyword=user_keyword,
)
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.get(f"{prefix}/{{order_id}}")
async def get_order(
order_id: str,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
return success_response(
market_support.decorate_allowed_actions(
'presign',
market_presign.get_presign_order(
db,
order_id=order_id,
ctx=_context(user, scope_kind),
user_info=user,
),
scope_kind=scope_kind,
detail=True,
)
)
@router.post(prefix)
async def create_order(
body: MarketPresignOrderCreateBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_presign.create_presign_order(
db,
body=body,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message="创建成功")
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.put(f"{prefix}/{{order_id}}")
async def update_order(
order_id: str,
body: MarketPresignOrderBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_presign.update_presign_order(
db,
order_id=order_id,
body=body,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message="保存成功")
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.delete(f"{prefix}/{{order_id}}")
async def delete_order(
order_id: str,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
market_presign.delete_presign_order(
db,
order_id=order_id,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(message="删除成功")
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
@router.post(f"{prefix}/{{order_id}}/transfers")
async def create_transfer(
order_id: str,
body: MarketPresignTransferBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_presign.create_presign_transfer(
db,
order_id=order_id,
body=body,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message="创建成功")
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
transfer_prefix = f"/mini/market/{scope_kind}/presign-transfers"
@router.put(f"{transfer_prefix}/{{transfer_id}}")
async def update_transfer(
transfer_id: str,
body: MarketPresignTransferBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_presign.update_presign_transfer(
db,
transfer_id=transfer_id,
body=body,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message="保存成功")
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
@router.delete(f"{transfer_prefix}/{{transfer_id}}")
async def delete_transfer(
transfer_id: str,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
market_presign.delete_presign_transfer(
db,
transfer_id=transfer_id,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(message="删除成功")
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
@router.post(f"{transfer_prefix}/{{transfer_id}}/submit")
async def submit_transfer(
transfer_id: str,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_presign.submit_presign_transfer(
db,
transfer_id=transfer_id,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message="已提交财务确认")
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
@router.post(f"{transfer_prefix}/{{transfer_id}}/withdraw")
async def withdraw_transfer(
transfer_id: str,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_presign.withdraw_presign_transfer(
db,
transfer_id=transfer_id,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message="已撤回")
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
@router.post(f"{prefix}/{{order_id}}/convert")
async def convert_order(
order_id: str,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_presign.convert_presign_order(
db,
order_id=order_id,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message="已转正式协议")
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
_register_presign_routes("specialist")
_register_presign_routes("manager")
def _register_protocol_routes(scope_kind: ScopeKind) -> None:
prefix = f'/mini/market/{scope_kind}/protocols'
permission = f'mini:market:{scope_kind}:protocol'
permission_guard = require_market_menu_code(permission)
@router.get(prefix)
async def list_protocols(
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100, alias='pageSize'),
keyword: Optional[str] = None,
source: Optional[Literal['cell_storage', 'research_donation']] = None,
protocol_status: Optional[str] = Query(None, alias='protocolStatus'),
create_time_start: Optional[str] = Query(None, alias='createTimeStart'),
create_time_end: Optional[str] = Query(None, alias='createTimeEnd'),
sample_collect_time_start: Optional[str] = Query(
None, alias='sampleCollectTimeStart'
),
sample_collect_time_end: Optional[str] = Query(
None, alias='sampleCollectTimeEnd'
),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
items, total = market_protocol.list_protocols(
db,
ctx=_context(user, scope_kind),
user_info=user,
page=page,
page_size=page_size,
keyword=keyword,
source=source,
protocol_status=protocol_status,
create_time_start=create_time_start,
create_time_end=create_time_end,
sample_collect_time_start=sample_collect_time_start,
sample_collect_time_end=sample_collect_time_end,
)
except ValueError as exc:
return error_response(400, str(exc))
items = [
market_support.decorate_allowed_actions(
'protocol', item, scope_kind=scope_kind
)
for item in items
]
return success_response(
{
'list': items,
'total': total,
'page': page,
'pageSize': page_size,
'allowedActions': market_support.allowed_actions(
'protocol-list', status='', scope_kind=scope_kind
),
}
)
@router.get(f'{prefix}/form-options')
async def protocol_form_options(
region_id: Optional[str] = Query(None, alias='regionId'),
business_id: Optional[str] = Query(None, alias='businessId'),
code_filter: Optional[str] = Query(None, alias='codeFilter'),
keyword: Optional[str] = Query(None, max_length=64),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_support.get_form_options(
db,
module='protocol',
ctx=_context(user, scope_kind),
user_info=user,
region_id=region_id,
business_id=business_id,
code_filter=code_filter,
sample_code_keyword=keyword,
)
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.get(f'{prefix}/customers')
async def protocol_customers(
keyword: Optional[str] = None,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100, alias='pageSize'),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_support.search_customers_for_protocol(
db,
ctx=_context(user, scope_kind),
user_info=user,
keyword=keyword,
page=page,
page_size=page_size,
)
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.get(f'{prefix}/{{protocol_id}}')
async def get_protocol(
protocol_id: str,
source: Optional[
Literal['cell_storage', 'research_donation']
] = Query(None),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
return success_response(
market_support.decorate_allowed_actions(
'protocol',
market_protocol.get_protocol(
db,
source=source,
protocol_id=protocol_id,
ctx=_context(user, scope_kind),
user_info=user,
),
scope_kind=scope_kind,
has_payment_permission=_has_menu(
db, user, f'mini:market:{scope_kind}:payment'
),
)
)
@router.post(prefix)
async def create_protocol(
body: MarketProtocolBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_protocol.create_protocol(
db,
body=body,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message='创建成功')
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.put(f'{prefix}/{{protocol_id}}')
async def update_protocol(
protocol_id: str,
body: MarketProtocolBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
data = market_protocol.update_protocol(
db,
source=body.source,
protocol_id=protocol_id,
body=body,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(data, message='保存成功')
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.delete(f'{prefix}/{{protocol_id}}')
async def delete_protocol(
protocol_id: str,
source: Literal['cell_storage', 'research_donation'] = Query(...),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
market_protocol.delete_protocol(
db,
source=source,
protocol_id=protocol_id,
ctx=_context(user, scope_kind),
user_info=user,
)
return success_response(message='删除成功')
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
_register_protocol_routes('specialist')
_register_protocol_routes('manager')
def _register_payment_routes(scope_kind: ScopeKind) -> None:
prefix = f'/mini/market/{scope_kind}'
chase_guard = require_market_menu_code(
f'mini:market:{scope_kind}:payment-chase'
)
payment_guard = require_market_menu_code(
f'mini:market:{scope_kind}:payment'
)
@router.get(f'{prefix}/payment-chase')
async def list_payment_chase(
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100, alias='pageSize'),
keyword: Optional[str] = None,
chase_type: Optional[Literal['overdue', 'upcoming']] = Query(
None, alias='chaseType'
),
pay_mode: Optional[str] = Query(None, alias='payMode'),
sales_user_id: Optional[str] = Query(None, alias='salesUserId'),
user: dict = Depends(chase_guard),
db: Session = Depends(get_db),
):
items, total = market_payment.list_payment_chase(
db,
ctx=_context(user, scope_kind),
user_info=user,
page=page,
page_size=page_size,
keyword=keyword,
chase_type=chase_type,
pay_mode=pay_mode,
sales_user_id=sales_user_id,
)
items = [
market_support.decorate_allowed_actions(
'payment-chase',
item,
scope_kind=scope_kind,
)
for item in items
]
return success_response(
{'list': items, 'total': total, 'page': page, 'pageSize': page_size}
)
@router.get(f'{prefix}/payment-chase/{{schedule_id}}')
async def get_payment_chase_detail(
schedule_id: str,
view: Literal['payment', 'protocol'] = Query(...),
user: dict = Depends(chase_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_payment.get_payment_chase_detail(
db,
schedule_id=schedule_id,
view=view,
ctx=_context(user, scope_kind),
user_info=user,
)
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.get(f'{prefix}/payments')
async def list_payments(
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100, alias='pageSize'),
keyword: Optional[str] = None,
pay_mode: Optional[str] = Query(None, alias='payMode'),
progress_status: Optional[str] = Query(None, alias='progressStatus'),
installment_due_date_start: Optional[str] = Query(
None, alias='installmentDueDateStart'
),
installment_due_date_end: Optional[str] = Query(
None, alias='installmentDueDateEnd'
),
user: dict = Depends(payment_guard),
db: Session = Depends(get_db),
):
try:
items, total = market_payment.list_payments(
db,
ctx=_context(user, scope_kind),
user_info=user,
page=page,
page_size=page_size,
keyword=keyword,
pay_mode=pay_mode,
progress_status=progress_status,
installment_due_date_start=installment_due_date_start,
installment_due_date_end=installment_due_date_end,
)
except ValueError as exc:
return error_response(400, str(exc))
items = [
market_support.decorate_allowed_actions(
'payment', item, scope_kind=scope_kind
)
for item in items
]
return success_response(
{'list': items, 'total': total, 'page': page, 'pageSize': page_size}
)
@router.get(f'{prefix}/payments/form-options')
async def payment_form_options(
user: dict = Depends(payment_guard),
db: Session = Depends(get_db),
):
return success_response(
market_support.get_form_options(
db,
module='payment',
ctx=_context(user, scope_kind),
user_info=user,
)
)
@router.get(f'{prefix}/payments/{{payment_id}}')
async def get_payment(
payment_id: str,
user: dict = Depends(payment_guard),
db: Session = Depends(get_db),
):
return success_response(
market_support.decorate_allowed_actions(
'payment',
market_payment.get_payment_detail(
db,
payment_id=payment_id,
ctx=_context(user, scope_kind),
user_info=user,
),
scope_kind=scope_kind,
)
)
@router.post(f'{prefix}/payment-transfers')
async def create_payment_transfer(
body: MarketPaymentTransferCreateBody,
user: dict = Depends(payment_guard),
db: Session = Depends(get_db),
):
try:
fields = body.model_dump(by_alias=True, exclude_unset=True)
schedule_id = fields.pop('scheduleId')
return success_response(
market_support.decorate_allowed_actions(
'payment-transfer',
market_payment.create_transfer(
db,
schedule_id=schedule_id,
fields=fields,
ctx=_context(user, scope_kind),
user_info=user,
),
scope_kind=scope_kind,
),
message='创建成功',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.put(f'{prefix}/payment-transfers/{{transfer_id}}')
async def update_payment_transfer(
transfer_id: str,
body: MarketPaymentTransferBody,
user: dict = Depends(payment_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_support.decorate_allowed_actions(
'payment-transfer',
market_payment.update_transfer(
db,
transfer_id=transfer_id,
fields=body.model_dump(by_alias=True, exclude_unset=True),
ctx=_context(user, scope_kind),
user_info=user,
),
scope_kind=scope_kind,
),
message='保存成功',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.post(f'{prefix}/payment-transfers/{{transfer_id}}/submit')
async def submit_payment_transfer(
transfer_id: str,
user: dict = Depends(payment_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_support.decorate_allowed_actions(
'payment-transfer',
market_payment.submit_transfer(
db,
transfer_id=transfer_id,
ctx=_context(user, scope_kind),
user_info=user,
),
scope_kind=scope_kind,
),
message='已提交财务确认',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.delete(f'{prefix}/payment-transfers/{{transfer_id}}')
async def delete_payment_transfer(
transfer_id: str,
user: dict = Depends(payment_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_payment.delete_transfer(
db,
transfer_id=transfer_id,
ctx=_context(user, scope_kind),
user_info=user,
),
message='删除成功',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.post(f'{prefix}/payment-transfers/{{transfer_id}}/withdraw')
async def withdraw_payment_transfer(
transfer_id: str,
user: dict = Depends(payment_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_support.decorate_allowed_actions(
'payment-transfer',
market_payment.withdraw_transfer(
db,
transfer_id=transfer_id,
ctx=_context(user, scope_kind),
user_info=user,
),
scope_kind=scope_kind,
),
message='已撤回',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
_register_payment_routes('specialist')
_register_payment_routes('manager')
def _register_freight_routes(scope_kind: ScopeKind) -> None:
prefix = f'/mini/market/{scope_kind}/freights'
permission_guard = require_market_menu_code(
f'mini:market:{scope_kind}:freight'
)
@router.get(prefix)
async def list_freights(
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100, alias='pageSize'),
keyword: Optional[str] = None,
transport_mode: Optional[str] = Query(None, alias='transportMode'),
sample_source: Optional[
Literal['cell_storage', 'research_donation']
] = Query(None, alias='sampleSource'),
include_items: bool = Query(False, alias='includeItems'),
create_time_start: Optional[str] = Query(None, alias='createTimeStart'),
create_time_end: Optional[str] = Query(None, alias='createTimeEnd'),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
items, total = market_freight.list_freights(
db,
ctx=_context(user, scope_kind),
user_info=user,
page=page,
page_size=page_size,
keyword=keyword,
transport_mode=transport_mode,
sample_source=sample_source,
include_items=include_items,
create_time_start=create_time_start,
create_time_end=create_time_end,
)
except ValueError as exc:
return error_response(400, str(exc))
items = [
market_support.decorate_allowed_actions(
'freight', item, scope_kind=scope_kind
)
for item in items
]
return success_response(
{
'list': items,
'total': total,
'page': page,
'pageSize': page_size,
'allowedActions': market_support.allowed_actions(
'freight-list', status='', scope_kind=scope_kind
),
}
)
@router.get(f'{prefix}/sample-options')
async def list_freight_sample_options(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100, alias='pageSize'),
keyword: Optional[str] = None,
sample_source: Optional[
Literal['cell_storage', 'research_donation']
] = Query(None, alias='sampleSource'),
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
items, total = market_freight.list_sample_options(
db,
ctx=_context(user, scope_kind),
user_info=user,
page=page,
page_size=page_size,
keyword=keyword,
sample_source=sample_source,
)
return success_response(
{'list': items, 'total': total, 'page': page, 'pageSize': page_size}
)
@router.get(f'{prefix}/form-options')
async def freight_form_options(
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
return success_response(
market_support.get_form_options(
db,
module='freight',
ctx=_context(user, scope_kind),
user_info=user,
)
)
@router.get(f'{prefix}/{{freight_id}}')
async def get_freight(
freight_id: str,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
return success_response(
market_support.decorate_allowed_actions(
'freight',
market_freight.get_freight_detail(
db,
freight_id,
ctx=_context(user, scope_kind),
user_info=user,
),
scope_kind=scope_kind,
detail=True,
)
)
@router.post(prefix)
async def create_freight(
body: MarketFreightCreateBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_freight.create_freight(
db,
ctx=_context(user, scope_kind),
user_info=user,
transport_mode=body.transport_mode,
freight_amount=body.freight_amount,
waybill_no=body.waybill_no,
items=[
item.model_dump(by_alias=True) for item in body.items
],
),
message='创建成功',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.put(f'{prefix}/{{freight_id}}')
async def update_freight(
freight_id: str,
body: MarketFreightUpdateBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_freight.update_freight(
db,
freight_id,
ctx=_context(user, scope_kind),
user_info=user,
transport_mode=body.transport_mode,
freight_amount=body.freight_amount,
waybill_no=body.waybill_no,
waybill_supplied='waybill_no' in body.model_fields_set,
amount_supplied='freight_amount' in body.model_fields_set,
),
message='保存成功',
)
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
@router.post(f'{prefix}/{{freight_id}}/link')
async def link_freight_samples(
freight_id: str,
body: MarketFreightItemsBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_freight.link_samples(
db,
freight_id,
ctx=_context(user, scope_kind),
user_info=user,
items=[
item.model_dump(by_alias=True) for item in body.items
],
),
message='关联成功',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.post(f'{prefix}/{{freight_id}}/unlink')
async def unlink_freight_samples(
freight_id: str,
body: MarketFreightItemsBody,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_freight.unlink_samples(
db,
freight_id,
ctx=_context(user, scope_kind),
user_info=user,
items=[
item.model_dump(by_alias=True) for item in body.items
],
),
message='已取消关联',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
@router.delete(f'{prefix}/{{freight_id}}')
async def delete_freight(
freight_id: str,
user: dict = Depends(permission_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_freight.delete_freight(
db,
freight_id,
ctx=_context(user, scope_kind),
user_info=user,
),
message='删除成功',
)
except PermissionError as exc:
return error_response(403, str(exc))
except ValueError as exc:
return error_response(400, str(exc))
_register_freight_routes('specialist')
_register_freight_routes('manager')
def _register_attachment_routes(scope_kind: ScopeKind) -> None:
for module in ('presign', 'protocol', 'payment'):
permission_guard = require_market_menu_code(
f'mini:market:{scope_kind}:{module}'
)
path = f'/mini/market/{scope_kind}/{module}/attachments'
download_path = f'{path}/download-url'
def build_upload_endpoint(bound_module, bound_guard):
async def upload_market_attachment(
file: UploadFile = File(...),
user: dict = Depends(bound_guard),
db: Session = Depends(get_db),
):
try:
max_bytes = (
market_support.PRESIGN_MAX_UPLOAD_BYTES
if bound_module == 'presign'
else market_support.PROTOCOL_MAX_UPLOAD_BYTES
if bound_module == 'protocol'
else market_support.MAX_UPLOAD_BYTES
)
content = await file.read(max_bytes + 1)
return success_response(
market_support.save_market_attachment(
db,
content=content,
filename=file.filename or '',
content_type=file.content_type or '',
module=bound_module,
ctx=_context(user, scope_kind),
user_info=user,
),
message='上传成功',
)
except (PermissionError, ValueError) as exc:
return error_response(400, str(exc))
finally:
await file.close()
return upload_market_attachment
router.add_api_route(
path,
build_upload_endpoint(module, permission_guard),
methods=['POST'],
name=f'upload_market_{scope_kind}_{module}_attachment',
)
def build_download_endpoint(bound_module, bound_guard):
async def download_market_attachment(
body: MarketAttachmentDownloadBody,
user: dict = Depends(bound_guard),
db: Session = Depends(get_db),
):
try:
return success_response(
market_support.get_market_attachment_download_url(
db,
file_url=body.file_url,
file_name=body.file_name,
module=bound_module,
ctx=_context(user, scope_kind),
user_info=user,
)
)
except (PermissionError, ValueError):
return error_response(403, '附件不存在或无权下载')
except Exception:
return error_response(500, '获取下载链接失败')
return download_market_attachment
router.add_api_route(
download_path,
build_download_endpoint(module, permission_guard),
methods=['POST'],
name=f'download_market_{scope_kind}_{module}_attachment',
)
_register_attachment_routes('specialist')
_register_attachment_routes('manager')