| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684 |
- """运营端管理接口(租户、角色下拉等)"""
- import os
- import uuid
- from typing import Any, List, Optional
- from fastapi import APIRouter, Depends, Query, UploadFile, File, Form
- from pydantic import BaseModel, ConfigDict, Field
- from sqlalchemy import or_
- from sqlalchemy.orm import Session
- from app.core.config import settings
- from app.core.data_scope import apply_data_scope, _get_child_dept_ids
- from app.core.database import get_db
- from app.core.menu_guard import require_admin_or_super_admin, require_menu_code
- from app.core.response import success_response, error_response
- from app.core.storage import storage
- from app.crud import tenant as tenant_svc
- from app.crud import dict_type as dict_type_svc
- from app.crud import dict_data as dict_data_svc
- from app.crud import dept as dept_svc
- from app.crud import site as site_svc
- from app.crud import post as post_svc
- from app.crud import item_unit as item_unit_svc
- from app.crud import express_company as express_company_svc
- from app.crud import menu as menu_svc
- from app.crud import role as role_svc
- from app.crud import user as user_svc
- from app.crud.collection_protocol import _lookup_attachment_by_url
- from app.models.models import BizAttachment, SysDept, SysRole, SysUser
- from app.services.web_auth_roles import (
- _map_role_name_to_web,
- list_user_ids_with_effective_role_name,
- )
- router = APIRouter(prefix="/web/admin", tags=["运营端管理"])
- # ---------------------------------------------------------------------------
- # 角色下拉(用户弹窗等)
- # ---------------------------------------------------------------------------
- @router.get("/roles")
- async def list_roles_for_select(
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- """返回运营端角色下拉选项(label=库中角色名,value=Web 角色码)。"""
- rows: List[SysRole] = (
- db.query(SysRole)
- .filter(SysRole.del_flag == 0, SysRole.status == "1")
- .order_by(SysRole.create_time.desc())
- .all()
- )
- seen: set[str] = set()
- out: List[dict] = []
- for r in rows:
- name = (r.role_name or "").strip()
- if not name:
- continue
- code = _map_role_name_to_web(name)
- key = f"{code}:{name}"
- if key in seen:
- continue
- seen.add(key)
- out.append({"label": name, "value": code})
- if not out:
- out = [
- {"label": "超级管理员", "value": "super_admin"},
- {"label": "运营管理员", "value": "admin"},
- ]
- return success_response(out)
- @router.get("/users/options")
- async def list_users_for_select(
- roleName: Optional[str] = Query(None, alias="roleName"),
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- """返回用户下拉选项(label=昵称,value=用户ID),供流程设计器审批人选择使用。
- 可选 roleName:按有效角色(直接绑定 ∪ 职位继承)精确过滤。
- """
- tenant_no = _user.get("tenant_no", "")
- q = db.query(SysUser).filter(SysUser.del_flag == 0, SysUser.status == "1")
- if tenant_no:
- q = q.filter(SysUser.tenant_no == tenant_no)
- rn = (roleName or "").strip()
- if rn:
- matched_ids = list_user_ids_with_effective_role_name(
- db, tenant_no=tenant_no or settings.DEFAULT_TENANT_NO, role_name=rn
- )
- if not matched_ids:
- return success_response([])
- q = q.filter(SysUser.id.in_(matched_ids))
- rows = q.order_by(SysUser.create_time.desc()).distinct().all()
- out = [{"label": (r.nick_name or r.user_no or ""), "value": r.id} for r in rows]
- return success_response(out)
- @router.get("/roles/options")
- async def list_roles_for_designer(
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- """返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。
- 与 /roles 不同,此端点返回角色 UUID 作为 value(引擎解析审批人需要 UUID)。"""
- tenant_no = _user.get("tenant_no", "")
- q = db.query(SysRole).filter(SysRole.del_flag == 0, SysRole.status == "1")
- if tenant_no:
- q = q.filter(SysRole.tenant_no == tenant_no)
- rows = q.order_by(SysRole.create_time.desc()).all()
- out = [{"label": (r.role_name or "").strip(), "value": r.id} for r in rows if (r.role_name or "").strip()]
- return success_response(out)
- # ---------------------------------------------------------------------------
- # 租户管理
- # ---------------------------------------------------------------------------
- class TenantCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- tenant_no: str = Field(..., alias="tenantNo")
- tenant_name: str = Field(..., alias="tenantName")
- contact_name: Optional[str] = Field(None, alias="contactName")
- contact_phone: Optional[str] = Field(None, alias="contactPhone")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class TenantUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- tenant_no: Optional[str] = Field(None, alias="tenantNo")
- tenant_name: Optional[str] = Field(None, alias="tenantName")
- contact_name: Optional[str] = Field(None, alias="contactName")
- contact_phone: Optional[str] = Field(None, alias="contactPhone")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/tenants")
- async def list_tenants(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- items, total = tenant_svc.list_tenants(db, page=page, page_size=pageSize, keyword=keyword, user_info=_user)
- total_pages = tenant_svc.total_pages(total, pageSize)
- return success_response(
- {
- "list": items,
- "total": total,
- "page": page,
- "pageSize": pageSize,
- "totalPages": total_pages,
- }
- )
- @router.post("/tenants")
- async def create_tenant(
- body: TenantCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- obj = tenant_svc.create_tenant(
- db,
- tenant_no=body.tenant_no,
- tenant_name=body.tenant_name,
- contact_name=body.contact_name,
- contact_phone=body.contact_phone,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(tenant_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/tenants/{tenant_id}")
- async def update_tenant(
- tenant_id: str,
- body: TenantUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = tenant_svc.update_tenant(
- db,
- tenant_id,
- tenant_no=payload.get("tenant_no"),
- tenant_name=payload.get("tenant_name"),
- contact_name=payload.get("contact_name"),
- contact_phone=payload.get("contact_phone"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(tenant_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/tenants/{tenant_id}")
- async def delete_tenant(
- tenant_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_svc.soft_delete_tenant(db, tenant_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # Helper: resolve tenant_no from user context
- # ---------------------------------------------------------------------------
- def _get_tenant_no(db: Session, user: dict) -> str:
- user_id = user.get("user_id")
- if not user_id:
- return "zkxw"
- sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
- return (sys_user.tenant_no if sys_user else None) or "zkxw"
- def _assert_user_tenant_access(db: Session, user: dict, target_user_id: str):
- """同租户或 super_admin 才可写操作用户;返回目标用户行。"""
- row = user_svc.get_user(db, target_user_id)
- if not row:
- raise ValueError("用户不存在")
- tenant_no = _get_tenant_no(db, user)
- if row.tenant_no != tenant_no and user.get("role") != "super_admin":
- raise ValueError("无权操作其他租户的用户")
- return row
- def _generate_temp_password(length: int = 12) -> str:
- """生成可读的临时密码(字母数字,避免易混淆字符)。"""
- import secrets
- alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789"
- return "".join(secrets.choice(alphabet) for _ in range(length))
- # ---------------------------------------------------------------------------
- # 用户管理
- # ---------------------------------------------------------------------------
- class UserCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- user_no: str = Field(..., alias="userNo")
- nick_name: str = Field(..., alias="nickName")
- password: str = Field(..., alias="password")
- dept_id: Optional[str] = Field(None, alias="deptId")
- supervisor_user_id: Optional[str] = Field(None, alias="supervisorUserId")
- email: Optional[str] = Field("", alias="email")
- phone_no: Optional[str] = Field("", alias="phoneNo")
- hire_date: Optional[str] = Field(None, alias="hireDate")
- probation_months: Optional[int] = Field(None, alias="probationMonths")
- last_work_day: Optional[str] = Field(None, alias="lastWorkDay")
- sex: Optional[str] = Field("0", alias="sex")
- avatar: Optional[str] = Field(None, alias="avatar")
- status: Optional[str] = Field("1", alias="status")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- role_ids: Optional[List[str]] = Field(None, alias="roleIds")
- post_ids: Optional[List[str]] = Field(None, alias="postIds")
- class UserUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- nick_name: Optional[str] = Field(None, alias="nickName")
- dept_id: Optional[str] = Field(None, alias="deptId")
- supervisor_user_id: Optional[str] = Field(None, alias="supervisorUserId")
- email: Optional[str] = Field(None, alias="email")
- phone_no: Optional[str] = Field(None, alias="phoneNo")
- hire_date: Optional[str] = Field(None, alias="hireDate")
- probation_months: Optional[int] = Field(None, alias="probationMonths")
- last_work_day: Optional[str] = Field(None, alias="lastWorkDay")
- sex: Optional[str] = Field(None, alias="sex")
- avatar: Optional[str] = Field(None, alias="avatar")
- status: Optional[str] = Field(None, alias="status")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- role_ids: Optional[List[str]] = Field(None, alias="roleIds")
- post_ids: Optional[List[str]] = Field(None, alias="postIds")
- @router.get("/users")
- async def list_users(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- deptId: Optional[str] = Query(None, alias="deptId"),
- status: Optional[str] = None,
- roleName: Optional[str] = Query(None, alias="roleName"),
- user: dict = Depends(require_menu_code("system:user:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- query = db.query(SysUser).filter(
- SysUser.del_flag == 0,
- SysUser.tenant_no == tenant_no,
- )
- kw = (keyword or "").strip()
- if kw:
- like = f"%{kw}%"
- query = query.filter(
- or_(
- SysUser.user_no.like(like),
- SysUser.nick_name.like(like),
- SysUser.phone_no.like(like),
- )
- )
- if deptId:
- dept_ids = _get_child_dept_ids(db, deptId, tenant_no)
- query = query.filter(SysUser.dept_id.in_(dept_ids))
- if status:
- query = query.filter(SysUser.status == status)
- rn = (roleName or "").strip()
- if rn:
- matched_ids = list_user_ids_with_effective_role_name(
- db, tenant_no=tenant_no, role_name=rn
- )
- if not matched_ids:
- return success_response(
- {"list": [], "total": 0, "page": page, "pageSize": pageSize, "totalPages": 0}
- )
- query = query.filter(SysUser.id.in_(matched_ids))
- query = apply_data_scope(query, SysUser, user, db, tenant_no)
- total = query.distinct().count()
- items = (
- query.distinct()
- .order_by(SysUser.create_time.desc())
- .offset((page - 1) * pageSize)
- .limit(pageSize)
- .all()
- )
- dept_ids = list({r.dept_id for r in items if r.dept_id})
- dept_map: dict[str, str] = {}
- if dept_ids:
- for d in db.query(SysDept).filter(SysDept.id.in_(dept_ids), SysDept.del_flag == 0).all():
- dept_map[d.id] = d.dept_name or ""
- result_list = [
- user_svc._row_to_dict(r, dept_name=dept_map.get(r.dept_id or "", "")) for r in items
- ]
- user_svc.enrich_user_dicts_with_relations(db, result_list)
- tp = user_svc.total_pages(total, pageSize)
- return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.get("/users/{user_id}")
- async def get_user_by_id(
- user_id: str,
- user: dict = Depends(require_menu_code("system:user:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- row = user_svc.get_user(db, user_id)
- if not row:
- return error_response(404, "用户不存在")
- if row.tenant_no != tenant_no and user.get("role") != "super_admin":
- return error_response(403, "无权查看该用户")
- data = user_svc.get_user_with_relations(db, user_id)
- return success_response(data)
- @router.post("/users")
- async def create_user(
- body: UserCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- if user_svc.user_no_exists(db, body.user_no, tenant_no):
- return error_response(400, "用户账号已存在")
- obj = user_svc.create_user(
- db,
- user_no=body.user_no,
- nick_name=body.nick_name,
- password=body.password,
- dept_id=body.dept_id,
- supervisor_user_id=body.supervisor_user_id,
- email=body.email,
- phone_no=body.phone_no,
- hire_date=body.hire_date,
- probation_months=body.probation_months,
- last_work_day=body.last_work_day,
- sex=body.sex,
- avatar=body.avatar,
- status=body.status,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- role_ids=body.role_ids,
- post_ids=body.post_ids,
- )
- return success_response(user_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/users/{user_id}")
- async def update_user(
- user_id: str,
- body: UserUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- _assert_user_tenant_access(db, user, user_id)
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- update_kwargs: dict[str, Any] = {
- "nick_name": payload.get("nick_name"),
- "dept_id": payload.get("dept_id"),
- "supervisor_user_id": payload.get("supervisor_user_id"),
- "email": payload.get("email"),
- "phone_no": payload.get("phone_no"),
- "sex": payload.get("sex"),
- "avatar": payload.get("avatar"),
- "status": payload.get("status"),
- "order_num": payload.get("order_num"),
- "remark": payload.get("remark"),
- "user_id_updater": user.get("user_id"),
- "role_ids": payload.get("role_ids"),
- "post_ids": payload.get("post_ids"),
- }
- if "hire_date" in payload:
- update_kwargs["hire_date"] = payload.get("hire_date")
- if "probation_months" in payload:
- update_kwargs["probation_months"] = payload.get("probation_months")
- if "last_work_day" in payload:
- update_kwargs["last_work_day"] = payload.get("last_work_day")
- obj = user_svc.update_user(db, user_id, **update_kwargs)
- return success_response(user_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/users/{user_id}")
- async def delete_user(
- user_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- _assert_user_tenant_access(db, user, user_id)
- user_svc.soft_delete_user(db, user_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.post("/users/{user_id}/reset-password")
- async def reset_user_password(
- user_id: str,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- _assert_user_tenant_access(db, user, user_id)
- temp_password = _generate_temp_password(12)
- user_svc.reset_password(db, user_id, temp_password)
- return success_response(
- {"tempPassword": temp_password},
- message="密码已重置,请将临时密码告知用户并督促尽快修改",
- )
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 字典类型管理
- # ---------------------------------------------------------------------------
- class DictTypeCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- dict_name: str = Field(..., alias="dictName")
- dict_type: str = Field(..., alias="dictType")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class DictTypeUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- dict_name: Optional[str] = Field(None, alias="dictName")
- dict_type: Optional[str] = Field(None, alias="dictType")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/dict-types")
- async def list_dict_types(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("system:dict:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = dict_type_svc.list_dict_types(
- db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user
- )
- tp = dict_type_svc.total_pages(total, pageSize)
- return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/dict-types")
- async def create_dict_type(
- body: DictTypeCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = dict_type_svc.create_dict_type(
- db,
- dict_name=body.dict_name,
- dict_type=body.dict_type,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(dict_type_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/dict-types/{dict_type_id}")
- async def update_dict_type(
- dict_type_id: str,
- body: DictTypeUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = dict_type_svc.update_dict_type(
- db,
- dict_type_id,
- dict_name=payload.get("dict_name"),
- dict_type=payload.get("dict_type"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- tenant_no=tenant_no,
- )
- return success_response(dict_type_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/dict-types/{dict_type_id}")
- async def delete_dict_type(
- dict_type_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- dict_type_svc.soft_delete_dict_type(db, dict_type_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 字典数据管理
- # ---------------------------------------------------------------------------
- class DictDataCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- dict_label: str = Field(..., alias="dictLabel")
- dict_value: str = Field(..., alias="dictValue")
- dict_type: str = Field(..., alias="dictType")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class DictDataUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- dict_label: Optional[str] = Field(None, alias="dictLabel")
- dict_value: Optional[str] = Field(None, alias="dictValue")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/dict-data")
- async def list_dict_data(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- dictType: Optional[str] = None,
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("system:dict:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = dict_data_svc.list_dict_data(
- db, page=page, page_size=pageSize, dict_type_code=dictType, keyword=keyword, tenant_no=tenant_no, user_info=user
- )
- tp = dict_data_svc.total_pages(total, pageSize)
- return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/dict-data")
- async def create_dict_data(
- body: DictDataCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = dict_data_svc.create_dict_data(
- db,
- dict_label=body.dict_label,
- dict_value=body.dict_value,
- dict_type=body.dict_type,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(dict_data_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/dict-data/{data_id}")
- async def update_dict_data(
- data_id: str,
- body: DictDataUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = dict_data_svc.update_dict_data(
- db,
- data_id,
- dict_label=payload.get("dict_label"),
- dict_value=payload.get("dict_value"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(dict_data_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/dict-data/{data_id}")
- async def delete_dict_data(
- data_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- dict_data_svc.soft_delete_dict_data(db, data_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.get("/dict-data/options")
- async def dict_data_options(
- dictType: str = Query(..., alias="dictType"),
- user: dict = Depends(require_menu_code("system:dict:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items = dict_data_svc.list_dict_data_options(db, dict_type_code=dictType, tenant_no=tenant_no)
- return success_response(items)
- # ---------------------------------------------------------------------------
- # 组织管理
- # ---------------------------------------------------------------------------
- class DeptCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- parent_id: str = Field("0", alias="parentId")
- dept_name: str = Field(..., alias="deptName")
- dept_category_code: Optional[str] = Field(None, alias="deptCategoryCode")
- status: Optional[str] = Field("1")
- leader: Optional[str] = None
- leader_phone: Optional[str] = Field(None, alias="leaderPhone")
- leader_user_id: Optional[str] = Field(None, alias="leaderUserId")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class DeptUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- dept_name: Optional[str] = Field(None, alias="deptName")
- dept_category_code: Optional[str] = Field(None, alias="deptCategoryCode")
- status: Optional[str] = None
- leader: Optional[str] = None
- leader_phone: Optional[str] = Field(None, alias="leaderPhone")
- leader_user_id: Optional[str] = Field(None, alias="leaderUserId")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/depts")
- async def list_depts(
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("system:dept:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- tree = dept_svc.list_depts_tree(db, keyword=keyword, tenant_no=tenant_no, user_info=user)
- return success_response(tree)
- @router.post("/depts")
- async def create_dept(
- body: DeptCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = dept_svc.create_dept(
- db,
- parent_id=body.parent_id,
- dept_name=body.dept_name,
- dept_category_code=body.dept_category_code,
- status=body.status,
- leader=body.leader,
- leader_phone=body.leader_phone,
- leader_user_id=body.leader_user_id,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(dept_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/depts/{dept_id}")
- async def update_dept(
- dept_id: str,
- body: DeptUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = dept_svc.update_dept(
- db,
- dept_id,
- dept_name=payload.get("dept_name"),
- dept_category_code=payload.get("dept_category_code"),
- status=payload.get("status"),
- leader=payload.get("leader"),
- leader_phone=payload.get("leader_phone"),
- leader_user_id=payload.get("leader_user_id"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(dept_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/depts/{dept_id}")
- async def delete_dept(
- dept_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- dept_svc.soft_delete_dept(db, dept_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 站点管理
- # ---------------------------------------------------------------------------
- class SiteCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- site_name: str = Field(..., alias="siteName")
- site_code: str = Field(..., alias="siteCode")
- site_type: Optional[str] = Field(None, alias="siteType")
- status: Optional[str] = Field("1")
- address: Optional[str] = None
- contact_name: Optional[str] = Field(None, alias="contactName")
- contact_phone: Optional[str] = Field(None, alias="contactPhone")
- business_scope: Optional[str] = Field(None, alias="businessScope")
- site_image: Optional[str] = Field(None, alias="siteImage")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class SiteUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- site_name: Optional[str] = Field(None, alias="siteName")
- site_code: Optional[str] = Field(None, alias="siteCode")
- site_type: Optional[str] = Field(None, alias="siteType")
- status: Optional[str] = None
- address: Optional[str] = None
- contact_name: Optional[str] = Field(None, alias="contactName")
- contact_phone: Optional[str] = Field(None, alias="contactPhone")
- business_scope: Optional[str] = Field(None, alias="businessScope")
- site_image: Optional[str] = Field(None, alias="siteImage")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/sites")
- async def list_sites(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("system:site:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = site_svc.list_sites(db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user)
- tp = site_svc.total_pages(total, pageSize)
- return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/sites")
- async def create_site(
- body: SiteCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = site_svc.create_site(
- db,
- site_name=body.site_name,
- site_code=body.site_code,
- site_type=body.site_type,
- status=body.status,
- address=body.address,
- contact_name=body.contact_name,
- contact_phone=body.contact_phone,
- business_scope=body.business_scope,
- site_image=body.site_image,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(site_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/sites/{site_id}")
- async def update_site(
- site_id: str,
- body: SiteUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = site_svc.update_site(
- db,
- site_id,
- site_name=payload.get("site_name"),
- site_code=payload.get("site_code"),
- site_type=payload.get("site_type"),
- status=payload.get("status"),
- address=payload.get("address"),
- contact_name=payload.get("contact_name"),
- contact_phone=payload.get("contact_phone"),
- business_scope=payload.get("business_scope"),
- site_image=payload.get("site_image"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(site_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/sites/{site_id}")
- async def delete_site(
- site_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- site_svc.soft_delete_site(db, site_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 职位管理
- # ---------------------------------------------------------------------------
- class PostCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- post_name: str = Field(..., alias="postName")
- post_code: str = Field(..., alias="postCode")
- status: Optional[str] = Field("1")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class PostUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- post_name: Optional[str] = Field(None, alias="postName")
- post_code: Optional[str] = Field(None, alias="postCode")
- status: Optional[str] = None
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/posts")
- async def list_posts(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("system:post:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = post_svc.list_posts(db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user)
- tp = post_svc.total_pages(total, pageSize)
- return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/posts")
- async def create_post(
- body: PostCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = post_svc.create_post(
- db,
- post_name=body.post_name,
- post_code=body.post_code,
- status=body.status,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(post_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/posts/{post_id}")
- async def update_post(
- post_id: str,
- body: PostUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = post_svc.update_post(
- db,
- post_id,
- post_name=payload.get("post_name"),
- post_code=payload.get("post_code"),
- status=payload.get("status"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(post_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/posts/{post_id}")
- async def delete_post(
- post_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- post_svc.soft_delete_post(db, post_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- class PostUsersUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- user_ids: List[str] = Field(default_factory=list, alias="userIds")
- @router.get("/posts/{post_id}/users")
- async def list_post_bound_users(
- post_id: str,
- user: dict = Depends(require_menu_code("system:post:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- post = post_svc.get_post(db, post_id)
- if not post or post.tenant_no != tenant_no:
- return error_response(404, "职位不存在")
- ids = user_svc.list_user_ids_bound_to_post(db, post_id, tenant_no)
- return success_response({"userIds": ids})
- @router.put("/posts/{post_id}/users")
- async def update_post_bound_users(
- post_id: str,
- body: PostUsersUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- post = post_svc.get_post(db, post_id)
- if not post or post.tenant_no != tenant_no:
- return error_response(404, "职位不存在")
- try:
- user_svc.sync_post_user_bindings(
- db, post_id, tenant_no, body.user_ids, user.get("user_id")
- )
- return success_response(None, message="保存成功")
- except ValueError as e:
- return error_response(400, str(e))
- class PostRolesUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- role_ids: List[str] = Field(default_factory=list, alias="roleIds")
- @router.get("/posts/{post_id}/roles")
- async def list_post_bound_roles(
- post_id: str,
- user: dict = Depends(require_menu_code("system:post:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- post = post_svc.get_post(db, post_id)
- if not post or post.tenant_no != tenant_no:
- return error_response(404, "职位不存在")
- ids = post_svc.list_role_ids_for_post(db, post_id, tenant_no)
- return success_response({"roleIds": ids})
- @router.put("/posts/{post_id}/roles")
- async def update_post_bound_roles(
- post_id: str,
- body: PostRolesUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- post = post_svc.get_post(db, post_id)
- if not post or post.tenant_no != tenant_no:
- return error_response(404, "职位不存在")
- try:
- post_svc.sync_post_role_bindings(
- db, post_id, tenant_no, body.role_ids, user.get("user_id")
- )
- return success_response(None, message="保存成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 物品单位管理
- # ---------------------------------------------------------------------------
- class ItemUnitCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- unit_code: str = Field(..., alias="unitCode")
- unit_name: str = Field(..., alias="unitName")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class ItemUnitUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- unit_code: Optional[str] = Field(None, alias="unitCode")
- unit_name: Optional[str] = Field(None, alias="unitName")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/item-units")
- async def list_item_units(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("system:unit:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = item_unit_svc.list_item_units(db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user)
- tp = item_unit_svc.total_pages(total, pageSize)
- return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/item-units")
- async def create_item_unit(
- body: ItemUnitCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = item_unit_svc.create_item_unit(
- db,
- unit_code=body.unit_code,
- unit_name=body.unit_name,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(item_unit_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/item-units/{unit_id}")
- async def update_item_unit(
- unit_id: str,
- body: ItemUnitUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = item_unit_svc.update_item_unit(
- db,
- unit_id,
- unit_code=payload.get("unit_code"),
- unit_name=payload.get("unit_name"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(item_unit_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/item-units/{unit_id}")
- async def delete_item_unit(
- unit_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- item_unit_svc.soft_delete_item_unit(db, unit_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 快递公司管理
- # ---------------------------------------------------------------------------
- class ExpressCompanyCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- express_code: str = Field(..., alias="expressCode")
- express_name: str = Field(..., alias="expressName")
- express_logo: Optional[str] = Field(None, alias="expressLogo")
- order_num: Optional[int] = Field(0, alias="orderNum")
- remark: Optional[str] = None
- class ExpressCompanyUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- express_code: Optional[str] = Field(None, alias="expressCode")
- express_name: Optional[str] = Field(None, alias="expressName")
- express_logo: Optional[str] = Field(None, alias="expressLogo")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/express-companies")
- async def list_express_companies(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("system:express:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = express_company_svc.list_express_companies(db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user)
- tp = express_company_svc.total_pages(total, pageSize)
- return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/express-companies")
- async def create_express_company(
- body: ExpressCompanyCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = express_company_svc.create_express_company(
- db,
- express_code=body.express_code,
- express_name=body.express_name,
- express_logo=body.express_logo,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(express_company_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/express-companies/{company_id}")
- async def update_express_company(
- company_id: str,
- body: ExpressCompanyUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = express_company_svc.update_express_company(
- db,
- company_id,
- express_code=payload.get("express_code"),
- express_name=payload.get("express_name"),
- express_logo=payload.get("express_logo"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(express_company_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/express-companies/{company_id}")
- async def delete_express_company(
- company_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- express_company_svc.soft_delete_express_company(db, company_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 菜单管理
- # ---------------------------------------------------------------------------
- class MenuCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- menu_name: str = Field(..., alias="menuName")
- parent_id: str = Field("0", alias="parentId")
- menu_type: str = Field("M", alias="menuType")
- path: Optional[str] = Field("", alias="path")
- component: Optional[str] = Field(None, alias="component")
- perms: Optional[str] = Field(None, alias="perms")
- is_frame: Optional[int] = Field(1, alias="isFrame")
- visible: Optional[str] = Field("1", alias="visible")
- status: Optional[str] = Field("1", alias="status")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- class MenuUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- menu_name: Optional[str] = Field(None, alias="menuName")
- parent_id: Optional[str] = Field(None, alias="parentId")
- menu_type: Optional[str] = Field(None, alias="menuType")
- path: Optional[str] = Field(None, alias="path")
- component: Optional[str] = Field(None, alias="component")
- perms: Optional[str] = Field(None, alias="perms")
- is_frame: Optional[int] = Field(None, alias="isFrame")
- visible: Optional[str] = Field(None, alias="visible")
- status: Optional[str] = Field(None, alias="status")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- @router.get("/menus")
- async def list_menus(
- keyword: Optional[str] = None,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- tree = menu_svc.list_menus_tree(db, keyword=keyword, tenant_no=tenant_no, user_info=user)
- return success_response(tree)
- @router.post("/menus")
- async def create_menu(
- body: MenuCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = menu_svc.create_menu(
- db,
- menu_name=body.menu_name,
- parent_id=body.parent_id,
- menu_type=body.menu_type,
- path=body.path,
- component=body.component,
- perms=body.perms,
- is_frame=body.is_frame,
- visible=body.visible,
- status=body.status,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(menu_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/menus/{menu_id}")
- async def update_menu(
- menu_id: str,
- body: MenuUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = menu_svc.update_menu(
- db,
- menu_id,
- menu_name=payload.get("menu_name"),
- parent_id=payload.get("parent_id"),
- menu_type=payload.get("menu_type"),
- path=payload.get("path"),
- component=payload.get("component"),
- perms=payload.get("perms"),
- is_frame=payload.get("is_frame"),
- visible=payload.get("visible"),
- status=payload.get("status"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(menu_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/menus/{menu_id}")
- async def delete_menu(
- menu_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- menu_svc.soft_delete_menu(db, menu_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- # ---------------------------------------------------------------------------
- # 角色管理
- # ---------------------------------------------------------------------------
- class RoleCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- role_name: str = Field(..., alias="roleName")
- data_scope: Optional[str] = Field("4", alias="dataScope")
- status: Optional[str] = Field("1", alias="status")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- class RoleUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- role_name: Optional[str] = Field(None, alias="roleName")
- data_scope: Optional[str] = Field(None, alias="dataScope")
- status: Optional[str] = Field(None, alias="status")
- order_num: Optional[int] = Field(None, alias="orderNum")
- remark: Optional[str] = None
- class RoleMenuBody(BaseModel):
- menu_ids: List[str] = Field(..., alias="menuIds")
- class RoleDataScopeBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- data_scope: str = Field(..., alias="dataScope")
- scope_details: Optional[List[dict]] = Field(None, alias="scopeDetails")
- @router.get("/role-management")
- async def list_role_management(
- page: int = Query(1, ge=1),
- pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
- keyword: Optional[str] = None,
- user: dict = Depends(require_menu_code("system:role:list")),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- items, total = role_svc.list_roles(db, page=page, page_size=pageSize, keyword=keyword, tenant_no=tenant_no, user_info=user)
- tp = role_svc.total_pages(total, pageSize)
- return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
- @router.post("/role-management")
- async def create_role_management(
- body: RoleCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- obj = role_svc.create_role(
- db,
- role_name=body.role_name,
- data_scope=body.data_scope,
- status=body.status,
- tenant_no=tenant_no,
- order_num=body.order_num,
- remark=body.remark,
- user_id=user.get("user_id"),
- )
- return success_response(role_svc._row_to_dict(obj), message="创建成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.put("/role-management/{role_id}")
- async def update_role_management(
- role_id: str,
- body: RoleUpdateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
- obj = role_svc.update_role(
- db,
- role_id,
- role_name=payload.get("role_name"),
- data_scope=payload.get("data_scope"),
- status=payload.get("status"),
- order_num=payload.get("order_num"),
- remark=payload.get("remark"),
- user_id=user.get("user_id"),
- )
- return success_response(role_svc._row_to_dict(obj), message="更新成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.delete("/role-management/{role_id}")
- async def delete_role_management(
- role_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- role_svc.soft_delete_role(db, role_id)
- return success_response(None, message="删除成功")
- except ValueError as e:
- return error_response(400, str(e))
- @router.get("/role-management/{role_id}/menus")
- async def get_role_menus(
- role_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- menu_ids = role_svc.get_role_menu_ids(db, role_id)
- return success_response({"menuIds": menu_ids})
- @router.put("/role-management/{role_id}/menus")
- async def update_role_menus(
- role_id: str,
- body: RoleMenuBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- role_svc.update_role_menus(db, role_id, body.menu_ids, 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("/role-management/{role_id}/data-scope")
- async def get_role_data_scope(
- role_id: str,
- _user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- role = role_svc.get_role(db, role_id)
- if not role:
- return error_response(404, "角色不存在")
- details = role_svc.get_role_data_scope(db, role_id)
- return success_response({"dataScope": role.data_scope or "1", "scopeDetails": details})
- @router.put("/role-management/{role_id}/data-scope")
- async def update_role_data_scope(
- role_id: str,
- body: RoleDataScopeBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- role_svc.update_role_data_scope(
- db,
- role_id,
- body.data_scope,
- body.scope_details,
- 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))
- # ---------------------------------------------------------------------------
- # 附件上传
- # ---------------------------------------------------------------------------
- def _format_file_size(size_bytes: int) -> str:
- if size_bytes < 1024:
- return f"{size_bytes} B"
- elif size_bytes < 1024 * 1024:
- return f"{size_bytes / 1024:.1f} KB"
- else:
- return f"{size_bytes / (1024 * 1024):.1f} MB"
- @router.post("/attachment/upload")
- async def upload_attachment(
- file: UploadFile = File(...),
- folder: Optional[str] = Form(""),
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- if not file or not file.filename:
- return error_response(400, "请选择文件")
- try:
- file_content = await file.read()
- full_url = storage.upload_file(file_content, file.filename, folder or "")
- base_url = settings.MINIO_BASE_URL.rstrip("/") if settings.MINIO_BASE_URL else ""
- if base_url and full_url.startswith(base_url + "/"):
- file_url = full_url[len(base_url) + 1:]
- else:
- file_url = full_url
- file_size = len(file_content)
- file_ext = os.path.splitext(file.filename)[1].lstrip(".") if file.filename else ""
- file_type = file.content_type or ""
- tenant_no = _get_tenant_no(db, user)
- record = BizAttachment(
- id=uuid.uuid4().hex,
- original_name=file.filename,
- file_ext=file_ext,
- file_url=file_url,
- file_size=file_size,
- file_type=file_type,
- tenant_no=tenant_no,
- create_by=user.get("user_id"),
- )
- db.add(record)
- db.commit()
- db.refresh(record)
- return success_response({
- "id": record.id,
- "fileName": record.original_name,
- "fileUrl": record.file_url,
- "fileSize": record.file_size,
- "fileSizeDisplay": _format_file_size(record.file_size or 0),
- "fileType": record.file_type,
- })
- except Exception as e:
- db.rollback()
- return error_response(500, f"上传失败: {str(e)}")
- class AttachmentDownloadUrlBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- file_url: str = Field(..., alias="fileUrl")
- file_name: Optional[str] = Field(None, alias="fileName")
- @router.post("/attachment/download-url")
- async def attachment_download_url(
- body: AttachmentDownloadUrlBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- """返回带 Content-Disposition 的下载 URL,保证浏览器使用原始文件名。"""
- file_url = (body.file_url or "").strip()
- if not file_url:
- return error_response(400, "文件地址不能为空")
- tenant_no = _get_tenant_no(db, user)
- att = _lookup_attachment_by_url(db, file_url, tenant_no)
- if not att:
- return error_response(403, "无权下载该附件或附件未登记")
- download_name = (body.file_name or "").strip()
- if not download_name:
- download_name = (att.original_name or "").strip()
- if not download_name:
- download_name = file_url.rsplit("/", 1)[-1] or "download"
- try:
- url = storage.presigned_download_url(file_url, download_name)
- return success_response({"downloadUrl": url})
- except Exception as e:
- return error_response(500, f"获取下载链接失败: {str(e)}")
|