员工工资查询接口逻辑梳理.md
13.5 KB
员工工资查询接口逻辑梳理
📋 概述
所有薪酬服务都提供了根据员工ID和月份查询工资的接口,供员工查看自己的工资条。本文档梳理了所有薪酬服务中的查询逻辑。
🔍 接口列表
1. 健康师工资查询
- 服务:
LqSalaryService - 路由:
GET /api/Extend/lqsalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
HealthCoachSalaryOutput - 数据表:
lq_salary_statistics
2. 店长工资查询
- 服务:
LqStoreManagerSalaryService - 路由:
GET /api/Extend/lqstoremanagersalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
StoreManagerSalaryOutput - 数据表:
lq_store_manager_salary_statistics
3. 主任工资查询
- 服务:
LqDirectorSalaryService - 路由:
GET /api/Extend/lqdirectorsalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
DirectorSalaryOutput - 数据表:
lq_director_salary_statistics
4. 店助工资查询
- 服务:
LqAssistantSalaryService - 路由:
GET /api/Extend/lqassistantsalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
AssistantSalaryOutput - 数据表:
lq_assistant_salary_statistics
5. 事业部总经理/经理工资查询
- 服务:
LqBusinessUnitManagerSalaryService - 路由:
GET /api/Extend/lqbusinessunitmanagersalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
BusinessUnitManagerSalaryOutput - 数据表:
lq_business_unit_manager_salary_statistics
6. 科技部老师工资查询
- 服务:
LqTechTeacherSalaryService - 路由:
GET /api/Extend/lqtechteachersalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
TechTeacherSalaryOutput - 数据表:
lq_tech_teacher_salary_statistics
7. 科技部总经理工资查询
- 服务:
LqTechGeneralManagerSalaryService - 路由:
GET /api/Extend/lqtechgeneralmanagersalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
TechGeneralManagerSalaryOutput - 数据表:
lq_tech_general_manager_salary_statistics
8. 大项目主管工资查询
- 服务:
LqMajorProjectDirectorSalaryService - 路由:
GET /api/Extend/lqmajorprojectdirectorsalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
MajorProjectDirectorSalaryOutput - 数据表:
lq_major_project_director_salary_statistics
9. 大项目部老师工资查询
- 服务:
LqMajorProjectTeacherSalaryService - 路由:
GET /api/Extend/lqmajorprojectteachersalary/query-by-employee - 方法:
GetSalaryByEmployee - 返回类型:
MajorProjectTeacherSalaryOutput - 数据表:
lq_major_project_teacher_salary_statistics
📝 统一查询逻辑
输入参数
所有接口都使用相同的输入参数类:SalaryQueryByEmployeeInput
public class SalaryQueryByEmployeeInput
{
/// <summary>
/// 年份
/// </summary>
public int Year { get; set; }
/// <summary>
/// 月份
/// </summary>
public int Month { get; set; }
/// <summary>
/// 员工ID
/// </summary>
public string EmployeeId { get; set; }
}
查询条件
所有接口的查询条件都相同:
.Where(x =>
x.StatisticsMonth == monthStr // 统计月份匹配
&& x.EmployeeId == input.EmployeeId // 员工ID匹配
&& x.IsLocked == 1 // 只查询已锁定的工资
&& x.EmployeeConfirmStatus != 1 // 只查询未确认的工资
)
关键点:
- ✅ 只查询已锁定的工资:
IsLocked == 1 - ✅ 只查询未确认的工资:
EmployeeConfirmStatus != 1(已确认的工资无法查询) - ✅ 员工ID匹配:
EmployeeId == input.EmployeeId - ✅ 月份匹配:
StatisticsMonth == monthStr(格式:YYYYMM)
重要说明:
- 员工只能查看已锁定但未确认的工资记录
- 一旦员工确认工资后(
EmployeeConfirmStatus = 1),该工资记录将无法通过此接口查询 - 这个设计确保员工在确认工资后,无法再次查看已确认的工资记录
参数验证
所有接口都进行相同的参数验证:
// 1. 验证年份和月份
if (input.Year <= 0 || input.Month <= 0 || input.Month > 12)
{
throw NCCException.Oh("年份和月份参数不正确");
}
// 2. 验证员工ID
if (string.IsNullOrWhiteSpace(input.EmployeeId))
{
throw NCCException.Oh("员工ID不能为空");
}
// 3. 格式化月份
var monthStr = $"{input.Year}{input.Month:D2}"; // 例如:202512
查询结果处理
// 查询工资记录
var salary = await _db.Queryable<SalaryStatisticsEntity>()
.Where(x => x.StatisticsMonth == monthStr
&& x.EmployeeId == input.EmployeeId
&& x.IsLocked == 1)
.Select(x => new SalaryOutput { /* 字段映射 */ })
.FirstAsync();
// 如果未找到,抛出异常
if (salary == null)
{
throw NCCException.Oh($"未找到员工{input.EmployeeId}在{input.Year}年{input.Month}月的工资记录");
}
return salary;
🔐 安全机制
1. 锁定机制
- 只查询已锁定的工资:
IsLocked == 1 - 目的:确保员工只能查看已完成的工资数据,避免查看未完成计算的工资
- 业务逻辑:工资计算完成后,管理员需要先锁定工资,员工才能查看
1.1 确认状态限制
- 只查询未确认的工资:
EmployeeConfirmStatus != 1 - 目的:员工确认工资后,该工资记录将无法再次查询
- 业务逻辑:
- 工资计算完成 → 管理员锁定(
IsLocked = 1) - 员工可以查看工资(
IsLocked = 1且EmployeeConfirmStatus != 1) - 员工确认工资(
EmployeeConfirmStatus = 1) - 确认后无法再次查询(
EmployeeConfirmStatus = 1的记录被排除)
- 工资计算完成 → 管理员锁定(
2. 员工ID匹配
- 精确匹配:
EmployeeId == input.EmployeeId - 目的:确保员工只能查看自己的工资,不能查看其他员工的工资
- 实现方式:
- 后端:通过SQL查询条件
EmployeeId == input.EmployeeId实现精确匹配 - 前端:从本地存储获取当前登录用户的ID,自动填充到查询参数中
- 后端:通过SQL查询条件
3. 月份限制
- 格式验证:月份必须在 1-12 之间
- 目的:确保查询参数的有效性
4. 权限验证说明
当前实现
- 后端:接口没有验证当前登录用户,只通过
EmployeeId参数查询 - 前端:从
uni.getStorageSync('userInfo')获取用户ID,自动填充到查询参数 - 安全依赖:依赖前端确保传入的
EmployeeId是当前登录用户的ID
潜在安全问题
- 风险:如果前端被篡改,可能会查询到其他员工的工资
- 现状:目前通过SQL查询条件
EmployeeId == input.EmployeeId实现精确匹配,但没有验证input.EmployeeId是否与当前登录用户ID一致
建议改进
可以在后端增加权限验证,确保员工只能查询自己的工资:
// 获取当前登录用户ID
var currentUserId = _userManager.UserId;
// 验证:员工只能查询自己的工资
if (input.EmployeeId != currentUserId && !_userManager.IsAdministrator)
{
throw NCCException.Oh("您只能查询自己的工资记录");
}
注意:管理员可能需要查询所有员工的工资,所以需要判断 IsAdministrator
📊 数据流程
查询流程
1. 接收请求参数(Year, Month, EmployeeId)
↓
2. 参数验证
- 年份和月份有效性检查
- 员工ID非空检查
↓
3. 格式化月份(YYYYMM格式)
↓
4. 查询数据库
- 条件:StatisticsMonth == monthStr
- 条件:EmployeeId == input.EmployeeId
- 条件:IsLocked == 1(已锁定)
- 条件:EmployeeConfirmStatus != 1(未确认)
↓
5. 数据映射(Entity → Output DTO)
↓
6. 结果验证
- 如果未找到,抛出异常
↓
7. 返回工资记录
数据表结构
每个薪酬服务对应一个工资统计表:
| 服务 | 数据表 | 主键字段 | 员工ID字段 | 月份字段 | 锁定字段 |
|---|---|---|---|---|---|
| 健康师 | lq_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
| 店长 | lq_store_manager_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
| 主任 | lq_director_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
| 店助 | lq_assistant_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
| 事业部总经理/经理 | lq_business_unit_manager_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
| 科技部老师 | lq_tech_teacher_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
| 科技部总经理 | lq_tech_general_manager_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
| 大项目主管 | lq_major_project_director_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
| 大项目部老师 | lq_major_project_teacher_salary_statistics |
F_Id |
F_EmployeeId |
F_StatisticsMonth |
F_IsLocked |
🔄 与其他功能的关系
1. 工资计算
- 关系:查询接口依赖工资计算接口生成的数据
- 流程:先执行计算接口(
calculate/*),生成工资记录,然后才能查询
2. 工资锁定
- 关系:查询接口只返回已锁定的工资
- 流程:工资计算完成后,需要锁定(
IsLocked = 1),员工才能查看
3. 员工确认
- 关系:查询接口返回的数据包含确认状态(
EmployeeConfirmStatus) - 流程:员工查看工资后,可以确认工资条
⚠️ 注意事项
1. 锁定状态
- 必须锁定:只有已锁定的工资才能被员工查询
- 未锁定处理:如果工资未锁定,查询接口会返回404错误
1.1 确认状态
- 必须未确认:只有未确认的工资才能被员工查询
- 已确认处理:如果工资已确认(
EmployeeConfirmStatus = 1),查询接口会返回404错误 - 业务含义:员工确认工资后,该工资记录将无法再次查询,确保数据安全
2. 员工ID匹配
- 精确匹配:必须使用正确的员工ID
- 安全考虑:接口不验证当前登录用户,需要前端或中间件确保员工只能查询自己的工资
3. 月份格式
- 格式要求:月份必须格式化为 YYYYMM(如:202512)
- 验证:月份必须在 1-12 之间
4. 数据完整性
- 字段映射:每个服务的Output DTO字段可能不同
- 空值处理:如果未找到记录,返回404错误
📋 接口调用示例
健康师工资查询
GET /api/Extend/lqsalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
店长工资查询
GET /api/Extend/lqstoremanagersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
主任工资查询
GET /api/Extend/lqdirectorsalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
店助工资查询
GET /api/Extend/lqassistantsalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
事业部总经理/经理工资查询
GET /api/Extend/lqbusinessunitmanagersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
科技部老师工资查询
GET /api/Extend/lqtechteachersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
科技部总经理工资查询
GET /api/Extend/lqtechgeneralmanagersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
大项目主管工资查询
GET /api/Extend/lqmajorprojectdirectorsalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
大项目部老师工资查询
GET /api/Extend/lqmajorprojectteachersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
🔍 代码实现对比
共同点
所有服务的查询逻辑都相同:
- 参数验证:年份、月份、员工ID验证
- 月份格式化:
$"{input.Year}{input.Month:D2}" - 查询条件:
StatisticsMonth == monthStr && EmployeeId == input.EmployeeId && IsLocked == 1 - 异常处理:未找到记录时抛出异常
- 返回类型:返回对应的Output DTO
差异点
- 数据表不同:每个服务查询不同的工资统计表
- Output DTO不同:每个服务返回的字段可能不同
- 字段映射不同:根据岗位不同,返回的工资字段不同
📝 总结
核心逻辑
- 统一接口:所有薪酬服务都提供
query-by-employee接口 - 统一参数:都使用
SalaryQueryByEmployeeInput作为输入参数 - 统一条件:都查询已锁定(
IsLocked == 1)的工资记录 - 统一验证:都进行相同的参数验证和异常处理
安全机制
- 锁定检查:只查询已锁定的工资
- 员工匹配:精确匹配员工ID
- 参数验证:验证年份、月份、员工ID的有效性
使用场景
- 员工查看工资条:员工通过小程序或PC端查看自己的工资
- 工资确认:员工查看工资后,可以确认工资条
- 历史查询:员工可以查询历史月份的工资记录
文档版本: v1.0
创建日期: 2026-01-09
适用范围: 所有薪酬服务的员工工资查询接口