API 文档

所有接口返回格式:

{
  "code": 200,
  "message": "错误信息(仅失败时)",
  "data": {}
}

需要认证的接口需在请求头携带:Authorization: Bearer <token>

认证

注册

POST /auth/register

请求体:

{
  "username": "admin",
  "password": "123456",
  "role_ids": [1]
}

username、password 为必填,password 最少 6 位。role_ids 可选:

  • 不传则默认 staff 角色
  • 第一个注册的用户自动成为 admin(忽略传入的 role_ids)
  • 非首次注册时 role_ids 仅 admin 可指定
响应codemessage
成功200-
缺少必填字段400invalid request body
用户名已存在400username already exists

登录

POST /auth/login

请求体:

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

响应:

{
  "code": 200,
  "data": { "token": "eyJhbGciOi..." }
}

JWT Token 存入 Redis,过期时间 24 小时。

Token payload 包含以下字段:

{
  "user_id": 1,
  "username": "admin",
  "role": "admin",
  "employee_id": 3,
  "jti": "xxx",
  "exp": 1746000000
}
字段类型说明
user_iduint用户 ID
usernamestring用户名
rolestring角色:admin / manager / staff
employee_id*uint关联的员工 ID,未关联时不存在此字段
响应codemessage
账号被禁用403user is disabled
响应codemessage
成功200-
用户名或密码错误401invalid username or password

获取当前用户信息

GET /auth/me

需要 JWT 认证。

响应示例:

{
  "code": 200,
  "data": {
    "id": 1,
    "username": "admin"
  }
}

错误响应:

状态码说明
401未认证
404用户不存在

注销

POST /auth/logout

需要认证。从 Redis 删除对应 Token。

响应codemessage
成功200-
未认证401unauthorized

部门

创建部门

POST /depts(需认证,admin/manager 且为部门负责人)

请求体:

{
  "name": "研发部",
  "description": "负责产品研发和技术创新",
  "leader_id": 3,
  "parent_id": null
}

name 为必填,description、leader_id、parent_id 可选。名称不能重复。leader_id 指定部门负责人(Employee ID),null 或不传表示暂无负责人。parent_id 指定父部门,null 或不传表示顶级部门。manager 只能在自己管辖范围内的父部门下创建子部门。

响应codemessage
成功200-
缺少必填字段400invalid request body
名称已存在400department name already exists
父部门不存在400parent department not found
无权限403forbidden: cannot create department outside your department scope

查询部门列表(分页+搜索+排序)

GET /depts(需认证)

查询参数:

参数类型默认值说明
keywordstring-按 name 模糊搜索
sort_bystringid排序字段:id, name
sort_descboolfalse是否降序(传 "true")
pageint1页码
page_sizeint10每页数量,最大 100

查询部门树

GET /depts/tree(需认证)

返回部门层级树结构,一次性加载全部部门。

响应示例:

{
  "code": 200,
  "data": [
    {
      "id": 1,
      "name": "研发部",
      "description": "负责产品研发和技术创新",
      "parent_id": null,
      "children": [
        {
          "id": 4,
          "name": "前端组",
          "description": "前端开发",
          "parent_id": 1,
          "children": []
        }
      ]
    }
  ]
}

查询单个部门

GET /depts/:id(需认证,manager 仅可查看管辖范围内的部门)

响应codemessage
成功200-
无效 ID400invalid id
无权限403forbidden: cannot access department outside your scope
不存在404department not found

更新部门

PUT /depts/:id(需认证,admin/manager 且为部门负责人,manager 仅可更新管辖范围内的部门)

请求体(只传需要更新的字段):

{
  "name": "技术部",
  "description": "技术中心",
  "leader_id": 5,
  "parent_id": 2
}

更新 parent_id 时会校验:不能设自己为父、不能产生循环引用、父部门必须存在。

响应codemessage
成功200-
无效 ID400invalid id
请求体无效400invalid request body
名称已存在400department name already exists
循环引用400cannot set parent: circular reference
无权限403forbidden: cannot update department outside your department scope
父部门不存在404department not found
部门不存在404department not found

删除部门

DELETE /depts/:id(需认证,admin)

有子部门或有员工的部门不能删除,需先迁移子部门和员工。

响应codemessage
成功200-
无效 ID400invalid id
有子部门400department has child departments
有员工400department has employees
部门不存在404department not found

员工

创建员工

POST /employees(需认证,admin/manager 且为部门负责人,manager 仅可在管辖范围内创建员工)

