Files
access-manage/docs/API.md
T
2026-06-02 12:23:00 +08:00

43 KiB
Raw Blame History

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。门店、角色、员工管理接口还要求当前账号具备后台管理权限。

通用响应

成功响应

{
  "success": true,
  "data": {}
}

分页响应

{
  "success": true,
  "data": {
    "items": [],
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "total": 0,
      "totalPages": 0
    }
  }
}

错误响应

{
  "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

员工创建后默认密码为:

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

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

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

interface RoleOption {
  id: number;
  code: string;
  name: string;
  description: string | null;
  isSystem: boolean;
}

interface Role extends RoleOption {
  createdAt: string;
  updatedAt: string;
}

Employee

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

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:allemployee: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 服务和数据库连接。

响应:

{
  "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 字符 登录密码

请求示例:

{
  "username": "admin",
  "password": "Admin@123456"
}

响应:

{
  "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,消息为 当前账号没有后台登录权限
  • cashierkitchenpart_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.accountTypeEMPLOYEEstoreIdstoreName 会返回。

GET /api/auth/me

获取当前登录用户。

请求头:

Authorization: Bearer <token>

响应:

{
  "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

获取当前用户可见菜单和权限码。任意已登录账号都可访问。

响应:

{
  "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

响应:

{
  "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

响应:

{
  "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,不会物理删除关系行。

请求:

PUT /api/permissions/roles/5
Authorization: Bearer <token>
Content-Type: application/json
{
  "permissions": [
    "store:view",
    "store:manage",
    "role:view",
    "role:manage",
    "permission:view",
    "permission:manage"
  ]
}

响应:

{
  "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 truefalse 不传时由接口模式决定;传 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[],兼容旧调用。
  • statuskeywordpagepageSize 中任意一个:返回分页结构,itemsStore[],供门店管理列表使用。

管理列表常用写法:

  • 全部状态:GET /api/stores?page=1&pageSize=20
  • 只看启用:GET /api/stores?status=ACTIVE&page=1&pageSize=20
  • 关键词搜索:GET /api/stores?keyword=人民&page=1&pageSize=20

请求示例:

GET /api/stores?status=ACTIVE&keyword=人民&page=1&pageSize=20
Authorization: Bearer <token>

响应示例:

{
  "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 正整数

响应 dataEmployee[]。员工对象会包含 storeStatusstatusTags,供前端直接展示员工状态和门店禁用标签。

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" 枚举 门店状态

请求示例:

{
  "name": "人民广场店",
  "address": "上海市黄浦区人民广场",
  "phone": "021-12345678",
  "status": "ACTIVE"
}

成功响应:201 CreateddataStore

可能的业务错误:

  • 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" 枚举 门店状态

请求示例:

{
  "name": "人民广场旗舰店",
  "status": "ACTIVE"
}

响应 dataStore

可能的业务错误:

  • 404 NOT_FOUND:门店不存在。
  • 409 CONFLICT:门店名称已存在。

业务规则:

  • 门店下还有员工时,允许把门店状态改为 INACTIVE
  • 停用门店后,该门店员工仍可在员工列表和门店详情中查看,并通过 statusTags 标识“门店被禁用”。

DELETE /api/stores/:storeId/employees/:employeeId

从门店详情中移除员工。需要 employee:manage

路径参数:

参数 类型 约束
storeId number 正整数
employeeId number 正整数

成功响应:204 No Content

业务规则:

  • 只有员工属于该门店时才会移除。
  • 移除沿用员工软删除规则:员工状态会变为 INACTIVEdeleted_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 truefalse 是否服务端内置角色
page number 1 正整数 页码;传了筛选或分页参数时才进入分页列表模式
pageSize number 20 1-100 每页数量

响应结构会随查询参数变化:

  • 不传任何查询参数:返回 Role[],兼容角色下拉和旧调用。
  • 传任意一个查询参数:返回分页结构,itemsRole[],供角色管理列表使用。

请求示例:

GET /api/roles?keyword=店长&isSystem=true&page=1&pageSize=20
Authorization: Bearer <token>

响应示例:

{
  "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 正整数

响应 dataRole

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

请求示例:

{
  "code": "auditor",
  "name": "审计员",
  "description": "只用于示例的自定义角色"
}

成功响应:201 CreateddataRole,其中 isSystemfalse

可能的业务错误:

  • 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

响应 dataRole

可能的业务错误:

  • 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:allemployee: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

请求示例:

GET /api/employees?storeId=1&status=ACTIVE&keyword=张&page=1&pageSize=20
Authorization: Bearer <token>

响应:

{
  "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 正整数

响应 dataEmployee

可能的业务错误:

  • 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 会自动去重

请求示例:

{
  "storeId": 1,
  "name": "张三",
  "phone": "13800000001",
  "status": "ACTIVE",
  "remark": null,
  "roleIds": [1, 5]
}

成功响应:201 CreateddataEmployee

业务规则:

  • 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

请求示例:

{
  "name": "张三丰",
  "roleIds": [5]
}

响应 dataEmployee

可能的业务错误:

  • 404 NOT_FOUND:员工不存在。
  • 400 BAD_REQUEST:更换后的门店不存在或已停用。
  • 400 BAD_REQUEST:提交的角色包含不存在或已删除的角色。
  • 409 CONFLICT:员工手机号已存在。

PATCH /api/employees/:id/status

修改员工状态。需要 employee:manage

请求体:

字段 类型 必填 约束
status "ACTIVE" | "INACTIVE" 枚举

请求示例:

{
  "status": "INACTIVE"
}

响应 dataEmployee

PATCH /api/employees/:id/password

修改员工密码。需要 employee:manage

路径参数:

参数 类型 约束
id number 正整数

请求体:

字段 类型 必填 约束
oldPassword string 8-128 字符
newPassword string 8-128 字符

请求示例:

{
  "oldPassword": "pw111111",
  "newPassword": "NewPw111111"
}

后端会先校验旧密码,旧密码不正确时不会写入新密码。

响应 dataEmployee,不会返回密码或密码哈希。

可能的业务错误:

  • 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

员工端首屏聚合,返回当前员工、门店、权限、未读公告数、待办任务数、逾期任务数、最近公告、待办任务和今日排班。

响应:

{
  "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 条,按截止时间优先返回当前员工被分配的 PENDINGIN_PROGRESS 任务。
  • pendingTaskCountoverdueTaskCount 当前都只统计已分配给当前员工的未完成任务;暂不把“门店级未指派任务”自动展开给全店员工。
  • todayShifts 保持数组结构,返回当前员工今日 SCHEDULED 班次。

GET /api/mobile/announcements

当前员工可见公告分页。支持 statuskeywordpagepageSize 查询参数;员工端只返回已发布且命中全员、门店、角色或员工目标的公告。

GET /api/mobile/announcements/:id

当前员工可见公告详情。不可见公告按不存在处理。

POST /api/mobile/announcements/:id/read

标记当前员工已读公告。

GET /api/mobile/tasks

当前员工任务分页。支持 statuskeywordpagepageSize。当前版本只返回已分配给当前员工的任务,门店级未指派任务不自动出现在员工端。

GET /api/mobile/tasks/:id

当前员工被分配的任务详情,包含任务事件日志。

POST /api/mobile/tasks/:id/start

开始处理当前员工被分配的待处理任务。

POST /api/mobile/tasks/:id/complete

完成当前员工被分配的待处理或处理中任务。

POST /api/mobile/tasks/:id/comment

为当前员工被分配的任务追加备注。

请求体:

{
  "comment": "已完成交接"
}

GET /api/mobile/shifts

当前员工排班分页。支持 statusstartDateendDatepagepageSize,日期格式为 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

公告请求体核心字段:

{
  "title": "端午排班通知",
  "content": "请按新排班表执行。",
  "level": "IMPORTANT",
  "targetType": "STORE",
  "targets": [{ "type": "STORE", "id": 1 }]
}

targetType 可选 ALLSTOREROLEEMPLOYEEALLtargets 必须为空,其他范围必须提交同类型目标。

任务管理

  • 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

任务请求体核心字段:

{
  "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

排班请求体核心字段:

{
  "storeId": 1,
  "employeeId": 2,
  "roleName": "收银",
  "startAt": "2026-06-01T01:00:00.000Z",
  "endAt": "2026-06-01T09:00:00.000Z",
  "status": "SCHEDULED"
}

排班冲突规则:

  • POST /api/admin/shiftsPATCH /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 只返回一次。

{
  "success": true,
  "data": {
    "employee": {},
    "temporaryPassword": "Tmp-xxxxxx9",
    "mustChangePassword": true
  }
}

GET /api/admin/credential-audits

凭据审计分页,需要 credential:audit:view

查询参数:

参数 类型 必填 默认值 说明
operatorId number 操作者 ID,同时匹配 actorAdminIdactorEmployeeId
targetEmployeeId number 被操作员工 ID
storeId number 被操作员工所属门店 ID
startDate string 操作开始日期,格式 YYYY-MM-DD,包含当天
endDate string 操作结束日期,格式 YYYY-MM-DD,包含当天
page number 1 页码
pageSize number 20 每页数量,最大 100

店长等只有当前门店员工数据范围的账号会被强制限定在自己的 storeIdoperatorId 不区分超级管理员和员工操作者,后端会同时匹配 actorAdminIdactorEmployeeId

响应 items 字段:

{
  "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 示例

登录:

curl -X POST http://localhost:3500/api/auth/admin/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"Admin@123456"}'

查询当前用户:

curl http://localhost:3500/api/auth/me \
  -H 'Authorization: Bearer <token>'

创建员工:

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]
  }'