健康师薪酬计算规则梳理-额外数据与金三角战队业绩整合逻辑.md 15.8 KB

健康师薪酬计算规则梳理 - 额外数据与金三角战队业绩整合逻辑

一、核心问题

健康师的额外数据(lq_salary_extra_calculation 表)会影响健康师的业绩计算,变动后的业绩需要整合到金三角战队业绩中。

二、数据流程梳理

2.1 数据来源

1. 基础业绩数据

  • 表名: lq_kd_jksyj (健康师业绩表)
  • 字段:
    • jkszh: 健康师账号/ID
    • jksyj: 健康师业绩(字符串类型,需转换)
    • PerformanceType: 业绩类型("基础业绩" 或 "合作业绩")
    • yjsj: 业绩时间
    • glkdbh: 关联开单编号

2. 额外计算数据

  • 表名: lq_salary_extra_calculation (健康师工资额外计算表)
  • 关键字段:
    • F_EmployeeId: 健康师ID
    • F_Year: 年份
    • F_Month: 月份
    • F_BaseRewardPerformance: 基础奖励业绩(需从基础业绩中扣除)
    • F_CooperationRewardPerformance: 合作奖励业绩(需从合作业绩中扣除)
    • F_OtherPerformanceAdd: 其他业绩加(需加到总业绩中)
    • F_OtherPerformanceSubtract: 其他业绩减(需从总业绩中扣除)
    • F_NewCustomerPerformance: 新客业绩(可能被额外数据覆盖)
    • F_UpgradePerformance: 升单业绩(可能被额外数据覆盖)

3. 退款数据

  • 表名: lq_hytk_jksyj (退款健康师业绩表)
  • 作用: 从总业绩中扣除退款金额

4. 金三角战队数据

  • 表名: lq_jinsanjiao_user (金三角用户表)
  • 表名: lq_ycsd_jsj (金三角基础信息表)
  • 作用: 确定健康师所属的战队

2.2 当前计算流程(代码位置:LqSalaryService.csCalculateHealthCoachSalary 方法)

步骤1: 计算基础业绩数据(第563-591行)

// 2.1 计算个人业绩
var myPerf = performanceData.Where(x => x.Jks == empId).ToList();
salary.BasePerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "基础业绩")
    .Sum(x => decimal.Parse(x.Jksyj ?? "0"));
salary.CooperationPerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "合作业绩")
    .Sum(x => decimal.Parse(x.Jksyj ?? "0"));
salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0")); // 第567行

// 扣除退款
var myRefunds = refundList.Where(x => x.Jks == empId).ToList();
if (myRefunds.Any())
{
    decimal totalRefund = myRefunds.Sum(x => x.Jksyj ?? 0);
    salary.TotalPerformance -= totalRefund; // 第584行
    salary.BasePerformance -= baseRefund;
    salary.CooperationPerformance -= cooperationRefund;
}

// 新客与升单业绩(从开单记录中统计)
salary.NewCustomerPerformance = myPerf.Where(x => string.Equals(x.Sfskdd, "是"))
    .Sum(x => decimal.Parse(x.Jksyj ?? "0"));
salary.UpgradePerformance = myPerf.Where(x => string.Equals(x.Sfskdd, "否"))
    .Sum(x => decimal.Parse(x.Jksyj ?? "0"));

当前问题:

  • TotalPerformance 只统计了开单业绩并扣除了退款
  • 没有加上 OtherPerformanceAdd
  • 没有减去 OtherPerformanceSubtract

步骤2: 填充额外计算数据(第593-605行)

// 2.1.1 填充额外计算数据
if (extraCalculationDict.ContainsKey(empId))
{
    var extraData = extraCalculationDict[empId];
    salary.BaseRewardPerformance = extraData.BaseRewardPerformance;
    salary.CooperationRewardPerformance = extraData.CooperationRewardPerformance;
    salary.OtherPerformanceAdd = extraData.OtherPerformanceAdd; // 第599行
    salary.OtherPerformanceSubtract = extraData.OtherPerformanceSubtract; // 第600行
    salary.UpgradeCustomerCount = extraData.UpgradeCustomerCount;
    salary.NewCustomerConversionRate = extraData.NewCustomerConversionRate;
    salary.NewCustomerPerformance = extraData.NewCustomerPerformance; // 可能覆盖之前的值
    salary.UpgradePerformance = extraData.UpgradePerformance; // 可能覆盖之前的值
}