请求体:

{
  "name": "张伟",
  "email": "zhangwei@pms.com",
  "phone": "13800000001",
  "id_number": "110101199001011234",
  "dept_id": 1,
  "user_id": 2,
  "status": "active"
}

name、email、dept_id 为必填。phone、id_number、user_id、status 可选。status 默认 "active"。user_id 关联登录账号,一个 User 最多关联一个 Employee。

响应codemessage
成功200-
缺少必填字段400invalid request body
邮箱已存在400email already exists
user_id 已关联其他员工400employee already linked to a user
部门不存在400department not found
无权限403forbidden: cannot create employee outside your department scope

查询员工列表(分页+搜索+排序)

GET /employees(需认证,admin 查全部,manager 查本部门+子部门,staff 返回 403)

查询参数:

参数类型默认值说明
keywordstring-按 name/email/phone/id_number 模糊搜索
sort_bystringid排序字段:id, name, email, phone, status, created_at
sort_descboolfalse是否降序(传 "true")
pageint1页码
page_sizeint10每页数量,最大 100

响应:

{
  "code": 200,
  "data": {
    "data": [
      {
        "id": 1,
        "name": "张伟",
        "email": "zhangwei@pms.com",
        "phone": "13800000001",
        "id_number": "110101199001011234",
        "dept_id": 1,
        "department": { "id": 1, "name": "研发部" },
        "status": "active",
        "created_at": "2026-04-28T12:00:00Z",
        "updated_at": "2026-04-28T12:00:00Z",
        "created_by": "admin",
        "updated_by": "admin"
      }
    ],
    "total": 50,
    "page": 1,
    "page_size": 10,
    "total_pages": 5
  }
}

列表结果通过 Redis 缓存,过期时间 10 分钟。创建/更新/删除员工后自动清除缓存。

获取当前用户员工档案

GET /employees/me(需认证,所有角色)

根据 JWT 中的 user_id 查询关联的 Employee。

响应codemessage
成功200-
未认证401unauthorized
未关联员工档案404employee profile not found

查询单个员工

GET /employees/:id(需认证,admin 全量,manager 仅本部门+子部门,staff 仅本部门)

响应codemessage
成功200-
无效 ID400invalid id
无权限403forbidden: cannot access employee outside your department scope
不存在404employee not found

更新员工

PUT /employees/:id(需认证,admin/manager 且为部门负责人,manager 仅可更新管辖范围内的员工,不能将员工移出管辖范围)

请求体(只传需要更新的字段):

{
  "name": "张伟2",
  "phone": "13900000001"
}
响应codemessage
成功200-
无效 ID400invalid id
请求体无效400invalid request body
邮箱已存在400email already exists
部门不存在400department not found
无权限403forbidden: cannot update employee outside your department scope

删除员工

DELETE /employees/:id(需认证,仅 admin)

响应codemessage
成功200-
无效 ID400invalid id
不存在404employee not found

薪资结构

创建薪资结构

POST /salaries/structures(需 admin 角色)

请求体:

{
  "employee_id": 1,
  "base_salary": 10000,
  "position_allowance": 2000,
  "performance_factor": 1.0
}

employee_id、base_salary、position_allowance 为必填。performance_factor 可选,默认 1.0。每个员工只能有一个薪资结构。

响应codemessage
成功200-
缺少必填字段400invalid request body
员工不存在400employee not found
薪资结构已存在400salary structure already exists for this employee

查询薪资结构列表(分页+搜索+排序)

GET /salaries/structures(需 admin 角色)

查询参数:

参数类型默认值说明
keywordstring-按员工姓名/邮箱模糊搜索
sort_bystringid排序字段:id, base_salary, position_allowance, performance_factor, created_at
sort_descboolfalse是否降序(传 "true")
pageint1页码
page_sizeint10每页数量,最大 100

查询员工的薪资结构

GET /salaries/structures/employees/:emp_id(需 admin 角色)

响应codemessage
成功200-
无效员工 ID400invalid employee id
不存在404salary structure not found

更新薪资结构

PUT /salaries/structures/:id(需 admin 角色)

请求体(只传需要更新的字段):

{
  "base_salary": 12000,
  "performance_factor": 1.2
}
响应codemessage
成功200-
无效 ID400invalid id
请求体无效400invalid request body
薪资结构不存在404salary structure not found

删除薪资结构

DELETE /salaries/structures/:id(需 admin 角色)

响应codemessage
成功200-
无效 ID400invalid id
薪资结构不存在404salary structure not found

