| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547 |
- """OA 审批管理接口(钉钉交互)"""
- from datetime import datetime
- from typing import Any, Dict, List, Optional
- from fastapi import APIRouter, Depends, Query
- from pydantic import BaseModel, ConfigDict, Field
- from sqlalchemy.orm import Session
- from app.core.database import get_db
- from app.core.menu_guard import require_admin_or_super_admin
- from app.core.response import error_response, success_response
- from app.core.utils import parse_datetime
- from app.crud import oa_approval as oa_crud
- from app.crud import oa_instance_draft as oid_crud
- from app.crud import oa_receive_account as ora_crud
- from app.models.models import SysUser
- from app.services.oa_receive_account import normalize_account_fields
- router = APIRouter(prefix="/web/admin/oa-approval", tags=["OA审批"])
- OA_START_HISTORY_ALL_PERM = "oa:start:history:all"
- OA_DONE_BANK_RECEIPT_PERM = "oa:done:bank-receipt"
- def _get_tenant_no(db: Session, user: dict) -> str:
- tenant = (user.get("tenant_no") or "").strip()
- if tenant:
- return tenant
- uid = user.get("user_id")
- if uid:
- row = db.query(SysUser).filter(SysUser.id == uid, SysUser.del_flag == 0).first()
- if row and row.tenant_no:
- return row.tenant_no
- return "zkxw"
- # ---------------------------------------------------------------------------
- # 收款账户
- # ---------------------------------------------------------------------------
- class OaReceiveAccountBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- account_type: str = Field(..., alias="accountType")
- account_name: str = Field(..., alias="accountName")
- account_no: str = Field(..., alias="accountNo")
- bank_name: Optional[str] = Field(None, alias="bankName")
- bank_province: Optional[str] = Field(None, alias="bankProvince")
- bank_city: Optional[str] = Field(None, alias="bankCity")
- bank_branch: Optional[str] = Field(None, alias="bankBranch")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- class OaReceiveAccountBatchDeleteBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- ids: List[str] = Field(default_factory=list)
- @router.get("/receive-accounts")
- async def list_receive_accounts(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100),
- keyword: Optional[str] = None,
- accountType: Optional[str] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = ora_crud.list_accounts(
- db,
- tenant_no=tenant_no,
- keyword=keyword,
- account_type=accountType,
- page=page,
- page_size=pageSize,
- )
- return success_response({"list": items, "total": total})
- @router.get("/receive-accounts/options")
- async def list_receive_account_options(
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- return success_response(
- {
- "list": ora_crud.list_options(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- )
- }
- )
- @router.post("/receive-accounts/batch-delete")
- async def batch_delete_receive_accounts(
- body: OaReceiveAccountBatchDeleteBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- count = ora_crud.soft_delete_accounts(
- db, body.ids, tenant_no=tenant_no, user_id=user.get("user_id")
- )
- return success_response({"deletedCount": count}, message="删除成功")
- @router.post("/receive-accounts")
- async def create_receive_account(
- body: OaReceiveAccountBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- ok, err, fields = normalize_account_fields(body.model_dump(by_alias=True))
- if not ok:
- return error_response(400, err or "参数无效")
- tenant_no = _get_tenant_no(db, user)
- obj = ora_crud.create_account(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- dept_id=user.get("dept_id") or "",
- fields=fields,
- )
- return success_response(obj, message="创建成功")
- @router.get("/receive-accounts/{account_id}")
- async def get_receive_account(
- account_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- obj = ora_crud.get_account(db, account_id, tenant_no)
- if not obj:
- return error_response(404, "收款账户不存在")
- return success_response(obj)
- @router.put("/receive-accounts/{account_id}")
- async def update_receive_account(
- account_id: str,
- body: OaReceiveAccountBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- ok, err, fields = normalize_account_fields(body.model_dump(by_alias=True))
- if not ok:
- return error_response(400, err or "参数无效")
- tenant_no = _get_tenant_no(db, user)
- obj = ora_crud.update_account(
- db,
- account_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- fields=fields,
- )
- if not obj:
- return error_response(404, "收款账户不存在")
- return success_response(obj, message="更新成功")
- @router.delete("/receive-accounts/{account_id}")
- async def delete_receive_account(
- account_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- if not ora_crud.soft_delete_account(
- db, account_id, tenant_no=tenant_no, user_id=user.get("user_id")
- ):
- return error_response(404, "收款账户不存在")
- return success_response(None, message="删除成功")
- # ---------------------------------------------------------------------------
- # 发起审批草稿箱(oa_instance_draft)
- # ---------------------------------------------------------------------------
- class OaInstanceDraftBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- app_id: str = Field(..., alias="appId")
- form_data: Optional[Dict[str, Any]] = Field(None, alias="formData")
- title: Optional[str] = None
- initiator_dept_id: Optional[str] = Field(None, alias="initiatorDeptId")
- initiator_select_by_node: Optional[Dict[str, List[str]]] = Field(
- None, alias="initiatorSelectByNode"
- )
- class OaInstanceDraftUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- form_data: Optional[Dict[str, Any]] = Field(None, alias="formData")
- title: Optional[str] = None
- initiator_dept_id: Optional[str] = Field(None, alias="initiatorDeptId")
- initiator_select_by_node: Optional[Dict[str, List[str]]] = Field(
- None, alias="initiatorSelectByNode"
- )
- class OaInstanceDraftBatchDeleteBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- ids: List[str] = Field(default_factory=list)
- class OaInstanceDraftSubmitBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- form_data: Optional[Dict[str, Any]] = Field(None, alias="formData")
- title: Optional[str] = None
- initiator_dept_id: Optional[str] = Field(None, alias="initiatorDeptId")
- initiator_select_by_node: Optional[Dict[str, List[str]]] = Field(
- None, alias="initiatorSelectByNode"
- )
- def _parse_draft_range_end(raw: Optional[str]) -> Optional[datetime]:
- """截止日期:仅日期时扩到当天 23:59:59。"""
- if not raw or not str(raw).strip():
- return None
- s = str(raw).strip()
- dt = parse_datetime(s)
- if dt is None and len(s) == 10:
- try:
- dt = datetime.strptime(s, "%Y-%m-%d")
- except ValueError:
- return None
- if dt is None:
- return None
- if len(s) <= 10:
- return dt.replace(hour=23, minute=59, second=59)
- return dt
- def _parse_draft_range_start(raw: Optional[str]) -> Optional[datetime]:
- if not raw or not str(raw).strip():
- return None
- s = str(raw).strip()
- dt = parse_datetime(s)
- if dt is None and len(s) == 10:
- try:
- return datetime.strptime(s, "%Y-%m-%d")
- except ValueError:
- return None
- return dt
- @router.get("/drafts/count")
- async def oa_instance_draft_count(
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- n = oid_crud.count_drafts(
- db,
- tenant_no=tenant_no,
- owner_user_id=user.get("user_id") or "",
- )
- return success_response({"count": int(n)})
- @router.get("/drafts")
- async def list_oa_instance_drafts(
- page: int = Query(1, ge=1),
- pageSize: int = Query(20, ge=1, le=100),
- keyword: Optional[str] = None,
- groupId: Optional[str] = None,
- updateTimeFrom: Optional[str] = None,
- updateTimeTo: Optional[str] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = oid_crud.list_drafts(
- db,
- tenant_no=tenant_no,
- owner_user_id=user.get("user_id") or "",
- keyword=keyword,
- group_id=groupId,
- update_time_from=_parse_draft_range_start(updateTimeFrom),
- update_time_to=_parse_draft_range_end(updateTimeTo),
- page=page,
- page_size=pageSize,
- )
- return success_response({"list": items, "total": total})
- @router.post("/drafts")
- async def create_oa_instance_draft(
- body: OaInstanceDraftBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oid_crud.create_draft(
- db,
- tenant_no=tenant_no,
- owner_user_id=user.get("user_id") or "",
- app_id=body.app_id,
- form_data=body.form_data,
- initiator_select_by_node=body.initiator_select_by_node,
- initiator_dept_id=body.initiator_dept_id,
- title=body.title,
- create_dept_id=user.get("dept_id") or "",
- )
- return success_response(data, message="已存入草稿箱")
- except ValueError as e:
- return error_response(400, str(e))
- @router.post("/drafts/batch-delete")
- async def batch_delete_oa_instance_drafts(
- body: OaInstanceDraftBatchDeleteBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- n = oid_crud.batch_soft_delete_drafts(
- db,
- body.ids,
- tenant_no=tenant_no,
- owner_user_id=user.get("user_id") or "",
- )
- return success_response({"deleted": n}, message="删除成功")
- except oid_crud.DraftAccessError as e:
- return error_response(403, str(e))
- except ValueError as e:
- return error_response(400, str(e))
- @router.get("/drafts/{draft_id}")
- async def get_oa_instance_draft(
- draft_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oid_crud.get_draft_detail(
- db,
- draft_id,
- tenant_no=tenant_no,
- owner_user_id=user.get("user_id") or "",
- )
- return success_response(data)
- except oid_crud.DraftAccessError as e:
- return error_response(404, str(e))
- @router.put("/drafts/{draft_id}")
- async def update_oa_instance_draft(
- draft_id: str,
- body: OaInstanceDraftUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oid_crud.update_draft(
- db,
- draft_id,
- tenant_no=tenant_no,
- owner_user_id=user.get("user_id") or "",
- form_data=body.form_data,
- initiator_select_by_node=body.initiator_select_by_node,
- initiator_dept_id=body.initiator_dept_id,
- title=body.title,
- )
- return success_response(data, message="草稿已保存")
- except oid_crud.DraftAccessError as e:
- return error_response(404, str(e))
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/drafts/{draft_id}")
- async def delete_oa_instance_draft(
- draft_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- oid_crud.soft_delete_draft(
- db,
- draft_id,
- tenant_no=tenant_no,
- owner_user_id=user.get("user_id") or "",
- )
- return success_response(None, message="删除成功")
- except oid_crud.DraftAccessError as e:
- return error_response(404, str(e))
- @router.post("/drafts/{draft_id}/submit")
- async def submit_oa_instance_draft(
- draft_id: str,
- body: Optional[OaInstanceDraftSubmitBody] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError
- payload = body or OaInstanceDraftSubmitBody()
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oid_crud.submit_draft(
- db,
- draft_id,
- tenant_no=tenant_no,
- owner_user_id=user.get("user_id") or "",
- form_data=payload.form_data,
- initiator_select_by_node=payload.initiator_select_by_node,
- initiator_dept_id=payload.initiator_dept_id,
- title=payload.title,
- )
- return success_response(data, message="发起成功")
- except oid_crud.DraftAccessError as e:
- return error_response(404, str(e))
- except OaEngineError as e:
- return error_response(400, str(e))
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 分组
- # ---------------------------------------------------------------------------
- class OaGroupCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- group_name: str = Field(..., alias="groupName")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- class OaGroupUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- group_name: Optional[str] = Field(None, alias="groupName")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/groups")
- async def list_oa_groups(
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- return success_response({"list": oa_crud.list_groups(db, tenant_no=tenant_no)})
- @router.post("/groups")
- async def create_oa_group(
- body: OaGroupCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = oa_crud.create_group(
- db,
- tenant_no=tenant_no,
- group_name=body.group_name,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(oa_crud._group_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/groups/{group_id}")
- async def update_oa_group(
- group_id: str,
- body: OaGroupUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- payload = body.model_dump(exclude_unset=True, by_alias=False)
- obj = oa_crud.update_group(
- db,
- group_id,
- tenant_no=tenant_no,
- group_name=payload.get("group_name"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(oa_crud._group_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/groups/{group_id}")
- async def delete_oa_group(
- group_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- oa_crud.delete_group(db, group_id, tenant_no=tenant_no, user_id=user.get("user_id"))
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 应用 / 草稿
- # ---------------------------------------------------------------------------
- class OaAppCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- app_name: str = Field(..., alias="appName")
- group_id: Optional[str] = Field(None, alias="groupId")
- icon_url: Optional[str] = Field(None, alias="iconUrl")
- description: Optional[str] = None
- initiator_scope: Optional[str] = Field("all", alias="initiatorScope")
- initiator_user_ids: Optional[List[str]] = Field(None, alias="initiatorUserIds")
- admin_scope: Optional[str] = Field("all_oa_admin", alias="adminScope")
- admin_user_ids: Optional[List[str]] = Field(None, alias="adminUserIds")
- status: Optional[str] = "1"
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- class OaAppUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- app_name: Optional[str] = Field(None, alias="appName")
- group_id: Optional[str] = Field(None, alias="groupId")
- icon_url: Optional[str] = Field(None, alias="iconUrl")
- description: Optional[str] = None
- initiator_scope: Optional[str] = Field(None, alias="initiatorScope")
- initiator_user_ids: Optional[List[str]] = Field(None, alias="initiatorUserIds")
- admin_scope: Optional[str] = Field(None, alias="adminScope")
- admin_user_ids: Optional[List[str]] = Field(None, alias="adminUserIds")
- status: Optional[str] = None
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- class OaDraftSaveBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- basic: Optional[Dict[str, Any]] = None
- form_schema_json: Optional[Any] = Field(None, alias="formSchemaJson")
- flow_tree_json: Optional[Any] = Field(None, alias="flowTreeJson")
- advanced_settings_json: Optional[Any] = Field(None, alias="advancedSettingsJson")
- @router.get("/apps")
- async def list_oa_apps(
- groupId: Optional[str] = None,
- keyword: Optional[str] = None,
- status: Optional[str] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items = oa_crud.list_apps(
- db, tenant_no=tenant_no, group_id=groupId, keyword=keyword, status=status
- )
- return success_response({"list": items})
- @router.post("/apps")
- async def create_oa_app(
- body: OaAppCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = oa_crud.create_app(
- db,
- tenant_no=tenant_no,
- app_name=body.app_name,
- group_id=body.group_id,
- icon_url=body.icon_url,
- description=body.description,
- initiator_scope=body.initiator_scope or "all",
- initiator_user_ids=body.initiator_user_ids,
- admin_scope=body.admin_scope or "all_oa_admin",
- admin_user_ids=body.admin_user_ids,
- status=body.status or "1",
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- group_name = ""
- if obj.group_id:
- g = oa_crud.get_group(db, obj.group_id, tenant_no)
- group_name = g.group_name if g else ""
- return success_response(
- oa_crud._app_to_dict(obj, group_name=group_name, has_draft=True),
- message="创建成功",
- )
- except ValueError as e:
- return error_response(400, str(e))
- @router.post("/apps/{app_id}/copy")
- async def copy_oa_app(
- app_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = oa_crud.copy_app(
- db, app_id, tenant_no=tenant_no, user_id=user.get("user_id")
- )
- group_name = ""
- if obj.group_id:
- g = oa_crud.get_group(db, obj.group_id, tenant_no)
- group_name = g.group_name if g else ""
- return success_response(
- oa_crud._app_to_dict(obj, group_name=group_name, has_draft=True),
- message="复制成功",
- )
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/apps/{app_id}")
- async def update_oa_app(
- app_id: str,
- body: OaAppUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- payload = body.model_dump(exclude_unset=True, by_alias=False)
- obj = oa_crud.update_app(db, app_id, tenant_no=tenant_no, user_id=user.get("user_id"), **payload)
- group_name = ""
- if obj.group_id:
- g = oa_crud.get_group(db, obj.group_id, tenant_no)
- group_name = g.group_name if g else ""
- return success_response(oa_crud._app_to_dict(obj, group_name=group_name), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/apps/{app_id}")
- async def delete_oa_app(
- app_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- oa_crud.delete_app(db, app_id, tenant_no=tenant_no, user_id=user.get("user_id"))
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.get("/apps/{app_id}/draft")
- async def get_oa_app_draft(
- app_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oa_crud.get_or_create_draft(
- db, app_id, tenant_no=tenant_no, user_id=user.get("user_id")
- )
- return success_response(data)
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/apps/{app_id}/draft")
- async def save_oa_app_draft(
- app_id: str,
- body: OaDraftSaveBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oa_crud.save_draft(
- db,
- app_id,
- tenant_no=tenant_no,
- basic=body.basic,
- form_schema_json=body.form_schema_json,
- flow_tree_json=body.flow_tree_json,
- advanced_settings_json=body.advanced_settings_json,
- user_id=user.get("user_id"),
- )
- return success_response(data, message="草稿已保存")
- except ValueError as e:
- return error_response(400, str(e))
- @router.post("/apps/{app_id}/publish")
- async def publish_oa_app(
- app_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_publish import publish_app
- tenant_no = _get_tenant_no(db, user)
- data, errors = publish_app(
- db, app_id, tenant_no=tenant_no, user_id=user.get("user_id")
- )
- if errors:
- return error_response(400, "发布校验未通过", data={"errors": errors})
- return success_response(data, message="发布成功")
- # ---------------------------------------------------------------------------
- # 运行时:模板 / 实例 / 任务
- # ---------------------------------------------------------------------------
- class OaStartInstanceBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- app_id: str = Field(..., alias="appId")
- form_data: Optional[Dict[str, Any]] = Field(None, alias="formData")
- title: Optional[str] = None
- initiator_dept_id: Optional[str] = Field(None, alias="initiatorDeptId")
- initiator_select_by_node: Optional[Dict[str, List[str]]] = Field(
- None, alias="initiatorSelectByNode"
- )
- related_instance_ids: Optional[List[str]] = Field(None, alias="relatedInstanceIds")
- source_instance_id: Optional[str] = Field(None, alias="sourceInstanceId")
- class OaFlowPreviewBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- form_data: Optional[Dict[str, Any]] = Field(None, alias="formData")
- initiator_select_by_node: Optional[Dict[str, List[str]]] = Field(
- None, alias="initiatorSelectByNode"
- )
- class OaTaskActionBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- comment: Optional[str] = None
- class OaCcReadBatchBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- ids: List[str] = Field(default_factory=list)
- class OaAddSignBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- user_ids: List[str] = Field(default_factory=list, alias="userIds")
- comment: Optional[str] = None
- class OaTransferBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- target_user_id: str = Field(..., alias="targetUserId")
- comment: Optional[str] = None
- class OaCommentCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- content: Optional[str] = ""
- attachment_refs: Optional[List[Dict[str, Any]]] = Field(
- None, alias="attachmentRefs"
- )
- mention_user_ids: Optional[List[str]] = Field(None, alias="mentionUserIds")
- class OaBankReceiptItemBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- file_url: str = Field(..., alias="fileUrl")
- file_name: Optional[str] = Field(None, alias="fileName")
- file_type: Optional[str] = Field(None, alias="fileType")
- file_size: Optional[int] = Field(None, alias="fileSize")
- class OaBankReceiptReplaceBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- items: List[OaBankReceiptItemBody] = Field(default_factory=list)
- class OaResubmitBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- form_data: Optional[Dict[str, Any]] = Field(None, alias="formData")
- initiator_select_by_node: Optional[Dict[str, List[str]]] = Field(
- None, alias="initiatorSelectByNode"
- )
- @router.get("/templates")
- async def list_oa_templates(
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_engine
- tenant_no = _get_tenant_no(db, user)
- items = oa_engine.list_templates(db, tenant_no=tenant_no, user_id=user.get("user_id") or "")
- return success_response({"list": items})
- @router.get("/templates/{app_id}")
- async def get_oa_template(
- app_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, get_template_detail
- try:
- tenant_no = _get_tenant_no(db, user)
- data = get_template_detail(
- db,
- tenant_no=tenant_no,
- app_id=app_id,
- user_id=user.get("user_id") or "",
- )
- return success_response(data)
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/templates/{app_id}/flow-preview")
- async def preview_oa_template_flow(
- app_id: str,
- body: OaFlowPreviewBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- """发起前预览审批链(条件齐后解析最终路径与审批人)。"""
- from app.services.oa_engine import OaEngineError, preview_start_flow
- try:
- tenant_no = _get_tenant_no(db, user)
- uid = user.get("user_id") or ""
- dept_id = None
- if uid:
- row = db.query(SysUser).filter(SysUser.id == uid, SysUser.del_flag == 0).first()
- if row:
- dept_id = row.dept_id
- data = preview_start_flow(
- db,
- tenant_no=tenant_no,
- app_id=app_id,
- initiator_user_id=uid,
- initiator_dept_id=dept_id,
- initiator_select_by_node=body.initiator_select_by_node,
- form_data=body.form_data,
- )
- return success_response(data)
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/templates/{app_id}/flow-preview")
- async def preview_oa_template_flow_get(
- app_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- """兼容旧 GET:无 formData,含条件分支时多为待确定占位。"""
- from app.services.oa_engine import OaEngineError, preview_start_flow
- try:
- tenant_no = _get_tenant_no(db, user)
- uid = user.get("user_id") or ""
- dept_id = None
- if uid:
- row = db.query(SysUser).filter(SysUser.id == uid, SysUser.del_flag == 0).first()
- if row:
- dept_id = row.dept_id
- data = preview_start_flow(
- db,
- tenant_no=tenant_no,
- app_id=app_id,
- initiator_user_id=uid,
- initiator_dept_id=dept_id,
- initiator_select_by_node=None,
- form_data=None,
- )
- return success_response(data)
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/instances")
- async def start_oa_instance(
- body: OaStartInstanceBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, start_instance
- try:
- tenant_no = _get_tenant_no(db, user)
- data = start_instance(
- db,
- tenant_no=tenant_no,
- app_id=body.app_id,
- initiator_user_id=user.get("user_id") or "",
- initiator_dept_id=body.initiator_dept_id,
- form_data=body.form_data,
- title=body.title,
- initiator_select_by_node=body.initiator_select_by_node,
- related_instance_ids=body.related_instance_ids,
- source_instance_id=body.source_instance_id,
- )
- return success_response(data, message="发起成功")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/instances/history")
- async def list_oa_instance_history(
- appId: str = Query(..., description="流程应用 ID"),
- instanceId: str = Query(""),
- instanceStatus: str = Query(""),
- initiatorName: str = Query(""),
- applyTimeFrom: str = Query(""),
- applyTimeTo: str = Query(""),
- finishTimeFrom: str = Query(""),
- finishTimeTo: str = Query(""),
- initiatorDeptId: str = Query(""),
- page: int = Query(1),
- pageSize: int = Query(20, alias="pageSize"),
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.core.menu_guard import user_has_menu_perm
- from app.services.oa_engine import OaEngineError, list_instance_history
- try:
- tenant_no = _get_tenant_no(db, user)
- can_view_all = user_has_menu_perm(db, user, OA_START_HISTORY_ALL_PERM)
- data = list_instance_history(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- can_view_all=can_view_all,
- app_id=appId,
- instance_id=instanceId or None,
- initiator_name=initiatorName or None,
- apply_time_from=applyTimeFrom or None,
- apply_time_to=applyTimeTo or None,
- finish_time_from=finishTimeFrom or None,
- finish_time_to=finishTimeTo or None,
- instance_status=instanceStatus or None,
- initiator_dept_id=initiatorDeptId or None,
- page=page,
- page_size=pageSize,
- )
- return success_response(data)
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/instances/history/export")
- async def export_oa_instance_history(
- appId: str = Query(..., description="流程应用 ID"),
- instanceId: str = Query(""),
- instanceIds: str = Query("", description="逗号分隔的实例 ID,用于导出勾选"),
- instanceStatus: str = Query(""),
- initiatorName: str = Query(""),
- applyTimeFrom: str = Query(""),
- applyTimeTo: str = Query(""),
- finishTimeFrom: str = Query(""),
- finishTimeTo: str = Query(""),
- initiatorDeptId: str = Query(""),
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.core.menu_guard import user_has_menu_perm
- from app.services.oa_engine import OaEngineError, export_instance_history
- try:
- tenant_no = _get_tenant_no(db, user)
- can_view_all = user_has_menu_perm(db, user, OA_START_HISTORY_ALL_PERM)
- id_list = [x.strip() for x in (instanceIds or "").split(",") if x.strip()]
- data = export_instance_history(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- can_view_all=can_view_all,
- app_id=appId,
- instance_id=instanceId or None,
- instance_ids=id_list or None,
- initiator_name=initiatorName or None,
- apply_time_from=applyTimeFrom or None,
- apply_time_to=applyTimeTo or None,
- finish_time_from=finishTimeFrom or None,
- finish_time_to=finishTimeTo or None,
- instance_status=instanceStatus or None,
- initiator_dept_id=initiatorDeptId or None,
- )
- return success_response(data)
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/instances/{instance_id}")
- async def get_oa_instance(
- instance_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, get_instance_detail
- try:
- tenant_no = _get_tenant_no(db, user)
- return success_response(
- get_instance_detail(
- db,
- instance_id,
- tenant_no=tenant_no,
- viewer_user_id=user.get("user_id") or "",
- )
- )
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/instances/{instance_id}/form-attachments")
- async def list_oa_instance_form_attachments(
- instance_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_instance_comment as oa_comment
- from app.services.oa_engine import OaEngineError
- try:
- tenant_no = _get_tenant_no(db, user)
- items = oa_comment.list_form_attachments(
- db, instance_id=instance_id, tenant_no=tenant_no
- )
- return success_response({"items": items})
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/instances/{instance_id}/bank-receipts")
- async def list_oa_bank_receipts(
- instance_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.core.menu_guard import user_has_menu_perm
- from app.services import oa_bank_receipt as br
- if not user_has_menu_perm(db, user, OA_DONE_BANK_RECEIPT_PERM):
- return error_response(403, "无权上传银行回执单")
- try:
- tenant_no = _get_tenant_no(db, user)
- items = br.list_bank_receipts(db, instance_id=instance_id, tenant_no=tenant_no)
- return success_response({"items": items})
- except br.OaBankReceiptError as e:
- return error_response(400, str(e))
- @router.put("/instances/{instance_id}/bank-receipts")
- async def replace_oa_bank_receipts(
- instance_id: str,
- body: OaBankReceiptReplaceBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.core.menu_guard import user_has_menu_perm
- from app.services import oa_bank_receipt as br
- if not user_has_menu_perm(db, user, OA_DONE_BANK_RECEIPT_PERM):
- return error_response(403, "无权上传银行回执单")
- try:
- tenant_no = _get_tenant_no(db, user)
- items = br.replace_bank_receipts(
- db,
- instance_id=instance_id,
- tenant_no=tenant_no,
- items=[it.model_dump(by_alias=True) for it in (body.items or [])],
- user_id=user.get("user_id") or "",
- dept_id=user.get("dept_id") or "",
- )
- db.commit()
- return success_response({"items": items})
- except br.OaBankReceiptError as e:
- db.rollback()
- return error_response(400, str(e))
- except Exception:
- db.rollback()
- raise
- @router.get("/instances/{instance_id}/comments")
- async def list_oa_instance_comments(
- instance_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_instance_comment as oa_comment
- from app.services.oa_engine import OaEngineError
- try:
- tenant_no = _get_tenant_no(db, user)
- items = oa_comment.list_comments(
- db,
- instance_id=instance_id,
- tenant_no=tenant_no,
- viewer_user_id=user.get("user_id") or "",
- )
- return success_response({"list": items})
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/instances/{instance_id}/comments")
- async def create_oa_instance_comment(
- instance_id: str,
- body: OaCommentCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_instance_comment as oa_comment
- from app.services.oa_engine import OaEngineError
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oa_comment.create_comment(
- db,
- instance_id=instance_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- dept_id=user.get("dept_id") or "",
- content=body.content,
- attachment_refs=body.attachment_refs,
- mention_user_ids=body.mention_user_ids,
- )
- return success_response(data)
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/instances/{instance_id}/comments/{comment_id}/revoke")
- async def revoke_oa_instance_comment(
- instance_id: str,
- comment_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_instance_comment as oa_comment
- from app.services.oa_engine import OaEngineError
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oa_comment.revoke_comment(
- db,
- instance_id=instance_id,
- comment_id=comment_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- )
- return success_response(data)
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/related-candidates")
- async def list_related_candidates(
- keyword: str = Query(""),
- tab: str = Query("mine"),
- page: int = Query(1),
- pageSize: int = Query(20, alias="pageSize"),
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_related_approval as rel
- from app.services.oa_engine import OaEngineError
- tenant_no = _get_tenant_no(db, user)
- try:
- data = rel.list_related_candidates(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- tab=tab,
- keyword=keyword,
- page=page,
- page_size=pageSize,
- )
- except OaEngineError as e:
- return error_response(400, str(e))
- return success_response(data)
- @router.get("/related-summaries")
- async def get_related_summaries(
- ids: str = Query("", description="comma-separated instance ids"),
- mode: str = Query("picker"),
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_related_approval as rel
- from app.services.oa_engine import OaEngineError
- tenant_no = _get_tenant_no(db, user)
- id_list = [x.strip() for x in (ids or "").split(",") if x.strip()]
- try:
- items = rel.build_related_summaries(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- ids=id_list,
- mode=mode,
- )
- except OaEngineError as e:
- return error_response(400, str(e))
- return success_response({"list": items})
- @router.get("/tasks/pending/count")
- async def oa_pending_task_count(
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, count_pending_tasks
- try:
- tenant_no = _get_tenant_no(db, user)
- n = count_pending_tasks(
- db, tenant_no=tenant_no, user_id=user.get("user_id") or ""
- )
- return success_response({"count": int(n)})
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/tasks/cc/unread-count")
- async def oa_cc_unread_count(
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, count_cc_unread
- try:
- tenant_no = _get_tenant_no(db, user)
- n = count_cc_unread(
- db, tenant_no=tenant_no, user_id=user.get("user_id") or ""
- )
- return success_response({"count": int(n)})
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/mentions/unread-count")
- async def oa_mention_unread_count(
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_instance_comment as oa_comment
- from app.services.oa_engine import OaEngineError
- try:
- tenant_no = _get_tenant_no(db, user)
- n = oa_comment.count_mention_unread(
- db, tenant_no=tenant_no, user_id=user.get("user_id") or ""
- )
- return success_response({"count": int(n)})
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/mentions/{instance_id}/read")
- async def oa_mention_mark_read(
- instance_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services import oa_instance_comment as oa_comment
- from app.services.oa_engine import OaEngineError
- try:
- tenant_no = _get_tenant_no(db, user)
- data = oa_comment.mark_mention_instance_read(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- instance_id=instance_id,
- )
- return success_response(data, message="已标记已读")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/tasks/cc/read-batch")
- async def oa_cc_read_batch(
- body: OaCcReadBatchBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, mark_cc_tasks_read_batch
- try:
- tenant_no = _get_tenant_no(db, user)
- data = mark_cc_tasks_read_batch(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- ids=body.ids or [],
- )
- return success_response(data, message="已标记已读")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/tasks/{task_id}/read")
- async def oa_task_mark_read(
- task_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, mark_cc_task_read
- try:
- tenant_no = _get_tenant_no(db, user)
- data = mark_cc_task_read(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- task_id=task_id,
- )
- return success_response(data, message="已标记已读")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.get("/tasks/{kind}")
- async def list_oa_tasks(
- kind: str,
- readStatus: Optional[str] = Query(None, alias="readStatus"),
- appName: Optional[str] = Query(None, alias="appName"),
- initiatorName: Optional[str] = Query(None, alias="initiatorName"),
- keyword: Optional[str] = Query(None, alias="keyword"),
- applyTimeFrom: Optional[str] = Query(None, alias="applyTimeFrom"),
- applyTimeTo: Optional[str] = Query(None, alias="applyTimeTo"),
- instanceStatus: Optional[str] = Query(None, alias="instanceStatus"),
- page: int = Query(1, ge=1),
- pageSize: int = Query(20, ge=1, le=100),
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, list_tasks
- try:
- tenant_no = _get_tenant_no(db, user)
- data = list_tasks(
- db,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- kind=kind,
- read_status=readStatus,
- app_name=appName,
- initiator_name=initiatorName,
- keyword=keyword,
- apply_time_from=applyTimeFrom,
- apply_time_to=applyTimeTo,
- instance_status=instanceStatus,
- page=page,
- page_size=pageSize,
- )
- return success_response({"list": data.get("list") or [], "total": int(data.get("total") or 0)})
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/tasks/{task_id}/approve")
- async def approve_oa_task(
- task_id: str,
- body: Optional[OaTaskActionBody] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, complete_task
- try:
- tenant_no = _get_tenant_no(db, user)
- data = complete_task(
- db,
- task_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- action="approve",
- comment=(body.comment if body else None),
- )
- return success_response(data, message="已同意")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/tasks/{task_id}/reject")
- async def reject_oa_task(
- task_id: str,
- body: Optional[OaTaskActionBody] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, complete_task
- try:
- tenant_no = _get_tenant_no(db, user)
- data = complete_task(
- db,
- task_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- action="reject",
- comment=(body.comment if body else None),
- )
- return success_response(data, message="已拒绝")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/tasks/{task_id}/return")
- async def return_oa_task(
- task_id: str,
- body: Optional[OaTaskActionBody] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, return_task
- try:
- tenant_no = _get_tenant_no(db, user)
- data = return_task(
- db,
- task_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- comment=(body.comment if body else None),
- )
- return success_response(data, message="已打回")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/instances/{instance_id}/resubmit")
- async def resubmit_oa_instance(
- instance_id: str,
- body: Optional[OaResubmitBody] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, resubmit_instance
- try:
- tenant_no = _get_tenant_no(db, user)
- data = resubmit_instance(
- db,
- instance_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- form_data=(body.form_data if body else None),
- initiator_select_by_node=(body.initiator_select_by_node if body else None),
- )
- return success_response(data, message="已再次提交")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/tasks/{task_id}/add-sign")
- async def add_sign_oa_task(
- task_id: str,
- body: OaAddSignBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, add_sign
- try:
- tenant_no = _get_tenant_no(db, user)
- data = add_sign(
- db,
- task_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- user_ids=body.user_ids,
- comment=body.comment,
- )
- return success_response(data, message="加签成功")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/tasks/{task_id}/transfer")
- async def transfer_oa_task(
- task_id: str,
- body: OaTransferBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, transfer_task
- try:
- tenant_no = _get_tenant_no(db, user)
- data = transfer_task(
- db,
- task_id,
- tenant_no=tenant_no,
- user_id=user.get("user_id") or "",
- target_user_id=body.target_user_id,
- comment=body.comment,
- )
- return success_response(data, message="已转交")
- except OaEngineError as e:
- return error_response(400, str(e))
- @router.post("/instances/{instance_id}/withdraw")
- async def withdraw_oa_instance(
- instance_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- from app.services.oa_engine import OaEngineError, withdraw_instance
- try:
- tenant_no = _get_tenant_no(db, user)
- data = withdraw_instance(
- db, instance_id, tenant_no=tenant_no, user_id=user.get("user_id") or ""
- )
- return success_response(data, message="已撤销")
- except OaEngineError as e:
- return error_response(400, str(e))
|