| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- """用户菜单收藏 CRUD"""
- from __future__ import annotations
- import uuid
- from datetime import datetime
- from typing import Any, Dict, List, Optional
- from sqlalchemy.orm import Session
- from app.models.models import SysUserMenuFavorite
- MAX_FAVORITES = 10
- def normalize_menu_path(path: str) -> str:
- p = (path or "").strip()
- if not p:
- return ""
- if not p.startswith("/"):
- p = "/" + p
- return p
- def _row_to_dict(row: SysUserMenuFavorite) -> Dict[str, Any]:
- return {
- "id": row.id,
- "path": row.menu_path or "",
- "orderNum": row.order_num,
- "createTime": row.create_time.isoformat(sep=" ", timespec="seconds") if row.create_time else None,
- }
- def _next_order_num(db: Session, *, user_id: str, tenant_no: str) -> int:
- max_order = (
- db.query(SysUserMenuFavorite.order_num)
- .filter(
- SysUserMenuFavorite.del_flag == 0,
- SysUserMenuFavorite.user_id == user_id,
- SysUserMenuFavorite.tenant_no == tenant_no,
- )
- .order_by(SysUserMenuFavorite.order_num.desc())
- .first()
- )
- return (max_order[0] if max_order and max_order[0] is not None else 0) + 1
- def list_favorites(
- db: Session,
- *,
- user_id: str,
- tenant_no: str,
- ) -> List[Dict[str, Any]]:
- rows = (
- db.query(SysUserMenuFavorite)
- .filter(
- SysUserMenuFavorite.del_flag == 0,
- SysUserMenuFavorite.user_id == user_id,
- SysUserMenuFavorite.tenant_no == tenant_no,
- )
- .order_by(
- SysUserMenuFavorite.order_num.asc(),
- SysUserMenuFavorite.create_time.asc(),
- )
- .all()
- )
- return [_row_to_dict(r) for r in rows]
- def count_active(db: Session, *, user_id: str, tenant_no: str) -> int:
- return (
- db.query(SysUserMenuFavorite)
- .filter(
- SysUserMenuFavorite.del_flag == 0,
- SysUserMenuFavorite.user_id == user_id,
- SysUserMenuFavorite.tenant_no == tenant_no,
- )
- .count()
- )
- def find_active(
- db: Session, *, user_id: str, tenant_no: str, menu_path: str
- ) -> Optional[SysUserMenuFavorite]:
- return (
- db.query(SysUserMenuFavorite)
- .filter(
- SysUserMenuFavorite.del_flag == 0,
- SysUserMenuFavorite.user_id == user_id,
- SysUserMenuFavorite.tenant_no == tenant_no,
- SysUserMenuFavorite.menu_path == menu_path,
- )
- .first()
- )
- def find_soft_deleted(
- db: Session, *, user_id: str, tenant_no: str, menu_path: str
- ) -> Optional[SysUserMenuFavorite]:
- return (
- db.query(SysUserMenuFavorite)
- .filter(
- SysUserMenuFavorite.del_flag == 1,
- SysUserMenuFavorite.user_id == user_id,
- SysUserMenuFavorite.tenant_no == tenant_no,
- SysUserMenuFavorite.menu_path == menu_path,
- )
- .first()
- )
- def add_favorite(
- db: Session,
- *,
- user_id: str,
- tenant_no: str,
- menu_path: str,
- create_dept_id: str = "",
- ) -> Dict[str, Any]:
- path = normalize_menu_path(menu_path)
- if not path:
- raise ValueError("菜单路径无效")
- existing = find_active(db, user_id=user_id, tenant_no=tenant_no, menu_path=path)
- if existing:
- return _row_to_dict(existing)
- if count_active(db, user_id=user_id, tenant_no=tenant_no) >= MAX_FAVORITES:
- raise ValueError(f"最多收藏 {MAX_FAVORITES} 个菜单")
- now = datetime.now()
- next_order = _next_order_num(db, user_id=user_id, tenant_no=tenant_no)
- deleted = find_soft_deleted(db, user_id=user_id, tenant_no=tenant_no, menu_path=path)
- if deleted:
- deleted.del_flag = 0
- deleted.order_num = next_order
- deleted.update_by = user_id
- deleted.update_time = now
- db.commit()
- db.refresh(deleted)
- return _row_to_dict(deleted)
- row = SysUserMenuFavorite(
- id=str(uuid.uuid4()),
- user_id=user_id,
- menu_path=path,
- tenant_no=tenant_no,
- order_num=next_order,
- create_by=user_id,
- create_dept_id=create_dept_id or "",
- create_time=now,
- update_by=user_id,
- update_time=now,
- del_flag=0,
- )
- db.add(row)
- db.commit()
- db.refresh(row)
- return _row_to_dict(row)
- def soft_delete_favorite(
- db: Session,
- *,
- user_id: str,
- tenant_no: str,
- menu_path: str,
- ) -> bool:
- path = normalize_menu_path(menu_path)
- row = find_active(db, user_id=user_id, tenant_no=tenant_no, menu_path=path)
- if not row:
- return False
- row.del_flag = 1
- row.update_by = user_id
- row.update_time = datetime.now()
- db.commit()
- return True
- def reorder_favorites(
- db: Session,
- *,
- user_id: str,
- tenant_no: str,
- paths: List[str],
- ) -> List[Dict[str, Any]]:
- """按传入 paths 顺序重写 order_num(1-based)。paths 须覆盖当前全部有效收藏。"""
- normalized: List[str] = []
- seen = set()
- for raw in paths or []:
- p = normalize_menu_path(raw)
- if not p or p in seen:
- continue
- seen.add(p)
- normalized.append(p)
- current = list_favorites(db, user_id=user_id, tenant_no=tenant_no)
- current_paths = {item["path"] for item in current}
- if set(normalized) != current_paths or len(normalized) != len(current_paths):
- raise ValueError("排序列表与当前收藏不一致")
- now = datetime.now()
- for idx, path in enumerate(normalized, start=1):
- row = find_active(db, user_id=user_id, tenant_no=tenant_no, menu_path=path)
- if not row:
- raise ValueError("排序列表与当前收藏不一致")
- row.order_num = idx
- row.update_by = user_id
- row.update_time = now
- db.commit()
- return list_favorites(db, user_id=user_id, tenant_no=tenant_no)
|