薪资记录

生成月度薪资记录

POST /salaries/records(需 admin 角色)

请求体:

{
  "employee_id": 1,
  "year": 2026,
  "month": 4,
  "performance_factor": 1.2
}

employee_id、year、month 为必填。performance_factor 可选,传 0 或不传则使用薪资结构中的系数。

计算公式:实际薪资 = (基本工资 + 岗位津贴) × 绩效系数

生成时自动关联薪资结构(structure_id),状态为 draft。

同一员工同一年月不能重复生成。

响应codemessage
成功200-
缺少必填字段400invalid request body
年月无效400invalid year or month
员工不存在400employee not found
薪资结构不存在400salary structure not found for this employee
记录已存在400salary record already exists for this employee and month

批量生成月度薪资记录

POST /salaries/records/batch(需 admin 角色)

请求体:

{
  "year": 2026,
  "month": 4,
  "employee_ids": [1, 2, 3],
  "dept_id": null
}

year、month 为必填。employee_ids 和 dept_id 可选,优先级:

  1. employee_ids 不为空:为指定员工生成
  2. dept_id 不为空:为该部门下有薪资结构的在职员工生成
  3. 都不传:为所有有薪资结构的在职员工生成

已存在的记录自动跳过,不报错。

响应codemessage
成功200-
缺少必填字段400invalid request body
年月无效400invalid year or month
部门不存在400department not found

修改薪资记录

PUT /salaries/records/:id(需 admin 角色)

仅 draft 或 rejected 状态可修改。

请求体(只传需要更新的字段):

{
  "performance_factor": 1.5
}

修改 performance_factor 时自动重算 actual_salary。

响应codemessage
成功200-
无效 ID400invalid id
非草稿/驳回状态400salary record is not in draft/rejected status
不存在404salary record not found

提交审核

PUT /salaries/records/:id/submit(需 admin 角色)

draft 或 rejected → pending。

响应codemessage
成功200-
非草稿/驳回状态400salary record is not in draft/rejected status
不存在404salary record not found

审核通过

PUT /salaries/records/:id/approve(需 admin 角色)

pending → approved。

响应codemessage
成功200-
非待审核状态400salary record is not in pending status
不存在404salary record not found

审核驳回

PUT /salaries/records/:id/reject(需 admin 角色)

pending → rejected。驳回后可修改绩效系数再重新提交。

响应codemessage
成功200-
非待审核状态400salary record is not in pending status
不存在404salary record not found

确认发放

PUT /salaries/records/:id/pay(需 admin 角色)

approved → paid。发放后不可修改。

响应codemessage
成功200-
非已审核状态400salary record is not in approved status
不存在404salary record not found

查询薪资记录列表(分页+筛选+排序)

GET /salaries/records(需 admin 角色)

查询参数:

参数类型默认值说明
employee_idint-按员工 ID 筛选
yearint-按年份筛选
monthint-按月份筛选
statusstring-按状态筛选:draft, pending, approved, rejected, paid
sort_bystringid排序字段:id, year, month, actual_salary, status, created_at
sort_descboolfalse是否降序(传 "true")
pageint1页码
page_sizeint10每页数量,最大 100

查询单条薪资记录

GET /salaries/records/:id(需 admin 角色)

响应codemessage
成功200-
无效 ID400invalid id
不存在404salary record not found

考勤管理

上班打卡

POST /attendance/clock-in(需认证,staff 仅能为自己打卡)

请求体:

{
  "employee_id": 1
}

employee_id 为必填。同一天同一员工不能重复打卡。staff 角色只能传自己的 employee_id(从 JWT 的 employee_id 字段获取),否则返回 403。

响应codemessage
成功200-
缺少必填字段400invalid request body
已打卡400already clocked in today
员工不存在400employee not found
staff 替别人打卡403can only clock in for yourself
staff 未关联员工档案403no employee profile linked

下班打卡

PUT /attendance/clock-out(需认证,staff 仅能为自己打卡)

请求体:

{
  "employee_id": 1
}

必须先上班打卡才能下班打卡。staff 角色只能传自己的 employee_id,否则返回 403。

响应codemessage
成功200-
缺少必填字段400invalid request body
未上班打卡400not clocked in today
已下班打卡400already clocked out today
staff 替别人打卡403can only clock out for yourself
staff 未关联员工档案403no employee profile linked

查询打卡记录列表

GET /attendance/records(需认证,staff 仅能查看自己记录)

查询参数:

