Explorar el Código

chore: add AGENTS.md, demand.md, openapi.json for development

yangyi hace 3 días
padre
commit
ca603364a2
Se han modificado 3 ficheros con 1284 adiciones y 0 borrados
  1. 34 0
      AGENTS.md
  2. 284 0
      demand.md
  3. 966 0
      openapi.json

+ 34 - 0
AGENTS.md

@@ -0,0 +1,34 @@
+# AGENTS.md — ui_template
+
+## Tech stack
+
+Vue 3 (`<script setup>`, Composition API) + TypeScript 5.8 + Vite 6 + Element Plus 2.13 + Pinia 3 + Vue Router 4 + Axios. No preprocessor (plain CSS + scoped styles). No test framework. No ESLint. npm (lockfile v3). Node >=18.
+
+## Commands
+
+| Command | What it does |
+|---------|-------------|
+| `npm run dev` | Vite dev server (proxies `/api` → `http://localhost:8080`, strips prefix) |
+| `npm run build` | `vue-tsc -b && vite build` — type-check **must** pass before build |
+| `npm run preview` | Vite preview of built output |
+| `npm run api:gen` | Runs Orval — requires backend at `localhost:8080` with `/v3/api-docs` |
+
+Format only with `npx prettier --write .` (no config file, uses Prettier defaults).
+
+## Key constraints
+
+- **`src/api/` is auto-generated by Orval.** Never hand-edit files under `src/api/` or `src/api/models/`. Edit the OpenAPI spec (backend) instead, then re-run `npm run api:gen`.
+- `@/` path alias maps to `./src/` (configured in both tsconfig and vite).
+- `npm run build` runs `vue-tsc -b` first — unused locals/params (`noUnusedLocals`, `noUnusedParameters`) cause build failure.
+- `erasableSyntaxOnly: true` — no enums, no namespaces, no `constructor` parameter properties.
+- Pinia persistence via `pinia-plugin-persistedstate` — both stores (`meta`, `user`) have `persist: true` (localStorage). Auth token is persisted there, not manually in localStorage.
+- Axios interceptor reads token from Pinia user store (injected as Bearer header). 401 response auto-redirects to `/login`.
+
+## Architecture notes
+
+- Entry: `src/main.ts` → creates Vue app, installs Pinia → Router → ElementPlus → mounts `#app`.
+- Router: history mode. Route meta `requiresAdmin: true` gates admin routes via `beforeEach` guard.
+- API layer: Orval-generated factories (`getAuthController`, `getUserController`) use custom Axios instance from `src/util/axios-instance.ts`.
+- Views: `LoginView` (auth), `UserView` (admin CRUD), `HomeView`, `AboutView`.
+- Layouts: `UserLayout` (public, horizontal nav), `AdminLayout` (sidebar + header).
+- No unit/e2e tests exist — no test dependencies, no test runner config.

+ 284 - 0
demand.md

