Files
Common_Platform/history/通用中后台框架建设规划.md
T
2026-04-28 16:27:16 +08:00

1210 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 通用中后台底座平台建设规划
## 1. 系统定位
本系统定位为一个通用中后台基础框架。
本框架只提供中后台常用基础能力,不绑定任何具体业务。它的职责是提供稳定、轻量、现代的管理端基础设施,便于后续在同一工程内按需扩展页面、接口和基础模块。
本次设计只包含:
- 前端通用框架
- 后端通用框架
- 用户、组织、角色、菜单、权限、字典、日志等基础模块
本次不包含:
- 任何具体业务模块
- 旧 Vue 页面兼容
- 接口文档平台
- 多语言
- 微前端
## 2. 建设目标
核心目标:
- UI 现代、极简、高级。
- 前端轻便快速,交互顺滑,首屏和页面切换速度快。
- 后端结构清晰,接口稳定,权限和日志能力完善。
- 项目可以快速获得稳定的中后台基础能力。
- 新页面开发路径固定、清晰、可复制。
- 代码可读性优先,避免过度封装和框架魔法。
设计原则:
- 只做底座,不预设业务。
- 组件库负责基础 UI,不自研复杂 UI 框架。
- 权限、菜单、字典、日志做成通用能力。
- 数据查询、缓存、分页、错误处理形成统一模式。
- 前后端边界清晰,前端不承载安全规则,后端必须做权限校验。
## 3. 技术栈
### 3.1 前端技术栈
推荐技术栈:
- Vue 3
- TypeScript
- Vite
- Vue Router
- Pinia
- TanStack Query
- Naive UI
- Tailwind CSS
- lucide-vue-next
- Axios
- VueUse
- Vitest
- Playwright
- ESLint
- Prettier
说明:
- Vue 3 使用 `<script setup lang="ts">` 和 Composition API。
- Pinia 只负责全局状态,不承载页面列表数据。
- TanStack Query 负责服务端状态、缓存、刷新、分页、错误重试。
- Naive UI 负责基础组件,不额外建设复杂主题系统。
- Tailwind CSS 负责布局、间距、响应式和少量自定义样式。
- lucide-vue-next 统一图标风格。
- 系统只使用中文,不做多语言。
### 3.2 后端技术栈
推荐技术栈:
- Kotlin
- Ktor
- kotlinx.serialization
- Exposed
- HikariCP
- JWT
- Logback
- PostgreSQL
- Redis
- Docker Compose
说明:
- Ktor 负责 HTTP 服务、认证、路由、异常处理。
- kotlinx.serialization 作为 JSON 序列化方案。
- Exposed 作为数据库访问层。
- HikariCP 作为数据库连接池。
- JWT 作为前后端认证令牌。
- Logback 输出系统运行日志。
- PostgreSQL 作为默认关系型数据库。
- Redis 作为缓存、验证码、临时会话、限流等基础设施。
- PostgreSQL、Redis 等配套组件统一用根目录 `docker-compose.yml` 管理。
## 4. 总体架构
```mermaid
flowchart LR
Web["Vue3 管理端"]
Api["Ktor API Server"]
DB["PostgreSQL"]
Cache["Redis"]
Web -->|"HTTP API / JWT"| Api
Api -->|"Exposed / HikariCP"| DB
Api -->|"Redisson"| Cache
```
本框架分为两部分:
- `web`:前端管理端。
- `server`:后端 API 服务。
前端负责:
- 登录页
- 主布局
- 动态菜单
- 路由守卫
- 页面渲染
- 表格、表单、弹窗等交互
- 查询缓存和请求状态管理
- 按钮级权限展示控制
后端负责:
- 登录认证
- JWT 签发与校验
- 用户管理
- 组织管理
- 角色管理
- 菜单管理
- 权限校验
- 字典管理
- 操作日志
- 开放接口调用日志
- 系统异常日志
- PostgreSQL 连接池
- Redis 客户端
## 5. 工程结构
建议采用前后端同仓库:
```text
platform-a/
docker-compose.yml
web/
server/
```
不建议一期引入复杂 monorepo。底座平台先保持清晰、直接、容易部署。
配套组件统一放在项目根目录 `docker-compose.yml`
```text
docker-compose.yml
postgres: PostgreSQL 18.3
redis: Redis 8
```
本地开发默认连接:
```text
PostgreSQL:
host: localhost
port: 5432
database: platform_a
user: platform_a
password: platform_a_password
Redis:
host: localhost
port: 6379
password: platform_a_redis_password
```
启动配套组件:
```bash
docker compose up -d
```
停止配套组件:
```bash
docker compose down
```
如果需要清空本地数据,再执行:
```bash
docker compose down -v
```
## 6. 前端架构设计
### 6.1 前端组织原则
前端按功能组织页面,不在文档中写死完整目录结构。目录和文件根据实际复杂度创建,避免为了满足规范而产生空目录、空文件。
基础原则:
- 页面按功能组织。
- 共享组件放在全局 `components` 目录。
- 页面私有组件、组合逻辑、类型定义放在页面目录内。
- 路由、状态、请求、布局、样式等基础能力保持清晰边界。
- 简单页面可以只保留一个页面文件,复杂页面再拆分私有组件和逻辑。
### 6.2 页面组织规则
页面组织以可读性为准,不强制固定文件数量。
建议规则:
- 页面入口负责结构和主要交互。
- 页面私有逻辑只服务当前页面时,放在页面目录内。
- 多页面复用的组件、工具、类型再上提到共享目录。
- 表格列、搜索条件、弹窗状态等页面强相关内容优先靠近页面。
### 6.3 路由设计
路由分为静态路由和动态路由。
静态路由:
- `/login`
- `/403`
- `/404`
- `/`
动态路由:
- 登录后从后端获取菜单。
- 前端根据菜单里的 `component` 字段加载页面。
- 菜单变化后,用户重新登录或刷新权限即可生效。
后端菜单返回示例:
```json
{
"id": "1001",
"parentId": null,
"type": "MENU",
"path": "/system/users",
"name": "SystemUsers",
"component": "system/users/index",
"title": "用户管理",
"icon": "Users",
"permission": "system:user:view",
"sort": 10,
"visible": true,
"keepAlive": true
}
```
前端页面加载:
```ts
const viewModules = import.meta.glob('../features/**/index.vue')
```
组件路径规则:
```text
system/users/index -> src/features/system/users/index.vue
```
SPA 页面需要支持页签栏和页面缓存:
- 后台主体使用单页应用模式,页面切换不整页刷新。
- 已打开页面进入页签栏,支持切换、关闭、刷新当前页签。
- 菜单配置中的 `keepAlive` 控制页面是否缓存。
- 页面缓存需要和动态路由、权限刷新配合,用户权限变化后应清理不可访问页面。
- 列表查询数据仍由 TanStack Query 管理,页签缓存主要保留页面组件状态和表单状态。
### 6.4 Pinia 状态边界
Pinia 只保存全局状态:
- 当前 token
- 当前用户
- 用户权限码
- 用户菜单树
- 当前布局状态
- 字典缓存
不建议放入 Pinia
- 表格列表数据
- 分页数据
- 搜索条件
- 表单弹窗状态
- 详情数据
这些服务端状态统一交给 TanStack Query 管理。
### 6.5 TanStack Query 使用规则
TanStack Query 用于:
- 列表查询
- 详情查询
- 字典查询
- 新增、修改、删除后的缓存刷新
- 后台刷新
- 请求错误状态
- 加载状态
建议封装统一 query key
```ts
export const userKeys = {
all: ['system', 'users'] as const,
list: (params: UserQuery) => [...userKeys.all, 'list', params] as const,
detail: (id: string) => [...userKeys.all, 'detail', id] as const,
}
```
Mutation 成功后按模块刷新:
```ts
queryClient.invalidateQueries({ queryKey: userKeys.all })
```
### 6.6 HTTP 请求封装
统一响应格式:
```ts
export interface ApiResult<T> {
code: string
message: string
data: T
traceId?: string
}
```
分页格式:
```ts
export interface PageResult<T> {
items: T[]
page: number
pageSize: number
total: number
}
```
HTTP 层职责:
- 自动附加 JWT。
- 处理 401 并跳转登录页。
- 处理 403 并提示无权限。
- 统一处理业务错误。
- 记录 `traceId`
### 6.7 权限控制
前端提供三种权限控制:
1. 路由权限:无菜单权限不能进入页面。
2. 按钮权限:无操作权限不显示按钮。
3. 组件权限:复杂页面局部区域可按权限隐藏。
按钮示例:
```vue
<PermissionButton permission="system:user:create">
新增用户
</PermissionButton>
```
前端权限只负责体验,后端接口必须再次校验权限。
### 6.8 UI 风格
整体风格:
- 现代
- 极简
- 高级
- 工作台
- 信息密度适中
- 视觉安静
布局建议:
- 左侧导航栏
- 顶部操作栏
- 面包屑
- 页签栏
- 内容区使用浅灰背景
- 页面主体使用白色内容面板
视觉规范:
- 不做花哨渐变背景。
- 不做厚重阴影。
- 不做复杂主题编辑器。
- 卡片只用于真正需要分组的信息。
- 表格页筛选区要轻,不做大面积表单堆叠。
- 图标统一使用 lucide-vue-next。
- 中文字体默认使用系统字体。
推荐 Tailwind 基础风格:
```css
body {
@apply bg-slate-50 text-slate-950 antialiased;
}
```
推荐主题变量:
```css
:root {
--app-bg: #f8fafc;
--app-surface: #ffffff;
--app-border: #e2e8f0;
--app-text: #0f172a;
--app-muted: #64748b;
--app-primary: #2563eb;
--app-success: #16a34a;
--app-warning: #d97706;
--app-danger: #dc2626;
}
```
### 6.9 Naive UI 主题
Naive UI 通过 `NConfigProvider` 统一配置:
- primary color
- border radius
- font family
- component size
- message provider
- dialog provider
- notification provider
建议默认尺寸:
- 表格:`small`
- 表单:`medium`
- 按钮:`medium`
- 弹窗:保持紧凑
### 6.10 前端基础页面
一期前端页面:
- 登录
- 工作台首页
- 用户管理
- 组织管理
- 角色管理
- 菜单管理
- 字典管理
- 操作日志
- 开放接口调用日志
## 7. 后端架构设计
### 7.1 后端目录结构
当前 `server` 已经按基础框架骨架整理为:
```text
server/
src/main/kotlin/com/bbit/platform/
Application.kt
common/
ApiResult.kt
BizException.kt
config/
AppConfig.kt
database/
system/
SysUserTable.kt
SysOrgTable.kt
SysRoleTable.kt
SysMenuTable.kt
SysUserRoleTable.kt
SysRoleMenuTable.kt
SysDictTypeTable.kt
SysDictItemTable.kt
SysOperationLogTable.kt
SysApiAccessLogTable.kt
business/
plugins/
CorsPlugin.kt
DatabasePlugin.kt
LoggingPlugin.kt
RedisPlugin.kt
SecurityPlugin.kt
SerializationPlugin.kt
StatusPagesPlugin.kt
```
当前结构说明:
- `Application.kt` 负责应用启动和插件装配。
- `common` 放统一响应、业务异常等公共模型。
- `config` 放应用配置读取。
- `plugins` 放 Ktor 插件和基础设施配置,数据库连接与 `dbQuery` 当前也放在 `DatabasePlugin.kt`
- `database/system` 放系统基础表定义。
- `database/business` 预留给后续具体功能表,不在底座阶段预设业务表。
后续只补必要目录,保持结构直接、清晰:
```text
server/
src/main/kotlin/com/bbit/platform/
common/
PageResult.kt
PageQuery.kt
ErrorCode.kt
TraceContext.kt
security/
JwtService.kt
PasswordService.kt
PermissionService.kt
SecurityPrincipal.kt
RequirePermission.kt
bootstrap/
DatabaseInitializer.kt
SeedData.kt
modules/
auth/
system/
user/
org/
role/
menu/
dict/
logs/
operation/
apiaccess/
```
模块目录内部按需拆分 `Routes``Service``Dtos`,不提前创建空文件。
### 7.2 当前默认配置
当前 `server/src/main/resources/application.yaml` 只保留底座需要的配置:
```yaml
ktor:
application:
modules:
- com.bbit.platform.ApplicationKt.module
deployment:
port: 8080
app:
name: "Platform A"
env: "local"
database:
url: "jdbc:postgresql://localhost:5432/platform_a"
user: "platform_a"
password: "platform_a_password"
maximumPoolSize: 16
minimumIdle: 4
redis:
url: "redis://127.0.0.1:6379"
password: "platform_a_redis_password"
security:
jwt:
issuer: "platform-a"
audience: "platform-a-admin"
realm: "Platform A"
secret: "change-me-to-a-strong-secret"
accessTokenTtlMinutes: 120
cors:
allowedHosts: "localhost:5173,127.0.0.1:5173"
```
已经移除的非底座配置:
- 邮件 SMTP
- 阿里云 SDK
- LLM API Key
- 具体业务配置
- 旧项目命名配置
### 7.3 后端分层规则
每个模块保持简单分层:
- `Routes`:HTTP 路由,处理参数绑定和响应。
- `Service`:可供路由复用的服务,承载业务逻辑、权限逻辑、事务边界和必要的数据访问。
- `Dtos`:请求和响应对象。
规则:
- Route 不写复杂业务逻辑。
- Service 是事务、业务规则和可复用能力的主要承载层。
- DTO 和数据库实体不要混用。
### 7.4 统一响应
```kotlin
@Serializable
data class ApiResult<T>(
val code: String,
val message: String,
val data: T? = null,
val traceId: String? = null
)
```
成功:
```json
{
"code": "0",
"message": "成功",
"data": {}
}
```
失败:
```json
{
"code": "SYSTEM.USER_NOT_FOUND",
"message": "用户不存在",
"traceId": "..."
}
```
### 7.5 认证设计
登录流程:
1. 用户提交账号和密码。
2. 后端校验用户状态。
3. 后端校验密码。
4. 后端签发 JWT。
5. 前端保存 token。
6. 前端调用 `/api/auth/me` 获取用户、菜单、权限。
JWT 内容建议:
```json
{
"sub": "userId",
"username": "admin",
"orgId": "1",
"roles": ["admin"],
"tokenVersion": 1
}
```
后端需要支持:
- token 过期。
- 用户禁用后拒绝访问。
- 修改密码后通过 `tokenVersion` 使旧 token 失效。
### 7.6 权限模型
采用 RBAC
```text
sys_user
sys_role
sys_menu
sys_user_role
sys_role_menu
```
菜单类型:
- `CATALOG`:目录
- `MENU`:页面
- `BUTTON`:按钮或接口权限
权限码格式:
```text
system:user:view
system:user:create
system:user:update
system:user:delete
system:role:assign
system:menu:update
system:dict:view
log:operation:view
log:api-access:view
```
接口权限校验示例:
```kotlin
post("/api/system/users") {
requirePermission("system:user:create")
}
```
### 7.7 组织模型
组织使用树结构:
```text
id
parent_id
name
code
sort
status
```
组织用于:
- 用户归属
- 数据范围预留
- 后续数据范围控制的基础
一期建议先完成组织树和用户归属,数据范围可以预留字段,不做复杂实现。
### 7.8 字典模型
字典分为:
- 字典类型
- 字典项
字典类型示例:
```text
user_status
org_status
menu_type
log_status
```
前端登录后可加载常用字典,也可以按需查询。字典适合放入 Pinia 缓存。
字典和代码枚举需要区分:
- 代码枚举用于系统逻辑强依赖的固定值,例如菜单类型、用户状态、角色状态、日志状态。
- 业务字典用于展示、筛选、颜色标识和可配置选项。
- 后端逻辑不能依赖后台随意维护的字典项来决定安全规则。
- 字典项可以影响前端展示,但不能绕过后端权限、状态和数据校验。
### 7.9 初始化数据
初始化数据放在后端工程内维护,由服务端负责创建和升级基础数据。初始化逻辑应支持重复执行,避免多次启动或多次部署后产生重复数据。
建议初始化内容:
- 默认组织:总部或默认组织。
- 默认管理员用户:admin。
- 默认角色:超级管理员。
- 默认菜单:工作台、系统管理、用户管理、组织管理、角色管理、菜单管理、字典管理、日志管理。
- 默认权限码:页面权限和关键按钮权限。
- 默认字典:用户状态、组织状态、角色状态、菜单类型、日志状态。
实现规则:
- 初始化数据按稳定编码执行 upsert,例如 `username``role.code``menu.permission``dict_type.code`
- 管理员账号固定为 `admin`,初始化密码固定写入后端种子数据,首次登录后可在系统内修改。
- 初始化逻辑不写在路由里,建议放在 `bootstrap/SeedData.kt`
- 数据库表初始化和种子数据初始化分开,方便后续迁移到正式迁移工具。
- 初始化失败应让服务启动失败,避免系统处于半初始化状态。
## 8. 日志设计
日志分三类:
1. 系统运行日志
2. 操作日志
3. 开放接口调用日志
### 8.1 系统运行日志
系统运行日志通过 Logback 输出。
记录内容:
- 应用启动
- 数据库连接
- 接口异常
- 未捕获异常
- 慢请求
- 关键配置加载结果
建议输出:
- 控制台日志
- 文件日志
- 按日期滚动
- 错误日志单独文件
### 8.2 操作日志
操作日志记录后台用户在系统内的关键操作。
典型场景:
- 登录成功
- 登录失败
- 新增用户
- 修改用户
- 禁用用户
- 删除用户
- 分配角色
- 修改菜单
- 修改字典
- 重置密码
操作日志字段:
```text
id
trace_id
user_id
username
org_id
operation_type
operation_name
http_method
request_path
request_params
ip
user_agent
status
error_message
cost_ms
created_at
```
敏感字段处理:
- 密码不记录。
- token 不记录。
- 身份证、手机号等可按规则脱敏。
实现方式:
- 提供 `OperationLog` 注解或路由扩展。
- 对关键接口显式声明操作名称。
- 在请求结束后统一落库。
示例:
```kotlin
post("/api/system/users") {
withOperationLog("新增用户") {
requirePermission("system:user:create")
userService.create(call.receive())
}
}
```
### 8.3 开放接口调用日志
开放接口调用日志用于记录平台对外提供的 API 被调用情况。
适用场景:
- 第三方系统调用平台接口。
- 内部其他系统调用平台接口。
- 后续开放 API 网关接入。
字段:
```text
id
trace_id
app_key
app_name
http_method
request_path
request_headers
request_body
response_code
response_body
ip
status
error_message
cost_ms
created_at
```
敏感字段处理:
- Authorization 不记录原文。
- 签名密钥不记录。
- 请求体和响应体限制长度。
- 大请求体不记录 body。
一期可以先设计数据表和查询页面,开放接口鉴权可后续完善。
## 9. 数据库设计
### 9.1 基础表
```text
sys_user
sys_org
sys_role
sys_menu
sys_user_role
sys_role_menu
sys_dict_type
sys_dict_item
sys_operation_log
sys_api_access_log
```
### 9.2 通用字段
建议所有业务表保留:
```text
id
created_at
created_by
updated_at
updated_by
deleted_at
deleted_by
version
```
说明:
- `id` 建议使用雪花 ID 或 UUID。
- `created_at``updated_at` 使用数据库时间或服务端统一时间。
- 默认使用软删除,删除时设置 `deleted_at``deleted_by`
- 查询默认过滤 `deleted_at is null`
- 唯一约束需要考虑软删除数据,避免已删除数据长期占用唯一值。
- `version` 用于乐观锁。
- 操作日志、开放接口调用日志等纯追加日志表不做软删除,也不需要 `updated_at``deleted_at`
软删除规则:
- 普通删除接口默认执行软删除,不直接物理删除数据。
- 删除前需要校验引用关系,例如角色被用户使用、组织下存在用户、菜单被角色引用。
- 系统内置数据可以禁止删除,只允许禁用。
- 物理删除只作为维护操作,不暴露为普通管理端能力。
- 恢复已删除数据不是一期能力,但表结构保留恢复可能性。
### 9.3 用户表
```text
sys_user
id
username
password_hash
nickname
real_name
phone
email
avatar
org_id
status
token_version
last_login_at
last_login_ip
created_at
created_by
updated_at
updated_by
deleted_at
deleted_by
version
```
### 9.4 组织表
```text
sys_org
id
parent_id
name
code
sort
status
created_at
created_by
updated_at
updated_by
deleted_at
deleted_by
version
```
### 9.5 角色表
```text
sys_role
id
name
code
description
status
data_scope
created_at
created_by
updated_at
updated_by
deleted_at
deleted_by
version
```
### 9.6 菜单表
```text
sys_menu
id
parent_id
type
title
name
path
component
icon
permission
sort
visible
keep_alive
status
created_at
created_by
updated_at
updated_by
deleted_at
deleted_by
version
```
### 9.7 字典表
```text
sys_dict_type
id
code
name
status
remark
created_at
created_by
updated_at
updated_by
deleted_at
deleted_by
version
sys_dict_item
id
type_id
label
value
color
sort
status
remark
created_at
created_by
updated_at
updated_by
deleted_at
deleted_by
version
```
## 10. 后端接口范围
一期接口范围:
```http
POST /api/auth/login
POST /api/auth/logout
GET /api/auth/me
GET /api/system/users
POST /api/system/users
GET /api/system/users/{id}
PUT /api/system/users/{id}
DELETE /api/system/users/{id}
PUT /api/system/users/{id}/status
PUT /api/system/users/{id}/password
PUT /api/system/users/{id}/roles
GET /api/system/orgs
POST /api/system/orgs
PUT /api/system/orgs/{id}
DELETE /api/system/orgs/{id}
GET /api/system/roles
POST /api/system/roles
GET /api/system/roles/{id}
PUT /api/system/roles/{id}
DELETE /api/system/roles/{id}
PUT /api/system/roles/{id}/menus
GET /api/system/menus
POST /api/system/menus
PUT /api/system/menus/{id}
DELETE /api/system/menus/{id}
GET /api/system/dict-types
POST /api/system/dict-types
PUT /api/system/dict-types/{id}
DELETE /api/system/dict-types/{id}
GET /api/system/dict-items
POST /api/system/dict-items
PUT /api/system/dict-items/{id}
DELETE /api/system/dict-items/{id}
GET /api/logs/operation
GET /api/logs/api-access
```
接口路径不带版本号,系统规模不大,默认使用稳定路径。为了避免后续频繁破坏前端和已有调用方,需要遵守接口兼容规则:
- 已发布接口路径尽量不改名、不删除。
- 响应字段只增不减,不修改已有字段含义。
- 请求字段新增时必须有默认行为,不能让旧前端请求失败。
- 枚举值可以新增,但前端必须对未知枚举有兜底展示。
- 废弃字段先保留一段时间,代码中标记 deprecated,再择机清理。
- 错误码保持稳定,前端不要依赖错误文案做逻辑判断。
- 破坏性调整优先新增接口,再逐步迁移旧接口。
## 11. 前端页面范围
一期页面范围:
```text
登录页
工作台首页
用户管理
组织管理
角色管理
菜单管理
字典类型管理
字典项管理
操作日志
开放接口调用日志
403
404
```
页面标准能力:
- 查询
- 分页
- 新增
- 编辑
- 删除
- 启用/禁用
- 权限控制
- 操作反馈
## 12. 开发规范
### 12.1 前端规范
- 统一使用 TypeScript。
- 统一使用 `<script setup>`
- 请求统一走 `api/http.ts`
- 服务端状态统一使用 TanStack Query。
- 全局状态才进入 Pinia。
- 页面文件夹按功能组织。
- 组件命名使用 PascalCase。
- API 文件按模块拆分。
- 表格列配置尽量靠近页面,不抽成难读的全局配置。
### 12.2 后端规范
- 统一使用 kotlinx.serialization DTO。
- Route 不写复杂业务逻辑。
- Service 负责事务、业务规则和必要的数据访问。
- 所有接口返回 `ApiResult`
- 所有异常进入统一异常处理。
- 所有需要权限的接口必须显式声明权限码。
- 关键操作必须记录操作日志。
- 新增和修改统一维护审计字段。
- 删除默认软删除,普通查询默认排除已删除数据。
- 初始化数据由后端 `bootstrap` 模块负责,必须可重复执行。
- 字典和代码枚举边界清晰,安全规则不能依赖可配置字典。
- 数据库结构先用 Exposed Table 定义表达清楚,正式生产前再补迁移脚本管理机制。
## 13. 建设顺序
详细建设顺序单独维护在 [建设顺序.md](./建设顺序.md)。
整体分为四期:
1. 一期:基础后端。
2. 二期:完整后端。
3. 三期:基础前端。
4. 四期:完整前端。