参数类型默认值说明
employee_idint-按员工 ID 筛选
start_datestring-起始日期 YYYY-MM-DD
end_datestring-结束日期 YYYY-MM-DD
keywordstring-按员工姓名/邮箱搜索
sort_bystringdate排序字段:id, date, created_at
sort_descboolfalse是否降序
pageint1页码
page_sizeint10每页数量,最大 100

查询单条打卡记录

GET /attendance/records/:id(需认证)

响应codemessage
成功200-
不存在404attendance record not found

删除打卡记录

DELETE /attendance/records/:id(需 admin 角色)

响应codemessage
成功200-
无效 ID400invalid id
不存在404attendance record not found

批量删除打卡记录

DELETE /attendance/records/batch(需 admin 角色)

请求体:

{
  "ids": [1, 2, 3]
}
响应codemessage
成功200-
缺少必填字段400invalid request body

生成考勤月度统计

POST /attendance/summaries/generate(需 admin/manager 角色,manager 仅可生成管辖范围内员工的汇总)

请求体:

{
  "employee_id": 1,
  "year": 2026,
  "month": 4
}

根据员工工作时间(WorkStartTime/WorkEndTime,默认 09:00/18:00)判定迟到/早退/缺勤。重复生成会覆盖旧数据。

响应codemessage
成功200-
缺少必填字段400invalid request body
年月无效400invalid year or month
员工不存在400employee not found
无权限403forbidden: cannot generate summary for employee outside your department scope

查询考勤统计列表

GET /attendance/summaries(需认证,staff 仅能查看自己统计)

查询参数:

参数类型默认值说明
employee_idint-按员工 ID 筛选
yearint-按年份筛选
monthint-按月份筛选
sort_bystringid排序字段:id, year, month, created_at
sort_descboolfalse是否降序
pageint1页码
page_sizeint10每页数量,最大 100

请假管理

提交请假申请

POST /leaves(需认证,staff 仅能为本人申请,manager 仅能为管辖范围内员工申请)

请求体:

{
  "employee_id": 1,
  "type": "annual",
  "start_date": "2026-04-28",
  "end_date": "2026-04-29",
  "reason": "年假"
}

employee_id、type、start_date、end_date 为必填。type 取值:annual(年假)/sick(病假)/personal(事假)。staff 角色只能传自己的 employee_id,否则返回 403。manager 只能为管辖范围内的员工创建请假。

响应codemessage
成功200-
缺少必填字段400invalid request body
无效请假类型400invalid leave type, must be annual/sick/personal
日期无效400end date must be >= start date
员工不存在400employee not found
staff 替别人请假403can only create leave for yourself
staff 未关联员工档案403no employee profile linked
无权限403forbidden: cannot create leave for employee outside your department scope

查询请假列表

GET /leaves(需认证,staff 仅能查看自己请假)

查询参数:

参数类型默认值说明
employee_idint-按员工 ID 筛选
statusstring-按状态筛选:pending/approved/rejected
keywordstring-按员工姓名/邮箱搜索
sort_bystringid排序字段:id, type, status, start_date, created_at
sort_descboolfalse是否降序
pageint1页码
page_sizeint10每页数量,最大 100

查询单条请假申请

GET /leaves/:id(需认证)

响应codemessage
成功200-
不存在404leave request not found

审批通过

PUT /leaves/:id/approve(需 admin/manager 且为部门负责人)

pending → approved。manager 仅能审批本部门及子部门的请假。

响应codemessage
成功200-
非待审批状态400leave request is not in pending status
不存在404leave request not found
manager 审批非本部门403forbidden: cannot approve leave outside your department

审批驳回

PUT /leaves/:id/reject(需 admin/manager 且为部门负责人)

pending → rejected。manager 仅能驳回本部门及子部门的请假。

响应codemessage
成功200-
非待审批状态400leave request is not in pending status
不存在404leave request not found
manager 驳回非本部门403forbidden: cannot reject leave outside your department

获取角色列表

GET /roles(需认证,所有角色可查看)

用户管理

创建用户

POST /users(需 admin 角色)

请求体:

{
  "username": "newuser",
  "password": "123456",
  "role_ids": [2]
}
  • password 最少 6 位
  • role_ids 可选,不传则默认分配 staff 角色
响应codemessage
成功200-
用户名已存在400username already exists
请求体无效400invalid request body

获取用户列表(分页+搜索+排序)

GET /users(需 admin 角色)

查询参数:

