员工工资查询接口逻辑梳理.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
  • 目的:员工确认工资后,该工资记录将无法再次查询
  • 业务逻辑
    1. 工资计算完成 → 管理员锁定(IsLocked = 1
    2. 员工可以查看工资(IsLocked = 1EmployeeConfirmStatus != 1
    3. 员工确认工资(EmployeeConfirmStatus = 1
    4. 确认后无法再次查询(EmployeeConfirmStatus = 1 的记录被排除)

2. 员工ID匹配

  • 精确匹配EmployeeId == input.EmployeeId
  • 目的:确保员工只能查看自己的工资,不能查看其他员工的工资
  • 实现方式
    • 后端:通过SQL查询条件 EmployeeId == input.EmployeeId 实现精确匹配
    • 前端:从本地存储获取当前登录用户的ID,自动填充到查询参数中

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

🔍 代码实现对比

共同点

所有服务的查询逻辑都相同:

  1. 参数验证:年份、月份、员工ID验证
  2. 月份格式化$"{input.Year}{input.Month:D2}"
  3. 查询条件StatisticsMonth == monthStr && EmployeeId == input.EmployeeId && IsLocked == 1
  4. 异常处理:未找到记录时抛出异常
  5. 返回类型:返回对应的Output DTO

差异点

  1. 数据表不同:每个服务查询不同的工资统计表
  2. Output DTO不同:每个服务返回的字段可能不同
  3. 字段映射不同:根据岗位不同,返回的工资字段不同

📝 总结

核心逻辑

  1. 统一接口:所有薪酬服务都提供 query-by-employee 接口
  2. 统一参数:都使用 SalaryQueryByEmployeeInput 作为输入参数
  3. 统一条件:都查询已锁定(IsLocked == 1)的工资记录
  4. 统一验证:都进行相同的参数验证和异常处理

安全机制

  1. 锁定检查:只查询已锁定的工资
  2. 员工匹配:精确匹配员工ID
  3. 参数验证:验证年份、月份、员工ID的有效性

使用场景

  1. 员工查看工资条:员工通过小程序或PC端查看自己的工资
  2. 工资确认:员工查看工资后,可以确认工资条
  3. 历史查询:员工可以查询历史月份的工资记录

文档版本: v1.0
创建日期: 2026-01-09
适用范围: 所有薪酬服务的员工工资查询接口