| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032 |
- """基础信息管理 API(客户、样本类型、采集地区/医院、协议号池、样本编码池)"""
- from typing import Any, List, Optional
- from fastapi import APIRouter, Depends, Query
- from pydantic import BaseModel, ConfigDict, Field
- from sqlalchemy import or_
- from sqlalchemy.orm import Session
- from app.core.data_scope import apply_data_scope
- from app.core.database import get_db
- from app.core.menu_guard import require_admin_or_super_admin, require_menu_code
- from app.core.response import error_response, success_response
- from app.crud import cell_sample_type as cell_type_svc
- from app.crud import collection_region as region_svc
- from app.crud import collection_protocol as collection_protocol_svc
- from app.crud import collection_protocol_no as protocol_no_svc
- from app.crud import customer as customer_svc
- from app.crud import sample_code as sample_code_svc
- from app.models.models import (
- BizCellSampleType,
- BizCollectionProtocolNo,
- BizCollectionRegion,
- BizCustomer,
- BizSampleCode,
- SysUser,
- )
- router = APIRouter(prefix="/web/admin", tags=["基础信息管理"])
- def _get_tenant_no(db: Session, user: dict) -> str:
- user_id = user.get("user_id")
- if not user_id:
- raise PermissionError("用户身份信息缺失,无法确定租户")
- sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
- if not sys_user or not sys_user.tenant_no:
- raise PermissionError("用户租户信息缺失")
- return sys_user.tenant_no
- # ---------------------------------------------------------------------------
- # 客户
- # ---------------------------------------------------------------------------
- class CustomerCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- customer_name: str = Field(..., alias="customerName")
- sex: Optional[str] = Field("2", alias="sex")
- id_card_no: Optional[str] = Field(None, alias="idCardNo")
- age: Optional[int] = None
- phone_no: Optional[str] = Field(None, alias="phoneNo")
- customer_tags: Optional[str] = Field(None, alias="customerTags")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class CustomerUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- customer_name: Optional[str] = Field(None, alias="customerName")
- sex: Optional[str] = None
- id_card_no: Optional[str] = Field(None, alias="idCardNo")
- age: Optional[int] = None
- phone_no: Optional[str] = Field(None, alias="phoneNo")
- customer_tags: Optional[str] = Field(None, alias="customerTags")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- def _normalize_date_bound(value: str, *, end_of_day: bool = False) -> str:
- val = (value or "").strip()
- if len(val) == 10:
- return f"{val} 23:59:59" if end_of_day else f"{val} 00:00:00"
- return val
- @router.get("/customers")
- async def list_customers(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- createTimeStart: Optional[str] = Query(None, alias="createTimeStart"),
- createTimeEnd: Optional[str] = Query(None, alias="createTimeEnd"),
- user: dict = Depends(require_menu_code("biz:customer:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- query = db.query(BizCustomer).filter(
- BizCustomer.del_flag == 0,
- BizCustomer.tenant_no == tenant_no,
- )
- if keyword:
- kw = keyword.strip()
- if kw:
- like = f"%{kw}%"
- query = query.filter(
- or_(
- BizCustomer.customer_name.like(like),
- BizCustomer.phone_no.like(like),
- BizCustomer.id_card_no.like(like),
- BizCustomer.customer_tags.like(like),
- )
- )
- if createTimeStart:
- query = query.filter(
- BizCustomer.create_time >= _normalize_date_bound(createTimeStart)
- )
- if createTimeEnd:
- query = query.filter(
- BizCustomer.create_time <= _normalize_date_bound(createTimeEnd, end_of_day=True)
- )
- query = apply_data_scope(query, BizCustomer, user, db, tenant_no)
- total = query.count()
- items = query.order_by(BizCustomer.create_time.desc()).offset((page - 1) * pageSize).limit(pageSize).all()
- result_list = [customer_svc._row_to_dict(r) for r in items]
- tp = customer_svc.total_pages(total, pageSize)
- return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/customers")
- async def create_customer(
- body: CustomerCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = customer_svc.create_customer(
- db,
- customer_name=body.customer_name,
- sex=body.sex or "2",
- id_card_no=body.id_card_no,
- age=body.age,
- phone_no=body.phone_no,
- customer_tags=body.customer_tags,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(customer_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/customers/{customer_id}")
- async def update_customer(
- customer_id: str,
- body: CustomerUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = customer_svc.update_customer(
- db,
- customer_id,
- customer_name=payload.get("customer_name"),
- sex=payload.get("sex"),
- id_card_no=payload.get("id_card_no"),
- age=payload.get("age"),
- phone_no=payload.get("phone_no"),
- customer_tags=payload.get("customer_tags"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- tenant_no=tenant_no,
- )
- return success_response(customer_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/customers/{customer_id}")
- async def delete_customer(
- customer_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- customer_svc.soft_delete_customer(db, customer_id, tenant_no=tenant_no)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 细胞样本类型
- # ---------------------------------------------------------------------------
- class CellSampleTypeCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- type_code: str = Field(..., alias="typeCode")
- type_name: str = Field(..., alias="typeName")
- type_logo: Optional[str] = Field(None, alias="typeLogo")
- storage_years: Optional[int] = Field(None, alias="storageYears")
- storage_price: Optional[float] = Field(None, alias="storagePrice")
- sample_code_filter: Optional[str] = Field(None, alias="sampleCodeFilter")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class CellSampleTypeUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- type_code: Optional[str] = Field(None, alias="typeCode")
- type_name: Optional[str] = Field(None, alias="typeName")
- type_logo: Optional[str] = Field(None, alias="typeLogo")
- storage_years: Optional[int] = Field(None, alias="storageYears")
- storage_price: Optional[float] = Field(None, alias="storagePrice")
- sample_code_filter: Optional[str] = Field(None, alias="sampleCodeFilter")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/cell-sample-types")
- async def list_cell_sample_types(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("biz:cellSampleType:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- query = db.query(BizCellSampleType).filter(
- BizCellSampleType.del_flag == 0,
- BizCellSampleType.tenant_no == tenant_no,
- )
- if keyword:
- kw = keyword.strip()
- if kw:
- like = f"%{kw}%"
- query = query.filter(
- or_(
- BizCellSampleType.type_code.like(like),
- BizCellSampleType.type_name.like(like),
- )
- )
- query = apply_data_scope(query, BizCellSampleType, user, db, tenant_no)
- total = query.count()
- items = query.order_by(BizCellSampleType.order_num.asc(), BizCellSampleType.create_time.desc()).offset((page - 1) * pageSize).limit(pageSize).all()
- result_list = [cell_type_svc._row_to_dict(r) for r in items]
- tp = cell_type_svc.total_pages(total, pageSize)
- return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/cell-sample-types")
- async def create_cell_sample_type(
- body: CellSampleTypeCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = cell_type_svc.create_cell_sample_type(
- db,
- type_code=body.type_code,
- type_name=body.type_name,
- type_logo=body.type_logo,
- storage_years=body.storage_years,
- storage_price=body.storage_price,
- sample_code_filter=body.sample_code_filter,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(cell_type_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/cell-sample-types/{row_id}")
- async def update_cell_sample_type(
- row_id: str,
- body: CellSampleTypeUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = cell_type_svc.update_cell_sample_type(
- db,
- row_id,
- type_code=payload.get("type_code"),
- type_name=payload.get("type_name"),
- type_logo=payload.get("type_logo"),
- storage_years=payload.get("storage_years"),
- storage_price=payload.get("storage_price"),
- sample_code_filter=payload.get("sample_code_filter"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- tenant_no=tenant_no,
- )
- return success_response(cell_type_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/cell-sample-types/{row_id}")
- async def delete_cell_sample_type(
- row_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- cell_type_svc.soft_delete_cell_sample_type(db, row_id, tenant_no=tenant_no)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 采集地区
- # ---------------------------------------------------------------------------
- class CollectionRegionCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- region_name: str = Field(..., alias="regionName")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class CollectionRegionUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- region_name: Optional[str] = Field(None, alias="regionName")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/collection-regions")
- async def list_collection_regions(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("biz:collectionRegion:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- query = db.query(BizCollectionRegion).filter(
- BizCollectionRegion.del_flag == 0,
- BizCollectionRegion.tenant_no == tenant_no,
- )
- if keyword:
- query = query.filter(BizCollectionRegion.region_name.like(f"%{keyword}%"))
- query = apply_data_scope(query, BizCollectionRegion, user, db, tenant_no)
- total = query.count()
- items = query.order_by(BizCollectionRegion.create_time.desc()).offset((page - 1) * pageSize).limit(pageSize).all()
- region_ids = [r.id for r in items]
- hospital_map = region_svc._hospital_names_by_region_ids(db, region_ids)
- result_list = [
- region_svc._region_to_dict(r, hospital_names=hospital_map.get(r.id, [])) for r in items
- ]
- tp = region_svc.total_pages(total, pageSize)
- return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/collection-regions")
- async def create_collection_region(
- body: CollectionRegionCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = region_svc.create_collection_region(
- db,
- region_name=body.region_name,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(region_svc._region_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/collection-regions/{region_id}")
- async def update_collection_region(
- region_id: str,
- body: CollectionRegionUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = region_svc.update_collection_region(
- db,
- region_id,
- region_name=payload.get("region_name"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- tenant_no=tenant_no,
- )
- return success_response(region_svc._region_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/collection-regions/{region_id}")
- async def delete_collection_region(
- region_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- region_svc.soft_delete_collection_region(db, region_id, tenant_no=tenant_no)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 采集地区 — 下属医院
- # ---------------------------------------------------------------------------
- class HospitalCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- hospital_name: str = Field(..., alias="hospitalName")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class HospitalUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- hospital_name: Optional[str] = Field(None, alias="hospitalName")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/collection-hospitals/search")
- async def search_collection_hospitals(
- keyword: str = Query(..., min_length=1),
- limit: int = Query(20, ge=1, le=50),
- user: dict = Depends(require_menu_code("biz:collectionRegion:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items = region_svc.search_hospitals_by_keyword(db, keyword=keyword, tenant_no=tenant_no, limit=limit)
- return success_response({"list": items})
- @router.get("/collection-regions/{region_id}/hospitals")
- async def list_hospitals(
- region_id: str,
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("biz:collectionRegion:list")),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- items, total = region_svc.list_hospitals(
- db, region_id=region_id, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no
- )
- tp = region_svc.total_pages(total, pageSize)
- return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- except ValueError as e:
- return error_response(400, str(e))
- @router.post("/collection-regions/{region_id}/hospitals")
- async def create_hospital(
- region_id: str,
- body: HospitalCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = region_svc.create_hospital(
- db,
- region_id=region_id,
- hospital_name=body.hospital_name,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(region_svc._hospital_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/collection-regions/{region_id}/hospitals/{hospital_id}")
- async def update_hospital(
- region_id: str,
- hospital_id: str,
- body: HospitalUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- h = region_svc.get_hospital(db, hospital_id, tenant_no=tenant_no)
- if not h or h.region_id != region_id:
- return error_response(400, "医院不存在")
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = region_svc.update_hospital(
- db,
- hospital_id,
- hospital_name=payload.get("hospital_name"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- tenant_no=tenant_no,
- )
- return success_response(region_svc._hospital_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/collection-regions/{region_id}/hospitals/{hospital_id}")
- async def delete_hospital(
- region_id: str,
- hospital_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- h = region_svc.get_hospital(db, hospital_id, tenant_no=tenant_no)
- if not h or h.region_id != region_id:
- return error_response(400, "医院不存在")
- region_svc.soft_delete_hospital(db, hospital_id, tenant_no=tenant_no)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 采集协议号池
- # ---------------------------------------------------------------------------
- class ProtocolNoCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- protocol_no: str = Field(..., alias="protocolNo")
- issued: int = 0
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- issued_user_id: Optional[str] = Field(None, alias="issuedUserId")
- issued_user_name: Optional[str] = Field(None, alias="issuedUserName")
- issued_time: Optional[str] = Field(None, alias="issuedTime")
- class ProtocolNoUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- protocol_no: Optional[str] = Field(None, alias="protocolNo")
- issued: Optional[int] = None
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- issued_user_id: Optional[str] = Field(None, alias="issuedUserId")
- issued_user_name: Optional[str] = Field(None, alias="issuedUserName")
- issued_time: Optional[str] = Field(None, alias="issuedTime")
- @router.get("/collection-protocol-nos")
- async def list_protocol_nos(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- issued_user_id: Optional[str] = Query(None, alias="issuedUserId"),
- user: dict = Depends(require_menu_code("biz:collectionProtocolNo:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- query = db.query(BizCollectionProtocolNo).filter(
- BizCollectionProtocolNo.del_flag == 0,
- BizCollectionProtocolNo.tenant_no == tenant_no,
- )
- kw = (keyword or "").strip()
- if kw:
- like = f"%{kw}%"
- query = query.filter(
- or_(
- BizCollectionProtocolNo.protocol_no.like(like),
- BizCollectionProtocolNo.remark.like(like),
- )
- )
- uid = (issued_user_id or "").strip()
- if uid:
- query = query.filter(BizCollectionProtocolNo.issued_user_id == uid)
- query = apply_data_scope(query, BizCollectionProtocolNo, user, db, tenant_no)
- total = query.count()
- items = query.order_by(BizCollectionProtocolNo.create_time.desc()).offset((page - 1) * pageSize).limit(pageSize).all()
- result_list = [protocol_no_svc._row_to_dict(r) for r in items]
- protocol_no_svc.enrich_issued_user_names(db, result_list)
- used_nos = collection_protocol_svc.get_protocol_nos_used_in_protocols(
- db,
- tenant_no=tenant_no,
- protocol_nos=[row["protocolNo"] for row in result_list],
- )
- for row in result_list:
- row["usedInProtocol"] = row.get("protocolNo") in used_nos
- tp = protocol_no_svc.total_pages(total, pageSize)
- return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- class ProtocolNoBatchGenerateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- prefix: str = Field("ZKZX", alias="prefix")
- middle: str = Field("34", alias="middle")
- seq_start: int = Field(..., alias="seqStart")
- seq_end: int = Field(..., alias="seqEnd")
- digit_count: int = Field(6, alias="digitCount")
- class ProtocolNoBatchIssueBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- ids: List[str] = Field(..., alias="ids")
- issued_user_id: str = Field(..., alias="issuedUserId")
- issued_user_name: Optional[str] = Field(None, alias="issuedUserName")
- @router.post("/collection-protocol-nos/batch-generate")
- async def batch_generate_protocol_nos(
- body: ProtocolNoBatchGenerateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- result = protocol_no_svc.batch_generate_protocol_nos(
- db,
- prefix=body.prefix,
- middle=body.middle,
- seq_start=body.seq_start,
- seq_end=body.seq_end,
- digit_count=body.digit_count,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- )
- msg = f"成功生成 {result['created']} 条"
- if result['skipped'] > 0:
- msg += f",跳过已存在 {result['skipped']} 条"
- return success_response(result, message=msg)
- except ValueError as e:
- return error_response(400, str(e))
- @router.post("/collection-protocol-nos/batch-issue")
- async def batch_issue_protocol_nos(
- body: ProtocolNoBatchIssueBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- result = protocol_no_svc.batch_issue_protocol_nos(
- db,
- ids=body.ids,
- issued_user_id=body.issued_user_id,
- issued_user_name=body.issued_user_name,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- )
- msg = f"成功下发 {result['issuedCount']} 条"
- if result['alreadyIssued']:
- msg += f",{len(result['alreadyIssued'])} 条已下发被跳过"
- return success_response(result, message=msg)
- except ValueError as e:
- return error_response(400, str(e))
- class ProtocolNoBatchRevokeBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- ids: List[str] = Field(..., alias="ids")
- @router.post("/collection-protocol-nos/batch-revoke")
- async def batch_revoke_protocol_nos(
- body: ProtocolNoBatchRevokeBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- result = protocol_no_svc.batch_revoke_protocol_nos(
- db,
- ids=body.ids,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- )
- msg = f"成功收回 {result['revokedCount']} 条"
- if result["notIssued"]:
- msg += f",{len(result['notIssued'])} 条未下发已跳过"
- return success_response(result, message=msg)
- except ValueError as e:
- return error_response(400, str(e))
- @router.post("/collection-protocol-nos")
- async def create_protocol_no(
- body: ProtocolNoCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = protocol_no_svc.create_protocol_no(
- db,
- protocol_no=body.protocol_no,
- issued=body.issued,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- issued_user_id=body.issued_user_id,
- issued_user_name=body.issued_user_name,
- issued_time=body.issued_time,
- )
- row = protocol_no_svc._row_to_dict(obj)
- protocol_no_svc.enrich_issued_user_names(db, [row])
- return success_response(row, message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/collection-protocol-nos/{row_id}")
- async def update_protocol_no(
- row_id: str,
- body: ProtocolNoUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = protocol_no_svc.update_protocol_no(
- db,
- row_id,
- protocol_no=payload.get("protocol_no"),
- issued=payload.get("issued"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- tenant_no=tenant_no,
- issued_user_id=payload.get("issued_user_id"),
- issued_user_name=payload.get("issued_user_name"),
- issued_time=payload.get("issued_time"),
- )
- return success_response(protocol_no_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/collection-protocol-nos/{row_id}")
- async def delete_protocol_no(
- row_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- protocol_no_svc.soft_delete_protocol_no(db, row_id, tenant_no=tenant_no)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 样本编码池
- # ---------------------------------------------------------------------------
- class SampleCodeCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- sample_code: str = Field(..., alias="sampleCode")
- sample_code_type: Optional[str] = Field(None, alias="sampleCodeType")
- issued: int = 0
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- issued_user_id: Optional[str] = Field(None, alias="issuedUserId")
- issued_user_name: Optional[str] = Field(None, alias="issuedUserName")
- issued_time: Optional[str] = Field(None, alias="issuedTime")
- class SampleCodeUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- sample_code: Optional[str] = Field(None, alias="sampleCode")
- issued: Optional[int] = None
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- issued_user_id: Optional[str] = Field(None, alias="issuedUserId")
- issued_user_name: Optional[str] = Field(None, alias="issuedUserName")
- issued_time: Optional[str] = Field(None, alias="issuedTime")
- class SampleCodeBatchGenerateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- sample_code_type: str = Field(..., alias="sampleCodeType")
- middle: str = Field("34", alias="middle")
- seq_start: int = Field(..., alias="seqStart")
- seq_end: int = Field(..., alias="seqEnd")
- digit_count: int = Field(6, alias="digitCount")
- class SampleCodeBatchIssueBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- ids: List[str] = Field(..., alias="ids")
- issued_user_id: str = Field(..., alias="issuedUserId")
- issued_user_name: Optional[str] = Field(None, alias="issuedUserName")
- @router.post("/sample-codes/batch-generate")
- async def batch_generate_sample_codes(
- body: SampleCodeBatchGenerateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- result = sample_code_svc.batch_generate_sample_codes(
- db,
- sample_code_type=body.sample_code_type,
- middle=body.middle,
- seq_start=body.seq_start,
- seq_end=body.seq_end,
- digit_count=body.digit_count,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- )
- msg = f"成功生成 {result['created']} 条"
- if result["skipped"] > 0:
- msg += f",跳过已存在 {result['skipped']} 条"
- return success_response(result, message=msg)
- except ValueError as e:
- return error_response(400, str(e))
- @router.post("/sample-codes/batch-issue")
- async def batch_issue_sample_codes(
- body: SampleCodeBatchIssueBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- result = sample_code_svc.batch_issue_sample_codes(
- db,
- ids=body.ids,
- issued_user_id=body.issued_user_id,
- issued_user_name=body.issued_user_name,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- )
- msg = f"成功下发 {result['issuedCount']} 条"
- if result["alreadyIssued"]:
- msg += f",{len(result['alreadyIssued'])} 条已下发被跳过"
- return success_response(result, message=msg)
- except ValueError as e:
- return error_response(400, str(e))
- class SampleCodeBatchRevokeBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- ids: List[str] = Field(..., alias="ids")
- @router.post("/sample-codes/batch-revoke")
- async def batch_revoke_sample_codes(
- body: SampleCodeBatchRevokeBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- result = sample_code_svc.batch_revoke_sample_codes(
- db,
- ids=body.ids,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- )
- msg = f"成功收回 {result['revokedCount']} 条"
- if result["notIssued"]:
- msg += f",{len(result['notIssued'])} 条未下发已跳过"
- return success_response(result, message=msg)
- except ValueError as e:
- return error_response(400, str(e))
- @router.get("/sample-codes")
- async def list_sample_codes(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- sample_code_type: Optional[str] = Query(None, alias="sampleCodeType"),
- code_filter: Optional[str] = Query(None, alias="codeFilter"),
- issued_user_id: Optional[str] = Query(None, alias="issuedUserId"),
- user: dict = Depends(require_menu_code("biz:sampleCode:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- query = db.query(BizSampleCode).filter(
- BizSampleCode.del_flag == 0,
- BizSampleCode.tenant_no == tenant_no,
- )
- kw = (keyword or "").strip()
- if kw:
- like = f"%{kw}%"
- query = query.filter(
- or_(
- BizSampleCode.sample_code.like(like),
- BizSampleCode.remark.like(like),
- )
- )
- code_type = (sample_code_type or "").strip()
- if code_type:
- query = query.filter(BizSampleCode.sample_code_type == code_type)
- cf = (code_filter or "").strip()
- if cf:
- query = query.filter(BizSampleCode.sample_code.like(f"%{cf}%"))
- uid = (issued_user_id or "").strip()
- if uid:
- query = query.filter(BizSampleCode.issued_user_id == uid)
- query = apply_data_scope(query, BizSampleCode, user, db, tenant_no)
- total = query.count()
- items = (
- query.order_by(BizSampleCode.create_time.desc(), BizSampleCode.sample_code.desc())
- .offset((page - 1) * pageSize)
- .limit(pageSize)
- .all()
- )
- result_list = [sample_code_svc._row_to_dict(r) for r in items]
- used_codes = collection_protocol_svc.get_sample_codes_used_in_protocol_samples(
- db,
- tenant_no=tenant_no,
- sample_codes=[row["sampleCode"] for row in result_list],
- )
- for row in result_list:
- row["usedInProtocol"] = row.get("sampleCode") in used_codes
- tp = sample_code_svc.total_pages(total, pageSize)
- return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/sample-codes")
- async def create_sample_code(
- body: SampleCodeCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = sample_code_svc.create_sample_code(
- db,
- sample_code=body.sample_code,
- sample_code_type=body.sample_code_type,
- issued=body.issued,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- issued_user_id=body.issued_user_id,
- issued_user_name=body.issued_user_name,
- issued_time=body.issued_time,
- )
- return success_response(sample_code_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/sample-codes/{row_id}")
- async def update_sample_code(
- row_id: str,
- body: SampleCodeUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = sample_code_svc.update_sample_code(
- db,
- row_id,
- sample_code=payload.get("sample_code"),
- issued=payload.get("issued"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- tenant_no=tenant_no,
- issued_user_id=payload.get("issued_user_id"),
- issued_user_name=payload.get("issued_user_name"),
- issued_time=payload.get("issued_time"),
- )
- return success_response(sample_code_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/sample-codes/{row_id}")
- async def delete_sample_code(
- row_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- sample_code_svc.soft_delete_sample_code(db, row_id, tenant_no=tenant_no)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
|