说明:

  • 额外数据被填充到工资统计对象中
  • NewCustomerPerformanceUpgradePerformance 可能被额外数据覆盖

步骤3: 计算实际基础业绩和实际合作业绩(第607-636行)

// 2.1.2 计算实际基础业绩和实际合作业绩
// 实际基础业绩 = 基础业绩 - 基础奖励业绩 + 其他业绩加 - 其他业绩减
decimal actualBasePerformance = salary.BasePerformance
    - salary.BaseRewardPerformance
    + salary.OtherPerformanceAdd  // 第615行:只影响实际基础业绩
    - salary.OtherPerformanceSubtract; // 第616行:只影响实际基础业绩

// 新店额外调整:根据阶段扣除新客业绩或升单业绩
if (isNewStore)
{
    if (newStoreStage == 1)
    {
        actualBasePerformance -= salary.NewCustomerPerformance;
    }
    else if (newStoreStage == 2)
    {
        actualBasePerformance -= salary.UpgradePerformance;
    }
}

salary.ActualBasePerformance = actualBasePerformance;

// 实际合作业绩 = 合作业绩 - 合作奖励业绩
salary.ActualCooperationPerformance = salary.CooperationPerformance - salary.CooperationRewardPerformance;

当前问题:

  • OtherPerformanceAddOtherPerformanceSubtract 只用于计算 ActualBasePerformance
  • 没有用于调整 TotalPerformance

步骤4: 计算金三角战队业绩(第700-753行)

// 3. 处理战队逻辑 (考勤规则)
// 规则:若出勤天数 < 20天,则该健康师不计入战队,按单人计算。

// 按战队分组
var teamGroups = employeeStats.Values
    .Where(x => !string.IsNullOrEmpty(x.GoldTriangleId))
    .GroupBy(x => x.GoldTriangleId)
    .ToList();

foreach (var group in teamGroups)
{
    var validMembers = new List<LqSalaryStatisticsEntity>();
    var invalidMembers = new List<LqSalaryStatisticsEntity>();

    foreach (var member in group)
    {
        if (member.WorkingDays >= 20)
        {
            validMembers.Add(member);
        }
        else
        {
            invalidMembers.Add(member);
        }
    }

    // 对于无效成员,移除战队标识,视为单人
    foreach (var member in invalidMembers)
    {
        member.GoldTriangleId = null;
        member.GoldTriangleTeam = "个人";
        member.Position = "健康师";
    }

    // 计算有效战队的总业绩和总消耗
    var teamTotalPerformance = validMembers.Sum(x => x.TotalPerformance); // 第735行
    var teamTotalConsumption = validMembers.Sum(x => x.Consumption);

    // 更新有效成员的战队业绩和战队总消耗
    foreach (var member in validMembers)
    {
        member.TeamPerformance = teamTotalPerformance; // 第741行
        member.TeamTotalConsumption = teamTotalConsumption;
    }
}

// 补充处理:对于没有战队ID的(个人,或被剔除的),战队业绩等于个人总业绩
foreach (var salary in employeeStats.Values)
{
    if (string.IsNullOrEmpty(salary.GoldTriangleId))
    {
        salary.TeamPerformance = salary.TotalPerformance; // 第751行
    }
}

当前问题:

  • 第735行计算战队总业绩时,使用的是 x.TotalPerformance
  • TotalPerformance 没有包含额外数据的调整OtherPerformanceAddOtherPerformanceSubtract
  • 因此,金三角战队业绩没有整合变动后的业绩

三、问题分析

3.1 核心问题

  1. 总业绩未包含额外数据调整

    • TotalPerformance 只统计了开单业绩并扣除了退款
    • 没有加上 OtherPerformanceAdd
    • 没有减去 OtherPerformanceSubtract
  2. 金三角战队业绩计算不准确

    • 战队业绩 = 所有有效成员的总业绩之和
    • 但使用的是未调整的 TotalPerformance
    • 导致额外数据的变动没有反映到战队业绩中

