| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095 |
- """市场专员/地区经理移动端专用 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')
|