1557 lines
43 KiB
Markdown
1557 lines
43 KiB
Markdown
# access-manage 接口文档
|
||
|
||
本文档按当前后端代码整理,供前端对接使用。接口来源主要是:
|
||
|
||
- `src/app.ts`
|
||
- `src/modules/auth/*`
|
||
- `src/modules/permissions/*`
|
||
- `src/modules/catalog/*`
|
||
- `src/modules/employees/*`
|
||
|
||
## 基础约定
|
||
|
||
| 项目 | 说明 |
|
||
| --- | --- |
|
||
| 本地 Base URL | `http://localhost:3500` |
|
||
| 业务接口前缀 | `/api` |
|
||
| 健康检查 | `/health`,不带 `/api` 前缀 |
|
||
| 请求体格式 | `Content-Type: application/json` |
|
||
| 鉴权方式 | `Authorization: Bearer <token>` |
|
||
| 时间格式 | ISO 字符串,例如 `2026-05-26T08:00:00.000Z` |
|
||
| 字段命名 | 请求和响应都使用 `camelCase` |
|
||
|
||
不需要登录的接口:
|
||
|
||
- `GET /health`
|
||
- `POST /api/auth/login`
|
||
- `POST /api/auth/admin/login`
|
||
- `POST /api/auth/employee/login`
|
||
|
||
其他接口都需要 Bearer token。门店、角色、员工管理接口还要求当前账号具备后台管理权限。
|
||
|
||
## 通用响应
|
||
|
||
### 成功响应
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
### 分页响应
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"items": [],
|
||
"pagination": {
|
||
"page": 1,
|
||
"pageSize": 20,
|
||
"total": 0,
|
||
"totalPages": 0
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 错误响应
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"error": {
|
||
"code": "VALIDATION_ERROR",
|
||
"message": "请求参数不合法",
|
||
"details": []
|
||
}
|
||
}
|
||
```
|
||
|
||
常见错误码:
|
||
|
||
| HTTP 状态码 | `error.code` | 说明 |
|
||
| --- | --- | --- |
|
||
| 400 | `VALIDATION_ERROR` | zod 参数校验失败 |
|
||
| 400 | `BAD_REQUEST` | 业务参数不合法,例如门店不存在或角色不存在 |
|
||
| 401 | `UNAUTHORIZED` | 未登录、token 过期、账号密码错误或后台登录权限不足 |
|
||
| 403 | `FORBIDDEN` | 已登录但没有访问或操作权限 |
|
||
| 404 | `NOT_FOUND` | 资源不存在 |
|
||
| 409 | `CONFLICT` | 唯一字段冲突、资源被绑定不能删除等 |
|
||
| 500 | `INTERNAL_SERVER_ERROR` | 未处理的服务端异常 |
|
||
|
||
`DELETE` 成功时返回 `204 No Content`,没有响应体。
|
||
|
||
## 测试账号
|
||
|
||
本地迁移会初始化超级管理员:
|
||
|
||
| 类型 | 账号 | 密码 |
|
||
| --- | --- | --- |
|
||
| 超级管理员 | `admin` | `Admin@123456` |
|
||
|
||
员工创建后默认密码为:
|
||
|
||
```text
|
||
pw111111
|
||
```
|
||
|
||
员工登录账号使用员工手机号。
|
||
|
||
## 权限模型
|
||
|
||
### 权限码
|
||
|
||
| 权限码 | 说明 |
|
||
| --- | --- |
|
||
| `*` | 超级管理员,拥有全部权限 |
|
||
| `store:view` | 查看门店和门店下员工 |
|
||
| `store:manage` | 新增、修改、停用、软删除门店 |
|
||
| `role:view` | 查看角色 |
|
||
| `role:manage` | 新增、修改、软删除自定义角色 |
|
||
| `employee:view:all` | 查看全部门店员工 |
|
||
| `employee:view:store` | 查看当前门店员工 |
|
||
| `employee:manage` | 新增、修改、启停、移除和软删除员工 |
|
||
| `permission:view` | 查看权限策略 |
|
||
| `permission:manage` | 分配角色权限 |
|
||
| `announcement:view` | 查看公告 |
|
||
| `announcement:manage` | 新增、编辑、发布、归档公告 |
|
||
| `task:view` | 查看任务 |
|
||
| `task:manage` | 新建、编辑、取消任务 |
|
||
| `shift:view` | 查看排班 |
|
||
| `shift:manage` | 新增、编辑、取消排班 |
|
||
| `credential:reset` | 重置下级员工密码 |
|
||
| `credential:audit:view` | 查看凭据操作审计 |
|
||
|
||
### 角色权限
|
||
|
||
| 角色 | 作用范围 | 权限 |
|
||
| --- | --- | --- |
|
||
| `super_admin` 超级管理员 | 全部门店 | `*` |
|
||
| `admin` 管理员 | 按权限点控制 | `store:view`, `store:manage`, `role:view`, `role:manage`, `employee:view:all`, `employee:manage`, `permission:view`, `permission:manage` |
|
||
| `store_manager` 店长 | 当前门店 | `employee:view:store` |
|
||
| `cashier` 收银员 | 默认无后台菜单 | 可通过权限管理动态分配 |
|
||
| `kitchen` 后厨 | 默认无后台菜单 | 可通过权限管理动态分配 |
|
||
| `part_time` 兼职 | 默认无后台菜单 | 可通过权限管理动态分配 |
|
||
|
||
角色权限保存在 `role_permissions` 表。前端通过 `GET /api/permissions/definitions` 获取可分配权限点,通过 `PUT /api/permissions/roles/:roleId` 保存角色权限。超级管理员是独立账号类型,固定拥有 `*`,不参与角色权限分配。
|
||
|
||
## 数据结构
|
||
|
||
### AuthUser
|
||
|
||
```ts
|
||
interface AuthUser {
|
||
id: number;
|
||
username: string;
|
||
displayName: string;
|
||
accountType: "SUPER_ADMIN" | "EMPLOYEE";
|
||
storeId?: number;
|
||
storeName?: string;
|
||
roles: Array<{
|
||
id: number;
|
||
code: string;
|
||
name: string;
|
||
}>;
|
||
permissions: string[];
|
||
canManage: boolean;
|
||
mustChangePassword?: boolean;
|
||
}
|
||
```
|
||
|
||
### Store
|
||
|
||
```ts
|
||
type StoreStatus = "ACTIVE" | "INACTIVE";
|
||
|
||
interface StoreOption {
|
||
id: number;
|
||
name: string;
|
||
address: string | null;
|
||
phone: string | null;
|
||
}
|
||
|
||
interface Store extends StoreOption {
|
||
status: StoreStatus;
|
||
createdAt: string;
|
||
updatedAt: string;
|
||
}
|
||
```
|
||
|
||
### Role
|
||
|
||
```ts
|
||
interface RoleOption {
|
||
id: number;
|
||
code: string;
|
||
name: string;
|
||
description: string | null;
|
||
isSystem: boolean;
|
||
}
|
||
|
||
interface Role extends RoleOption {
|
||
createdAt: string;
|
||
updatedAt: string;
|
||
}
|
||
```
|
||
|
||
### Employee
|
||
|
||
```ts
|
||
type EmployeeStatus = "ACTIVE" | "INACTIVE";
|
||
type EmployeeStoreStatus = "ACTIVE" | "INACTIVE";
|
||
|
||
interface EmployeeStatusTag {
|
||
code: "EMPLOYEE_ACTIVE" | "EMPLOYEE_INACTIVE" | "STORE_INACTIVE";
|
||
label: string;
|
||
variant: "success" | "warning" | "default";
|
||
}
|
||
|
||
interface Employee {
|
||
id: number;
|
||
storeId: number;
|
||
storeName: string;
|
||
storeStatus: EmployeeStoreStatus;
|
||
name: string;
|
||
phone: string;
|
||
status: EmployeeStatus;
|
||
statusTags: EmployeeStatusTag[];
|
||
remark: string | null;
|
||
roles: Array<{
|
||
id: number;
|
||
code: string;
|
||
name: string;
|
||
}>;
|
||
createdAt: string;
|
||
updatedAt: string;
|
||
}
|
||
```
|
||
|
||
### PermissionMenu
|
||
|
||
```ts
|
||
interface PermissionMenu {
|
||
key: string;
|
||
title: string;
|
||
icon?: string;
|
||
permission: string;
|
||
actions: string[];
|
||
}
|
||
```
|
||
|
||
## 接口总览
|
||
|
||
| 方法 | 路径 | 鉴权 | 权限 | 说明 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `GET` | `/health` | 否 | 无 | 健康检查 |
|
||
| `POST` | `/api/auth/login` | 否 | 无 | 后台登录,兼容入口 |
|
||
| `POST` | `/api/auth/admin/login` | 否 | 无 | 后台登录 |
|
||
| `POST` | `/api/auth/employee/login` | 否 | 无 | 员工端登录 |
|
||
| `GET` | `/api/auth/me` | 是 | 登录即可 | 当前用户 |
|
||
| `PATCH` | `/api/auth/me/password` | 是 | 登录即可 | 修改本人密码 |
|
||
| `GET` | `/api/permissions/me` | 是 | 登录即可 | 当前用户权限和菜单 |
|
||
| `GET` | `/api/permissions/policies` | 是 | `permission:view` | 角色权限策略 |
|
||
| `GET` | `/api/permissions/definitions` | 是 | `permission:view` | 可分配权限点定义 |
|
||
| `PUT` | `/api/permissions/roles/:roleId` | 是 | `permission:manage` | 更新角色权限 |
|
||
| `GET` | `/api/stores` | 是 | `store:view` | 门店列表或门店下拉选项 |
|
||
| `GET` | `/api/stores/:id` | 是 | `store:view` | 门店详情 |
|
||
| `GET` | `/api/stores/:id/employees` | 是 | `store:view` | 门店员工列表 |
|
||
| `POST` | `/api/stores` | 是 | `store:manage` | 新增门店 |
|
||
| `PATCH` | `/api/stores/:id` | 是 | `store:manage` | 修改门店 |
|
||
| `DELETE` | `/api/stores/:storeId/employees/:employeeId` | 是 | `employee:manage` | 从门店移除员工 |
|
||
| `DELETE` | `/api/stores/:id` | 是 | `store:manage` | 软删除门店 |
|
||
| `GET` | `/api/roles` | 是 | `role:view` | 角色列表 |
|
||
| `GET` | `/api/roles/:id` | 是 | `role:view` | 角色详情 |
|
||
| `POST` | `/api/roles` | 是 | `role:manage` | 新增自定义角色 |
|
||
| `PATCH` | `/api/roles/:id` | 是 | `role:manage` | 修改自定义角色 |
|
||
| `DELETE` | `/api/roles/:id` | 是 | `role:manage` | 软删除自定义角色 |
|
||
| `GET` | `/api/employees` | 是 | `employee:view:all` 或 `employee:view:store` | 员工分页列表 |
|
||
| `GET` | `/api/employees/:id` | 是 | `employee:view:all` 或当前门店 `employee:view:store` | 员工详情 |
|
||
| `POST` | `/api/employees` | 是 | `employee:manage` | 新增员工 |
|
||
| `PATCH` | `/api/employees/:id` | 是 | `employee:manage` | 修改员工 |
|
||
| `PATCH` | `/api/employees/:id/status` | 是 | `employee:manage` | 修改员工状态 |
|
||
| `PATCH` | `/api/employees/:id/password` | 是 | `employee:manage` | 修改员工密码 |
|
||
| `PATCH` | `/api/employees/:id/password/reset` | 是 | `credential:reset` | 兼容旧路径,重置临时密码 |
|
||
| `DELETE` | `/api/employees/:id` | 是 | `employee:manage` | 软删除员工 |
|
||
| `GET` | `/api/mobile/bootstrap` | 是 | 登录即可 | 员工端首屏聚合 |
|
||
| `GET` | `/api/mobile/announcements` | 是 | 登录即可 | 当前员工可见公告 |
|
||
| `GET` | `/api/mobile/announcements/:id` | 是 | 登录即可 | 员工端公告详情 |
|
||
| `POST` | `/api/mobile/announcements/:id/read` | 是 | 登录即可 | 标记公告已读 |
|
||
| `GET` | `/api/mobile/tasks` | 是 | 登录即可 | 当前员工任务 |
|
||
| `GET` | `/api/mobile/tasks/:id` | 是 | 登录即可 | 员工端任务详情 |
|
||
| `POST` | `/api/mobile/tasks/:id/start` | 是 | 登录即可 | 开始处理任务 |
|
||
| `POST` | `/api/mobile/tasks/:id/complete` | 是 | 登录即可 | 完成任务 |
|
||
| `POST` | `/api/mobile/tasks/:id/comment` | 是 | 登录即可 | 添加任务备注 |
|
||
| `GET` | `/api/mobile/shifts` | 是 | 登录即可 | 当前员工排班 |
|
||
| `GET` | `/api/mobile/shifts/today` | 是 | 登录即可 | 当前员工今日排班 |
|
||
| `GET` | `/api/admin/announcements` | 是 | `announcement:view` | 后台公告分页 |
|
||
| `POST` | `/api/admin/announcements` | 是 | `announcement:manage` | 新建公告 |
|
||
| `PATCH` | `/api/admin/announcements/:id` | 是 | `announcement:manage` | 编辑公告 |
|
||
| `POST` | `/api/admin/announcements/:id/publish` | 是 | `announcement:manage` | 发布公告 |
|
||
| `POST` | `/api/admin/announcements/:id/archive` | 是 | `announcement:manage` | 归档公告 |
|
||
| `GET` | `/api/admin/tasks` | 是 | `task:view` | 后台任务分页 |
|
||
| `POST` | `/api/admin/tasks` | 是 | `task:manage` | 新建任务 |
|
||
| `PATCH` | `/api/admin/tasks/:id` | 是 | `task:manage` | 编辑任务 |
|
||
| `POST` | `/api/admin/tasks/:id/cancel` | 是 | `task:manage` | 取消任务 |
|
||
| `GET` | `/api/admin/shifts` | 是 | `shift:view` | 后台排班分页 |
|
||
| `POST` | `/api/admin/shifts` | 是 | `shift:manage` | 新增排班 |
|
||
| `PATCH` | `/api/admin/shifts/:id` | 是 | `shift:manage` | 编辑排班 |
|
||
| `DELETE` | `/api/admin/shifts/:id` | 是 | `shift:manage` | 取消排班 |
|
||
| `POST` | `/api/admin/employees/:id/password/reset` | 是 | `credential:reset` | 重置员工临时密码 |
|
||
| `GET` | `/api/admin/credential-audits` | 是 | `credential:audit:view` | 凭据审计分页 |
|
||
|
||
## 健康检查
|
||
|
||
### GET /health
|
||
|
||
检查 HTTP 服务和数据库连接。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"status": "ok",
|
||
"database": "up",
|
||
"now": "2026-05-26T08:00:00.000Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 认证接口
|
||
|
||
### POST /api/auth/login
|
||
|
||
后台登录兼容入口,逻辑等同于 `POST /api/auth/admin/login`。
|
||
|
||
请求体:
|
||
|
||
| 字段 | 类型 | 必填 | 约束 | 说明 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `username` | `string` | 是 | trim 后 1-50 字符 | 超级管理员用户名,或员工手机号 |
|
||
| `password` | `string` | 是 | 8-128 字符 | 登录密码 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"username": "admin",
|
||
"password": "Admin@123456"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"token": "<jwt-token>",
|
||
"tokenType": "Bearer",
|
||
"expiresIn": "2h",
|
||
"user": {
|
||
"id": 1,
|
||
"username": "admin",
|
||
"displayName": "超级管理员",
|
||
"accountType": "SUPER_ADMIN",
|
||
"roles": [
|
||
{
|
||
"id": 0,
|
||
"code": "super_admin",
|
||
"name": "超级管理员"
|
||
}
|
||
],
|
||
"permissions": ["*"],
|
||
"canManage": true
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
后台登录规则:
|
||
|
||
- 超级管理员使用 `super_admins.username` 登录。
|
||
- 员工使用手机号登录。
|
||
- 登录会先校验密码;密码正确但账号停用时返回 `401 UNAUTHORIZED`,消息为 `账号已被禁用`。
|
||
- 员工所属门店停用时返回 `401 UNAUTHORIZED`,消息为 `所属门店已被禁用`。
|
||
- 员工必须拥有后台菜单权限,否则返回 `401 UNAUTHORIZED`,消息为 `当前账号没有后台登录权限`。
|
||
- `cashier`、`kitchen`、`part_time` 默认没有后台登录权限。
|
||
|
||
### POST /api/auth/admin/login
|
||
|
||
后台登录正式入口。请求和响应与 `POST /api/auth/login` 一致。
|
||
|
||
### POST /api/auth/employee/login
|
||
|
||
员工端登录入口。员工使用手机号和密码登录,不要求后台管理权限。
|
||
密码正确但员工账号停用时返回 `账号已被禁用`;所属门店停用时返回 `所属门店已被禁用`。
|
||
|
||
请求体:
|
||
|
||
| 字段 | 类型 | 必填 | 约束 | 说明 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `username` | `string` | 是 | trim 后 1-50 字符 | 员工手机号 |
|
||
| `password` | `string` | 是 | 8-128 字符 | 员工密码 |
|
||
|
||
响应中的 `user.accountType` 为 `EMPLOYEE`,`storeId`、`storeName` 会返回。
|
||
|
||
### GET /api/auth/me
|
||
|
||
获取当前登录用户。
|
||
|
||
请求头:
|
||
|
||
```http
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 1,
|
||
"username": "admin",
|
||
"displayName": "超级管理员",
|
||
"accountType": "SUPER_ADMIN",
|
||
"roles": [
|
||
{
|
||
"id": 0,
|
||
"code": "super_admin",
|
||
"name": "超级管理员"
|
||
}
|
||
],
|
||
"permissions": ["*"],
|
||
"canManage": true
|
||
}
|
||
}
|
||
```
|
||
|
||
## 权限接口
|
||
|
||
### GET /api/permissions/me
|
||
|
||
获取当前用户可见菜单和权限码。任意已登录账号都可访问。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"permissions": ["*"],
|
||
"menus": [
|
||
{
|
||
"key": "stores",
|
||
"title": "门店管理",
|
||
"permission": "store:view",
|
||
"actions": ["view", "create", "update", "delete"]
|
||
},
|
||
{
|
||
"key": "roles",
|
||
"title": "角色管理",
|
||
"permission": "role:view",
|
||
"actions": ["view", "create", "update", "delete"]
|
||
},
|
||
{
|
||
"key": "employees",
|
||
"title": "员工管理",
|
||
"permission": "employee:view:all",
|
||
"actions": ["view", "create", "update", "delete"]
|
||
},
|
||
{
|
||
"key": "permissions",
|
||
"title": "权限管理",
|
||
"icon": "key",
|
||
"permission": "permission:view",
|
||
"actions": ["view", "update"]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
前端建议用此接口决定菜单显隐和按钮显隐。
|
||
|
||
### GET /api/permissions/policies
|
||
|
||
获取当前数据库里的角色权限策略。需要 `permission:view`。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"roleId": 0,
|
||
"roleCode": "super_admin",
|
||
"roleName": "超级管理员",
|
||
"roleDescription": "系统内置最高权限账号,不参与角色权限分配。",
|
||
"isSystem": true,
|
||
"editable": false,
|
||
"scope": "全部门店",
|
||
"permissions": ["*"],
|
||
"menus": [
|
||
{
|
||
"key": "stores",
|
||
"title": "门店管理",
|
||
"actions": ["view", "create", "update", "delete"]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"roleId": 5,
|
||
"roleCode": "admin",
|
||
"roleName": "管理员",
|
||
"roleDescription": "系统管理角色,仅授予可信人员",
|
||
"isSystem": true,
|
||
"editable": true,
|
||
"scope": "按权限点控制",
|
||
"permissions": [
|
||
"store:view",
|
||
"store:manage",
|
||
"role:view",
|
||
"role:manage",
|
||
"employee:view:all",
|
||
"employee:manage",
|
||
"permission:view",
|
||
"permission:manage"
|
||
],
|
||
"menus": [
|
||
{
|
||
"key": "stores",
|
||
"title": "门店管理",
|
||
"actions": ["view", "create", "update", "delete"]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"roleId": 1,
|
||
"roleCode": "store_manager",
|
||
"roleName": "店长",
|
||
"roleDescription": "负责门店日常管理、排班和权限审批",
|
||
"isSystem": true,
|
||
"editable": true,
|
||
"scope": "当前门店",
|
||
"permissions": ["employee:view:store"],
|
||
"menus": [
|
||
{
|
||
"key": "employees",
|
||
"title": "员工管理",
|
||
"actions": ["view"]
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### GET /api/permissions/definitions
|
||
|
||
获取后端允许分配的权限点定义。需要 `permission:view`。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"permissions": [
|
||
{
|
||
"code": "store:view",
|
||
"title": "查看门店",
|
||
"description": "查看门店列表、门店详情和门店下拉选项。",
|
||
"groupKey": "stores",
|
||
"groupTitle": "门店管理"
|
||
}
|
||
],
|
||
"groups": [
|
||
{
|
||
"key": "stores",
|
||
"title": "门店管理",
|
||
"permissions": [
|
||
{
|
||
"code": "store:view",
|
||
"title": "查看门店",
|
||
"description": "查看门店列表、门店详情和门店下拉选项。",
|
||
"groupKey": "stores",
|
||
"groupTitle": "门店管理"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"menus": [
|
||
{
|
||
"key": "stores",
|
||
"title": "门店管理",
|
||
"permission": "store:view",
|
||
"actions": ["view", "create", "update", "delete"],
|
||
"actionLabels": {
|
||
"view": "查看",
|
||
"create": "新增",
|
||
"update": "编辑",
|
||
"delete": "删除"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### PUT /api/permissions/roles/:roleId
|
||
|
||
更新指定角色拥有的权限点。需要 `permission:manage`。
|
||
|
||
后端只接受 `GET /api/permissions/definitions` 返回的权限码。保存时会自动补齐依赖权限,例如提交 `permission:manage` 会自动保留 `permission:view`。本次保存中被移除的权限关系会写入 `role_permissions.deleted_at`,不会物理删除关系行。
|
||
|
||
请求:
|
||
|
||
```http
|
||
PUT /api/permissions/roles/5
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"permissions": [
|
||
"store:view",
|
||
"store:manage",
|
||
"role:view",
|
||
"role:manage",
|
||
"permission:view",
|
||
"permission:manage"
|
||
]
|
||
}
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"roleId": 5,
|
||
"roleCode": "admin",
|
||
"roleName": "管理员",
|
||
"roleDescription": "系统管理角色,仅授予可信人员",
|
||
"isSystem": true,
|
||
"editable": true,
|
||
"scope": "按权限点控制",
|
||
"permissions": [
|
||
"store:view",
|
||
"store:manage",
|
||
"role:view",
|
||
"role:manage",
|
||
"permission:view",
|
||
"permission:manage"
|
||
],
|
||
"menus": [
|
||
{
|
||
"key": "stores",
|
||
"title": "门店管理",
|
||
"actions": ["view", "create", "update", "delete"]
|
||
},
|
||
{
|
||
"key": "roles",
|
||
"title": "角色管理",
|
||
"actions": ["view", "create", "update", "delete"]
|
||
},
|
||
{
|
||
"key": "permissions",
|
||
"title": "权限管理",
|
||
"actions": ["view", "update"]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
上面示例中的 `menus` 为截断示例,真实响应会返回完整菜单数组。
|
||
|
||
## 门店接口
|
||
|
||
### GET /api/stores
|
||
|
||
查询门店。需要 `store:view`。
|
||
|
||
查询参数:
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 约束 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `includeInactive` | `boolean` | 否 | 无 | `true` 或 `false` | 不传时由接口模式决定;传 `false` 且不传 `status` 时只查启用门店 |
|
||
| `status` | `"ACTIVE" \| "INACTIVE"` | 否 | 无 | 枚举 | 按门店状态筛选;优先级高于 `includeInactive` |
|
||
| `keyword` | `string` | 否 | 无 | trim 后 1-100 字符 | 按门店名称、地址、电话模糊搜索 |
|
||
| `page` | `number` | 否 | `1` | 正整数 | 页码;传了筛选或分页参数时才进入分页列表模式 |
|
||
| `pageSize` | `number` | 否 | `20` | 1-100 | 每页数量 |
|
||
|
||
响应结构会随查询参数变化:
|
||
|
||
- 不传任何查询参数,或只传 `includeInactive=false`:返回启用门店下拉选项 `StoreOption[]`,供表单选择门店。
|
||
- 只传 `includeInactive=true`:返回未删除门店完整数组 `Store[]`,兼容旧调用。
|
||
- 传 `status`、`keyword`、`page`、`pageSize` 中任意一个:返回分页结构,`items` 为 `Store[]`,供门店管理列表使用。
|
||
|
||
管理列表常用写法:
|
||
|
||
- 全部状态:`GET /api/stores?page=1&pageSize=20`
|
||
- 只看启用:`GET /api/stores?status=ACTIVE&page=1&pageSize=20`
|
||
- 关键词搜索:`GET /api/stores?keyword=人民&page=1&pageSize=20`
|
||
|
||
请求示例:
|
||
|
||
```http
|
||
GET /api/stores?status=ACTIVE&keyword=人民&page=1&pageSize=20
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
响应示例:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"name": "示例门店",
|
||
"address": "请改成你的真实门店地址",
|
||
"phone": "13800000000",
|
||
"status": "ACTIVE",
|
||
"createdAt": "2026-05-26T08:00:00.000Z",
|
||
"updatedAt": "2026-05-26T08:00:00.000Z"
|
||
}
|
||
],
|
||
"pagination": {
|
||
"page": 1,
|
||
"pageSize": 20,
|
||
"total": 1,
|
||
"totalPages": 1
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### GET /api/stores/:id
|
||
|
||
查询门店详情。需要 `store:view`。
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 约束 |
|
||
| --- | --- | --- |
|
||
| `id` | `number` | 正整数 |
|
||
|
||
响应 `data` 为门店详情,结构是在 `Store` 基础上增加 `employees: Employee[]`。
|
||
|
||
门店停用后,员工仍保留在该门店下。员工对象会返回:
|
||
|
||
- `storeStatus: "INACTIVE"`
|
||
- `statusTags` 中包含 `{ "code": "STORE_INACTIVE", "label": "门店被禁用", "variant": "warning" }`
|
||
|
||
### GET /api/stores/:id/employees
|
||
|
||
查询某个门店下的员工。需要 `store:view`。
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 约束 |
|
||
| --- | --- | --- |
|
||
| `id` | `number` | 正整数 |
|
||
|
||
响应 `data` 为 `Employee[]`。员工对象会包含 `storeStatus` 和 `statusTags`,供前端直接展示员工状态和门店禁用标签。
|
||
|
||
### POST /api/stores
|
||
|
||
新增门店。需要 `store:manage`。
|
||
|
||
请求体:
|
||
|
||
| 字段 | 类型 | 必填 | 默认值 | 约束 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `name` | `string` | 是 | 无 | trim 后 1-100 字符 | 门店名称,未删除门店内唯一 |
|
||
| `address` | `string \| null` | 否 | `null` | trim 后最多 255 字符 | 空字符串会保存为 `null` |
|
||
| `phone` | `string \| null` | 否 | `null` | trim 后最多 30 字符 | 空字符串会保存为 `null` |
|
||
| `status` | `"ACTIVE" \| "INACTIVE"` | 否 | `"ACTIVE"` | 枚举 | 门店状态 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"name": "人民广场店",
|
||
"address": "上海市黄浦区人民广场",
|
||
"phone": "021-12345678",
|
||
"status": "ACTIVE"
|
||
}
|
||
```
|
||
|
||
成功响应:`201 Created`,`data` 为 `Store`。
|
||
|
||
可能的业务错误:
|
||
|
||
- `409 CONFLICT`:门店名称已存在。
|
||
|
||
### PATCH /api/stores/:id
|
||
|
||
修改门店。需要 `store:manage`。
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 约束 |
|
||
| --- | --- | --- |
|
||
| `id` | `number` | 正整数 |
|
||
|
||
请求体至少提交一个字段:
|
||
|
||
| 字段 | 类型 | 约束 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `name` | `string` | trim 后 1-100 字符 | 门店名称 |
|
||
| `address` | `string \| null` | trim 后最多 255 字符 | 空字符串会保存为 `null` |
|
||
| `phone` | `string \| null` | trim 后最多 30 字符 | 空字符串会保存为 `null` |
|
||
| `status` | `"ACTIVE" \| "INACTIVE"` | 枚举 | 门店状态 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"name": "人民广场旗舰店",
|
||
"status": "ACTIVE"
|
||
}
|
||
```
|
||
|
||
响应 `data` 为 `Store`。
|
||
|
||
可能的业务错误:
|
||
|
||
- `404 NOT_FOUND`:门店不存在。
|
||
- `409 CONFLICT`:门店名称已存在。
|
||
|
||
业务规则:
|
||
|
||
- 门店下还有员工时,允许把门店状态改为 `INACTIVE`。
|
||
- 停用门店后,该门店员工仍可在员工列表和门店详情中查看,并通过 `statusTags` 标识“门店被禁用”。
|
||
|
||
### DELETE /api/stores/:storeId/employees/:employeeId
|
||
|
||
从门店详情中移除员工。需要 `employee:manage`。
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 约束 |
|
||
| --- | --- | --- |
|
||
| `storeId` | `number` | 正整数 |
|
||
| `employeeId` | `number` | 正整数 |
|
||
|
||
成功响应:`204 No Content`。
|
||
|
||
业务规则:
|
||
|
||
- 只有员工属于该门店时才会移除。
|
||
- 移除沿用员工软删除规则:员工状态会变为 `INACTIVE`,`deleted_at` 写入删除时间,手机号唯一约束释放。
|
||
|
||
可能的业务错误:
|
||
|
||
- `404 NOT_FOUND`:门店员工不存在。
|
||
|
||
### DELETE /api/stores/:id
|
||
|
||
软删除门店。需要 `store:manage`。
|
||
|
||
成功响应:`204 No Content`。
|
||
|
||
可能的业务错误:
|
||
|
||
- `404 NOT_FOUND`:门店不存在。
|
||
- `409 CONFLICT`:门店下还有员工,不能删除。
|
||
|
||
## 角色接口
|
||
|
||
### GET /api/roles
|
||
|
||
查询角色列表。需要 `role:view`。
|
||
|
||
查询参数:
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 约束 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `keyword` | `string` | 否 | 无 | trim 后 1-100 字符 | 按角色编码、名称、说明模糊搜索 |
|
||
| `isSystem` | `boolean` | 否 | 无 | `true` 或 `false` | 是否服务端内置角色 |
|
||
| `page` | `number` | 否 | `1` | 正整数 | 页码;传了筛选或分页参数时才进入分页列表模式 |
|
||
| `pageSize` | `number` | 否 | `20` | 1-100 | 每页数量 |
|
||
|
||
响应结构会随查询参数变化:
|
||
|
||
- 不传任何查询参数:返回 `Role[]`,兼容角色下拉和旧调用。
|
||
- 传任意一个查询参数:返回分页结构,`items` 为 `Role[]`,供角色管理列表使用。
|
||
|
||
请求示例:
|
||
|
||
```http
|
||
GET /api/roles?keyword=店长&isSystem=true&page=1&pageSize=20
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
响应示例:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"code": "store_manager",
|
||
"name": "店长",
|
||
"description": "负责门店日常管理、排班和权限审批",
|
||
"isSystem": true,
|
||
"createdAt": "2026-05-26T08:00:00.000Z",
|
||
"updatedAt": "2026-05-26T08:00:00.000Z"
|
||
}
|
||
],
|
||
"pagination": {
|
||
"page": 1,
|
||
"pageSize": 20,
|
||
"total": 1,
|
||
"totalPages": 1
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### GET /api/roles/:id
|
||
|
||
查询角色详情。需要 `role:view`。
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 约束 |
|
||
| --- | --- | --- |
|
||
| `id` | `number` | 正整数 |
|
||
|
||
响应 `data` 为 `Role`。
|
||
|
||
### POST /api/roles
|
||
|
||
新增自定义角色。需要 `role:manage`。当前内置 `admin` 角色没有 `role:manage`,通常只有超级管理员可调用。
|
||
|
||
请求体:
|
||
|
||
| 字段 | 类型 | 必填 | 约束 | 说明 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `code` | `string` | 是 | trim 后 1-50 字符,必须匹配 `^[a-z][a-z0-9_]*$` | 角色编码,全局唯一 |
|
||
| `name` | `string` | 是 | trim 后 1-50 字符 | 角色名称 |
|
||
| `description` | `string \| null` | 否 | trim 后最多 255 字符 | 空字符串会保存为 `null` |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"code": "auditor",
|
||
"name": "审计员",
|
||
"description": "只用于示例的自定义角色"
|
||
}
|
||
```
|
||
|
||
成功响应:`201 Created`,`data` 为 `Role`,其中 `isSystem` 为 `false`。
|
||
|
||
可能的业务错误:
|
||
|
||
- `409 CONFLICT`:角色编码已存在。
|
||
|
||
### PATCH /api/roles/:id
|
||
|
||
修改自定义角色。需要 `role:manage`。
|
||
|
||
请求体至少提交一个字段:
|
||
|
||
| 字段 | 类型 | 约束 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `code` | `string` | trim 后 1-50 字符,必须匹配 `^[a-z][a-z0-9_]*$` | 角色编码 |
|
||
| `name` | `string` | trim 后 1-50 字符 | 角色名称 |
|
||
| `description` | `string \| null` | trim 后最多 255 字符 | 空字符串会保存为 `null` |
|
||
|
||
响应 `data` 为 `Role`。
|
||
|
||
可能的业务错误:
|
||
|
||
- `404 NOT_FOUND`:角色不存在。
|
||
- `409 CONFLICT`:服务端内置角色不可修改。
|
||
- `409 CONFLICT`:角色编码已存在。
|
||
|
||
### DELETE /api/roles/:id
|
||
|
||
软删除自定义角色。需要 `role:manage`。
|
||
|
||
成功响应:`204 No Content`。
|
||
|
||
删除后:
|
||
|
||
- `roles.deleted_at` 会写入删除时间。
|
||
- 角色不会再出现在角色列表、角色下拉选项和权限策略中。
|
||
- 该角色编码的唯一约束会释放,之后可以重新创建同编码角色。
|
||
- 员工角色关系不会被物理删除,解绑和重新绑定通过 `employee_roles.deleted_at` 记录当前关系状态。
|
||
|
||
可能的业务错误:
|
||
|
||
- `404 NOT_FOUND`:角色不存在。
|
||
- `409 CONFLICT`:服务端内置角色不可删除。
|
||
- `409 CONFLICT`:角色已绑定员工,不能删除。
|
||
|
||
## 员工接口
|
||
|
||
### GET /api/employees
|
||
|
||
分页查询员工。需要 `employee:view:all` 或 `employee:view:store`。
|
||
|
||
查询参数:
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 约束 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `storeId` | `number` | 否 | 无 | 正整数 | 门店筛选 |
|
||
| `status` | `"ACTIVE" \| "INACTIVE"` | 否 | 无 | 枚举 | 员工状态 |
|
||
| `keyword` | `string` | 否 | 无 | trim 后 1-100 字符 | 按姓名或手机号模糊搜索 |
|
||
| `page` | `number` | 否 | `1` | 正整数 | 页码 |
|
||
| `pageSize` | `number` | 否 | `20` | 1-100 | 每页数量 |
|
||
|
||
权限范围:
|
||
|
||
- 超级管理员和 `admin`:可看全部员工,`storeId` 参数生效。
|
||
- `store_manager`:只能看当前门店员工,即使传了其他 `storeId`,后端也会强制改成当前用户的 `storeId`。
|
||
|
||
请求示例:
|
||
|
||
```http
|
||
GET /api/employees?storeId=1&status=ACTIVE&keyword=张&page=1&pageSize=20
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"storeId": 1,
|
||
"storeName": "示例门店",
|
||
"storeStatus": "ACTIVE",
|
||
"name": "张三",
|
||
"phone": "13800000001",
|
||
"status": "ACTIVE",
|
||
"statusTags": [
|
||
{
|
||
"code": "EMPLOYEE_ACTIVE",
|
||
"label": "员工启用",
|
||
"variant": "success"
|
||
}
|
||
],
|
||
"remark": null,
|
||
"roles": [
|
||
{
|
||
"id": 1,
|
||
"code": "store_manager",
|
||
"name": "店长"
|
||
}
|
||
],
|
||
"createdAt": "2026-05-26T08:00:00.000Z",
|
||
"updatedAt": "2026-05-26T08:00:00.000Z"
|
||
}
|
||
],
|
||
"pagination": {
|
||
"page": 1,
|
||
"pageSize": 20,
|
||
"total": 1,
|
||
"totalPages": 1
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
如果员工所属门店已停用,`storeStatus` 会返回 `"INACTIVE"`,且 `statusTags` 会同时包含员工自身状态标签和“门店被禁用”标签。
|
||
|
||
### GET /api/employees/:id
|
||
|
||
查询员工详情。需要员工查看权限。
|
||
|
||
权限范围:
|
||
|
||
- `employee:view:all`:可看任意员工。
|
||
- `employee:view:store`:只能看当前门店员工。
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 约束 |
|
||
| --- | --- | --- |
|
||
| `id` | `number` | 正整数 |
|
||
|
||
响应 `data` 为 `Employee`。
|
||
|
||
可能的业务错误:
|
||
|
||
- `404 NOT_FOUND`:员工不存在。
|
||
- `403 FORBIDDEN`:没有查看该员工的权限。
|
||
|
||
### POST /api/employees
|
||
|
||
新增员工。需要 `employee:manage`。
|
||
|
||
请求体:
|
||
|
||
| 字段 | 类型 | 必填 | 默认值 | 约束 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `storeId` | `number` | 是 | 无 | 正整数 | 必须是启用且未删除的门店 |
|
||
| `name` | `string` | 是 | 无 | trim 后 1-50 字符 | 员工姓名 |
|
||
| `phone` | `string` | 是 | 无 | 中国大陆手机号,`/^1[3-9]\d{9}$/` | 员工登录账号,未删除员工范围内全局唯一 |
|
||
| `status` | `"ACTIVE" \| "INACTIVE"` | 否 | `"ACTIVE"` | 枚举 | 员工状态 |
|
||
| `remark` | `string \| null` | 否 | `null` | trim 后最多 500 字符 | 备注 |
|
||
| `roleIds` | `number[]` | 否 | `[]` | 正整数数组,最多 20 个 | 绑定角色 ID,重复 ID 会自动去重 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"storeId": 1,
|
||
"name": "张三",
|
||
"phone": "13800000001",
|
||
"status": "ACTIVE",
|
||
"remark": null,
|
||
"roleIds": [1, 5]
|
||
}
|
||
```
|
||
|
||
成功响应:`201 Created`,`data` 为 `Employee`。
|
||
|
||
业务规则:
|
||
|
||
- `storeId` 必须对应启用且未删除门店。
|
||
- `phone` 在未删除员工范围内全局唯一。
|
||
- `roleIds` 中的角色必须存在且未软删除;自定义角色可先通过角色接口创建,再通过权限接口分配权限。
|
||
- 新员工默认密码为 `pw111111`。
|
||
|
||
可能的业务错误:
|
||
|
||
- `400 BAD_REQUEST`:门店不存在或已停用。
|
||
- `400 BAD_REQUEST`:提交的角色包含不存在或已删除的角色。
|
||
- `409 CONFLICT`:员工手机号已存在。
|
||
|
||
### PATCH /api/employees/:id
|
||
|
||
修改员工。需要 `employee:manage`。
|
||
|
||
请求体至少提交一个字段:
|
||
|
||
| 字段 | 类型 | 约束 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `storeId` | `number` | 正整数 | 更换门店时,新门店必须启用且未删除 |
|
||
| `name` | `string` | trim 后 1-50 字符 | 员工姓名 |
|
||
| `phone` | `string` | 中国大陆手机号,`/^1[3-9]\d{9}$/` | 员工手机号,未删除员工范围内全局唯一 |
|
||
| `status` | `"ACTIVE" \| "INACTIVE"` | 枚举 | 员工状态 |
|
||
| `remark` | `string \| null` | trim 后最多 500 字符 | 备注 |
|
||
| `roleIds` | `number[]` | 正整数数组,最多 20 个 | 不传表示不修改角色,传空数组表示清空角色;解绑会写入 `employee_roles.deleted_at` |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"name": "张三丰",
|
||
"roleIds": [5]
|
||
}
|
||
```
|
||
|
||
响应 `data` 为 `Employee`。
|
||
|
||
可能的业务错误:
|
||
|
||
- `404 NOT_FOUND`:员工不存在。
|
||
- `400 BAD_REQUEST`:更换后的门店不存在或已停用。
|
||
- `400 BAD_REQUEST`:提交的角色包含不存在或已删除的角色。
|
||
- `409 CONFLICT`:员工手机号已存在。
|
||
|
||
### PATCH /api/employees/:id/status
|
||
|
||
修改员工状态。需要 `employee:manage`。
|
||
|
||
请求体:
|
||
|
||
| 字段 | 类型 | 必填 | 约束 |
|
||
| --- | --- | --- | --- |
|
||
| `status` | `"ACTIVE" \| "INACTIVE"` | 是 | 枚举 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"status": "INACTIVE"
|
||
}
|
||
```
|
||
|
||
响应 `data` 为 `Employee`。
|
||
|
||
### PATCH /api/employees/:id/password
|
||
|
||
修改员工密码。需要 `employee:manage`。
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 约束 |
|
||
| --- | --- | --- |
|
||
| `id` | `number` | 正整数 |
|
||
|
||
请求体:
|
||
|
||
| 字段 | 类型 | 必填 | 约束 |
|
||
| --- | --- | --- | --- |
|
||
| `oldPassword` | `string` | 是 | 8-128 字符 |
|
||
| `newPassword` | `string` | 是 | 8-128 字符 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"oldPassword": "pw111111",
|
||
"newPassword": "NewPw111111"
|
||
}
|
||
```
|
||
|
||
后端会先校验旧密码,旧密码不正确时不会写入新密码。
|
||
|
||
响应 `data` 为 `Employee`,不会返回密码或密码哈希。
|
||
|
||
可能的业务错误:
|
||
|
||
- `404 NOT_FOUND`:员工不存在。
|
||
- `400 BAD_REQUEST`:旧密码不正确。
|
||
|
||
### PATCH /api/employees/:id/password/reset
|
||
|
||
兼容旧路径。重置员工临时密码。需要 `credential:reset`。
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 约束 |
|
||
| --- | --- | --- |
|
||
| `id` | `number` | 正整数 |
|
||
|
||
请求体:不需要请求体。
|
||
|
||
响应 `data.temporaryPassword` 只返回一次,后端只保存哈希,并把员工标记为必须改密。
|
||
|
||
## C 端员工接口
|
||
|
||
所有 `/api/mobile/*` 接口都只从 Bearer token 推导当前员工,不接受客户端传入 `employeeId` 做越权查询。
|
||
|
||
### GET /api/mobile/bootstrap
|
||
|
||
员工端首屏聚合,返回当前员工、门店、权限、未读公告数、待办任务数、逾期任务数、最近公告、待办任务和今日排班。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"user": {
|
||
"id": 2,
|
||
"username": "13800000000",
|
||
"displayName": "张三",
|
||
"accountType": "EMPLOYEE",
|
||
"storeId": 1,
|
||
"storeName": "示例门店",
|
||
"roles": [{ "id": 1, "code": "cashier", "name": "收银员" }],
|
||
"permissions": ["task:view"],
|
||
"mustChangePassword": false
|
||
},
|
||
"store": {
|
||
"id": 1,
|
||
"name": "示例门店"
|
||
},
|
||
"permissions": {
|
||
"codes": ["task:view"],
|
||
"menus": []
|
||
},
|
||
"counters": {
|
||
"unreadAnnouncementCount": 2,
|
||
"pendingTaskCount": 3,
|
||
"overdueTaskCount": 1
|
||
},
|
||
"latestAnnouncements": [
|
||
{
|
||
"id": 10,
|
||
"title": "端午排班通知",
|
||
"content": "请按新排班表执行。",
|
||
"level": "IMPORTANT",
|
||
"status": "PUBLISHED",
|
||
"targetType": "STORE",
|
||
"publishedAt": "2026-06-01T02:00:00.000Z",
|
||
"readAt": null,
|
||
"createdAt": "2026-06-01T01:00:00.000Z",
|
||
"updatedAt": "2026-06-01T02:00:00.000Z",
|
||
"targets": []
|
||
}
|
||
],
|
||
"tasks": [
|
||
{
|
||
"id": 20,
|
||
"storeId": 1,
|
||
"storeName": "示例门店",
|
||
"title": "检查库存",
|
||
"description": "盘点饮料区库存",
|
||
"status": "PENDING",
|
||
"priority": "NORMAL",
|
||
"dueAt": "2026-06-01T10:00:00.000Z",
|
||
"assignees": [{ "id": 2, "name": "张三", "phone": "13800000000" }],
|
||
"createdAt": "2026-06-01T01:00:00.000Z",
|
||
"updatedAt": "2026-06-01T01:00:00.000Z"
|
||
}
|
||
],
|
||
"todayShifts": []
|
||
}
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
- `latestAnnouncements` 最多 3 条,按发布时间倒序返回当前员工可见的已发布公告。
|
||
- `tasks` 最多 5 条,按截止时间优先返回当前员工被分配的 `PENDING`、`IN_PROGRESS` 任务。
|
||
- `pendingTaskCount` 和 `overdueTaskCount` 当前都只统计已分配给当前员工的未完成任务;暂不把“门店级未指派任务”自动展开给全店员工。
|
||
- `todayShifts` 保持数组结构,返回当前员工今日 `SCHEDULED` 班次。
|
||
|
||
### GET /api/mobile/announcements
|
||
|
||
当前员工可见公告分页。支持 `status`、`keyword`、`page`、`pageSize` 查询参数;员工端只返回已发布且命中全员、门店、角色或员工目标的公告。
|
||
|
||
### GET /api/mobile/announcements/:id
|
||
|
||
当前员工可见公告详情。不可见公告按不存在处理。
|
||
|
||
### POST /api/mobile/announcements/:id/read
|
||
|
||
标记当前员工已读公告。
|
||
|
||
### GET /api/mobile/tasks
|
||
|
||
当前员工任务分页。支持 `status`、`keyword`、`page`、`pageSize`。当前版本只返回已分配给当前员工的任务,门店级未指派任务不自动出现在员工端。
|
||
|
||
### GET /api/mobile/tasks/:id
|
||
|
||
当前员工被分配的任务详情,包含任务事件日志。
|
||
|
||
### POST /api/mobile/tasks/:id/start
|
||
|
||
开始处理当前员工被分配的待处理任务。
|
||
|
||
### POST /api/mobile/tasks/:id/complete
|
||
|
||
完成当前员工被分配的待处理或处理中任务。
|
||
|
||
### POST /api/mobile/tasks/:id/comment
|
||
|
||
为当前员工被分配的任务追加备注。
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"comment": "已完成交接"
|
||
}
|
||
```
|
||
|
||
### GET /api/mobile/shifts
|
||
|
||
当前员工排班分页。支持 `status`、`startDate`、`endDate`、`page`、`pageSize`,日期格式为 `YYYY-MM-DD`。
|
||
|
||
### GET /api/mobile/shifts/today
|
||
|
||
当前员工今日有效排班。
|
||
|
||
## 后台工作台接口
|
||
|
||
### 公告管理
|
||
|
||
- `GET /api/admin/announcements`:公告分页,需要 `announcement:view`。
|
||
- `POST /api/admin/announcements`:新建公告,需要 `announcement:manage`。
|
||
- `PATCH /api/admin/announcements/:id`:编辑公告,需要 `announcement:manage`。
|
||
- `POST /api/admin/announcements/:id/publish`:发布公告,需要 `announcement:manage`。
|
||
- `POST /api/admin/announcements/:id/archive`:归档公告,需要 `announcement:manage`。
|
||
|
||
公告请求体核心字段:
|
||
|
||
```json
|
||
{
|
||
"title": "端午排班通知",
|
||
"content": "请按新排班表执行。",
|
||
"level": "IMPORTANT",
|
||
"targetType": "STORE",
|
||
"targets": [{ "type": "STORE", "id": 1 }]
|
||
}
|
||
```
|
||
|
||
`targetType` 可选 `ALL`、`STORE`、`ROLE`、`EMPLOYEE`。`ALL` 时 `targets` 必须为空,其他范围必须提交同类型目标。
|
||
|
||
### 任务管理
|
||
|
||
- `GET /api/admin/tasks`:任务分页,需要 `task:view`。
|
||
- `POST /api/admin/tasks`:新建任务,需要 `task:manage`。
|
||
- `PATCH /api/admin/tasks/:id`:编辑任务,需要 `task:manage`。
|
||
- `POST /api/admin/tasks/:id/cancel`:取消任务,需要 `task:manage`。
|
||
|
||
任务请求体核心字段:
|
||
|
||
```json
|
||
{
|
||
"storeId": 1,
|
||
"title": "检查库存",
|
||
"description": "盘点饮料区库存",
|
||
"priority": "NORMAL",
|
||
"dueAt": "2026-06-01T10:00:00.000Z",
|
||
"assigneeIds": [2, 3]
|
||
}
|
||
```
|
||
|
||
### 排班管理
|
||
|
||
- `GET /api/admin/shifts`:排班分页,需要 `shift:view`。
|
||
- `POST /api/admin/shifts`:新增排班,需要 `shift:manage`。
|
||
- `PATCH /api/admin/shifts/:id`:编辑排班,需要 `shift:manage`。
|
||
- `DELETE /api/admin/shifts/:id`:取消排班,需要 `shift:manage`。
|
||
|
||
排班请求体核心字段:
|
||
|
||
```json
|
||
{
|
||
"storeId": 1,
|
||
"employeeId": 2,
|
||
"roleName": "收银",
|
||
"startAt": "2026-06-01T01:00:00.000Z",
|
||
"endAt": "2026-06-01T09:00:00.000Z",
|
||
"status": "SCHEDULED"
|
||
}
|
||
```
|
||
|
||
排班冲突规则:
|
||
|
||
- `POST /api/admin/shifts` 和 `PATCH /api/admin/shifts/:id` 都会校验同一员工在同一时间段不能存在重叠的未取消班次。
|
||
- 时间段重叠按 `existing.startAt < new.endAt && existing.endAt > new.startAt` 判断,首尾相接不算冲突。
|
||
- 编辑排班时会排除当前排班自身;状态为 `CANCELLED` 的班次不参与冲突校验。
|
||
- 发生冲突时返回 `409 CONFLICT`。
|
||
|
||
## 凭据安全接口
|
||
|
||
后端不提供任何明文密码查看接口。员工密码只保存哈希。
|
||
|
||
### PATCH /api/auth/me/password
|
||
|
||
当前员工修改本人密码,需要提交旧密码和新密码。成功后清除 `mustChangePassword` 状态,并写入 `credential_audits` 审计。
|
||
|
||
### POST /api/admin/employees/:id/password/reset
|
||
|
||
重置权限范围内员工的临时密码,需要 `credential:reset`。响应中的 `temporaryPassword` 只返回一次。
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"employee": {},
|
||
"temporaryPassword": "Tmp-xxxxxx9",
|
||
"mustChangePassword": true
|
||
}
|
||
}
|
||
```
|
||
|
||
### GET /api/admin/credential-audits
|
||
|
||
凭据审计分页,需要 `credential:audit:view`。
|
||
|
||
查询参数:
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `operatorId` | `number` | 否 | 无 | 操作者 ID,同时匹配 `actorAdminId` 或 `actorEmployeeId` |
|
||
| `targetEmployeeId` | `number` | 否 | 无 | 被操作员工 ID |
|
||
| `storeId` | `number` | 否 | 无 | 被操作员工所属门店 ID |
|
||
| `startDate` | `string` | 否 | 无 | 操作开始日期,格式 `YYYY-MM-DD`,包含当天 |
|
||
| `endDate` | `string` | 否 | 无 | 操作结束日期,格式 `YYYY-MM-DD`,包含当天 |
|
||
| `page` | `number` | 否 | `1` | 页码 |
|
||
| `pageSize` | `number` | 否 | `20` | 每页数量,最大 100 |
|
||
|
||
店长等只有当前门店员工数据范围的账号会被强制限定在自己的 `storeId`。`operatorId` 不区分超级管理员和员工操作者,后端会同时匹配 `actorAdminId` 和 `actorEmployeeId`。
|
||
|
||
响应 `items` 字段:
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"actorAccountType": "SUPER_ADMIN",
|
||
"actorAdminId": 1,
|
||
"actorEmployeeId": null,
|
||
"actorName": "超级管理员",
|
||
"targetEmployeeId": 2,
|
||
"targetEmployeeName": "张三",
|
||
"targetEmployeePhone": "13800000000",
|
||
"storeName": "示例门店",
|
||
"action": "RESET_PASSWORD",
|
||
"reason": "员工忘记密码",
|
||
"ip": "127.0.0.1",
|
||
"userAgent": "Mozilla/5.0",
|
||
"createdAt": "2026-06-01T08:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
### DELETE /api/employees/:id
|
||
|
||
软删除员工。需要 `employee:manage`。
|
||
|
||
成功响应:`204 No Content`。
|
||
|
||
删除后:
|
||
|
||
- 员工 `status` 会改为 `INACTIVE`。
|
||
- `deleted_at` 会写入删除时间。
|
||
- 该手机号的唯一约束会释放,之后可以重新创建同手机号员工。
|
||
|
||
## 前端推荐接入流程
|
||
|
||
1. 调用 `POST /api/auth/admin/login` 完成后台登录。
|
||
2. 保存 `data.token`,后续请求统一带 `Authorization: Bearer <token>`。
|
||
3. 调用 `GET /api/auth/me` 获取账号基础信息。
|
||
4. 调用 `GET /api/permissions/me` 渲染菜单和按钮权限。
|
||
5. 员工表单初始化时调用 `GET /api/stores` 获取启用门店选项,调用 `GET /api/roles` 获取角色选项。
|
||
6. 员工列表使用 `GET /api/employees`,按返回的 `pagination` 渲染分页器。
|
||
|
||
## curl 示例
|
||
|
||
登录:
|
||
|
||
```bash
|
||
curl -X POST http://localhost:3500/api/auth/admin/login \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"username":"admin","password":"Admin@123456"}'
|
||
```
|
||
|
||
查询当前用户:
|
||
|
||
```bash
|
||
curl http://localhost:3500/api/auth/me \
|
||
-H 'Authorization: Bearer <token>'
|
||
```
|
||
|
||
创建员工:
|
||
|
||
```bash
|
||
curl -X POST http://localhost:3500/api/employees \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'Authorization: Bearer <token>' \
|
||
-d '{
|
||
"storeId": 1,
|
||
"name": "张三",
|
||
"phone": "13800000001",
|
||
"roleIds": [1, 5]
|
||
}'
|
||
```
|