1553 lines
53 KiB
Python
1553 lines
53 KiB
Python
"""运营端管理接口(租户、角色下拉等)"""
|
|
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
|
|
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
|
|
|
|
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(
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""返回用户下拉选项(label=昵称,value=用户ID),供流程设计器审批人选择使用。"""
|
|
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)
|
|
rows = q.order_by(SysUser.create_time.desc()).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"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 用户管理
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
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")
|
|
email: Optional[str] = Field("", alias="email")
|
|
phone_no: Optional[str] = Field("", alias="phoneNo")
|
|
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")
|
|
email: Optional[str] = Field(None, alias="email")
|
|
phone_no: Optional[str] = Field(None, alias="phoneNo")
|
|
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,
|
|
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:
|
|
query = query.filter(SysUser.dept_id == deptId)
|
|
if status:
|
|
query = query.filter(SysUser.status == status)
|
|
query = apply_data_scope(query, SysUser, user, db, tenant_no)
|
|
total = query.count()
|
|
items = query.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
|
|
]
|
|
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,
|
|
email=body.email,
|
|
phone_no=body.phone_no,
|
|
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:
|
|
payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
|
|
obj = user_svc.update_user(
|
|
db,
|
|
user_id,
|
|
nick_name=payload.get("nick_name"),
|
|
dept_id=payload.get("dept_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"),
|
|
)
|
|
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:
|
|
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:
|
|
user_svc.reset_password(db, user_id, "111111")
|
|
return success_response(None, message="密码已重置为 111111")
|
|
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")
|
|
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")
|
|
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,
|
|
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"),
|
|
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")
|
|
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")
|
|
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,
|
|
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"),
|
|
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 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")
|
|
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")
|
|
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,
|
|
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"),
|
|
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)
|
|
download_name = (body.file_name or "").strip()
|
|
if not download_name and att:
|
|
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)}")
|