pilot_service.py 87 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394
  1. """中试服务管理 API(细胞产品类型/规格/价格体系/项目价格、渠道信息、体检机构)"""
  2. from typing import Any, List, Optional
  3. from fastapi import APIRouter, Depends, Query
  4. from pydantic import BaseModel, ConfigDict, Field
  5. from sqlalchemy.orm import Session
  6. from app.core.database import get_db
  7. from app.core.menu_guard import require_admin_or_super_admin
  8. from app.core.response import error_response, success_response
  9. from app.crud import pilot_service as ps_svc
  10. from app.models.models import SysUser
  11. router = APIRouter(prefix="/web/admin", tags=["中试服务管理"])
  12. def _get_tenant_no(db: Session, user: dict) -> str:
  13. user_id = user.get("user_id")
  14. if not user_id:
  15. raise PermissionError("用户身份信息缺失,无法确定租户")
  16. sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
  17. if not sys_user or not sys_user.tenant_no:
  18. raise PermissionError("用户租户信息缺失")
  19. return sys_user.tenant_no
  20. # ---------------------------------------------------------------------------
  21. # 1. 细胞产品类型
  22. # ---------------------------------------------------------------------------
  23. class CellProductTypeCreateBody(BaseModel):
  24. model_config = ConfigDict(populate_by_name=True)
  25. product_type_code: str = Field(..., alias="productTypeCode")
  26. product_type_name: str = Field(..., alias="productTypeName")
  27. order_num: Optional[int] = Field(0, alias="orderNum")
  28. remark: Optional[str] = None
  29. class CellProductTypeUpdateBody(BaseModel):
  30. model_config = ConfigDict(populate_by_name=True)
  31. product_type_code: Optional[str] = Field(None, alias="productTypeCode")
  32. product_type_name: Optional[str] = Field(None, alias="productTypeName")
  33. order_num: Optional[int] = Field(None, alias="orderNum")
  34. remark: Optional[str] = None
  35. @router.get("/cell-product-types")
  36. async def list_cell_product_types(
  37. page: int = Query(1, ge=1),
  38. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  39. keyword: Optional[str] = None,
  40. user: dict = Depends(require_admin_or_super_admin),
  41. db: Session = Depends(get_db),
  42. ):
  43. tenant_no = _get_tenant_no(db, user)
  44. items, total = ps_svc.list_cell_product_types(
  45. db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user
  46. )
  47. tp = ps_svc.total_pages(total, pageSize)
  48. return success_response(
  49. {"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}
  50. )
  51. @router.post("/cell-product-types")
  52. async def create_cell_product_type(
  53. body: CellProductTypeCreateBody,
  54. user: dict = Depends(require_admin_or_super_admin),
  55. db: Session = Depends(get_db),
  56. ):
  57. try:
  58. tenant_no = _get_tenant_no(db, user)
  59. obj = ps_svc.create_cell_product_type(
  60. db,
  61. product_type_code=body.product_type_code,
  62. product_type_name=body.product_type_name,
  63. tenant_no=tenant_no,
  64. order_num=body.order_num,
  65. remark=body.remark,
  66. user_id=user.get("user_id"),
  67. )
  68. return success_response(ps_svc._row_to_dict_cell_product_type(obj), message="创建成功")
  69. except ValueError as e:
  70. return error_response(400, str(e))
  71. @router.put("/cell-product-types/{row_id}")
  72. async def update_cell_product_type(
  73. row_id: str,
  74. body: CellProductTypeUpdateBody,
  75. user: dict = Depends(require_admin_or_super_admin),
  76. db: Session = Depends(get_db),
  77. ):
  78. try:
  79. tenant_no = _get_tenant_no(db, user)
  80. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  81. obj = ps_svc.update_cell_product_type(
  82. db,
  83. row_id,
  84. product_type_code=payload.get("product_type_code"),
  85. product_type_name=payload.get("product_type_name"),
  86. order_num=payload.get("order_num"),
  87. remark=payload.get("remark"),
  88. user_id=user.get("user_id"),
  89. tenant_no=tenant_no,
  90. )
  91. return success_response(ps_svc._row_to_dict_cell_product_type(obj), message="更新成功")
  92. except ValueError as e:
  93. return error_response(400, str(e))
  94. @router.delete("/cell-product-types/{row_id}")
  95. async def delete_cell_product_type(
  96. row_id: str,
  97. user: dict = Depends(require_admin_or_super_admin),
  98. db: Session = Depends(get_db),
  99. ):
  100. try:
  101. tenant_no = _get_tenant_no(db, user)
  102. ps_svc.soft_delete_cell_product_type(db, row_id, tenant_no=tenant_no)
  103. return success_response(None, message="删除成功")
  104. except ValueError as e:
  105. return error_response(400, str(e))
  106. # ---------------------------------------------------------------------------
  107. # 2. 细胞产品规格
  108. # ---------------------------------------------------------------------------
  109. class CellProductSpecCreateBody(BaseModel):
  110. model_config = ConfigDict(populate_by_name=True)
  111. spec_code: str = Field(..., alias="specCode")
  112. spec_name: str = Field(..., alias="specName")
  113. order_num: Optional[int] = Field(0, alias="orderNum")
  114. remark: Optional[str] = None
  115. class CellProductSpecUpdateBody(BaseModel):
  116. model_config = ConfigDict(populate_by_name=True)
  117. spec_code: Optional[str] = Field(None, alias="specCode")
  118. spec_name: Optional[str] = Field(None, alias="specName")
  119. order_num: Optional[int] = Field(None, alias="orderNum")
  120. remark: Optional[str] = None
  121. @router.get("/cell-product-specs")
  122. async def list_cell_product_specs(
  123. page: int = Query(1, ge=1),
  124. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  125. keyword: Optional[str] = None,
  126. user: dict = Depends(require_admin_or_super_admin),
  127. db: Session = Depends(get_db),
  128. ):
  129. tenant_no = _get_tenant_no(db, user)
  130. items, total = ps_svc.list_cell_product_specs(
  131. db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user
  132. )
  133. tp = ps_svc.total_pages(total, pageSize)
  134. return success_response(
  135. {"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}
  136. )
  137. @router.post("/cell-product-specs")
  138. async def create_cell_product_spec(
  139. body: CellProductSpecCreateBody,
  140. user: dict = Depends(require_admin_or_super_admin),
  141. db: Session = Depends(get_db),
  142. ):
  143. try:
  144. tenant_no = _get_tenant_no(db, user)
  145. obj = ps_svc.create_cell_product_spec(
  146. db,
  147. spec_code=body.spec_code,
  148. spec_name=body.spec_name,
  149. tenant_no=tenant_no,
  150. order_num=body.order_num,
  151. remark=body.remark,
  152. user_id=user.get("user_id"),
  153. )
  154. return success_response(ps_svc._row_to_dict_cell_product_spec(obj), message="创建成功")
  155. except ValueError as e:
  156. return error_response(400, str(e))
  157. @router.put("/cell-product-specs/{row_id}")
  158. async def update_cell_product_spec(
  159. row_id: str,
  160. body: CellProductSpecUpdateBody,
  161. user: dict = Depends(require_admin_or_super_admin),
  162. db: Session = Depends(get_db),
  163. ):
  164. try:
  165. tenant_no = _get_tenant_no(db, user)
  166. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  167. obj = ps_svc.update_cell_product_spec(
  168. db,
  169. row_id,
  170. spec_code=payload.get("spec_code"),
  171. spec_name=payload.get("spec_name"),
  172. order_num=payload.get("order_num"),
  173. remark=payload.get("remark"),
  174. user_id=user.get("user_id"),
  175. tenant_no=tenant_no,
  176. )
  177. return success_response(ps_svc._row_to_dict_cell_product_spec(obj), message="更新成功")
  178. except ValueError as e:
  179. return error_response(400, str(e))
  180. @router.delete("/cell-product-specs/{row_id}")
  181. async def delete_cell_product_spec(
  182. row_id: str,
  183. user: dict = Depends(require_admin_or_super_admin),
  184. db: Session = Depends(get_db),
  185. ):
  186. try:
  187. tenant_no = _get_tenant_no(db, user)
  188. ps_svc.soft_delete_cell_product_spec(db, row_id, tenant_no=tenant_no)
  189. return success_response(None, message="删除成功")
  190. except ValueError as e:
  191. return error_response(400, str(e))
  192. # ---------------------------------------------------------------------------
  193. # 3. 细胞产品价格
  194. # ---------------------------------------------------------------------------
  195. class CellProductPriceCreateBody(BaseModel):
  196. model_config = ConfigDict(populate_by_name=True)
  197. project_name: str = Field(..., alias="projectName")
  198. project_code: str = Field(..., alias="projectCode")
  199. cell_type_code: Optional[str] = Field(None, alias="cellTypeCode")
  200. cell_type_name: Optional[str] = Field(None, alias="cellTypeName")
  201. spec_text: Optional[str] = Field(None, alias="specText")
  202. spec_id: Optional[str] = Field(None, alias="specId")
  203. cell_product_type_id: Optional[str] = Field(None, alias="cellProductTypeId")
  204. unit_price: Optional[float] = Field(None, alias="unitPrice")
  205. order_num: Optional[int] = Field(0, alias="orderNum")
  206. remark: Optional[str] = None
  207. class CellProductPriceUpdateBody(BaseModel):
  208. model_config = ConfigDict(populate_by_name=True)
  209. project_name: Optional[str] = Field(None, alias="projectName")
  210. project_code: Optional[str] = Field(None, alias="projectCode")
  211. cell_type_code: Optional[str] = Field(None, alias="cellTypeCode")
  212. cell_type_name: Optional[str] = Field(None, alias="cellTypeName")
  213. spec_text: Optional[str] = Field(None, alias="specText")
  214. spec_id: Optional[str] = Field(None, alias="specId")
  215. cell_product_type_id: Optional[str] = Field(None, alias="cellProductTypeId")
  216. unit_price: Optional[float] = Field(None, alias="unitPrice")
  217. order_num: Optional[int] = Field(None, alias="orderNum")
  218. remark: Optional[str] = None
  219. @router.get("/cell-product-prices")
  220. async def list_cell_product_prices(
  221. page: int = Query(1, ge=1),
  222. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  223. keyword: Optional[str] = None,
  224. cell_product_type_id: Optional[str] = Query(None, alias="cellProductTypeId"),
  225. user: dict = Depends(require_admin_or_super_admin),
  226. db: Session = Depends(get_db),
  227. ):
  228. tenant_no = _get_tenant_no(db, user)
  229. items, total = ps_svc.list_cell_product_prices(
  230. db,
  231. page=page,
  232. page_size=pageSize,
  233. keyword=keyword,
  234. tenant_no=tenant_no,
  235. cell_product_type_id=cell_product_type_id,
  236. user_info=user,
  237. )
  238. tp = ps_svc.total_pages(total, pageSize)
  239. return success_response(
  240. {"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}
  241. )
  242. @router.post("/cell-product-prices")
  243. async def create_cell_product_price(
  244. body: CellProductPriceCreateBody,
  245. user: dict = Depends(require_admin_or_super_admin),
  246. db: Session = Depends(get_db),
  247. ):
  248. try:
  249. tenant_no = _get_tenant_no(db, user)
  250. obj = ps_svc.create_cell_product_price(
  251. db,
  252. project_name=body.project_name,
  253. project_code=body.project_code,
  254. cell_type_code=body.cell_type_code or "",
  255. cell_type_name=body.cell_type_name or "",
  256. spec_text=body.spec_text or "",
  257. spec_id=body.spec_id or "",
  258. cell_product_type_id=body.cell_product_type_id or "",
  259. unit_price=body.unit_price,
  260. tenant_no=tenant_no,
  261. order_num=body.order_num,
  262. remark=body.remark,
  263. user_id=user.get("user_id"),
  264. )
  265. return success_response(ps_svc._row_to_dict_cell_product_price(obj), message="创建成功")
  266. except ValueError as e:
  267. return error_response(400, str(e))
  268. @router.put("/cell-product-prices/{row_id}")
  269. async def update_cell_product_price(
  270. row_id: str,
  271. body: CellProductPriceUpdateBody,
  272. user: dict = Depends(require_admin_or_super_admin),
  273. db: Session = Depends(get_db),
  274. ):
  275. try:
  276. tenant_no = _get_tenant_no(db, user)
  277. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  278. obj = ps_svc.update_cell_product_price(
  279. db,
  280. row_id,
  281. project_name=payload.get("project_name"),
  282. project_code=payload.get("project_code"),
  283. cell_type_code=payload.get("cell_type_code"),
  284. cell_type_name=payload.get("cell_type_name"),
  285. spec_text=payload.get("spec_text"),
  286. spec_id=payload.get("spec_id"),
  287. cell_product_type_id=payload.get("cell_product_type_id"),
  288. unit_price=payload.get("unit_price"),
  289. order_num=payload.get("order_num"),
  290. remark=payload.get("remark"),
  291. user_id=user.get("user_id"),
  292. tenant_no=tenant_no,
  293. )
  294. return success_response(ps_svc._row_to_dict_cell_product_price(obj), message="更新成功")
  295. except ValueError as e:
  296. return error_response(400, str(e))
  297. @router.delete("/cell-product-prices/{row_id}")
  298. async def delete_cell_product_price(
  299. row_id: str,
  300. user: dict = Depends(require_admin_or_super_admin),
  301. db: Session = Depends(get_db),
  302. ):
  303. try:
  304. tenant_no = _get_tenant_no(db, user)
  305. ps_svc.soft_delete_cell_product_price(db, row_id, tenant_no=tenant_no)
  306. return success_response(None, message="删除成功")
  307. except ValueError as e:
  308. return error_response(400, str(e))
  309. # ---------------------------------------------------------------------------
  310. # 4. 项目价格体系(主表 + 明细子表)
  311. # ---------------------------------------------------------------------------
  312. class ProjectSystemCreateBody(BaseModel):
  313. model_config = ConfigDict(populate_by_name=True)
  314. system_name: str = Field(..., alias="systemName")
  315. system_code: str = Field(..., alias="systemCode")
  316. status: Optional[str] = Field("1", alias="status")
  317. order_num: Optional[int] = Field(0, alias="orderNum")
  318. remark: Optional[str] = None
  319. class ProjectSystemUpdateBody(BaseModel):
  320. model_config = ConfigDict(populate_by_name=True)
  321. system_name: Optional[str] = Field(None, alias="systemName")
  322. system_code: Optional[str] = Field(None, alias="systemCode")
  323. status: Optional[str] = Field(None, alias="status")
  324. order_num: Optional[int] = Field(None, alias="orderNum")
  325. remark: Optional[str] = None
  326. @router.get("/project-price-systems")
  327. async def list_project_systems(
  328. page: int = Query(1, ge=1),
  329. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  330. keyword: Optional[str] = None,
  331. user: dict = Depends(require_admin_or_super_admin),
  332. db: Session = Depends(get_db),
  333. ):
  334. tenant_no = _get_tenant_no(db, user)
  335. items, total = ps_svc.list_project_systems(
  336. db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user
  337. )
  338. tp = ps_svc.total_pages(total, pageSize)
  339. return success_response(
  340. {"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}
  341. )
  342. @router.post("/project-price-systems")
  343. async def create_project_system(
  344. body: ProjectSystemCreateBody,
  345. user: dict = Depends(require_admin_or_super_admin),
  346. db: Session = Depends(get_db),
  347. ):
  348. try:
  349. tenant_no = _get_tenant_no(db, user)
  350. obj = ps_svc.create_project_system(
  351. db,
  352. system_name=body.system_name,
  353. system_code=body.system_code,
  354. status=body.status,
  355. tenant_no=tenant_no,
  356. order_num=body.order_num,
  357. remark=body.remark,
  358. user_id=user.get("user_id"),
  359. )
  360. return success_response(ps_svc._row_to_dict_project_system(obj), message="创建成功")
  361. except ValueError as e:
  362. return error_response(400, str(e))
  363. @router.put("/project-price-systems/{row_id}")
  364. async def update_project_system(
  365. row_id: str,
  366. body: ProjectSystemUpdateBody,
  367. user: dict = Depends(require_admin_or_super_admin),
  368. db: Session = Depends(get_db),
  369. ):
  370. try:
  371. tenant_no = _get_tenant_no(db, user)
  372. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  373. obj = ps_svc.update_project_system(
  374. db,
  375. row_id,
  376. system_name=payload.get("system_name"),
  377. system_code=payload.get("system_code"),
  378. status=payload.get("status"),
  379. order_num=payload.get("order_num"),
  380. remark=payload.get("remark"),
  381. user_id=user.get("user_id"),
  382. tenant_no=tenant_no,
  383. )
  384. return success_response(ps_svc._row_to_dict_project_system(obj), message="更新成功")
  385. except ValueError as e:
  386. return error_response(400, str(e))
  387. @router.delete("/project-price-systems/{row_id}")
  388. async def delete_project_system(
  389. row_id: str,
  390. user: dict = Depends(require_admin_or_super_admin),
  391. db: Session = Depends(get_db),
  392. ):
  393. try:
  394. tenant_no = _get_tenant_no(db, user)
  395. ps_svc.soft_delete_project_system(db, row_id, tenant_no=tenant_no)
  396. return success_response(None, message="删除成功")
  397. except ValueError as e:
  398. return error_response(400, str(e))
  399. # --- 项目价格体系明细 ---
  400. class ProjectSystemItemCreateBody(BaseModel):
  401. model_config = ConfigDict(populate_by_name=True)
  402. source_price_id: Optional[str] = Field(None, alias="sourcePriceId")
  403. project_name: str = Field(..., alias="projectName")
  404. project_code: str = Field(..., alias="projectCode")
  405. cell_type_code: Optional[str] = Field(None, alias="cellTypeCode")
  406. cell_type_name: Optional[str] = Field(None, alias="cellTypeName")
  407. spec_text: Optional[str] = Field(None, alias="specText")
  408. spec_id: Optional[str] = Field(None, alias="specId")
  409. cell_product_type_id: Optional[str] = Field(None, alias="cellProductTypeId")
  410. unit_price: Optional[float] = Field(None, alias="unitPrice")
  411. order_num: Optional[int] = Field(0, alias="orderNum")
  412. remark: Optional[str] = None
  413. class ProjectSystemItemUpdateBody(BaseModel):
  414. model_config = ConfigDict(populate_by_name=True)
  415. source_price_id: Optional[str] = Field(None, alias="sourcePriceId")
  416. project_name: Optional[str] = Field(None, alias="projectName")
  417. project_code: Optional[str] = Field(None, alias="projectCode")
  418. cell_type_code: Optional[str] = Field(None, alias="cellTypeCode")
  419. cell_type_name: Optional[str] = Field(None, alias="cellTypeName")
  420. spec_text: Optional[str] = Field(None, alias="specText")
  421. spec_id: Optional[str] = Field(None, alias="specId")
  422. cell_product_type_id: Optional[str] = Field(None, alias="cellProductTypeId")
  423. unit_price: Optional[float] = Field(None, alias="unitPrice")
  424. order_num: Optional[int] = Field(None, alias="orderNum")
  425. remark: Optional[str] = None
  426. @router.get("/project-price-systems/{system_id}/items")
  427. async def list_project_system_items(
  428. system_id: str,
  429. user: dict = Depends(require_admin_or_super_admin),
  430. db: Session = Depends(get_db),
  431. ):
  432. try:
  433. tenant_no = _get_tenant_no(db, user)
  434. items = ps_svc.list_project_system_items(db, system_id, tenant_no=tenant_no, user_info=user)
  435. return success_response(items)
  436. except ValueError as e:
  437. return error_response(400, str(e))
  438. @router.post("/project-price-systems/{system_id}/items")
  439. async def create_project_system_item(
  440. system_id: str,
  441. body: ProjectSystemItemCreateBody,
  442. user: dict = Depends(require_admin_or_super_admin),
  443. db: Session = Depends(get_db),
  444. ):
  445. try:
  446. tenant_no = _get_tenant_no(db, user)
  447. obj = ps_svc.create_project_system_item(
  448. db,
  449. project_system_id=system_id,
  450. source_price_id=body.source_price_id or "",
  451. project_name=body.project_name,
  452. project_code=body.project_code,
  453. cell_type_code=body.cell_type_code or "",
  454. cell_type_name=body.cell_type_name or "",
  455. spec_text=body.spec_text or "",
  456. spec_id=body.spec_id or "",
  457. cell_product_type_id=body.cell_product_type_id or "",
  458. unit_price=body.unit_price,
  459. tenant_no=tenant_no,
  460. order_num=body.order_num,
  461. remark=body.remark,
  462. user_id=user.get("user_id"),
  463. )
  464. return success_response(ps_svc._row_to_dict_project_system_item(obj), message="创建成功")
  465. except ValueError as e:
  466. return error_response(400, str(e))
  467. @router.put("/project-system-items/{item_id}")
  468. async def update_project_system_item(
  469. item_id: str,
  470. body: ProjectSystemItemUpdateBody,
  471. user: dict = Depends(require_admin_or_super_admin),
  472. db: Session = Depends(get_db),
  473. ):
  474. try:
  475. tenant_no = _get_tenant_no(db, user)
  476. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  477. obj = ps_svc.update_project_system_item(
  478. db,
  479. item_id,
  480. source_price_id=payload.get("source_price_id"),
  481. project_name=payload.get("project_name"),
  482. project_code=payload.get("project_code"),
  483. cell_type_code=payload.get("cell_type_code"),
  484. cell_type_name=payload.get("cell_type_name"),
  485. spec_text=payload.get("spec_text"),
  486. spec_id=payload.get("spec_id"),
  487. cell_product_type_id=payload.get("cell_product_type_id"),
  488. unit_price=payload.get("unit_price"),
  489. order_num=payload.get("order_num"),
  490. remark=payload.get("remark"),
  491. user_id=user.get("user_id"),
  492. tenant_no=tenant_no,
  493. )
  494. return success_response(ps_svc._row_to_dict_project_system_item(obj), message="更新成功")
  495. except ValueError as e:
  496. return error_response(400, str(e))
  497. @router.delete("/project-system-items/{item_id}")
  498. async def delete_project_system_item(
  499. item_id: str,
  500. user: dict = Depends(require_admin_or_super_admin),
  501. db: Session = Depends(get_db),
  502. ):
  503. try:
  504. tenant_no = _get_tenant_no(db, user)
  505. ps_svc.soft_delete_project_system_item(db, item_id, tenant_no=tenant_no)
  506. return success_response(None, message="删除成功")
  507. except ValueError as e:
  508. return error_response(400, str(e))
  509. # ---------------------------------------------------------------------------
  510. # 5. 渠道信息(主表 + 开票/邮寄地址/协议/协议产品价格子表)
  511. # ---------------------------------------------------------------------------
  512. class ChannelCreateBody(BaseModel):
  513. model_config = ConfigDict(populate_by_name=True)
  514. channel_name: str = Field(..., alias="channelName")
  515. credit_code: Optional[str] = Field(None, alias="creditCode")
  516. channel_type: Optional[str] = Field(None, alias="channelType")
  517. contact_name: Optional[str] = Field(None, alias="contactName")
  518. contact_phone: Optional[str] = Field(None, alias="contactPhone")
  519. contact_email: Optional[str] = Field(None, alias="contactEmail")
  520. protocol_attachments: Optional[str] = Field(None, alias="protocolAttachments")
  521. order_num: Optional[int] = Field(0, alias="orderNum")
  522. remark: Optional[str] = None
  523. class ChannelUpdateBody(BaseModel):
  524. model_config = ConfigDict(populate_by_name=True)
  525. channel_name: Optional[str] = Field(None, alias="channelName")
  526. credit_code: Optional[str] = Field(None, alias="creditCode")
  527. channel_type: Optional[str] = Field(None, alias="channelType")
  528. contact_name: Optional[str] = Field(None, alias="contactName")
  529. contact_phone: Optional[str] = Field(None, alias="contactPhone")
  530. contact_email: Optional[str] = Field(None, alias="contactEmail")
  531. protocol_attachments: Optional[str] = Field(None, alias="protocolAttachments")
  532. order_num: Optional[int] = Field(None, alias="orderNum")
  533. remark: Optional[str] = None
  534. @router.get("/channels")
  535. async def list_channels(
  536. page: int = Query(1, ge=1),
  537. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  538. keyword: Optional[str] = None,
  539. user: dict = Depends(require_admin_or_super_admin),
  540. db: Session = Depends(get_db),
  541. ):
  542. tenant_no = _get_tenant_no(db, user)
  543. items, total = ps_svc.list_channels(
  544. db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user
  545. )
  546. tp = ps_svc.total_pages(total, pageSize)
  547. return success_response(
  548. {"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}
  549. )
  550. @router.post("/channels")
  551. async def create_channel(
  552. body: ChannelCreateBody,
  553. user: dict = Depends(require_admin_or_super_admin),
  554. db: Session = Depends(get_db),
  555. ):
  556. try:
  557. tenant_no = _get_tenant_no(db, user)
  558. obj = ps_svc.create_channel(
  559. db,
  560. channel_name=body.channel_name,
  561. credit_code=body.credit_code,
  562. channel_type=body.channel_type,
  563. contact_name=body.contact_name,
  564. contact_phone=body.contact_phone,
  565. contact_email=body.contact_email,
  566. protocol_attachments=body.protocol_attachments,
  567. tenant_no=tenant_no,
  568. order_num=body.order_num,
  569. remark=body.remark,
  570. user_id=user.get("user_id"),
  571. )
  572. return success_response(
  573. ps_svc._row_to_dict_channel(obj, db, tenant_no),
  574. message="创建成功",
  575. )
  576. except ValueError as e:
  577. return error_response(400, str(e))
  578. @router.put("/channels/{row_id}")
  579. async def update_channel(
  580. row_id: str,
  581. body: ChannelUpdateBody,
  582. user: dict = Depends(require_admin_or_super_admin),
  583. db: Session = Depends(get_db),
  584. ):
  585. try:
  586. tenant_no = _get_tenant_no(db, user)
  587. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  588. obj = ps_svc.update_channel(
  589. db,
  590. row_id,
  591. channel_name=payload.get("channel_name"),
  592. credit_code=payload.get("credit_code"),
  593. channel_type=payload.get("channel_type"),
  594. contact_name=payload.get("contact_name"),
  595. contact_phone=payload.get("contact_phone"),
  596. contact_email=payload.get("contact_email"),
  597. protocol_attachments=payload.get("protocol_attachments"),
  598. order_num=payload.get("order_num"),
  599. remark=payload.get("remark"),
  600. user_id=user.get("user_id"),
  601. tenant_no=tenant_no,
  602. )
  603. return success_response(
  604. ps_svc._row_to_dict_channel(obj, db, tenant_no),
  605. message="更新成功",
  606. )
  607. except ValueError as e:
  608. return error_response(400, str(e))
  609. @router.delete("/channels/{row_id}")
  610. async def delete_channel(
  611. row_id: str,
  612. user: dict = Depends(require_admin_or_super_admin),
  613. db: Session = Depends(get_db),
  614. ):
  615. try:
  616. tenant_no = _get_tenant_no(db, user)
  617. ps_svc.soft_delete_channel(db, row_id, tenant_no=tenant_no)
  618. return success_response(None, message="删除成功")
  619. except ValueError as e:
  620. return error_response(400, str(e))
  621. # --- 渠道开票信息 ---
  622. class ChannelInvoiceCreateBody(BaseModel):
  623. model_config = ConfigDict(populate_by_name=True)
  624. company_name: str = Field(..., alias="companyName")
  625. tax_no: Optional[str] = Field(None, alias="taxNo")
  626. company_address: Optional[str] = Field(None, alias="companyAddress")
  627. company_phone: Optional[str] = Field(None, alias="companyPhone")
  628. bank_name: Optional[str] = Field(None, alias="bankName")
  629. bank_account: Optional[str] = Field(None, alias="bankAccount")
  630. order_num: Optional[int] = Field(0, alias="orderNum")
  631. remark: Optional[str] = None
  632. class ChannelInvoiceUpdateBody(BaseModel):
  633. model_config = ConfigDict(populate_by_name=True)
  634. company_name: Optional[str] = Field(None, alias="companyName")
  635. tax_no: Optional[str] = Field(None, alias="taxNo")
  636. company_address: Optional[str] = Field(None, alias="companyAddress")
  637. company_phone: Optional[str] = Field(None, alias="companyPhone")
  638. bank_name: Optional[str] = Field(None, alias="bankName")
  639. bank_account: Optional[str] = Field(None, alias="bankAccount")
  640. order_num: Optional[int] = Field(None, alias="orderNum")
  641. remark: Optional[str] = None
  642. @router.get("/channels/{channel_id}/invoices")
  643. async def list_channel_invoices(
  644. channel_id: str,
  645. user: dict = Depends(require_admin_or_super_admin),
  646. db: Session = Depends(get_db),
  647. ):
  648. try:
  649. tenant_no = _get_tenant_no(db, user)
  650. items = ps_svc.list_channel_invoices(db, channel_id, tenant_no=tenant_no, user_info=user)
  651. return success_response(items)
  652. except ValueError as e:
  653. return error_response(400, str(e))
  654. @router.post("/channels/{channel_id}/invoices")
  655. async def create_channel_invoice(
  656. channel_id: str,
  657. body: ChannelInvoiceCreateBody,
  658. user: dict = Depends(require_admin_or_super_admin),
  659. db: Session = Depends(get_db),
  660. ):
  661. try:
  662. tenant_no = _get_tenant_no(db, user)
  663. obj = ps_svc.create_channel_invoice(
  664. db,
  665. channel_id=channel_id,
  666. company_name=body.company_name,
  667. tax_no=body.tax_no,
  668. company_address=body.company_address,
  669. company_phone=body.company_phone,
  670. bank_name=body.bank_name,
  671. bank_account=body.bank_account,
  672. tenant_no=tenant_no,
  673. order_num=body.order_num,
  674. remark=body.remark,
  675. user_id=user.get("user_id"),
  676. )
  677. return success_response(ps_svc._row_to_dict_channel_invoice(obj), message="创建成功")
  678. except ValueError as e:
  679. return error_response(400, str(e))
  680. @router.put("/channel-invoices/{invoice_id}")
  681. async def update_channel_invoice(
  682. invoice_id: str,
  683. body: ChannelInvoiceUpdateBody,
  684. user: dict = Depends(require_admin_or_super_admin),
  685. db: Session = Depends(get_db),
  686. ):
  687. try:
  688. tenant_no = _get_tenant_no(db, user)
  689. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  690. obj = ps_svc.update_channel_invoice(
  691. db,
  692. invoice_id,
  693. company_name=payload.get("company_name"),
  694. tax_no=payload.get("tax_no"),
  695. company_address=payload.get("company_address"),
  696. company_phone=payload.get("company_phone"),
  697. bank_name=payload.get("bank_name"),
  698. bank_account=payload.get("bank_account"),
  699. order_num=payload.get("order_num"),
  700. remark=payload.get("remark"),
  701. user_id=user.get("user_id"),
  702. tenant_no=tenant_no,
  703. )
  704. return success_response(ps_svc._row_to_dict_channel_invoice(obj), message="更新成功")
  705. except ValueError as e:
  706. return error_response(400, str(e))
  707. @router.delete("/channel-invoices/{invoice_id}")
  708. async def delete_channel_invoice(
  709. invoice_id: str,
  710. user: dict = Depends(require_admin_or_super_admin),
  711. db: Session = Depends(get_db),
  712. ):
  713. try:
  714. tenant_no = _get_tenant_no(db, user)
  715. ps_svc.soft_delete_channel_invoice(db, invoice_id, tenant_no=tenant_no)
  716. return success_response(None, message="删除成功")
  717. except ValueError as e:
  718. return error_response(400, str(e))
  719. # --- 渠道邮寄地址 ---
  720. class ChannelMailAddressCreateBody(BaseModel):
  721. model_config = ConfigDict(populate_by_name=True)
  722. mail_address: str = Field(..., alias="mailAddress")
  723. mail_contact: Optional[str] = Field(None, alias="mailContact")
  724. mail_phone: Optional[str] = Field(None, alias="mailPhone")
  725. order_num: Optional[int] = Field(0, alias="orderNum")
  726. remark: Optional[str] = None
  727. class ChannelMailAddressUpdateBody(BaseModel):
  728. model_config = ConfigDict(populate_by_name=True)
  729. mail_address: Optional[str] = Field(None, alias="mailAddress")
  730. mail_contact: Optional[str] = Field(None, alias="mailContact")
  731. mail_phone: Optional[str] = Field(None, alias="mailPhone")
  732. order_num: Optional[int] = Field(None, alias="orderNum")
  733. remark: Optional[str] = None
  734. @router.get("/channels/{channel_id}/mail-addresses")
  735. async def list_channel_mail_addresses(
  736. channel_id: str,
  737. user: dict = Depends(require_admin_or_super_admin),
  738. db: Session = Depends(get_db),
  739. ):
  740. try:
  741. tenant_no = _get_tenant_no(db, user)
  742. items = ps_svc.list_channel_mail_addresses(db, channel_id, tenant_no=tenant_no, user_info=user)
  743. return success_response(items)
  744. except ValueError as e:
  745. return error_response(400, str(e))
  746. @router.post("/channels/{channel_id}/mail-addresses")
  747. async def create_channel_mail_address(
  748. channel_id: str,
  749. body: ChannelMailAddressCreateBody,
  750. user: dict = Depends(require_admin_or_super_admin),
  751. db: Session = Depends(get_db),
  752. ):
  753. try:
  754. tenant_no = _get_tenant_no(db, user)
  755. obj = ps_svc.create_channel_mail_address(
  756. db,
  757. channel_id=channel_id,
  758. mail_address=body.mail_address,
  759. mail_contact=body.mail_contact,
  760. mail_phone=body.mail_phone,
  761. tenant_no=tenant_no,
  762. order_num=body.order_num,
  763. remark=body.remark,
  764. user_id=user.get("user_id"),
  765. )
  766. return success_response(ps_svc._row_to_dict_channel_mail_address(obj), message="创建成功")
  767. except ValueError as e:
  768. return error_response(400, str(e))
  769. @router.put("/channel-mail-addresses/{address_id}")
  770. async def update_channel_mail_address(
  771. address_id: str,
  772. body: ChannelMailAddressUpdateBody,
  773. user: dict = Depends(require_admin_or_super_admin),
  774. db: Session = Depends(get_db),
  775. ):
  776. try:
  777. tenant_no = _get_tenant_no(db, user)
  778. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  779. obj = ps_svc.update_channel_mail_address(
  780. db,
  781. address_id,
  782. mail_address=payload.get("mail_address"),
  783. mail_contact=payload.get("mail_contact"),
  784. mail_phone=payload.get("mail_phone"),
  785. order_num=payload.get("order_num"),
  786. remark=payload.get("remark"),
  787. user_id=user.get("user_id"),
  788. tenant_no=tenant_no,
  789. )
  790. return success_response(ps_svc._row_to_dict_channel_mail_address(obj), message="更新成功")
  791. except ValueError as e:
  792. return error_response(400, str(e))
  793. @router.delete("/channel-mail-addresses/{address_id}")
  794. async def delete_channel_mail_address(
  795. address_id: str,
  796. user: dict = Depends(require_admin_or_super_admin),
  797. db: Session = Depends(get_db),
  798. ):
  799. try:
  800. tenant_no = _get_tenant_no(db, user)
  801. ps_svc.soft_delete_channel_mail_address(db, address_id, tenant_no=tenant_no)
  802. return success_response(None, message="删除成功")
  803. except ValueError as e:
  804. return error_response(400, str(e))
  805. # --- 渠道协议 ---
  806. class ChannelAgreementCreateBody(BaseModel):
  807. model_config = ConfigDict(populate_by_name=True)
  808. agreement_type: str = Field(..., alias="agreementType")
  809. business_type: Optional[str] = Field(None, alias="businessType")
  810. settle_mode: Optional[str] = Field(None, alias="settleMode")
  811. credit_limit_amount: Optional[float] = Field(None, alias="creditLimitAmount")
  812. effective_date: Optional[str] = Field(None, alias="effectiveDate")
  813. expire_date: Optional[str] = Field(None, alias="expireDate")
  814. order_num: Optional[int] = Field(0, alias="orderNum")
  815. remark: Optional[str] = None
  816. class ChannelAgreementUpdateBody(BaseModel):
  817. model_config = ConfigDict(populate_by_name=True)
  818. agreement_type: Optional[str] = Field(None, alias="agreementType")
  819. business_type: Optional[str] = Field(None, alias="businessType")
  820. settle_mode: Optional[str] = Field(None, alias="settleMode")
  821. credit_limit_amount: Optional[float] = Field(None, alias="creditLimitAmount")
  822. effective_date: Optional[str] = Field(None, alias="effectiveDate")
  823. expire_date: Optional[str] = Field(None, alias="expireDate")
  824. order_num: Optional[int] = Field(None, alias="orderNum")
  825. remark: Optional[str] = None
  826. @router.get("/channels/{channel_id}/agreements")
  827. async def list_channel_agreements(
  828. channel_id: str,
  829. user: dict = Depends(require_admin_or_super_admin),
  830. db: Session = Depends(get_db),
  831. ):
  832. try:
  833. tenant_no = _get_tenant_no(db, user)
  834. items = ps_svc.list_channel_agreements(db, channel_id, tenant_no=tenant_no, user_info=user)
  835. return success_response(items)
  836. except ValueError as e:
  837. return error_response(400, str(e))
  838. @router.post("/channels/{channel_id}/agreements")
  839. async def create_channel_agreement(
  840. channel_id: str,
  841. body: ChannelAgreementCreateBody,
  842. user: dict = Depends(require_admin_or_super_admin),
  843. db: Session = Depends(get_db),
  844. ):
  845. try:
  846. tenant_no = _get_tenant_no(db, user)
  847. obj = ps_svc.create_channel_agreement(
  848. db,
  849. channel_id=channel_id,
  850. agreement_type=body.agreement_type,
  851. business_type=body.business_type,
  852. settle_mode=body.settle_mode,
  853. credit_limit_amount=body.credit_limit_amount,
  854. effective_date=body.effective_date,
  855. expire_date=body.expire_date,
  856. tenant_no=tenant_no,
  857. order_num=body.order_num,
  858. remark=body.remark,
  859. user_id=user.get("user_id"),
  860. )
  861. return success_response(ps_svc._row_to_dict_channel_agreement(obj), message="创建成功")
  862. except ValueError as e:
  863. return error_response(400, str(e))
  864. @router.put("/channel-agreements/{agreement_id}")
  865. async def update_channel_agreement(
  866. agreement_id: str,
  867. body: ChannelAgreementUpdateBody,
  868. user: dict = Depends(require_admin_or_super_admin),
  869. db: Session = Depends(get_db),
  870. ):
  871. try:
  872. tenant_no = _get_tenant_no(db, user)
  873. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  874. obj = ps_svc.update_channel_agreement(
  875. db,
  876. agreement_id,
  877. agreement_type=payload.get("agreement_type"),
  878. business_type=payload.get("business_type"),
  879. settle_mode=payload.get("settle_mode"),
  880. credit_limit_amount=payload.get("credit_limit_amount"),
  881. effective_date=payload.get("effective_date"),
  882. expire_date=payload.get("expire_date"),
  883. order_num=payload.get("order_num"),
  884. remark=payload.get("remark"),
  885. user_id=user.get("user_id"),
  886. tenant_no=tenant_no,
  887. )
  888. return success_response(ps_svc._row_to_dict_channel_agreement(obj), message="更新成功")
  889. except ValueError as e:
  890. return error_response(400, str(e))
  891. @router.delete("/channel-agreements/{agreement_id}")
  892. async def delete_channel_agreement(
  893. agreement_id: str,
  894. user: dict = Depends(require_admin_or_super_admin),
  895. db: Session = Depends(get_db),
  896. ):
  897. try:
  898. tenant_no = _get_tenant_no(db, user)
  899. ps_svc.soft_delete_channel_agreement(db, agreement_id, tenant_no=tenant_no)
  900. return success_response(None, message="删除成功")
  901. except ValueError as e:
  902. return error_response(400, str(e))
  903. @router.put("/channel-agreements/{agreement_id}/activate")
  904. async def activate_channel_agreement(
  905. agreement_id: str,
  906. user: dict = Depends(require_admin_or_super_admin),
  907. db: Session = Depends(get_db),
  908. ):
  909. try:
  910. tenant_no = _get_tenant_no(db, user)
  911. obj = ps_svc.activate_channel_agreement(
  912. db,
  913. agreement_id,
  914. tenant_no=tenant_no,
  915. user_id=user.get("user_id"),
  916. )
  917. return success_response(ps_svc._row_to_dict_channel_agreement(obj), message="生效成功")
  918. except ValueError as e:
  919. return error_response(400, str(e))
  920. @router.put("/channel-agreements/{agreement_id}/void")
  921. async def void_channel_agreement(
  922. agreement_id: str,
  923. user: dict = Depends(require_admin_or_super_admin),
  924. db: Session = Depends(get_db),
  925. ):
  926. try:
  927. tenant_no = _get_tenant_no(db, user)
  928. obj = ps_svc.void_channel_agreement(
  929. db,
  930. agreement_id,
  931. tenant_no=tenant_no,
  932. user_id=user.get("user_id"),
  933. )
  934. return success_response(ps_svc._row_to_dict_channel_agreement(obj), message="作废成功")
  935. except ValueError as e:
  936. return error_response(400, str(e))
  937. # --- 渠道协议产品价格 ---
  938. class ChannelAgreementPriceCreateBody(BaseModel):
  939. model_config = ConfigDict(populate_by_name=True)
  940. project_name: Optional[str] = Field(None, alias="projectName")
  941. project_code: Optional[str] = Field(None, alias="projectCode")
  942. cell_type_code: Optional[str] = Field(None, alias="cellTypeCode")
  943. cell_type_name: Optional[str] = Field(None, alias="cellTypeName")
  944. spec_text: Optional[str] = Field(None, alias="specText")
  945. original_price: Optional[float] = Field(None, alias="originalPrice")
  946. agreement_price: Optional[float] = Field(None, alias="agreementPrice")
  947. order_num: Optional[int] = Field(0, alias="orderNum")
  948. remark: Optional[str] = None
  949. class ChannelAgreementPriceUpdateBody(BaseModel):
  950. model_config = ConfigDict(populate_by_name=True)
  951. project_name: Optional[str] = Field(None, alias="projectName")
  952. project_code: Optional[str] = Field(None, alias="projectCode")
  953. cell_type_code: Optional[str] = Field(None, alias="cellTypeCode")
  954. cell_type_name: Optional[str] = Field(None, alias="cellTypeName")
  955. spec_text: Optional[str] = Field(None, alias="specText")
  956. original_price: Optional[float] = Field(None, alias="originalPrice")
  957. agreement_price: Optional[float] = Field(None, alias="agreementPrice")
  958. order_num: Optional[int] = Field(None, alias="orderNum")
  959. remark: Optional[str] = None
  960. @router.get("/channel-agreements/{agreement_id}/product-prices")
  961. async def list_channel_agreement_prices(
  962. agreement_id: str,
  963. user: dict = Depends(require_admin_or_super_admin),
  964. db: Session = Depends(get_db),
  965. ):
  966. try:
  967. tenant_no = _get_tenant_no(db, user)
  968. items = ps_svc.list_channel_agreement_prices(db, agreement_id, tenant_no=tenant_no, user_info=user)
  969. return success_response(items)
  970. except ValueError as e:
  971. return error_response(400, str(e))
  972. @router.post("/channel-agreements/{agreement_id}/product-prices")
  973. async def create_channel_agreement_price(
  974. agreement_id: str,
  975. body: ChannelAgreementPriceCreateBody,
  976. user: dict = Depends(require_admin_or_super_admin),
  977. db: Session = Depends(get_db),
  978. ):
  979. try:
  980. tenant_no = _get_tenant_no(db, user)
  981. obj = ps_svc.create_channel_agreement_price(
  982. db,
  983. channel_agreement_id=agreement_id,
  984. project_name=body.project_name,
  985. project_code=body.project_code,
  986. cell_type_code=body.cell_type_code,
  987. cell_type_name=body.cell_type_name,
  988. spec_text=body.spec_text,
  989. original_price=body.original_price,
  990. agreement_price=body.agreement_price,
  991. tenant_no=tenant_no,
  992. order_num=body.order_num,
  993. remark=body.remark,
  994. user_id=user.get("user_id"),
  995. )
  996. return success_response(ps_svc._row_to_dict_channel_agreement_price(obj), message="创建成功")
  997. except ValueError as e:
  998. return error_response(400, str(e))
  999. @router.put("/channel-agreement-product-prices/{price_id}")
  1000. async def update_channel_agreement_price(
  1001. price_id: str,
  1002. body: ChannelAgreementPriceUpdateBody,
  1003. user: dict = Depends(require_admin_or_super_admin),
  1004. db: Session = Depends(get_db),
  1005. ):
  1006. try:
  1007. tenant_no = _get_tenant_no(db, user)
  1008. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  1009. obj = ps_svc.update_channel_agreement_price(
  1010. db,
  1011. price_id,
  1012. project_name=payload.get("project_name"),
  1013. project_code=payload.get("project_code"),
  1014. cell_type_code=payload.get("cell_type_code"),
  1015. cell_type_name=payload.get("cell_type_name"),
  1016. spec_text=payload.get("spec_text"),
  1017. original_price=payload.get("original_price"),
  1018. agreement_price=payload.get("agreement_price"),
  1019. order_num=payload.get("order_num"),
  1020. remark=payload.get("remark"),
  1021. user_id=user.get("user_id"),
  1022. tenant_no=tenant_no,
  1023. )
  1024. return success_response(ps_svc._row_to_dict_channel_agreement_price(obj), message="更新成功")
  1025. except ValueError as e:
  1026. return error_response(400, str(e))
  1027. @router.delete("/channel-agreement-product-prices/{price_id}")
  1028. async def delete_channel_agreement_price(
  1029. price_id: str,
  1030. user: dict = Depends(require_admin_or_super_admin),
  1031. db: Session = Depends(get_db),
  1032. ):
  1033. try:
  1034. tenant_no = _get_tenant_no(db, user)
  1035. ps_svc.soft_delete_channel_agreement_price(db, price_id, tenant_no=tenant_no)
  1036. return success_response(None, message="删除成功")
  1037. except ValueError as e:
  1038. return error_response(400, str(e))
  1039. # ---------------------------------------------------------------------------
  1040. # 6. 体检机构(主表 + 开票信息/项目及价格子表)
  1041. # ---------------------------------------------------------------------------
  1042. class CheckupInstitutionCreateBody(BaseModel):
  1043. model_config = ConfigDict(populate_by_name=True)
  1044. institution_name: str = Field(..., alias="institutionName")
  1045. institution_address: Optional[str] = Field(None, alias="institutionAddress")
  1046. contact_name: Optional[str] = Field(None, alias="contactName")
  1047. contact_phone: Optional[str] = Field(None, alias="contactPhone")
  1048. institution_logo: Optional[str] = Field(None, alias="institutionLogo")
  1049. status: Optional[str] = Field("1", alias="status")
  1050. order_num: Optional[int] = Field(0, alias="orderNum")
  1051. remark: Optional[str] = None
  1052. class CheckupInstitutionUpdateBody(BaseModel):
  1053. model_config = ConfigDict(populate_by_name=True)
  1054. institution_name: Optional[str] = Field(None, alias="institutionName")
  1055. institution_address: Optional[str] = Field(None, alias="institutionAddress")
  1056. contact_name: Optional[str] = Field(None, alias="contactName")
  1057. contact_phone: Optional[str] = Field(None, alias="contactPhone")
  1058. institution_logo: Optional[str] = Field(None, alias="institutionLogo")
  1059. status: Optional[str] = Field(None, alias="status")
  1060. order_num: Optional[int] = Field(None, alias="orderNum")
  1061. remark: Optional[str] = None
  1062. @router.get("/checkup-institutions")
  1063. async def list_checkup_institutions(
  1064. page: int = Query(1, ge=1),
  1065. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  1066. keyword: Optional[str] = None,
  1067. status: Optional[str] = None,
  1068. user: dict = Depends(require_admin_or_super_admin),
  1069. db: Session = Depends(get_db),
  1070. ):
  1071. tenant_no = _get_tenant_no(db, user)
  1072. items, total = ps_svc.list_checkup_institutions(
  1073. db,
  1074. page=page,
  1075. page_size=pageSize,
  1076. keyword=keyword,
  1077. status=status,
  1078. tenant_no=tenant_no,
  1079. user_info=user,
  1080. )
  1081. tp = ps_svc.total_pages(total, pageSize)
  1082. return success_response(
  1083. {"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}
  1084. )
  1085. @router.post("/checkup-institutions")
  1086. async def create_checkup_institution(
  1087. body: CheckupInstitutionCreateBody,
  1088. user: dict = Depends(require_admin_or_super_admin),
  1089. db: Session = Depends(get_db),
  1090. ):
  1091. try:
  1092. tenant_no = _get_tenant_no(db, user)
  1093. obj = ps_svc.create_checkup_institution(
  1094. db,
  1095. institution_name=body.institution_name,
  1096. institution_address=body.institution_address,
  1097. contact_name=body.contact_name,
  1098. contact_phone=body.contact_phone,
  1099. institution_logo=body.institution_logo,
  1100. status=body.status,
  1101. tenant_no=tenant_no,
  1102. order_num=body.order_num,
  1103. remark=body.remark,
  1104. user_id=user.get("user_id"),
  1105. )
  1106. return success_response(ps_svc._row_to_dict_checkup_institution(obj), message="创建成功")
  1107. except ValueError as e:
  1108. return error_response(400, str(e))
  1109. @router.put("/checkup-institutions/{row_id}")
  1110. async def update_checkup_institution(
  1111. row_id: str,
  1112. body: CheckupInstitutionUpdateBody,
  1113. user: dict = Depends(require_admin_or_super_admin),
  1114. db: Session = Depends(get_db),
  1115. ):
  1116. try:
  1117. tenant_no = _get_tenant_no(db, user)
  1118. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  1119. obj = ps_svc.update_checkup_institution(
  1120. db,
  1121. row_id,
  1122. institution_name=payload.get("institution_name"),
  1123. institution_address=payload.get("institution_address"),
  1124. contact_name=payload.get("contact_name"),
  1125. contact_phone=payload.get("contact_phone"),
  1126. institution_logo=payload.get("institution_logo"),
  1127. status=payload.get("status"),
  1128. order_num=payload.get("order_num"),
  1129. remark=payload.get("remark"),
  1130. user_id=user.get("user_id"),
  1131. tenant_no=tenant_no,
  1132. )
  1133. return success_response(ps_svc._row_to_dict_checkup_institution(obj), message="更新成功")
  1134. except ValueError as e:
  1135. return error_response(400, str(e))
  1136. @router.delete("/checkup-institutions/{row_id}")
  1137. async def delete_checkup_institution(
  1138. row_id: str,
  1139. user: dict = Depends(require_admin_or_super_admin),
  1140. db: Session = Depends(get_db),
  1141. ):
  1142. try:
  1143. tenant_no = _get_tenant_no(db, user)
  1144. ps_svc.soft_delete_checkup_institution(db, row_id, tenant_no=tenant_no)
  1145. return success_response(None, message="删除成功")
  1146. except ValueError as e:
  1147. return error_response(400, str(e))
  1148. # --- 体检机构开票信息 ---
  1149. class CheckupInstitutionInvoiceCreateBody(BaseModel):
  1150. model_config = ConfigDict(populate_by_name=True)
  1151. company_name: str = Field(..., alias="companyName")
  1152. tax_no: Optional[str] = Field(None, alias="taxNo")
  1153. company_address: Optional[str] = Field(None, alias="companyAddress")
  1154. company_phone: Optional[str] = Field(None, alias="companyPhone")
  1155. bank_name: Optional[str] = Field(None, alias="bankName")
  1156. bank_account: Optional[str] = Field(None, alias="bankAccount")
  1157. order_num: Optional[int] = Field(0, alias="orderNum")
  1158. remark: Optional[str] = None
  1159. class CheckupInstitutionInvoiceUpdateBody(BaseModel):
  1160. model_config = ConfigDict(populate_by_name=True)
  1161. company_name: Optional[str] = Field(None, alias="companyName")
  1162. tax_no: Optional[str] = Field(None, alias="taxNo")
  1163. company_address: Optional[str] = Field(None, alias="companyAddress")
  1164. company_phone: Optional[str] = Field(None, alias="companyPhone")
  1165. bank_name: Optional[str] = Field(None, alias="bankName")
  1166. bank_account: Optional[str] = Field(None, alias="bankAccount")
  1167. order_num: Optional[int] = Field(None, alias="orderNum")
  1168. remark: Optional[str] = None
  1169. @router.get("/checkup-institutions/{institution_id}/invoices")
  1170. async def list_checkup_institution_invoices(
  1171. institution_id: str,
  1172. user: dict = Depends(require_admin_or_super_admin),
  1173. db: Session = Depends(get_db),
  1174. ):
  1175. try:
  1176. tenant_no = _get_tenant_no(db, user)
  1177. items = ps_svc.list_checkup_institution_invoices(db, institution_id, tenant_no=tenant_no, user_info=user)
  1178. return success_response(items)
  1179. except ValueError as e:
  1180. return error_response(400, str(e))
  1181. @router.post("/checkup-institutions/{institution_id}/invoices")
  1182. async def create_checkup_institution_invoice(
  1183. institution_id: str,
  1184. body: CheckupInstitutionInvoiceCreateBody,
  1185. user: dict = Depends(require_admin_or_super_admin),
  1186. db: Session = Depends(get_db),
  1187. ):
  1188. try:
  1189. tenant_no = _get_tenant_no(db, user)
  1190. obj = ps_svc.create_checkup_institution_invoice(
  1191. db,
  1192. checkup_institution_id=institution_id,
  1193. company_name=body.company_name,
  1194. tax_no=body.tax_no,
  1195. company_address=body.company_address,
  1196. company_phone=body.company_phone,
  1197. bank_name=body.bank_name,
  1198. bank_account=body.bank_account,
  1199. tenant_no=tenant_no,
  1200. order_num=body.order_num,
  1201. remark=body.remark,
  1202. user_id=user.get("user_id"),
  1203. )
  1204. return success_response(ps_svc._row_to_dict_checkup_institution_invoice(obj), message="创建成功")
  1205. except ValueError as e:
  1206. return error_response(400, str(e))
  1207. @router.put("/checkup-institution-invoices/{invoice_id}")
  1208. async def update_checkup_institution_invoice(
  1209. invoice_id: str,
  1210. body: CheckupInstitutionInvoiceUpdateBody,
  1211. user: dict = Depends(require_admin_or_super_admin),
  1212. db: Session = Depends(get_db),
  1213. ):
  1214. try:
  1215. tenant_no = _get_tenant_no(db, user)
  1216. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  1217. obj = ps_svc.update_checkup_institution_invoice(
  1218. db,
  1219. invoice_id,
  1220. company_name=payload.get("company_name"),
  1221. tax_no=payload.get("tax_no"),
  1222. company_address=payload.get("company_address"),
  1223. company_phone=payload.get("company_phone"),
  1224. bank_name=payload.get("bank_name"),
  1225. bank_account=payload.get("bank_account"),
  1226. order_num=payload.get("order_num"),
  1227. remark=payload.get("remark"),
  1228. user_id=user.get("user_id"),
  1229. tenant_no=tenant_no,
  1230. )
  1231. return success_response(ps_svc._row_to_dict_checkup_institution_invoice(obj), message="更新成功")
  1232. except ValueError as e:
  1233. return error_response(400, str(e))
  1234. @router.delete("/checkup-institution-invoices/{invoice_id}")
  1235. async def delete_checkup_institution_invoice(
  1236. invoice_id: str,
  1237. user: dict = Depends(require_admin_or_super_admin),
  1238. db: Session = Depends(get_db),
  1239. ):
  1240. try:
  1241. tenant_no = _get_tenant_no(db, user)
  1242. ps_svc.soft_delete_checkup_institution_invoice(db, invoice_id, tenant_no=tenant_no)
  1243. return success_response(None, message="删除成功")
  1244. except ValueError as e:
  1245. return error_response(400, str(e))
  1246. # --- 体检机构项目及价格 ---
  1247. class CheckupInstitutionProjectCreateBody(BaseModel):
  1248. model_config = ConfigDict(populate_by_name=True)
  1249. project_name: str = Field(..., alias="projectName")
  1250. project_fee: Optional[float] = Field(None, alias="projectFee")
  1251. status: Optional[str] = Field("1", alias="status")
  1252. order_num: Optional[int] = Field(0, alias="orderNum")
  1253. remark: Optional[str] = None
  1254. class CheckupInstitutionProjectUpdateBody(BaseModel):
  1255. model_config = ConfigDict(populate_by_name=True)
  1256. project_name: Optional[str] = Field(None, alias="projectName")
  1257. project_fee: Optional[float] = Field(None, alias="projectFee")
  1258. status: Optional[str] = Field(None, alias="status")
  1259. order_num: Optional[int] = Field(None, alias="orderNum")
  1260. remark: Optional[str] = None
  1261. @router.get("/checkup-institutions/{institution_id}/projects")
  1262. async def list_checkup_institution_projects(
  1263. institution_id: str,
  1264. user: dict = Depends(require_admin_or_super_admin),
  1265. db: Session = Depends(get_db),
  1266. ):
  1267. try:
  1268. tenant_no = _get_tenant_no(db, user)
  1269. items = ps_svc.list_checkup_institution_projects(db, institution_id, tenant_no=tenant_no, user_info=user)
  1270. return success_response(items)
  1271. except ValueError as e:
  1272. return error_response(400, str(e))
  1273. @router.post("/checkup-institutions/{institution_id}/projects")
  1274. async def create_checkup_institution_project(
  1275. institution_id: str,
  1276. body: CheckupInstitutionProjectCreateBody,
  1277. user: dict = Depends(require_admin_or_super_admin),
  1278. db: Session = Depends(get_db),
  1279. ):
  1280. try:
  1281. tenant_no = _get_tenant_no(db, user)
  1282. obj = ps_svc.create_checkup_institution_project(
  1283. db,
  1284. checkup_institution_id=institution_id,
  1285. project_name=body.project_name,
  1286. project_fee=body.project_fee,
  1287. status=body.status,
  1288. tenant_no=tenant_no,
  1289. order_num=body.order_num,
  1290. remark=body.remark,
  1291. user_id=user.get("user_id"),
  1292. )
  1293. return success_response(ps_svc._row_to_dict_checkup_institution_project(obj), message="创建成功")
  1294. except ValueError as e:
  1295. return error_response(400, str(e))
  1296. @router.put("/checkup-institution-projects/{project_id}")
  1297. async def update_checkup_institution_project(
  1298. project_id: str,
  1299. body: CheckupInstitutionProjectUpdateBody,
  1300. user: dict = Depends(require_admin_or_super_admin),
  1301. db: Session = Depends(get_db),
  1302. ):
  1303. try:
  1304. tenant_no = _get_tenant_no(db, user)
  1305. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  1306. obj = ps_svc.update_checkup_institution_project(
  1307. db,
  1308. project_id,
  1309. project_name=payload.get("project_name"),
  1310. project_fee=payload.get("project_fee"),
  1311. status=payload.get("status"),
  1312. order_num=payload.get("order_num"),
  1313. remark=payload.get("remark"),
  1314. user_id=user.get("user_id"),
  1315. tenant_no=tenant_no,
  1316. )
  1317. return success_response(ps_svc._row_to_dict_checkup_institution_project(obj), message="更新成功")
  1318. except ValueError as e:
  1319. return error_response(400, str(e))
  1320. @router.delete("/checkup-institution-projects/{project_id}")
  1321. async def delete_checkup_institution_project(
  1322. project_id: str,
  1323. user: dict = Depends(require_admin_or_super_admin),
  1324. db: Session = Depends(get_db),
  1325. ):
  1326. try:
  1327. tenant_no = _get_tenant_no(db, user)
  1328. ps_svc.soft_delete_checkup_institution_project(db, project_id, tenant_no=tenant_no)
  1329. return success_response(None, message="删除成功")
  1330. except ValueError as e:
  1331. return error_response(400, str(e))
  1332. # ---------------------------------------------------------------------------
  1333. # 7. 渠道服务购买订单
  1334. # ---------------------------------------------------------------------------
  1335. class ServiceOrderProductItem(BaseModel):
  1336. model_config = ConfigDict(populate_by_name=True)
  1337. project_code: Optional[str] = Field(None, alias="projectCode")
  1338. project_name: Optional[str] = Field(None, alias="projectName")
  1339. cell_type: Optional[str] = Field(None, alias="cellType")
  1340. spec_text: Optional[str] = Field(None, alias="specText")
  1341. agreement_unit_price: Optional[float] = Field(None, alias="agreementUnitPrice")
  1342. buy_qty: Optional[float] = Field(None, alias="buyQty")
  1343. buy_amount: Optional[float] = Field(None, alias="buyAmount")
  1344. class ServiceOrderCreateBody(BaseModel):
  1345. model_config = ConfigDict(populate_by_name=True)
  1346. channel_id: str = Field(..., alias="channelId")
  1347. order_type: str = Field(..., alias="orderType") # yfk / gmcp
  1348. order_name: Optional[str] = Field(None, alias="orderName")
  1349. dept_name: Optional[str] = Field(None, alias="deptName")
  1350. prepay_amount: Optional[float] = Field(None, alias="prepayAmount")
  1351. channel_agreement_id: Optional[str] = Field(None, alias="channelAgreementId")
  1352. products: Optional[List[ServiceOrderProductItem]] = None
  1353. remark: Optional[str] = None
  1354. class ServiceOrderUpdateBody(BaseModel):
  1355. model_config = ConfigDict(populate_by_name=True)
  1356. dept_name: Optional[str] = Field(None, alias="deptName")
  1357. order_name: Optional[str] = Field(None, alias="orderName")
  1358. remark: Optional[str] = None
  1359. prepay_amount: Optional[float] = Field(None, alias="prepayAmount")
  1360. channel_agreement_id: Optional[str] = Field(None, alias="channelAgreementId")
  1361. products: Optional[List[ServiceOrderProductItem]] = None
  1362. class FinanceSubmitBody(BaseModel):
  1363. model_config = ConfigDict(populate_by_name=True)
  1364. finance_submit_remark: Optional[str] = Field(None, alias="financeSubmitRemark")
  1365. finance_submit_attachments: Optional[str] = Field(None, alias="financeSubmitAttachments")
  1366. class FinanceConfirmBody(BaseModel):
  1367. model_config = ConfigDict(populate_by_name=True)
  1368. finance_remark: Optional[str] = Field(None, alias="financeRemark")
  1369. finance_confirm_attachments: Optional[str] = Field(None, alias="financeConfirmAttachments")
  1370. class RefundSubmitBody(BaseModel):
  1371. model_config = ConfigDict(populate_by_name=True)
  1372. refund_amount: Optional[float] = Field(None, alias="refundAmount")
  1373. refund_remark: Optional[str] = Field(None, alias="refundRemark")
  1374. refund_attachments: Optional[str] = Field(None, alias="refundAttachments")
  1375. class RefundConfirmBody(BaseModel):
  1376. model_config = ConfigDict(populate_by_name=True)
  1377. refund_finance_remark: Optional[str] = Field(None, alias="refundFinanceRemark")
  1378. refund_finance_confirm_attachments: Optional[str] = Field(
  1379. None, alias="refundFinanceConfirmAttachments"
  1380. )
  1381. class TxnRechargeBody(BaseModel):
  1382. model_config = ConfigDict(populate_by_name=True)
  1383. amount: float = Field(..., gt=0, alias="amount")
  1384. remark: Optional[str] = Field(None, alias="remark")
  1385. finance_submit_attachments: Optional[str] = Field(None, alias="financeSubmitAttachments")
  1386. class TxnRefundBody(BaseModel):
  1387. model_config = ConfigDict(populate_by_name=True)
  1388. amount: float = Field(..., gt=0, alias="amount")
  1389. remark: Optional[str] = Field(None, alias="remark")
  1390. attachments: Optional[str] = Field(None, alias="attachments")
  1391. class TxnProductItem(BaseModel):
  1392. model_config = ConfigDict(populate_by_name=True)
  1393. project_code: Optional[str] = Field(None, alias="projectCode")
  1394. project_name: Optional[str] = Field(None, alias="projectName")
  1395. cell_type: Optional[str] = Field(None, alias="cellType")
  1396. spec_text: Optional[str] = Field(None, alias="specText")
  1397. agreement_unit_price: Optional[float] = Field(None, alias="agreementUnitPrice")
  1398. qty: Optional[float] = Field(None, alias="qty")
  1399. amount: Optional[float] = Field(None, alias="amount")
  1400. class TxnPurchaseBody(BaseModel):
  1401. model_config = ConfigDict(populate_by_name=True)
  1402. products: List[TxnProductItem] = Field(..., alias="products")
  1403. remark: Optional[str] = Field(None, alias="remark")
  1404. class TxnUnsubscribeBody(BaseModel):
  1405. model_config = ConfigDict(populate_by_name=True)
  1406. products: List[TxnProductItem] = Field(..., alias="products")
  1407. remark: Optional[str] = Field(None, alias="remark")
  1408. class TxnUpdateBody(BaseModel):
  1409. model_config = ConfigDict(populate_by_name=True)
  1410. amount: Optional[float] = Field(None, gt=0, alias="amount")
  1411. remark: Optional[str] = Field(None, alias="remark")
  1412. finance_submit_attachments: Optional[str] = Field(None, alias="financeSubmitAttachments")
  1413. refund_attachments: Optional[str] = Field(None, alias="refundAttachments")
  1414. products: Optional[List[TxnProductItem]] = Field(None, alias="products")
  1415. class TxnFinanceSubmitBody(BaseModel):
  1416. model_config = ConfigDict(populate_by_name=True)
  1417. finance_submit_remark: Optional[str] = Field(None, alias="financeSubmitRemark")
  1418. finance_submit_attachments: Optional[str] = Field(None, alias="financeSubmitAttachments")
  1419. class TxnFinanceConfirmBody(BaseModel):
  1420. model_config = ConfigDict(populate_by_name=True)
  1421. finance_remark: Optional[str] = Field(None, alias="financeRemark")
  1422. finance_confirm_attachments: Optional[str] = Field(None, alias="financeConfirmAttachments")
  1423. @router.get("/channel-service-orders")
  1424. async def list_channel_service_orders(
  1425. page: int = Query(1, ge=1),
  1426. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  1427. orderType: Optional[str] = Query(None, alias="orderType"),
  1428. orderStatus: Optional[str] = Query(None, alias="orderStatus"),
  1429. channelId: Optional[str] = Query(None, alias="channelId"),
  1430. keyword: Optional[str] = None,
  1431. createTimeStart: Optional[str] = Query(None, alias="createTimeStart"),
  1432. createTimeEnd: Optional[str] = Query(None, alias="createTimeEnd"),
  1433. user: dict = Depends(require_admin_or_super_admin),
  1434. db: Session = Depends(get_db),
  1435. ):
  1436. tenant_no = _get_tenant_no(db, user)
  1437. items, total = ps_svc.list_channel_service_orders(
  1438. db,
  1439. page=page,
  1440. page_size=pageSize,
  1441. order_type=orderType,
  1442. order_status=orderStatus,
  1443. channel_id=channelId,
  1444. keyword=keyword,
  1445. create_time_start=createTimeStart,
  1446. create_time_end=createTimeEnd,
  1447. tenant_no=tenant_no,
  1448. user_info=user,
  1449. )
  1450. tp = ps_svc.total_pages(total, pageSize)
  1451. return success_response(
  1452. {"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}
  1453. )
  1454. @router.get("/channel-service-orders/{order_id}")
  1455. async def get_channel_service_order_detail(
  1456. order_id: str,
  1457. user: dict = Depends(require_admin_or_super_admin),
  1458. db: Session = Depends(get_db),
  1459. ):
  1460. try:
  1461. tenant_no = _get_tenant_no(db, user)
  1462. order = ps_svc.get_service_order(db, order_id, tenant_no=tenant_no)
  1463. if not order:
  1464. return error_response(404, "订单不存在")
  1465. order_dict = ps_svc._row_to_dict_service_order(order, db, tenant_no)
  1466. user_ids = [x for x in (order.create_by, order.finance_submit_by) if x]
  1467. name_map = ps_svc._user_nick_name_map(db, user_ids)
  1468. order_dict["createByName"] = name_map.get(order.create_by or "", "") or (order.create_by or "")
  1469. order_dict["financeSubmitByName"] = name_map.get(order.finance_submit_by or "", "") or (
  1470. order.finance_submit_by or ""
  1471. )
  1472. products = ps_svc.get_service_order_products(db, order_id, tenant_no=tenant_no)
  1473. order_dict["products"] = products
  1474. txns, _ = ps_svc.list_service_order_txns(db, service_order_id=order_id, tenant_no=tenant_no, page_size=200)
  1475. order_dict["transactions"] = txns
  1476. if order.order_type == "gmcp":
  1477. has_lock = ps_svc._gmcp_has_purchase_lock(db, order_id, tenant_no)
  1478. order_dict["hasPurchaseTxn"] = has_lock
  1479. order_dict["agreementEditable"] = ps_svc.gmcp_agreement_editable(db, order, tenant_no)
  1480. else:
  1481. order_dict["hasPurchaseTxn"] = False
  1482. order_dict["agreementEditable"] = order.order_status == "dsx"
  1483. return success_response(order_dict)
  1484. except ValueError as e:
  1485. return error_response(400, str(e))
  1486. @router.post("/channel-service-orders")
  1487. async def create_channel_service_order(
  1488. body: ServiceOrderCreateBody,
  1489. user: dict = Depends(require_admin_or_super_admin),
  1490. db: Session = Depends(get_db),
  1491. ):
  1492. try:
  1493. tenant_no = _get_tenant_no(db, user)
  1494. products_data = None
  1495. if body.products:
  1496. products_data = [p.model_dump(by_alias=False) for p in body.products]
  1497. order = ps_svc.create_channel_service_order(
  1498. db,
  1499. channel_id=body.channel_id,
  1500. order_type=body.order_type,
  1501. order_name=body.order_name,
  1502. dept_name=body.dept_name,
  1503. prepay_amount=body.prepay_amount,
  1504. channel_agreement_id=body.channel_agreement_id,
  1505. products=products_data,
  1506. tenant_no=tenant_no,
  1507. remark=body.remark,
  1508. user_id=user.get("user_id"),
  1509. )
  1510. order_dict = ps_svc._row_to_dict_service_order(order, db, tenant_no)
  1511. order_dict["products"] = ps_svc.get_service_order_products(db, order.id, tenant_no=tenant_no)
  1512. return success_response(order_dict, message="创建成功")
  1513. except ValueError as e:
  1514. return error_response(400, str(e))
  1515. @router.put("/channel-service-orders/{order_id}")
  1516. async def update_channel_service_order(
  1517. order_id: str,
  1518. body: ServiceOrderUpdateBody,
  1519. user: dict = Depends(require_admin_or_super_admin),
  1520. db: Session = Depends(get_db),
  1521. ):
  1522. try:
  1523. tenant_no = _get_tenant_no(db, user)
  1524. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  1525. products_data = None
  1526. if "products" in payload:
  1527. products_data = payload.pop("products")
  1528. if products_data is not None:
  1529. products_data = [
  1530. p.model_dump(by_alias=False) if hasattr(p, "model_dump") else p
  1531. for p in (body.products or [])
  1532. ]
  1533. obj = ps_svc.update_channel_service_order(
  1534. db,
  1535. order_id,
  1536. tenant_no=tenant_no,
  1537. user_id=user.get("user_id"),
  1538. products=products_data,
  1539. **payload,
  1540. )
  1541. return success_response(
  1542. ps_svc._row_to_dict_service_order(obj, db, tenant_no), message="更新成功"
  1543. )
  1544. except ValueError as e:
  1545. return error_response(400, str(e))
  1546. @router.delete("/channel-service-orders/{order_id}")
  1547. async def delete_channel_service_order(
  1548. order_id: str,
  1549. user: dict = Depends(require_admin_or_super_admin),
  1550. db: Session = Depends(get_db),
  1551. ):
  1552. try:
  1553. tenant_no = _get_tenant_no(db, user)
  1554. ps_svc.soft_delete_service_order(db, order_id, tenant_no=tenant_no)
  1555. return success_response(None, message="删除成功")
  1556. except ValueError as e:
  1557. return error_response(400, str(e))
  1558. @router.post("/channel-service-orders/{order_id}/submit-finance-confirm")
  1559. async def submit_service_order_finance_confirm(
  1560. order_id: str,
  1561. body: FinanceSubmitBody,
  1562. user: dict = Depends(require_admin_or_super_admin),
  1563. db: Session = Depends(get_db),
  1564. ):
  1565. try:
  1566. tenant_no = _get_tenant_no(db, user)
  1567. obj = ps_svc.submit_finance_confirm(
  1568. db,
  1569. order_id,
  1570. finance_submit_remark=body.finance_submit_remark,
  1571. finance_submit_attachments=body.finance_submit_attachments,
  1572. tenant_no=tenant_no,
  1573. user_id=user.get("user_id"),
  1574. )
  1575. return success_response(
  1576. ps_svc._row_to_dict_service_order(obj, db, tenant_no), message="已发起财务确认"
  1577. )
  1578. except ValueError as e:
  1579. return error_response(400, str(e))
  1580. @router.post("/channel-service-orders/{order_id}/confirm-receipt")
  1581. async def confirm_service_order_receipt(
  1582. order_id: str,
  1583. body: FinanceConfirmBody,
  1584. user: dict = Depends(require_admin_or_super_admin),
  1585. db: Session = Depends(get_db),
  1586. ):
  1587. try:
  1588. tenant_no = _get_tenant_no(db, user)
  1589. obj = ps_svc.confirm_receipt(
  1590. db,
  1591. order_id,
  1592. finance_remark=body.finance_remark,
  1593. finance_confirm_attachments=body.finance_confirm_attachments,
  1594. tenant_no=tenant_no,
  1595. user_id=user.get("user_id"),
  1596. )
  1597. return success_response(
  1598. ps_svc._row_to_dict_service_order(obj, db, tenant_no), message="确认收款成功"
  1599. )
  1600. except ValueError as e:
  1601. return error_response(400, str(e))
  1602. @router.post("/channel-service-orders/{order_id}/submit-refund")
  1603. async def submit_service_order_refund(
  1604. order_id: str,
  1605. body: RefundSubmitBody,
  1606. user: dict = Depends(require_admin_or_super_admin),
  1607. db: Session = Depends(get_db),
  1608. ):
  1609. try:
  1610. tenant_no = _get_tenant_no(db, user)
  1611. obj = ps_svc.submit_refund(
  1612. db,
  1613. order_id,
  1614. refund_amount=body.refund_amount,
  1615. refund_remark=body.refund_remark,
  1616. refund_attachments=body.refund_attachments,
  1617. tenant_no=tenant_no,
  1618. user_id=user.get("user_id"),
  1619. )
  1620. return success_response(
  1621. ps_svc._row_to_dict_service_order(obj, db, tenant_no), message="提交退款成功"
  1622. )
  1623. except ValueError as e:
  1624. return error_response(400, str(e))
  1625. @router.post("/channel-service-orders/{order_id}/confirm-refund")
  1626. async def confirm_service_order_refund(
  1627. order_id: str,
  1628. body: RefundConfirmBody,
  1629. user: dict = Depends(require_admin_or_super_admin),
  1630. db: Session = Depends(get_db),
  1631. ):
  1632. try:
  1633. tenant_no = _get_tenant_no(db, user)
  1634. obj = ps_svc.confirm_refund(
  1635. db,
  1636. order_id,
  1637. refund_finance_remark=body.refund_finance_remark,
  1638. refund_finance_confirm_attachments=body.refund_finance_confirm_attachments,
  1639. tenant_no=tenant_no,
  1640. user_id=user.get("user_id"),
  1641. )
  1642. return success_response(
  1643. ps_svc._row_to_dict_service_order(obj, db, tenant_no), message="确认退款成功"
  1644. )
  1645. except ValueError as e:
  1646. return error_response(400, str(e))
  1647. @router.put("/channel-service-orders/{order_id}/void")
  1648. async def void_channel_service_order(
  1649. order_id: str,
  1650. user: dict = Depends(require_admin_or_super_admin),
  1651. db: Session = Depends(get_db),
  1652. ):
  1653. try:
  1654. tenant_no = _get_tenant_no(db, user)
  1655. obj = ps_svc.void_service_order(
  1656. db,
  1657. order_id,
  1658. tenant_no=tenant_no,
  1659. user_id=user.get("user_id"),
  1660. )
  1661. return success_response(
  1662. ps_svc._row_to_dict_service_order(obj, db, tenant_no), message="作废成功"
  1663. )
  1664. except ValueError as e:
  1665. return error_response(400, str(e))
  1666. @router.get("/channel-service-order-txns")
  1667. async def list_service_order_txns(
  1668. page: int = Query(1, ge=1),
  1669. pageSize: int = Query(20, ge=1, le=100, alias="pageSize"),
  1670. serviceOrderId: Optional[str] = Query(None, alias="serviceOrderId"),
  1671. channelId: Optional[str] = Query(None, alias="channelId"),
  1672. txnStatus: Optional[str] = Query(None, alias="txnStatus"),
  1673. txnType: Optional[str] = Query(None, alias="txnType"),
  1674. keyword: Optional[str] = None,
  1675. createTimeStart: Optional[str] = Query(None, alias="createTimeStart"),
  1676. createTimeEnd: Optional[str] = Query(None, alias="createTimeEnd"),
  1677. financeDoneOnly: Optional[bool] = Query(False, alias="financeDoneOnly"),
  1678. user: dict = Depends(require_admin_or_super_admin),
  1679. db: Session = Depends(get_db),
  1680. ):
  1681. tenant_no = _get_tenant_no(db, user)
  1682. items, total = ps_svc.list_service_order_txns(
  1683. db,
  1684. service_order_id=serviceOrderId,
  1685. channel_id=channelId,
  1686. txn_status=txnStatus,
  1687. txn_type=txnType,
  1688. keyword=keyword,
  1689. create_time_start=createTimeStart,
  1690. create_time_end=createTimeEnd,
  1691. finance_done_only=bool(financeDoneOnly),
  1692. page=page,
  1693. page_size=pageSize,
  1694. tenant_no=tenant_no,
  1695. )
  1696. tp = ps_svc.total_pages(total, pageSize)
  1697. return success_response(
  1698. {"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}
  1699. )
  1700. @router.get("/channel-service-order-txns/{txn_id}")
  1701. async def get_service_order_txn_detail(
  1702. txn_id: str,
  1703. user: dict = Depends(require_admin_or_super_admin),
  1704. db: Session = Depends(get_db),
  1705. ):
  1706. try:
  1707. tenant_no = _get_tenant_no(db, user)
  1708. txn = ps_svc.get_service_order_txn(db, txn_id, tenant_no=tenant_no)
  1709. if not txn:
  1710. return error_response(404, "流水不存在")
  1711. txn_dict = ps_svc._service_order_txn_to_response_dict(db, txn, tenant_no)
  1712. return success_response(txn_dict)
  1713. except ValueError as e:
  1714. return error_response(400, str(e))
  1715. @router.post("/channel-service-orders/{order_id}/recharge")
  1716. async def create_recharge_txn(
  1717. order_id: str,
  1718. body: TxnRechargeBody,
  1719. user: dict = Depends(require_admin_or_super_admin),
  1720. db: Session = Depends(get_db),
  1721. ):
  1722. try:
  1723. tenant_no = _get_tenant_no(db, user)
  1724. txn = ps_svc.create_recharge_txn(
  1725. db,
  1726. order_id,
  1727. amount=body.amount,
  1728. remark=body.remark,
  1729. finance_submit_attachments=body.finance_submit_attachments,
  1730. tenant_no=tenant_no,
  1731. user_id=user.get("user_id"),
  1732. )
  1733. return success_response(
  1734. ps_svc._row_to_dict_service_order_txn(txn, db, tenant_no), message="充值流水创建成功"
  1735. )
  1736. except ValueError as e:
  1737. return error_response(400, str(e))
  1738. @router.post("/channel-service-orders/{order_id}/refund")
  1739. async def create_refund_txn(
  1740. order_id: str,
  1741. body: TxnRefundBody,
  1742. user: dict = Depends(require_admin_or_super_admin),
  1743. db: Session = Depends(get_db),
  1744. ):
  1745. try:
  1746. tenant_no = _get_tenant_no(db, user)
  1747. txn = ps_svc.create_refund_txn(
  1748. db,
  1749. order_id,
  1750. amount=body.amount,
  1751. remark=body.remark,
  1752. attachments=body.attachments,
  1753. tenant_no=tenant_no,
  1754. user_id=user.get("user_id"),
  1755. )
  1756. return success_response(
  1757. ps_svc._row_to_dict_service_order_txn(txn, db, tenant_no), message="退款流水创建成功"
  1758. )
  1759. except ValueError as e:
  1760. return error_response(400, str(e))
  1761. @router.post("/channel-service-orders/{order_id}/purchase")
  1762. async def create_purchase_txn(
  1763. order_id: str,
  1764. body: TxnPurchaseBody,
  1765. user: dict = Depends(require_admin_or_super_admin),
  1766. db: Session = Depends(get_db),
  1767. ):
  1768. try:
  1769. tenant_no = _get_tenant_no(db, user)
  1770. products_data = [p.model_dump(by_alias=False) for p in body.products]
  1771. txn = ps_svc.create_purchase_txn(
  1772. db,
  1773. order_id,
  1774. products=products_data,
  1775. remark=body.remark,
  1776. tenant_no=tenant_no,
  1777. user_id=user.get("user_id"),
  1778. )
  1779. return success_response(
  1780. ps_svc._row_to_dict_service_order_txn(txn, db, tenant_no), message="购买产品流水创建成功"
  1781. )
  1782. except ValueError as e:
  1783. return error_response(400, str(e))
  1784. @router.post("/channel-service-orders/{order_id}/unsubscribe")
  1785. async def create_unsubscribe_txn(
  1786. order_id: str,
  1787. body: TxnUnsubscribeBody,
  1788. user: dict = Depends(require_admin_or_super_admin),
  1789. db: Session = Depends(get_db),
  1790. ):
  1791. try:
  1792. tenant_no = _get_tenant_no(db, user)
  1793. products_data = [p.model_dump(by_alias=False) for p in body.products]
  1794. txn = ps_svc.create_unsubscribe_txn(
  1795. db,
  1796. order_id,
  1797. products=products_data,
  1798. remark=body.remark,
  1799. tenant_no=tenant_no,
  1800. user_id=user.get("user_id"),
  1801. )
  1802. return success_response(
  1803. ps_svc._row_to_dict_service_order_txn(txn, db, tenant_no), message="退订产品流水创建成功"
  1804. )
  1805. except ValueError as e:
  1806. return error_response(400, str(e))
  1807. @router.put("/channel-service-order-txns/{txn_id}")
  1808. async def update_service_order_txn(
  1809. txn_id: str,
  1810. body: TxnUpdateBody,
  1811. user: dict = Depends(require_admin_or_super_admin),
  1812. db: Session = Depends(get_db),
  1813. ):
  1814. try:
  1815. tenant_no = _get_tenant_no(db, user)
  1816. products_data = None
  1817. if body.products is not None:
  1818. products_data = [p.model_dump(by_alias=False) for p in body.products]
  1819. txn = ps_svc.update_service_order_txn(
  1820. db,
  1821. txn_id,
  1822. amount=body.amount,
  1823. remark=body.remark,
  1824. finance_submit_attachments=body.finance_submit_attachments,
  1825. refund_attachments=body.refund_attachments,
  1826. products=products_data,
  1827. tenant_no=tenant_no,
  1828. user_id=user.get("user_id"),
  1829. )
  1830. return success_response(
  1831. ps_svc._row_to_dict_service_order_txn(txn, db, tenant_no), message="更新成功"
  1832. )
  1833. except ValueError as e:
  1834. return error_response(400, str(e))
  1835. @router.post("/channel-service-order-txns/{txn_id}/submit-finance")
  1836. async def submit_txn_finance(
  1837. txn_id: str,
  1838. body: TxnFinanceSubmitBody,
  1839. user: dict = Depends(require_admin_or_super_admin),
  1840. db: Session = Depends(get_db),
  1841. ):
  1842. try:
  1843. tenant_no = _get_tenant_no(db, user)
  1844. txn = ps_svc.submit_txn_finance(
  1845. db,
  1846. txn_id,
  1847. finance_submit_remark=body.finance_submit_remark,
  1848. finance_submit_attachments=body.finance_submit_attachments,
  1849. tenant_no=tenant_no,
  1850. user_id=user.get("user_id"),
  1851. )
  1852. return success_response(
  1853. ps_svc._row_to_dict_service_order_txn(txn, db, tenant_no), message="已发起财务确认"
  1854. )
  1855. except ValueError as e:
  1856. return error_response(400, str(e))
  1857. @router.post("/channel-service-order-txns/{txn_id}/confirm")
  1858. async def confirm_txn(
  1859. txn_id: str,
  1860. body: TxnFinanceConfirmBody,
  1861. user: dict = Depends(require_admin_or_super_admin),
  1862. db: Session = Depends(get_db),
  1863. ):
  1864. try:
  1865. tenant_no = _get_tenant_no(db, user)
  1866. txn = ps_svc.confirm_txn(
  1867. db,
  1868. txn_id,
  1869. finance_remark=body.finance_remark,
  1870. finance_confirm_attachments=body.finance_confirm_attachments,
  1871. tenant_no=tenant_no,
  1872. user_id=user.get("user_id"),
  1873. )
  1874. return success_response(
  1875. ps_svc._service_order_txn_to_response_dict(db, txn, tenant_no),
  1876. message="财务确认成功",
  1877. )
  1878. except ValueError as e:
  1879. return error_response(400, str(e))
  1880. @router.put("/channel-service-order-txns/{txn_id}/void")
  1881. async def void_txn(
  1882. txn_id: str,
  1883. user: dict = Depends(require_admin_or_super_admin),
  1884. db: Session = Depends(get_db),
  1885. ):
  1886. try:
  1887. tenant_no = _get_tenant_no(db, user)
  1888. txn = ps_svc.void_txn(
  1889. db,
  1890. txn_id,
  1891. tenant_no=tenant_no,
  1892. user_id=user.get("user_id"),
  1893. )
  1894. return success_response(
  1895. ps_svc._service_order_txn_to_response_dict(db, txn, tenant_no),
  1896. message="作废成功",
  1897. )
  1898. except ValueError as e:
  1899. return error_response(400, str(e))
  1900. @router.get("/channels/{channel_id}/account")
  1901. async def get_channel_account_info(
  1902. channel_id: str,
  1903. user: dict = Depends(require_admin_or_super_admin),
  1904. db: Session = Depends(get_db),
  1905. ):
  1906. try:
  1907. tenant_no = _get_tenant_no(db, user)
  1908. account = ps_svc.get_channel_account(db, channel_id, tenant_no=tenant_no)
  1909. if not account:
  1910. return success_response({"channelId": channel_id, "balanceAmount": 0.0})
  1911. return success_response(ps_svc._row_to_dict_channel_account(account))
  1912. except ValueError as e:
  1913. return error_response(400, str(e))
  1914. @router.get("/channels/{channel_id}/product-quotas")
  1915. async def list_channel_product_quotas(
  1916. channel_id: str,
  1917. user: dict = Depends(require_admin_or_super_admin),
  1918. db: Session = Depends(get_db),
  1919. ):
  1920. try:
  1921. tenant_no = _get_tenant_no(db, user)
  1922. items = ps_svc.list_channel_product_quotas(db, channel_id, tenant_no=tenant_no)
  1923. return success_response(items)
  1924. except ValueError as e:
  1925. return error_response(400, str(e))
  1926. # --- 体检机构项目套餐 ---
  1927. class CheckupPackageItemBody(BaseModel):
  1928. model_config = ConfigDict(populate_by_name=True)
  1929. checkup_institution_project_id: str = Field(..., alias="checkupInstitutionProjectId")
  1930. project_name: Optional[str] = Field(None, alias="projectName")
  1931. project_fee: Optional[float] = Field(None, alias="projectFee")
  1932. order_num: Optional[int] = Field(None, alias="orderNum")
  1933. remark: Optional[str] = None
  1934. class CheckupInstitutionPackageCreateBody(BaseModel):
  1935. model_config = ConfigDict(populate_by_name=True)
  1936. package_name: str = Field(..., alias="packageName")
  1937. package_code: str = Field(..., alias="packageCode")
  1938. status: Optional[str] = Field("1", alias="status")
  1939. order_num: Optional[int] = Field(0, alias="orderNum")
  1940. remark: Optional[str] = None
  1941. items: List[CheckupPackageItemBody] = Field(default_factory=list)
  1942. class CheckupInstitutionPackageUpdateBody(BaseModel):
  1943. model_config = ConfigDict(populate_by_name=True)
  1944. package_name: Optional[str] = Field(None, alias="packageName")
  1945. package_code: Optional[str] = Field(None, alias="packageCode")
  1946. status: Optional[str] = Field(None, alias="status")
  1947. order_num: Optional[int] = Field(None, alias="orderNum")
  1948. remark: Optional[str] = None
  1949. items: Optional[List[CheckupPackageItemBody]] = None
  1950. def _package_items_to_crud(items) -> List[dict]:
  1951. out = []
  1952. for x in items:
  1953. if isinstance(x, dict):
  1954. out.append(
  1955. {
  1956. "checkup_institution_project_id": x.get("checkup_institution_project_id")
  1957. or x.get("checkupInstitutionProjectId"),
  1958. "project_name": x.get("project_name") or x.get("projectName"),
  1959. "project_fee": x.get("project_fee") if x.get("project_fee") is not None else x.get("projectFee"),
  1960. "order_num": x.get("order_num") if x.get("order_num") is not None else x.get("orderNum"),
  1961. "remark": x.get("remark"),
  1962. }
  1963. )
  1964. else:
  1965. out.append(
  1966. {
  1967. "checkup_institution_project_id": x.checkup_institution_project_id,
  1968. "project_name": x.project_name,
  1969. "project_fee": x.project_fee,
  1970. "order_num": x.order_num,
  1971. "remark": x.remark,
  1972. }
  1973. )
  1974. return out
  1975. @router.get("/checkup-institutions/{institution_id}/packages")
  1976. async def list_checkup_institution_packages(
  1977. institution_id: str,
  1978. user: dict = Depends(require_admin_or_super_admin),
  1979. db: Session = Depends(get_db),
  1980. ):
  1981. try:
  1982. tenant_no = _get_tenant_no(db, user)
  1983. items = ps_svc.list_checkup_institution_packages(db, institution_id, tenant_no=tenant_no, user_info=user)
  1984. return success_response(items)
  1985. except ValueError as e:
  1986. return error_response(400, str(e))
  1987. @router.post("/checkup-institutions/{institution_id}/packages")
  1988. async def create_checkup_institution_package(
  1989. institution_id: str,
  1990. body: CheckupInstitutionPackageCreateBody,
  1991. user: dict = Depends(require_admin_or_super_admin),
  1992. db: Session = Depends(get_db),
  1993. ):
  1994. try:
  1995. tenant_no = _get_tenant_no(db, user)
  1996. detail = ps_svc.create_checkup_institution_package(
  1997. db,
  1998. checkup_institution_id=institution_id,
  1999. package_name=body.package_name,
  2000. package_code=body.package_code,
  2001. status=body.status,
  2002. items=_package_items_to_crud(body.items),
  2003. tenant_no=tenant_no,
  2004. order_num=body.order_num,
  2005. remark=body.remark,
  2006. user_id=user.get("user_id"),
  2007. )
  2008. return success_response(detail, message="创建成功")
  2009. except ValueError as e:
  2010. return error_response(400, str(e))
  2011. @router.get("/checkup-institution-packages/{package_id}")
  2012. async def get_checkup_institution_package_detail(
  2013. package_id: str,
  2014. user: dict = Depends(require_admin_or_super_admin),
  2015. db: Session = Depends(get_db),
  2016. ):
  2017. try:
  2018. tenant_no = _get_tenant_no(db, user)
  2019. detail = ps_svc.get_checkup_institution_package_detail(db, package_id, tenant_no=tenant_no)
  2020. if not detail:
  2021. return error_response(404, "记录不存在")
  2022. return success_response(detail)
  2023. except ValueError as e:
  2024. return error_response(400, str(e))
  2025. @router.put("/checkup-institution-packages/{package_id}")
  2026. async def update_checkup_institution_package(
  2027. package_id: str,
  2028. body: CheckupInstitutionPackageUpdateBody,
  2029. user: dict = Depends(require_admin_or_super_admin),
  2030. db: Session = Depends(get_db),
  2031. ):
  2032. try:
  2033. tenant_no = _get_tenant_no(db, user)
  2034. payload = body.model_dump(exclude_unset=True, by_alias=False)
  2035. items_raw = payload.pop("items", None)
  2036. items = _package_items_to_crud(items_raw) if items_raw is not None else None
  2037. detail = ps_svc.update_checkup_institution_package(
  2038. db,
  2039. package_id,
  2040. package_name=payload.get("package_name"),
  2041. package_code=payload.get("package_code"),
  2042. status=payload.get("status"),
  2043. items=items,
  2044. order_num=payload.get("order_num"),
  2045. remark=payload.get("remark"),
  2046. user_id=user.get("user_id"),
  2047. tenant_no=tenant_no,
  2048. )
  2049. return success_response(detail, message="更新成功")
  2050. except ValueError as e:
  2051. return error_response(400, str(e))
  2052. @router.delete("/checkup-institution-packages/{package_id}")
  2053. async def delete_checkup_institution_package(
  2054. package_id: str,
  2055. user: dict = Depends(require_admin_or_super_admin),
  2056. db: Session = Depends(get_db),
  2057. ):
  2058. try:
  2059. tenant_no = _get_tenant_no(db, user)
  2060. ps_svc.soft_delete_checkup_institution_package(db, package_id, tenant_no=tenant_no)
  2061. return success_response(None, message="删除成功")
  2062. except ValueError as e:
  2063. return error_response(400, str(e))