Files
chengkai3 cdc0b4b054 feat:[FL-165][角色管理页面查询时合并角色和菜单请求]
将角色管理页面的角色和菜单两个独立API请求合并为单个请求,减少网络开销。

后端改动:
- 新增 RolesWithMenusResponse 响应模型
- 新增 list_roles_with_menus 服务函数
- 新增 GET /api/v1/admin/roles-with-menus 接口

前端改动:
- 更新 loadData 函数使用新的合并接口
- 减少从两个并发请求改为单个请求

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-06-18 00:05:11 +08:00

139 lines
3.4 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, Field
class PermissionPublic(BaseModel):
id: int
code: str
name: str
class RolePublic(BaseModel):
id: str
code: str
name: str
permission_codes: list[str]
menu_ids: list[str] = Field(default_factory=list)
class RoleListResponse(BaseModel):
items: list[RolePublic]
total: int
class RoleCreateRequest(BaseModel):
code: str = Field(min_length=2, max_length=64)
name: str = Field(min_length=2, max_length=128)
permission_codes: list[str] = Field(default_factory=list)
menu_ids: list[str] = Field(default_factory=list)
class RoleUpdateRequest(BaseModel):
name: str | None = Field(default=None, min_length=2, max_length=128)
permission_codes: list[str] | None = None
menu_ids: list[str] | None = None
class MenuPublic(BaseModel):
id: str
code: str
name: str
path: str | None = None
icon: str | None = None
parent_id: str | None = None
type: str
sort_order: int
status: str
visible: bool
cacheable: bool
component: str | None = None
permission_code: str | None = None
class MenuTreeItem(MenuPublic):
children: list["MenuTreeItem"] = Field(default_factory=list)
class MenuListResponse(BaseModel):
items: list[MenuPublic]
total: int
class MenuCreateRequest(BaseModel):
code: str = Field(min_length=2, max_length=128)
name: str = Field(min_length=2, max_length=128)
path: str | None = Field(default=None, max_length=255)
icon: str | None = Field(default=None, max_length=64)
parent_id: str | None = None
type: str = Field(default="menu")
sort_order: int = 0
status: str = Field(default="enabled")
visible: bool = True
cacheable: bool = False
component: str | None = Field(default=None, max_length=255)
permission_code: str | None = Field(default=None, max_length=64)
class MenuUpdateRequest(BaseModel):
name: str | None = Field(default=None, min_length=2, max_length=128)
path: str | None = Field(default=None, max_length=255)
icon: str | None = Field(default=None, max_length=64)
parent_id: str | None = None
type: str | None = Field(default=None)
sort_order: int | None = None
status: str | None = Field(default=None)
visible: bool | None = None
cacheable: bool | None = None
component: str | None = Field(default=None, max_length=255)
permission_code: str | None = Field(default=None, max_length=64)
class RoleMenuUpdateRequest(BaseModel):
menu_ids: list[str] = Field(default_factory=list)
class AuditLogPublic(BaseModel):
id: int
user_id: str | None = None
username: str | None = None
action: str
detail: str | None = None
created_at: datetime
class AuditLogListResponse(BaseModel):
items: list[AuditLogPublic]
total: int
limit: int
offset: int
class SeedCategorySummary(BaseModel):
created: int = 0
updated: int = 0
linked: int = 0
unchanged: int = 0
overwritten: int = 0
class SeedDefaultsResponse(BaseModel):
success: bool
force: bool
mode: Literal["missing_only", "force_overwrite"]
overwrote_existing: bool
summary: dict[str, SeedCategorySummary] = Field(default_factory=dict)
class RolesWithMenusResponse(BaseModel):
roles: list[RolePublic]
roles_total: int
menus: list[MenuPublic]
menus_total: int
MenuTreeItem.model_rebuild()