@@ -0,0 +1,284 @@
+# 咕咕嘎嘎论坛前端详细设计文档(v2.0)
+
+> **修订说明**:根据最新需求调整,底部导航变更为“首页”、“我的订单”、“我的信息”、“我的账户”。打赏支付仅使用钱包余额,采用即时扣款模式。细化帖子详情(专家信息、标签、往期命中)及订单状态逻辑。
+
+---
+
+## 1. 引言
+
+本文档为“咕咕嘎嘎论坛”用户端前端的详细设计,基于最新确认的需求编写,指导开发人员使用 Vue 3 + Element Plus 进行工程实现。文档覆盖项目架构、路由、组件拆分、状态管理、样式、接口对接及关键业务流程。
+
+## 2. 技术栈
+
+| 类别         | 选型                            | 说明                                    |
+| ------------ | ------------------------------- | --------------------------------------- |
+| 框架         | Vue 3 (Composition API)         | 组合式 API,逻辑复用与类型推断          |
+| UI 组件库    | Element Plus                    | 提供统一的企业级 UI 组件                |
+| 状态管理     | Pinia                           | Vue 3 官方推荐,模块化、TypeScript 友好 |
+| 路由         | Vue Router 4                    | SPA 路由管理                            |
+| HTTP 客户端  | Axios                           | 拦截器封装、统一错误处理                |
+| CSS 预处理器 | SCSS                            | 变量、混入、嵌套                        |
+| 图标库       | Element Plus Icons / 自定义 SVG | 导航及功能图标                          |
+| 构建工具     | Vite                            | 快速开发与打包                          |
+| 适配方案     | postcss-px-to-viewport (可选)   | H5 移动端适配,按需启用                 |
+
+## 3. 项目结构
+
+
+
+## 4. 路由设计
+
+底部导航四个主 Tab 对应路由,使用 `meta.tab` 标识:
+
+| 路径              | 页面组件      | Tab 标识      | 说明                   |
+| ----------------- | ------------- | ------------- | ---------------------- |
+| `/`               | Home          | home          | 首页(帖子列表、搜索) |
+| `/orders`         | Orders        | orders        | 我的订单(打赏订单)   |
+| `/notifications`  | Notifications | notifications | 我的信息(系统消息)   |
+| `/profile`        | Profile       | profile       | 我的账户(个人中心)   |
+| `/post/:id`       | PostDetail    | -             | 帖子详情(从首页进入) |
+| `/wallet`         | Wallet        | -             | 钱包(余额/充值/提现) |
+| `/edit-profile`   | EditProfile   | -             | 修改个人信息           |
+| `/page/privacy`   | StaticPage    | -             | 隐私协议               |
+| `/page/complaint` | StaticPage    | -             | 投诉反馈               |
+| `/page/contact`   | StaticPage    | -             | 联系客服               |
+| `/page/faq`       | StaticPage    | -             | 常见问题               |
+
+注意:`PostDetail` 由首页点击进入,不在底部导航,返回时保留浏览位置。
+
+## 5. 全局状态管理(Pinia)
+
+### userStore
+
+- **state**: `userInfo` (id, username, avatar, mobile, role, level, balance, isRealname), `token`
+- **actions**: `fetchUserInfo()`, `updateBalance(amount)`, `updateProfile(data)`, `logout()`
+- **getters**: `isLoggedIn`, `levelBadge` (黄金/钻石/大师)
+
+### postStore
+
+- **state**: `posts`, `currentStatus` (全部/公开/在售/命中/未命中), `searchKeyword`, `pagination`
+- **actions**: `fetchPosts(status, keyword)`, `fetchPostDetail(id)`, `loadMore()`
+
+### orderStore
+
+- **state**: `orders`, `activeFilter` (全部/未支付/已取消/已完成)
+- **actions**: `fetchOrders(filter)`, `createAndPayOrder(postId)` (创建并即时支付), `cancelOrder(id)`
+
+### notificationStore
+
+- **state**: `messages`, `unreadCount`
+- **actions**: `fetchNotifications()`, `markRead(id)`
+
+## 6. 组件设计
+
+### 6.1 公共组件
+
+#### BottomNav.vue
+
+- 底部固定栏,四个入口:首页、我的订单、我的信息、我的账户。
+- 图标使用 Element Plus Icons 或自定义 SVG,激活时高亮。
+- 监听 `route.meta.tab` 切换激活样式。
+
+#### PostCard.vue
+
+- **Props**: `post` { id, title, expertName, tags, price, publishTime, viewCount, status, isTodayNew }
+- **UI**: 卡片展示标题、发布专家(可点击)、帖子标签(`el-tag` 列表)、价格、发布时间(相对时间)、查看人数图标。当日帖子显示“最新”角标。
+- 点击卡片跳转 `/post/:id`。
+
+#### ExpertInfoCard.vue
+
+- **Props**: `expert` { name, avatar, level, realnameVerified, brief }
+- **UI**: 在详情页顶部展示专家头像、名称、等级徽章、实名认证标识、简介。可带关注按钮(暂不做)。
+
+#### PayConfirm.vue
+
+- **Props**: `visible`, `postTitle`, `amount`, `balance`
+- **Events**: `@confirm`, `@cancel`
+- **UI**: `el-dialog` 模态框,标题“确认打赏”,展示内容:帖子标题、打赏金额、当前钱包余额。确认按钮带 loading,避免重复提交。不允许点击遮罩关闭。
+
+#### CountdownTimer.vue
+
+- **Props**: `endTime` (时间戳)
+- **Events**: `@timeout`
+- **UI**: 显示 `mm:ss` 倒计时,归零触发事件。用于订单未支付倒计时(若存在)。
+
+#### GlassBanner.vue
+
+- **Props**: `text`
+- **UI**: 毛玻璃效果欢迎语区域,背景半透明模糊。
+
+#### MarqueeNotice.vue
+
+- **Props**: `noticeText`
+- **UI**: 水平滚动公告栏,点击弹出论坛声明弹窗(含“我已知晓”按钮)。
+
+#### EmptyState.vue
+
+- **Props**: `description`
+- **UI**: 复用 `el-empty`,自定义图片与文字。
+
+### 6.2 页面组件
+
+#### Home.vue
+
+- **布局**: 顶部 Logo 及论坛名(动态配置)→ 欢迎语(GlassBanner)→ 滚动公告(MarqueeNotice)→ 搜索栏与状态筛选 → 帖子列表(PostCard 列表)
+- **搜索栏**: `el-input` 支持标题关键字,`el-select` 选择状态(全部/公开/在售/命中/未命中),搜索按钮或回车触发。
+- **帖子列表**: 滚动加载(`v-infinite-scroll`)或分页,空状态使用 `EmptyState`。
+- **数据加载**: `onMounted` 调用 `postStore.fetchPosts`。
+
+#### PostDetail.vue
+
+- **路由参数**: `id`
+- **布局**: 专家信息卡片 → 帖子详情(标题、内容简介、免责声明)→ 付费内容区域(未打赏显示遮罩与锁图标,已打赏显示完整内容)→ 立即打赏按钮(余额足够时)→ 往期帖子列表
+- **专家信息**: 使用 `ExpertInfoCard` 展示。
+- **立即打赏**:
+   - 点击时校验余额 `userStore.balance >= post.price`,不足则 `ElMessage.warning('余额不足,请先充值')` 并引导去钱包。
+   - 余额充足则打开 `PayConfirm` 弹窗,确认后调用 `orderStore.createAndPayOrder(postId)`。
+   - 支付成功:关闭弹窗,刷新帖子详情(付费内容可见),更新余额,提示“打赏成功”。
+   - 支付失败(如并发余额不足):弹窗提示错误信息,关闭弹窗。
+- **往期帖子列表**: 展示该专家所有已过时效的帖子,每个条目显示标题、日期及命中性标签(`el-tag`:已命中/未命中)。
+
+#### Orders.vue (我的订单)
+
+- **筛选栏**: `el-radio-group` 或 `el-tabs`,选项:全部、未支付、已取消、已完成。
+- **订单列表**: 每个订单卡片显示帖子标题、专家名、金额、状态、创建时间。对于状态为“未支付”且未超时的订单,可显示倒计时并提供“去支付”按钮(调用支付流程,但本项目打赏基本即时完成,此状态极少出现,保留以兼容未来扩展)。
+- **空状态**: 无订单时使用 `EmptyState`。
+- **订单过期处理**: 若存在未支付订单且已超时,自动显示“已过期”并置灰操作。
+
+#### Notifications.vue (我的信息)
+
+- **消息类型**: 系统通知,包括打赏成功、充值成功/失败、提现发起/成功/失败等。
+- **列表**: 按时间倒序,每条消息显示图标、标题、内容摘要、时间戳。未读显示红点。
+- **交互**: 点击消息可标记已读,无跳转。
+- **空状态**: 无消息时“暂无任何消息通知”。
+
+#### Profile.vue (我的账户)
+
+- **用户信息卡片**: 头像(`el-avatar`)、用户名称、等级徽章、用户ID(右侧复制按钮,使用 `navigator.clipboard.writeText`)。
+- **功能菜单**:
+   - 钱包 → 路由 `/wallet`
+   - 隐私协议 → `/page/privacy`
+   - 投诉反馈 → `/page/complaint`
+   - 联系客服 → `/page/contact`
+   - 常见问题 → `/page/faq`
+   - 修改我的信息 → `/edit-profile`
+   - 设置(预留入口,可跳转至占位页面)
+- 使用 `el-cell-group` 构建菜单。
+
+#### Wallet.vue
+
+- **余额展示**: 大号字体,`el-statistic` 组件。
+- **充值/提现按钮**: 点击后弹出 `el-dialog`,输入金额(充值:模拟增加余额;提现:提交申请,需后台审核,前端仅展示交互)。
+- **资金明细**: 列表/表格显示时间、金额、类型(充值/提现/打赏支出)。无记录时显示空状态。
+- **充值/提现流程**:
+   - 充值:输入金额 → 确认 → 调接口(模拟支付)→ 成功后更新余额并记录明细。
+   - 提现:输入金额 → 确认 → 调用提现申请接口 → 提示“提现申请已提交,等待处理”。
+- 注意:钱包页面展示充值订单和提现记录,不展示打赏订单(打赏订单在“我的订单”页)。
+
+#### EditProfile.vue
+
+- 表单字段:头像(可上传)、名称、手机号码、修改密码(原密码、新密码、确认密码)、实名认证(如未认证显示认证入口,已认证显示已认证状态)。
+- 使用 `el-form`,提交后调用更新用户信息接口。
+- 头像上传:使用 `el-upload` 组件,限制大小和格式。
+
+#### StaticPage.vue
+
+- 动态路由参数 `type`,根据类型渲染不同静态内容(隐私协议等)。目前为占位,内容由后端配置或前端硬编码占位文本。
+
+## 7. 样式与主题
+
+- **Element Plus 主题覆盖**: SCSS 变量修改主色、圆角、阴影等,论坛风格偏向暖色或深蓝,具体与 UI 设计对齐。
+- **玻璃效果**: 通过 `backdrop-filter: blur(12px)` 配合半透明背景实现。
+- **底部导航**: 高度 50px,图标大小 24px,安全区适配 (`padding-bottom: env(safe-area-inset-bottom)`)。
+- **帖子卡片**: 轻微阴影 `box-shadow: 0 2px 8px rgba(0,0,0,0.08)`,圆角 8px。
+- **响应式**: 最大宽度 750px 居中,适配移动端,PC 端显示居中窄版。
+
+## 8. 网络请求封装 (Axios)
+
+`api/request.ts`:
+
+- `baseURL` 来自环境变量。
+- 请求拦截器添加 `Authorization` token。
+- 响应拦截器:统一处理 `code`,非正常时 `ElMessage.error`;401 清除登录态并跳转登录页。
+- 封装通用 `get/post/put/delete` 方法。
+
+## 9. 关键业务流程
+
+### 9.1 搜索与状态筛选
+
+- 状态使用 `el-select`,选项:全部、公开、在售、命中、未命中。
+- 关键字使用 `el-input`,支持防抖 (300ms)。
+- 任一条件变化重新请求帖子列表,重置分页。
+- URL 同步 query 参数:`?status=在售&keyword=xx`。
+
+### 9.2 打赏支付流程(钱包余额即时扣款)
+
+1. 详情页点击“立即打赏” → 前端判断余额是否充足。
+2. 充足则打开 `PayConfirm` 弹窗,展示价格和余额。
+3. 用户确认 → 调用 `POST /api/orders/pay-direct` (或合并创建与支付) 传入 `postId`,请求头携带幂等键 `X-Request-Id`。
+4. 后端原子操作:创建订单,扣减余额,记录流水,返回订单状态已完成。
+5. 前端处理:更新余额,关闭弹窗,刷新帖子详情使付费内容可见,显示“打赏成功”。
+6. 异常处理:余额不足或其他错误,后端返回 `code` 对应消息,前端提示,不关闭弹窗或关闭后提示。
+
+> 由于是即时扣款,订单状态直接为“已完成”,不存在未支付状态,因此“订单过期时间”不适用。
+
+### 9.3 我的订单状态展示
+
+- 筛选栏支持全部、未支付、已取消、已完成。
+- 正常打赏订单为“已完成”。
+- 若系统未来扩展其他支付方式或出现异常未支付订单,列表会包含相应状态。倒计时仅对未支付且有过期时间的订单显示。
+- 点击订单可查看详情(暂定不可操作)。
+
+### 9.4 系统通知(我的信息)
+
+- 后端在某些操作后推送通知(如打赏成功、充值成功、提现状态变更)。
+- 前端定期拉取或使用轮询(或 WebSocket 但初期可简化为接口拉取)。
+- 未读消息数显示在底部导航“我的信息”Tab 上(`el-badge`)。
+- 点击消息标记已读。
+
+### 9.5 钱包充值/提现
+
+- 充值:输入金额 → 确认 → 调用模拟支付接口 → 成功则增加余额并生成资金明细。
+- 提现:输入金额 → 确认 → 调用提现申请接口 → 后台审核,前端仅提示已提交。
+- 资金明细接口返回流水列表,支持分页。
+
+### 9.6 “我已知晓”声明弹窗
+
+- 首次进入首页时,若无 `localStorage` 标记,自动弹出声明弹窗;点击“我已知晓”后标记并关闭。
+
+## 10. 接口对接规范
+
+所有接口返回统一结构:`{ code: 200, message: "success", data: {...} }`
+
+关键接口列表:
+
+| 接口                          | 方法    | 说明                                                         |
+| ----------------------------- | ------- | ------------------------------------------------------------ |
+| `/api/posts`                  | GET     | 获取帖子列表,参数:`status`, `keyword`, `page`, `size`      |
+| `/api/posts/:id`              | GET     | 帖子详情,包含付费内容(如果已购买)                         |
+| `/api/posts/:id/pay`          | POST    | 创建订单并支付,请求体:`{ postId }` (可省略),header: `X-Request-Id` |
+| `/api/orders`                 | GET     | 我的订单列表,参数:`status` (all/unpaid/cancelled/completed) |
+| `/api/notifications`          | GET     | 系统消息列表                                                 |
+| `/api/notifications/:id/read` | PUT     | 标记消息已读                                                 |
+| `/api/user/profile`           | GET/PUT | 获取/更新个人信息                                            |
+| `/api/user/balance`           | GET     | 获取余额                                                     |
+| `/api/wallet/recharge`        | POST    | 充值,请求体:`{ amount }`                                   |
+| `/api/wallet/withdraw`        | POST    | 提现申请,请求体:`{ amount }`                               |
+| `/api/wallet/transactions`    | GET     | 资金明细,参数:`page`, `size`                               |
+| `/api/config/site`            | GET     | 获取网站名称、logo、公告等动态配置                           |
+
+分页格式:`{ list: [], total: 100, page: 1, pageSize: 10 }`
+
+## 11. 错误处理与状态展示
+
+- **网络异常**: Axios 拦截器统一 `ElMessage.error('网络异常,请稍后重试')`。
+- **业务错误**: 如余额不足,在 catch 中解析 `message` 并提示。
+- **加载态**: 列表使用 `v-loading` 或骨架屏 `el-skeleton`;按钮点击后 `loading` 禁止重复提交。
+- **空状态**: 所有列表和数据区域使用 `EmptyState` 组件。
+- **认证失效**: 401 时清除 token 并跳转登录页(登录页需补充实现,假定需登录)。
+
+---
+
+**文档版本**:v2.0  
+**编写日期**:2026-06-14  
+**状态**:待评审

