1323 lines
43 KiB
Python
1323 lines
43 KiB
Python
"""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"
|
||
|
||
|
||
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)})
|
||
|
||
|
||
@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 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(""),
|
||
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,
|
||
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(""),
|
||
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,
|
||
)
|
||
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))
|
||
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.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))
|