|
@@ -0,0 +1,1017 @@
|
|
|
|
|
+"""市场专员/地区经理移动端专用 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 (
|
|
|
|
|
+ MarketFreightCreateBody,
|
|
|
|
|
+ MarketFreightItemsBody,
|
|
|
|
|
+ MarketFreightUpdateBody,
|
|
|
|
|
+ MarketPaymentTransferBody,
|
|
|
|
|
+ MarketPaymentTransferCreateBody,
|
|
|
|
|
+ MarketPresignOrderBody,
|
|
|
|
|
+ MarketPresignTransferBody,
|
|
|
|
|
+ MarketProtocolBody,
|
|
|
|
|
+ MarketSampleInsuranceBody,
|
|
|
|
|
+ MarketSampleStorageCertBody,
|
|
|
|
|
+)
|
|
|
|
|
+from app.services import (
|
|
|
|
|
+ market_freight,
|
|
|
|
|
+ market_payment,
|
|
|
|
|
+ market_presign,
|
|
|
|
|
+ market_protocol,
|
|
|
|
|
+ market_sample,
|
|
|
|
|
+ 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: 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,
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ 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: MarketPresignOrderBody,
|
|
|
|
|
+ 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,
|
|
|
|
|
+ user: dict = Depends(permission_guard),
|
|
|
|
|
+ db: Session = Depends(get_db),
|
|
|
|
|
+ ):
|
|
|
|
|
+ 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,
|
|
|
|
|
+ )
|
|
|
|
|
+ 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'),
|
|
|
|
|
+ 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,
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ 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,
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ @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))
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+_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,
|
|
|
|
|
+ )
|
|
|
|
|
+ payment_allowed = _has_menu(
|
|
|
|
|
+ db, user, f'mini:market:{scope_kind}:payment'
|
|
|
|
|
+ )
|
|
|
|
|
+ items = [
|
|
|
|
|
+ market_support.decorate_allowed_actions(
|
|
|
|
|
+ 'payment-chase',
|
|
|
|
|
+ item,
|
|
|
|
|
+ scope_kind=scope_kind,
|
|
|
|
|
+ has_payment_permission=payment_allowed,
|
|
|
|
|
+ )
|
|
|
|
|
+ for item in items
|
|
|
|
|
+ ]
|
|
|
|
|
+ return success_response(
|
|
|
|
|
+ {'list': items, 'total': total, 'page': page, 'pageSize': page_size}
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ @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'),
|
|
|
|
|
+ user: dict = Depends(payment_guard),
|
|
|
|
|
+ db: Session = Depends(get_db),
|
|
|
|
|
+ ):
|
|
|
|
|
+ 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,
|
|
|
|
|
+ )
|
|
|
|
|
+ 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_payment.create_transfer(
|
|
|
|
|
+ db,
|
|
|
|
|
+ schedule_id=schedule_id,
|
|
|
|
|
+ fields=fields,
|
|
|
|
|
+ ctx=_context(user, scope_kind),
|
|
|
|
|
+ user_info=user,
|
|
|
|
|
+ ),
|
|
|
|
|
+ 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_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,
|
|
|
|
|
+ ),
|
|
|
|
|
+ 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_payment.submit_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_payment.withdraw_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))
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+_register_payment_routes('specialist')
|
|
|
|
|
+_register_payment_routes('manager')
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _register_sample_routes(scope_kind: ScopeKind) -> None:
|
|
|
|
|
+ prefix = f'/mini/market/{scope_kind}/samples'
|
|
|
|
|
+ permission_guard = require_market_menu_code(
|
|
|
|
|
+ f'mini:market:{scope_kind}:sample'
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ @router.get(prefix)
|
|
|
|
|
+ async def list_samples(
|
|
|
|
|
+ page: int = Query(1, ge=1),
|
|
|
|
|
+ page_size: int = Query(10, ge=1, le=100, alias='pageSize'),
|
|
|
|
|
+ keyword: Optional[str] = None,
|
|
|
|
|
+ status: Optional[str] = None,
|
|
|
|
|
+ source: Optional[Literal['cell_storage', 'research_donation']] = None,
|
|
|
|
|
+ user: dict = Depends(permission_guard),
|
|
|
|
|
+ db: Session = Depends(get_db),
|
|
|
|
|
+ ):
|
|
|
|
|
+ items, total = market_sample.list_samples(
|
|
|
|
|
+ db,
|
|
|
|
|
+ ctx=_context(user, scope_kind),
|
|
|
|
|
+ user_info=user,
|
|
|
|
|
+ page=page,
|
|
|
|
|
+ page_size=page_size,
|
|
|
|
|
+ keyword=keyword,
|
|
|
|
|
+ status=status,
|
|
|
|
|
+ source=source,
|
|
|
|
|
+ )
|
|
|
|
|
+ items = [
|
|
|
|
|
+ market_support.decorate_allowed_actions(
|
|
|
|
|
+ 'sample', 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}/form-options')
|
|
|
|
|
+ async def sample_form_options(
|
|
|
|
|
+ sample_id: str = Query(..., alias='sampleId'),
|
|
|
|
|
+ user: dict = Depends(permission_guard),
|
|
|
|
|
+ db: Session = Depends(get_db),
|
|
|
|
|
+ ):
|
|
|
|
|
+ try:
|
|
|
|
|
+ return success_response(
|
|
|
|
|
+ market_sample.get_sample_form_options(
|
|
|
|
|
+ db,
|
|
|
|
|
+ sample_id=sample_id,
|
|
|
|
|
+ ctx=_context(user, scope_kind),
|
|
|
|
|
+ user_info=user,
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ except (PermissionError, ValueError) as exc:
|
|
|
|
|
+ return error_response(400, str(exc))
|
|
|
|
|
+
|
|
|
|
|
+ @router.get(f'{prefix}/{{sample_id}}')
|
|
|
|
|
+ async def get_sample(
|
|
|
|
|
+ sample_id: str,
|
|
|
|
|
+ user: dict = Depends(permission_guard),
|
|
|
|
|
+ db: Session = Depends(get_db),
|
|
|
|
|
+ ):
|
|
|
|
|
+ return success_response(
|
|
|
|
|
+ market_support.decorate_allowed_actions(
|
|
|
|
|
+ 'sample',
|
|
|
|
|
+ market_sample.get_sample_detail(
|
|
|
|
|
+ db,
|
|
|
|
|
+ sample_id=sample_id,
|
|
|
|
|
+ ctx=_context(user, scope_kind),
|
|
|
|
|
+ user_info=user,
|
|
|
|
|
+ ),
|
|
|
|
|
+ scope_kind=scope_kind,
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ @router.put(f'{prefix}/{{sample_id}}/storage-cert')
|
|
|
|
|
+ async def update_storage_cert(
|
|
|
|
|
+ sample_id: str,
|
|
|
|
|
+ body: MarketSampleStorageCertBody,
|
|
|
|
|
+ user: dict = Depends(permission_guard),
|
|
|
|
|
+ db: Session = Depends(get_db),
|
|
|
|
|
+ ):
|
|
|
|
|
+ try:
|
|
|
|
|
+ return success_response(
|
|
|
|
|
+ market_sample.update_storage_cert(
|
|
|
|
|
+ db,
|
|
|
|
|
+ sample_id=sample_id,
|
|
|
|
|
+ fields=body.model_dump(by_alias=True, exclude_unset=True),
|
|
|
|
|
+ ctx=_context(user, scope_kind),
|
|
|
|
|
+ user_info=user,
|
|
|
|
|
+ ),
|
|
|
|
|
+ message='保存成功',
|
|
|
|
|
+ )
|
|
|
|
|
+ except (PermissionError, ValueError) as exc:
|
|
|
|
|
+ return error_response(400, str(exc))
|
|
|
|
|
+
|
|
|
|
|
+ @router.put(f'{prefix}/{{sample_id}}/insurance')
|
|
|
|
|
+ async def update_insurance(
|
|
|
|
|
+ sample_id: str,
|
|
|
|
|
+ body: MarketSampleInsuranceBody,
|
|
|
|
|
+ user: dict = Depends(permission_guard),
|
|
|
|
|
+ db: Session = Depends(get_db),
|
|
|
|
|
+ ):
|
|
|
|
|
+ try:
|
|
|
|
|
+ return success_response(
|
|
|
|
|
+ market_sample.update_insurance(
|
|
|
|
|
+ db,
|
|
|
|
|
+ sample_id=sample_id,
|
|
|
|
|
+ fields=body.model_dump(by_alias=True, exclude_unset=True),
|
|
|
|
|
+ ctx=_context(user, scope_kind),
|
|
|
|
|
+ user_info=user,
|
|
|
|
|
+ ),
|
|
|
|
|
+ message='保存成功',
|
|
|
|
|
+ )
|
|
|
|
|
+ except (PermissionError, ValueError) as exc:
|
|
|
|
|
+ return error_response(400, str(exc))
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+_register_sample_routes('specialist')
|
|
|
|
|
+_register_sample_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'),
|
|
|
|
|
+ user: dict = Depends(permission_guard),
|
|
|
|
|
+ db: Session = Depends(get_db),
|
|
|
|
|
+ ):
|
|
|
|
|
+ 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,
|
|
|
|
|
+ )
|
|
|
|
|
+ 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,
|
|
|
|
|
+ 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,
|
|
|
|
|
+ ),
|
|
|
|
|
+ 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))
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+_register_freight_routes('specialist')
|
|
|
|
|
+_register_freight_routes('manager')
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _register_attachment_routes(scope_kind: ScopeKind) -> None:
|
|
|
|
|
+ for module in ('presign', 'protocol', 'payment', 'sample'):
|
|
|
|
|
+ permission_guard = require_market_menu_code(
|
|
|
|
|
+ f'mini:market:{scope_kind}:{module}'
|
|
|
|
|
+ )
|
|
|
|
|
+ path = f'/mini/market/{scope_kind}/{module}/attachments'
|
|
|
|
|
+
|
|
|
|
|
+ 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.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',
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+_register_attachment_routes('specialist')
|
|
|
|
|
+_register_attachment_routes('manager')
|