+ 966 - 0
openapi.json

@@ -0,0 +1,966 @@
+{
+  "openapi": "3.1.0",
+  "info": {
+    "title": "Serve API",
+    "description": "Serve应用接口文档",
+    "contact": {
+      "name": "杨逸"
+    },
+    "version": "0.0.1-SNAPSHOT"
+  },
+  "servers": [
+    {
+      "url": "http://localhost:8080",
+      "description": "Generated server url"
+    }
+  ],
+  "tags": [
+    {
+      "name": "AuthController",
+      "description": "用户认证相关接口"
+    }
+  ],
+  "paths": {
+    "/user": {
+      "get": {
+        "tags": [
+          "user-controller"
+        ],
+        "operationId": "queryByPage",
+        "parameters": [
+          {
+            "name": "account",
+            "in": "query",
+            "required": true,
+            "schema": {
+              "type": "string",
+              "default": ""
+            }
+          },
+          {
+            "name": "username",
+            "in": "query",
+            "required": true,
+            "schema": {
+              "type": "string",
+              "default": ""
+            }
+          },
+          {
+            "name": "role",
+            "in": "query",
+            "required": true,
+            "schema": {
+              "type": "string",
+              "default": ""
+            }
+          },
+          {
+            "name": "enable",
+            "in": "query",
+            "required": true,
+            "schema": {
+              "type": "integer",
+              "format": "int32",
+              "default": 1
+            }
+          },
+          {
+            "name": "pageNum",
+            "in": "query",
+            "required": true,
+            "schema": {
+              "type": "integer",
+              "format": "int32",
+              "default": 1
+            }
+          },
+          {
+            "name": "pageSize",
+            "in": "query",
+            "required": true,
+            "schema": {
+              "type": "integer",
+              "format": "int32",
+              "default": 10
+            }
+          }
+        ],
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponsePageVoListUserVo"
+                }
+              }
+            }
+          }
+        }
+      },
+      "put": {
+        "tags": [
+          "user-controller"
+        ],
+        "operationId": "edit",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/UserDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseBoolean"
+                }
+              }
+            }
+          }
+        }
+      },
+      "post": {
+        "tags": [
+          "user-controller"
+        ],
+        "operationId": "add",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/UserDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseBoolean"
+                }
+              }
+            }
+          }
+        }
+      },
+      "delete": {
+        "tags": [
+          "user-controller"
+        ],
+        "operationId": "deleteById",
+        "parameters": [
+          {
+            "name": "ids",
+            "in": "query",
+            "required": true,
+            "schema": {
+              "type": "array",
+              "items": {
+                "type": "string"
+              }
+            }
+          }
+        ],
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseBoolean"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/user/updateStatus": {
+      "put": {
+        "tags": [
+          "user-controller"
+        ],
+        "operationId": "updateUserStatus",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/UpdateUserStatusDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/Response"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/user/updatePassword": {
+      "put": {
+        "tags": [
+          "user-controller"
+        ],
+        "operationId": "updatePassword",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/UpdateUserPasswordDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/Response"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/user/updateAvatar": {
+      "put": {
+        "tags": [
+          "user-controller"
+        ],
+        "operationId": "updateUserAvatar",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/UpdateUserAvatarDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/Response"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/meta/updateWebsiteMeta": {
+      "post": {
+        "tags": [
+          "meta-controller"
+        ],
+        "operationId": "updateWebsiteMeta",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/WebsiteMetaDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/Response"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/auth/register": {
+      "post": {
+        "tags": [
+          "AuthController"
+        ],
+        "summary": "用户注册",
+        "operationId": "register",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/RegisterDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseAuthTokenVo"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/auth/login": {
+      "post": {
+        "tags": [
+          "AuthController"
+        ],
+        "summary": "用户登录",
+        "operationId": "login",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/LoginDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseAuthTokenVo"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/user/{id}": {
+      "get": {
+        "tags": [
+          "user-controller"
+        ],
+        "operationId": "queryById",
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "required": true,
+            "schema": {
+              "type": "string",
+              "minLength": 1
+            }
+          }
+        ],
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseUserVo"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/meta/getWebsiteMeta": {
+      "get": {
+        "tags": [
+          "meta-controller"
+        ],
+        "operationId": "getWebsiteMeta",
+        "responses": {
+          "400": {
+            "description": "Bad Request",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResponseListFieldError"
+                }
+              }
+            }
+          },
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "$ref": "#/components/schemas/Response"
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  },
+  "components": {
+    "schemas": {
+      "FieldError": {
+        "type": "object",
+        "properties": {
+          "codes": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          },
+          "arguments": {
+            "type": "array",
+            "items": {
+
+            }
+          },
+          "defaultMessage": {
+            "type": "string"
+          },
+          "objectName": {
+            "type": "string"
+          },
+          "field": {
+            "type": "string"
+          },
+          "rejectedValue": {
+
+          },
+          "bindingFailure": {
+            "type": "boolean"
+          },
+          "code": {
+            "type": "string"
+          }
+        }
+      },
+      "ResponseListFieldError": {
+        "type": "object",
+        "description": "后端统一的响应实体",
+        "properties": {
+          "code": {
+            "type": "integer",
+            "format": "int32",
+            "description": "状态码;200:成功"
+          },
+          "message": {
+            "type": "string",
+            "description": "响应附加信息"
+          },
+          "data": {
+            "type": "array",
+            "description": "响应的具体数据",
+            "items": {
+              "$ref": "#/components/schemas/FieldError"
+            }
+          }
+        }
+      },
+      "UserDto": {
+        "type": "object",
+        "description": "用户新增和修改使用的DTO",
+        "properties": {
+          "id": {
+            "type": "string",
+            "description": "用户ID"
+          },
+          "account": {
+            "type": "string",
+            "description": "用户账号",
+            "maxLength": 20,
+            "minLength": 4
+          },
+          "password": {
+            "type": "string",
+            "description": "用户密码",
+            "maxLength": 20,
+            "minLength": 4
+          },
+          "username": {
+            "type": "string",
+            "description": "用户名称",
+            "maxLength": 32,
+            "minLength": 2
+          },
+          "role": {
+            "type": "string",
+            "description": "用户角色",
+            "maxLength": 32,
+            "minLength": 4
+          }
+        },
+        "required": [
+          "account",
+          "password",
+          "role",
+          "username"
+        ]
+      },
+      "ResponseBoolean": {
+        "type": "object",
+        "description": "后端统一的响应实体",
+        "properties": {
+          "code": {
+            "type": "integer",
+            "format": "int32",
+            "description": "状态码;200:成功"
+          },
+          "message": {
+            "type": "string",
+            "description": "响应附加信息"
+          },
+          "data": {
+            "type": "boolean",
+            "description": "响应的具体数据"
+          }
+        }
+      },
+      "UpdateUserStatusDto": {
+        "type": "object",
+        "description": "用户状态更新使用的DTO",
+        "properties": {
+          "id": {
+            "type": "string",
+            "description": "用户ID",
+            "minLength": 1
+          },
+          "enable": {
+            "type": "integer",
+            "format": "int32",
+            "description": "更新后的状态;1:启用,0:禁用",
+            "maximum": 1,
+            "minLength": 1,
+            "minimum": 0
+          }
+        },
+        "required": [
+          "enable",
+          "id"
+        ]
+      },
+      "Response": {
+        "type": "object",
+        "description": "后端统一的响应实体",
+        "properties": {
+          "code": {
+            "type": "integer",
+            "format": "int32",
+            "description": "状态码;200:成功"
+          },
+          "message": {
+            "type": "string",
+            "description": "响应附加信息"
+          },
+          "data": {
+            "description": "响应的具体数据"
+          }
+        }
+      },
+      "UpdateUserPasswordDto": {
+        "type": "object",
+        "description": "用户密码修改使用的DTO",
+        "properties": {
+          "id": {
+            "type": "string",
+            "description": "用户ID",
+            "minLength": 1
+          },
+          "password": {
+            "type": "string",
+            "description": "用户的当前密码",
+            "maxLength": 20,
+            "minLength": 4
+          },
+          "oldPassword": {
+            "type": "string",
+            "description": "用户的新密码",
+            "maxLength": 20,
+            "minLength": 4
+          }
+        },
+        "required": [
+          "id",
+          "oldPassword",
+          "password"
+        ]
+      },
+      "UpdateUserAvatarDto": {
+        "type": "object",
+        "description": "更新用户头像的实体DTO",
+        "properties": {
+          "id": {
+            "type": "string",
+            "description": "用户的ID",
+            "minLength": 1
+          },
+          "avatar": {
+            "type": "string",
+            "description": "头像地址的链接",
+            "minLength": 1,
+            "pattern": "^(https?)://[^\\s/$.?#].[^\\s]*$"
+          }
+        },
+        "required": [
+          "avatar",
+          "id"
+        ]
+      },
+      "WebsiteMetaDto": {
+        "type": "object",
+        "description": "网站元数据DTO,用于更新网站配置信息",
+        "properties": {
+          "title": {
+            "type": "string",
+            "description": "网站标题",
+            "maxLength": 200,
+            "minLength": 0
+          },
+          "logo": {
+            "type": "string",
+            "description": "网站logo URL",
+            "maxLength": 500,
+            "minLength": 0
+          },
+          "statement": {
+            "type": "string",
+            "description": "网站声明",
+            "maxLength": 500,
+            "minLength": 0
+          },
+          "announcement": {
+            "type": "string",
+            "description": "网站公告",
+            "maxLength": 500,
+            "minLength": 0
+          }
+        }
+      },
+      "RegisterDto": {
+        "type": "object",
+        "description": "注册请求参数",
+        "properties": {
+          "account": {
+            "type": "string",
+            "description": "账号",
+            "maxLength": 20,
+            "minLength": 4
+          },
+          "password": {
+            "type": "string",
+            "description": "密码",
+            "maxLength": 20,
+            "minLength": 4
+          },
+          "username": {
+            "type": "string",
+            "description": "用户名",
+            "maxLength": 32,
+            "minLength": 2
+          }
+        },
+        "required": [
+          "account",
+          "password",
+          "username"
+        ]
+      },
+      "AuthTokenVo": {
+        "type": "object",
+        "description": "登录响应",
+        "properties": {
+          "token": {
+            "type": "string",
+            "description": "JWT token"
+          },
+          "userId": {
+            "type": "integer",
+            "format": "int64",
+            "description": "用户ID"
+          },
+          "account": {
+            "type": "string",
+            "description": "账号"
+          },
+          "username": {
+            "type": "string",
+            "description": "用户名"
+          },
+          "role": {
+            "type": "string",
+            "description": "角色"
+          }
+        }
+      },
+      "ResponseAuthTokenVo": {
+        "type": "object",
+        "description": "后端统一的响应实体",
+        "properties": {
+          "code": {
+            "type": "integer",
+            "format": "int32",
+            "description": "状态码;200:成功"
+          },
+          "message": {
+            "type": "string",
+            "description": "响应附加信息"
+          },
+          "data": {
+            "$ref": "#/components/schemas/AuthTokenVo",
+            "description": "响应的具体数据"
+          }
+        }
+      },
+      "LoginDto": {
+        "type": "object",
+        "description": "登录请求参数",
+        "properties": {
+          "account": {
+            "type": "string",
+            "description": "账号",
+            "minLength": 1
+          },
+          "password": {
+            "type": "string",
+            "description": "密码",
+            "minLength": 1
+          }
+        },
+        "required": [
+          "account",
+          "password"
+        ]
+      },
+      "PageVoListUserVo": {
+        "type": "object",
+        "description": "分页数据对象",
+        "properties": {
+          "total": {
+            "type": "integer",
+            "format": "int64",
+            "description": "总数据条数"
+          },
+          "data": {
+            "type": "array",
+            "description": "当前页的数据",
+            "items": {
+              "$ref": "#/components/schemas/UserVo"
+            }
+          }
+        }
+      },
+      "ResponsePageVoListUserVo": {
+        "type": "object",
+        "description": "后端统一的响应实体",
+        "properties": {
+          "code": {
+            "type": "integer",
+            "format": "int32",
+            "description": "状态码;200:成功"
+          },
+          "message": {
+            "type": "string",
+            "description": "响应附加信息"
+          },
+          "data": {
+            "$ref": "#/components/schemas/PageVoListUserVo",
+            "description": "响应的具体数据"
+          }
+        }
+      },
+      "UserVo": {
+        "type": "object",
+        "description": "用户响应展示对象",
+        "properties": {
+          "id": {
+            "type": "string",
+            "description": "用户ID"
+          },
+          "account": {
+            "type": "string",
+            "description": "用户账号"
+          },
+          "username": {
+            "type": "string",
+            "description": "用户名称"
+          },
+          "role": {
+            "type": "string",
+            "description": "用户角色"
+          },
+          "avatar": {
+            "type": "string",
+            "description": "用户头像地址"
+          },
+          "enable": {
+            "type": "integer",
+            "format": "int32",
+            "description": "用户状态;0:禁用,1:启用"
+          }
+        }
+      },
+      "ResponseUserVo": {
+        "type": "object",
+        "description": "后端统一的响应实体",
+        "properties": {
+          "code": {
+            "type": "integer",
+            "format": "int32",
+            "description": "状态码;200:成功"
+          },
+          "message": {
+            "type": "string",
+            "description": "响应附加信息"
+          },
+          "data": {
+            "$ref": "#/components/schemas/UserVo",
+            "description": "响应的具体数据"
+          }
+        }
+      }
+    }
+  }
+}