参数类型默认值说明
keywordstring-按 username 模糊搜索
sort_bystringid排序字段:id, username, status, created_at
sort_descboolfalse是否降序(传 "true")
pageint1页码
page_sizeint10每页数量,最大 100

响应:

{
  "code": 200,
  "data": {
    "data": [
      {
        "id": 1,
        "username": "admin",
        "status": "active",
        "roles": [{"id": 1, "name": "admin"}],
        "created_at": "2026-04-29T12:00:00Z",
        "updated_at": "2026-04-29T12:00:00Z"
      }
    ],
    "total": 10,
    "page": 1,
    "page_size": 10,
    "total_pages": 1
  }
}

获取单个用户

GET /users/:id(需 admin 角色)

响应codemessage
成功200-
无效 ID400invalid id
不存在404user not found

修改用户状态

PUT /users/:id/status(需 admin 角色)

请求体:

{
  "status": "disabled"
}

status 取值:active / disabled

响应codemessage
成功200-
无效 ID400invalid id
请求体无效400invalid request body
无效状态400invalid status

分配角色

PUT /users/:id/roles(需 admin 角色)

请求体:

{
  "role_ids": [1, 2]
}
响应codemessage
成功200-
无效 ID400invalid id
请求体无效400invalid request body

修改密码

PUT /users/:id/password(需 admin 角色)

请求体:

{
  "new_password": "654321"
}

admin 可重置任意用户密码,仅需提供 new_password。new_password 最少 6 位。

响应codemessage
成功200-
无效 ID400invalid id
请求体无效400invalid request body

删除用户

DELETE /users/:id(需 admin 角色)

不能删除自己。

响应codemessage
成功200-
无效 ID400invalid id
删除自己400cannot delete yourself

权限矩阵

操作adminmanager(部门负责人)staff
查看部门/部门树YYY
创建/编辑部门YY(本部门范围)-
删除部门Y--
查看员工列表Y(全部)Y(本部门+子部门)-
查看 /employees/meYYY
创建/编辑员工YY(本部门范围)-
删除员工Y--
管理用户(含删除)Y--
查看角色列表YYY
薪资管理(全部)Y--
打卡YY仅自己
删除考勤记录Y--
查看考勤记录/统计YY仅自己
生成考勤统计YY-
提交请假YY仅自己
查看请假列表YY仅自己
审批请假YY(本部门范围)-
查看审计日志Y--
删除审计日志Y--

"本部门范围" 指该 manager 作为 leader_id 所属部门及其子部门。manager 如果不是任何部门的负责人,访问需要部门权限的接口会返回 403。

"仅自己" 指后端强制使用 JWT 中的 employee_id,忽略请求中的 employee_id 参数或列表筛选。

操作日志

所有写操作(创建、更新、删除、审批等)自动记录审计日志,包含操作人、操作类型、实体类型、实体 ID、变更内容、IP 地址和时间。

查询审计日志

GET /audit-logs(需 admin 角色)

查询参数:

参数类型默认值说明
operatorstring-按操作人筛选
entity_typestring-按实体类型筛选:user, department, employee, salary_structure, salary_record, attendance_record, attendance_summary, leave_request
start_timestring-起始时间 RFC3339 格式
end_timestring-结束时间 RFC3339 格式
sort_bystringid排序字段:id, created_at, action, entity_type
sort_descboolfalse是否降序(传 "true")
pageint1页码
page_sizeint10每页数量,最大 100

响应示例:

{
  "code": 200,
  "data": {
    "data": [
      {
        "id": 1,
        "operator": "admin",
        "action": "create",
        "entity_type": "employee",
        "entity_id": 1,
        "changes": "{\"name\":\"Alice\",\"email\":\"alice@test.com\"}",
        "ip_address": "192.168.1.1",
        "created_at": "2026-04-28T12:00:00Z"
      }
    ],
    "total": 50,
    "page": 1,
    "page_size": 10,
    "total_pages": 5
  }
}

action 取值:create, update, delete, update_status, assign_roles, reset_password, update_password, clock_in, clock_out, generate_summary, approve, reject, submit, pay, batch_create

删除审计日志

DELETE /audit-logs/:id(需 admin 角色)

响应codemessage
成功200-
无效 ID400invalid id
不存在404audit log not found

批量删除审计日志

DELETE /audit-logs/batch(需 admin 角色)

请求体:

{
  "ids": [1, 2, 3]
}
响应codemessage
成功200-
缺少必填字段400invalid request body

健康检查

GET /ping

{ "message": "pong" }