3.2 影响范围

  1. 健康师个人总业绩: 不准确(缺少额外数据调整)
  2. 金三角战队业绩: 不准确(基于不准确的个人总业绩)
  3. 提成计算: 可能受影响(如果提成计算依赖 TotalPerformance
  4. 业绩占比: 不准确(个人业绩占比 = 个人总业绩 / 战队总业绩)

四、解决方案建议

4.1 方案一:在计算总业绩时直接加上额外数据调整

修改位置: LqSalaryService.cs 第567-605行

修改逻辑:

// 2.1 计算个人业绩
var myPerf = performanceData.Where(x => x.Jks == empId).ToList();
salary.BasePerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "基础业绩")
    .Sum(x => decimal.Parse(x.Jksyj ?? "0"));
salary.CooperationPerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "合作业绩")
    .Sum(x => decimal.Parse(x.Jksyj ?? "0"));
salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0"));

// 扣除退款
var myRefunds = refundList.Where(x => x.Jks == empId).ToList();
if (myRefunds.Any())
{
    decimal totalRefund = myRefunds.Sum(x => x.Jksyj ?? 0);
    salary.TotalPerformance -= totalRefund;
    salary.BasePerformance -= baseRefund;
    salary.CooperationPerformance -= cooperationRefund;
}

// 新客与升单业绩(从开单记录中统计)
salary.NewCustomerPerformance = myPerf.Where(x => string.Equals(x.Sfskdd, "是"))
    .Sum(x => decimal.Parse(x.Jksyj ?? "0"));
salary.UpgradePerformance = myPerf.Where(x => string.Equals(x.Sfskdd, "否"))
    .Sum(x => decimal.Parse(x.Jksyj ?? "0"));

// 2.1.1 填充额外计算数据
if (extraCalculationDict.ContainsKey(empId))
{
    var extraData = extraCalculationDict[empId];
    salary.BaseRewardPerformance = extraData.BaseRewardPerformance;
    salary.CooperationRewardPerformance = extraData.CooperationRewardPerformance;
    salary.OtherPerformanceAdd = extraData.OtherPerformanceAdd;
    salary.OtherPerformanceSubtract = extraData.OtherPerformanceSubtract;
    salary.UpgradeCustomerCount = extraData.UpgradeCustomerCount;
    salary.NewCustomerConversionRate = extraData.NewCustomerConversionRate;
    salary.NewCustomerPerformance = extraData.NewCustomerPerformance;
    salary.UpgradePerformance = extraData.UpgradePerformance;

    // 【新增】调整总业绩:加上其他业绩加,减去其他业绩减
    salary.TotalPerformance += salary.OtherPerformanceAdd;
    salary.TotalPerformance -= salary.OtherPerformanceSubtract;
}

优点:

  • 简单直接,在填充额外数据后立即调整总业绩
  • 确保后续所有使用 TotalPerformance 的地方都能获取到调整后的值

缺点:

  • 需要确认 OtherPerformanceAddOtherPerformanceSubtract 是否应该影响总业绩
  • 需要确认是否应该影响基础业绩或合作业绩

4.2 方案二:在计算战队业绩前调整总业绩

修改位置: LqSalaryService.cs 第700-753行

修改逻辑:

// 3. 处理战队逻辑 (考勤规则)
// 规则:若出勤天数 < 20天,则该健康师不计入战队,按单人计算。

// 【新增】在计算战队业绩前,先调整所有健康师的总业绩(加上额外数据)
foreach (var salary in employeeStats.Values)
{
    salary.TotalPerformance += salary.OtherPerformanceAdd;
    salary.TotalPerformance -= salary.OtherPerformanceSubtract;
}

// 按战队分组
var teamGroups = employeeStats.Values
    .Where(x => !string.IsNullOrEmpty(x.GoldTriangleId))
    .GroupBy(x => x.GoldTriangleId)
    .ToList();

// ... 后续逻辑不变

优点:

  • 集中处理,逻辑清晰
  • 确保在计算战队业绩前,所有成员的总业绩都已调整

缺点:

  • 如果其他地方也使用 TotalPerformance,可能也需要调整

4.3 方案三:使用调整后的总业绩字段

修改逻辑:

  • 保持 TotalPerformance 不变(原始总业绩)
  • 新增 AdjustedTotalPerformance 字段(调整后的总业绩)
  • 在计算战队业绩时使用 AdjustedTotalPerformance

优点:

  • 保留原始数据,便于追溯
  • 明确区分原始业绩和调整后业绩

