13 KiB
13 KiB
后台管理(Admin API + 前端)规范
Purpose
本能力描述"后台管理侧"的完整实现:包括后端接口和前端管理网站。
- 后端接口:用于查询用户与会话记录,供管理端页面/外部系统使用,以
AdminController提供的 API 为主。 - 前端管理网站:基于 Vue 3.x + Element Plus + Vite 构建的管理员操作界面,用于系统配置、数据管理和监控。
Requirements
Requirement: 前端技术架构
前端管理网站 SHALL 使用以下技术栈开发:
- 前端框架:Vue 3.x(使用 Composition API 和
<script setup>语法) - UI组件库:Element Plus
- 路由管理:vue-router
- 状态管理:Pinia(用于深色/浅色模式状态)
- HTTP客户端:axios
- 构建工具:Vite
- 开发环境:Node.js
- 后端服务:与微信小程序共用 ASP.NET Core MVC 后端(AdminController)
Scenario: 前端项目初始化
- WHEN 创建前端项目
- THEN 使用 Vite 初始化 Vue 3.x 项目
- AND 安装 Element Plus、vue-router、Pinia、axios 等依赖
- AND 配置项目目录结构(src/router、src/components、src/store、src/styles)
Requirement: 用户认证(前端验证)
前端管理网站 SHALL 提供管理员登录功能,使用前端验证方式。
Scenario: 管理员登录
- WHEN 管理员访问登录页面
- THEN 显示登录表单(用户名、密码字段)
- AND 输入正确的用户名和密码(均为 Admin)后能够成功登录
- AND 登录成功后跳转到首页
- AND 登录状态保存在 Pinia store 中
Scenario: 登录验证失败
- WHEN 管理员输入错误的用户名或密码
- THEN 显示错误提示信息
- AND 不允许进入系统
Scenario: 未登录访问保护页面
- WHEN 未登录用户访问受保护页面
- THEN 自动重定向到登录页面
Requirement: 响应式设计
前端管理网站 SHALL 支持响应式设计,优先适配手机宽度。
Scenario: 手机端访问
- WHEN 管理员使用手机(宽度 < 768px)访问后台管理网站
- THEN 网站布局能够自动调整,完美适配手机屏幕
- AND 菜单采用折叠或抽屉式布局
- AND 表格支持横向滚动
Scenario: 平板端访问
- WHEN 管理员使用平板(768px <= 宽度 < 1024px)访问后台管理网站
- THEN 网站布局能够自动调整,适配平板屏幕
- AND 菜单采用侧边栏布局
Scenario: 桌面端访问
- WHEN 管理员使用桌面(宽度 >= 1024px)访问后台管理网站
- THEN 网站布局能够自动调整,适配桌面屏幕
- AND 菜单采用侧边栏布局
Requirement: 深色/浅色模式切换
前端管理网站 SHALL 支持深色模式和浅色模式的自由切换。
Scenario: 切换到深色模式
- WHEN 管理员点击深色模式切换按钮
- THEN 网站切换到深色主题
- AND 所有组件和页面使用深色配色
- AND 主题状态保存在 Pinia store 中
Scenario: 切换到浅色模式
- WHEN 管理员点击浅色模式切换按钮
- THEN 网站切换到浅色主题
- AND 所有组件和页面使用浅色配色
- AND 主题状态保存在 Pinia store 中
Scenario: 主题状态持久化
- WHEN 管理员刷新页面
- THEN 网站保持上次选择的主题模式
Requirement: 路由管理
前端管理网站 SHALL 使用 vue-router 管理多页面路由。
Scenario: 路由配置
- WHEN 配置路由
- THEN 定义登录页、首页、会话记录管理页、用户管理页等路由
- AND 设置路由守卫,未登录用户重定向到登录页
Scenario: 菜单导航
- WHEN 管理员点击菜单项
- THEN 路由跳转到对应页面
- AND 菜单高亮显示当前页面
Requirement: 菜单布局
前端管理网站 SHALL 使用合理的菜单布局来控制多页面。
Scenario: 侧边栏菜单
- WHEN 管理员在桌面或平板端访问
- THEN 显示侧边栏菜单
- AND 菜单包含:首页、会话记录管理、用户管理等功能入口
Scenario: 抽屉式菜单
- WHEN 管理员在手机端访问
- THEN 显示抽屉式菜单(点击菜单按钮展开)
- AND 菜单包含:首页、会话记录管理、用户管理等功能入口
Requirement: 模块化设计
前端管理网站 SHALL 采用模块化设计思路。
Scenario: 组件模块化
- WHEN 开发页面功能
- THEN 将可复用的UI元素封装为独立组件
- AND 每个组件职责单一,可复用
Scenario: 页面模块化
- WHEN 开发页面
- THEN 每个页面独立管理自己的状态和逻辑
- AND 通过 Pinia store 共享全局状态
Requirement: 查询会话记录(管理端)
系统 SHALL 提供接口按条件查询会话记录,并返回会话与用户的联合视图。
接口:POST /api/Admin/QueryConversations
过滤规则(均为可选):
UserKey:用户唯一标识键MessageType:消息类型(1-公有,2-私有)StartTime、EndTime(按RecordTimeUTCStamp过滤,datetime参数转换为UTC时间戳)Department:用户部门(来自用户表)
默认规则:
- 仅返回
xcx_conversation.IsDeleted = 0的记录 - 按
RecordTimeUTCStamp DESC排序 - 通过
LEFT JOIN xcx_users补全用户字段
返回字段(会话):
- Id, Guid, UserKey, ConversationContent, SendMethod
- UserLocation, Latitude, Longitude
- RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime
- MessageType, SpeakingTime
返回字段(用户,通过LEFT JOIN关联):
- UserName, WeChatName, PhoneNumber, AvatarUrl, Department
Scenario: 管理端按时间范围查询
- WHEN 管理端提交包含
StartTime与EndTime的查询请求 - THEN 系统将datetime参数转换为UTC时间戳
- AND 返回
RecordTimeUTCStamp落在区间内的会话记录 - AND 结果按
RecordTimeUTCStamp倒序
Scenario: 管理端按部门筛选
- WHEN 管理端提交包含
Department的查询请求 - THEN 系统仅返回用户部门匹配的会话记录
Scenario: 管理端按UserKey筛选
- WHEN 管理端提交包含
UserKey的查询请求 - THEN 系统仅返回指定用户的会话记录
Scenario: 管理端按MessageType筛选
- WHEN 管理端提交包含
MessageType的查询请求 - THEN 系统仅返回指定消息类型的会话记录
Scenario: 管理端查询全部会话记录
- WHEN 管理端提交不包含任何筛选条件的查询请求
- THEN 系统返回所有
IsDeleted = 0的会话记录 - AND 结果按
RecordTimeUTCStamp倒序
Scenario: 管理端组合多条件查询
- WHEN 管理端提交包含多个筛选条件的查询请求
- THEN 系统返回同时满足所有条件的会话记录
Requirement: 查询用户列表(管理端)
系统 SHALL 提供接口返回可用用户列表,用于管理侧选择/筛选。
接口:GET /api/Admin/QueryUsers
过滤规则(内置):
PhoneNumber不为空且不为空字符串UserName不为空且不为空字符串UserKey不为空且不为空字符串IsDisabled = 0(未禁用)- 按
FirstLoginTime DESC排序
返回字段:
- Id, UserName, UserKey, WeChatName, PhoneNumber
- FirstLoginTime, IsDisabled, CreateTime, UpdateTime
- AvatarUrl, Department
Scenario: 获取用户列表
- WHEN 管理端请求用户列表
- THEN 返回已完善基础信息且未禁用的用户
- AND 结果按首次登录时间倒序排列
Known Limitations
- 当前接口未提供分页与导出能力(若管理端数据量很大,需在后续能力中补齐)。
- 当前未强制鉴权(JWT 配置存在但未启用认证中间件/授权标注)。
- 前端验证仅使用固定账号密码(Admin/Admin),安全性较低,后续应考虑接入后端JWT认证。
Implementation Details
后端技术实现
- 控制器文件:
WxCheckMvc/Controllers/AdminController.cs - 数据库连接: 使用
MySqlConnection进行数据库操作 - 路由模式:
[Route("api/[controller]/[action]")] - API特性:
[ApiController]提供自动模型验证和绑定
前端技术实现
- 项目目录:
admin-web/ - 构建工具: Vite
- 路由配置:
src/router/index.js - 状态管理:
src/store/index.js(Pinia) - 样式目录:
src/styles/ - 组件目录:
src/components/ - 页面目录:
src/views/
项目结构
admin-web/
├── src/
│ ├── assets/ # 静态资源
│ ├── components/ # 可复用组件
│ │ ├── Layout/ # 布局组件(侧边栏、顶部栏)
│ │ ├── ThemeSwitcher.vue # 主题切换组件
│ │ └── ...
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── store/ # Pinia store
│ │ ├── index.js
│ │ ├── auth.js # 认证状态
│ │ └── theme.js # 主题状态
│ ├── styles/ # 全局样式
│ │ ├── variables.scss # CSS变量(深色/浅色主题)
│ │ ├── responsive.scss # 响应式样式
│ │ └── main.scss
│ ├── utils/ # 工具函数
│ │ └── request.js # axios封装
│ ├── views/ # 页面组件
│ │ ├── Login.vue # 登录页
│ │ ├── Home.vue # 首页
│ │ ├── ConversationList.vue # 会话记录管理页
│ │ └── UserList.vue # 用户管理页
│ ├── App.vue
│ └── main.js
├── index.html
├── package.json
└── vite.config.js
Pinia Store 结构
auth.js(认证状态)
import { defineStore } from 'pinia'
export const useAuthStore = defineStore('auth', {
state: () => ({
isLoggedIn: false,
username: ''
}),
actions: {
login(username, password) {
if (username === 'Admin' && password === 'Admin') {
this.isLoggedIn = true
this.username = username
return true
}
return false
},
logout() {
this.isLoggedIn = false
this.username = ''
}
}
})
theme.js(主题状态)
import { defineStore } from 'pinia'
export const useThemeStore = defineStore('theme', {
state: () => ({
isDark: localStorage.getItem('theme') === 'dark'
}),
actions: {
toggleTheme() {
this.isDark = !this.isDark
localStorage.setItem('theme', this.isDark ? 'dark' : 'light')
document.documentElement.setAttribute('data-theme', this.isDark ? 'dark' : 'light')
},
initTheme() {
const theme = localStorage.getItem('theme') || 'light'
this.isDark = theme === 'dark'
document.documentElement.setAttribute('data-theme', theme)
}
}
})
响应式断点
- 手机端: < 768px
- 平板端: 768px - 1023px
- 桌面端: >= 1024px
数据模型
ConversationQueryRequest
public class ConversationQueryRequest
{
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public string UserKey { get; set; }
public int? MessageType { get; set; }
public string Department { get; set; }
}
ConversationQueryResponse
public class ConversationQueryResponse
{
public long Id { get; set; }
public string Guid { get; set; }
public string UserKey { get; set; }
public string ConversationContent { get; set; }
public string SendMethod { get; set; }
public string UserLocation { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public DateTime RecordTime { get; set; }
public long RecordTimeUTCStamp { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreateTime { get; set; }
public int MessageType { get; set; }
public int? SpeakingTime { get; set; }
public string UserName { get; set; }
public string WeChatName { get; set; }
public string PhoneNumber { get; set; }
public string AvatarUrl { get; set; }
public string Department { get; set; }
}
UserQueryResponse
public class UserQueryResponse
{
public long Id { get; set; }
public string UserName { get; set; }
public string UserKey { get; set; }
public string WeChatName { get; set; }
public string PhoneNumber { get; set; }
public DateTime FirstLoginTime { get; set; }
public bool IsDisabled { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
public string AvatarUrl { get; set; }
public string Department { get; set; }
}
异常处理
- 所有方法使用 try-catch-finally 模式
- 异常时返回 HTTP 500 状态码
- 响应格式:
{ success: false, message: "错误描述", error: "异常详情" } - finally 块确保数据库连接正确关闭
数据库操作
- 使用参数化查询防止 SQL 注入
- 动态构建 SQL 查询条件,仅添加非空参数
- 使用
LEFT JOIN关联xcx_users表获取用户信息 - 时间转换:
DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()