缺点:

  • 需要修改数据库表结构
  • 需要修改所有使用总业绩的地方

五、推荐方案

推荐使用方案一,原因:

  1. 逻辑简单,在填充额外数据后立即调整总业绩
  2. 确保后续所有使用 TotalPerformance 的地方都能获取到调整后的值
  3. 不需要修改数据库表结构
  4. 符合业务逻辑:额外数据应该影响总业绩

六、需要确认的问题

  1. OtherPerformanceAddOtherPerformanceSubtract 是否应该影响总业绩?

    • 当前代码中,这两个字段只影响 ActualBasePerformance
    • 需要确认业务规则:是否应该同时影响 TotalPerformance
  2. 额外数据中的 NewCustomerPerformanceUpgradePerformance 是否应该覆盖从开单记录中统计的值?

    • 当前代码中,如果存在额外数据,会覆盖之前统计的值
    • 需要确认业务规则:是覆盖还是累加?
  3. 额外数据是否应该影响基础业绩或合作业绩?

    • 当前代码中,OtherPerformanceAddOtherPerformanceSubtract 只影响 ActualBasePerformance
    • 需要确认:是否应该同时调整 BasePerformanceCooperationPerformance
  4. 金三角战队业绩的计算时机

    • 当前在计算战队业绩时,使用的是 TotalPerformance
    • 如果 TotalPerformance 没有包含额外数据调整,战队业绩就不准确
    • 需要确认:是否应该在计算战队业绩前,先调整所有成员的总业绩?

七、代码修改建议

7.1 修改位置

文件: netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
方法: CalculateHealthCoachSalary
位置: 第593-605行(填充额外计算数据后)

7.2 修改内容

在填充额外计算数据后,立即调整总业绩:

// 2.1.1 填充额外计算数据
if (extraCalculationDict.ContainsKey(empId))
{
    var extraData = extraCalculationDict[empId];
    salary.BaseRewardPerformance = extraData.BaseRewardPerformance;
    salary.CooperationRewardPerformance = extraData.CooperationRewardPerformance;
    salary.OtherPerformanceAdd = extraData.OtherPerformanceAdd;
    salary.OtherPerformanceSubtract = extraData.OtherPerformanceSubtract;
    salary.UpgradeCustomerCount = extraData.UpgradeCustomerCount;
    salary.NewCustomerConversionRate = extraData.NewCustomerConversionRate;
    salary.NewCustomerPerformance = extraData.NewCustomerPerformance;
    salary.UpgradePerformance = extraData.UpgradePerformance;

    // 【新增】调整总业绩:加上其他业绩加,减去其他业绩减
    // 注意:这里调整的是总业绩,确保后续计算(包括战队业绩)使用的是调整后的值
    salary.TotalPerformance += salary.OtherPerformanceAdd;
    salary.TotalPerformance -= salary.OtherPerformanceSubtract;
}

7.3 影响分析

修改后的影响:

  1. ✅ 健康师个人总业绩包含额外数据调整
  2. ✅ 金三角战队业绩 = 所有有效成员的调整后总业绩之和
  3. ✅ 提成计算(如果依赖 TotalPerformance)会使用调整后的值
  4. ✅ 业绩占比计算会使用调整后的值

需要注意:

  • 确保 OtherPerformanceAddOtherPerformanceSubtract 的值是正确的
  • 确保额外数据表(lq_salary_extra_calculation)的数据是准确的
  • 如果后续有其他地方也使用 TotalPerformance,需要确认是否应该使用调整后的值

八、测试建议

  1. 单元测试:

    • 测试额外数据为0时,总业绩不变
    • 测试 OtherPerformanceAdd > 0 时,总业绩增加
    • 测试 OtherPerformanceSubtract > 0 时,总业绩减少
    • 测试两者同时存在时的计算
  2. 集成测试:

    • 测试单个健康师的额外数据调整后,总业绩是否正确
    • 测试战队中多个成员都有额外数据时,战队总业绩是否正确
    • 测试战队中部分成员有额外数据时,战队总业绩是否正确
  3. 数据验证:

    • 对比修改前后的计算结果
    • 验证金三角战队业绩是否包含额外数据调整
    • 验证业绩占比计算是否正确

文档版本: v1.0
创建日期: 2026-01-09
适用范围: 健康师薪酬计算 - 额外数据与金三角战队业绩整合