Commit 26d4de1ee358beccd5f56530c8ede693fb6393ae

Authored by “wangming”
1 parent b50927a5

feat: 添加健康师工资额外计算表和工资表新字段

- 创建健康师工资额外计算表(lq_salary_extra_calculation),包含基础奖励业绩、合作奖励业绩、新客业绩、新客成交率、升单业绩、升单成交率、升单人头数、其他业绩加、其他业绩减等字段
- 在健康师工资表中添加:基础奖励业绩、合作奖励业绩、升单人头数、新客业绩提成金额、升单业绩提成金额、其他业绩加、其他业绩减、实际基础业绩、实际合作业绩
- 创建健康师工资额外计算服务,包含列表查询和Excel导入功能
- 添加为新店健康师生成模拟数据的方法
- 添加相关SQL脚本用于创建表和添加字段
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs
... ... @@ -111,5 +111,45 @@ namespace NCC.Extend.Entitys.Dto.LqSalary
111 111 /// 更新时间
112 112 /// </summary>
113 113 public DateTime UpdateTime { get; set; }
  114 +
  115 + /// <summary>
  116 + /// 是否新店
  117 + /// </summary>
  118 + public string IsNewStore { get; set; }
  119 +
  120 + /// <summary>
  121 + /// 新店保护阶段
  122 + /// </summary>
  123 + public int NewStoreProtectionStage { get; set; }
  124 +
  125 + /// <summary>
  126 + /// 门店类型
  127 + /// </summary>
  128 + public int? StoreType { get; set; }
  129 +
  130 + /// <summary>
  131 + /// 门店类别
  132 + /// </summary>
  133 + public int? StoreCategory { get; set; }
  134 +
  135 + /// <summary>
  136 + /// 实际基础业绩
  137 + /// </summary>
  138 + public decimal ActualBasePerformance { get; set; }
  139 +
  140 + /// <summary>
  141 + /// 实际合作业绩
  142 + /// </summary>
  143 + public decimal ActualCooperationPerformance { get; set; }
  144 +
  145 + /// <summary>
  146 + /// 新客业绩提成
  147 + /// </summary>
  148 + public decimal NewCustomerCommission { get; set; }
  149 +
  150 + /// <summary>
  151 + /// 升单业绩提成
  152 + /// </summary>
  153 + public decimal UpgradeCommission { get; set; }
114 154 }
115 155 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationImportInput.cs 0 → 100644
  1 +namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation
  2 +{
  3 + /// <summary>
  4 + /// 健康师工资额外计算导入数据
  5 + /// </summary>
  6 + public class SalaryExtraCalculationImportInput
  7 + {
  8 + /// <summary>
  9 + /// ID(可选,用于更新已有记录)
  10 + /// </summary>
  11 + public string Id { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 健康师姓名
  15 + /// </summary>
  16 + public string EmployeeName { get; set; }
  17 +
  18 + /// <summary>
  19 + /// 健康师电话
  20 + /// </summary>
  21 + public string EmployeePhone { get; set; }
  22 +
  23 + /// <summary>
  24 + /// 年份
  25 + /// </summary>
  26 + public int Year { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 月份
  30 + /// </summary>
  31 + public int Month { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 基础奖励业绩
  35 + /// </summary>
  36 + public decimal BaseRewardPerformance { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 合作奖励业绩
  40 + /// </summary>
  41 + public decimal CooperationRewardPerformance { get; set; }
  42 +
  43 + /// <summary>
  44 + /// 新客业绩
  45 + /// </summary>
  46 + public decimal NewCustomerPerformance { get; set; }
  47 +
  48 + /// <summary>
  49 + /// 新客成交率
  50 + /// </summary>
  51 + public decimal NewCustomerConversionRate { get; set; }
  52 +
  53 + /// <summary>
  54 + /// 升单业绩
  55 + /// </summary>
  56 + public decimal UpgradePerformance { get; set; }
  57 +
  58 + /// <summary>
  59 + /// 升单成交率
  60 + /// </summary>
  61 + public decimal UpgradeConversionRate { get; set; }
  62 +
  63 + /// <summary>
  64 + /// 升单人头数
  65 + /// </summary>
  66 + public decimal UpgradeCustomerCount { get; set; }
  67 +
  68 + /// <summary>
  69 + /// 其他业绩加
  70 + /// </summary>
  71 + public decimal OtherPerformanceAdd { get; set; }
  72 +
  73 + /// <summary>
  74 + /// 其他业绩减
  75 + /// </summary>
  76 + public decimal OtherPerformanceSubtract { get; set; }
  77 + }
  78 +}
  79 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationInput.cs 0 → 100644
  1 +using NCC.Common.Filter;
  2 +using System;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation
  5 +{
  6 + /// <summary>
  7 + /// 健康师工资额外计算查询参数
  8 + /// </summary>
  9 + public class SalaryExtraCalculationInput : PageInputBase
  10 + {
  11 + /// <summary>
  12 + /// 年份
  13 + /// </summary>
  14 + public int? Year { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 月份
  18 + /// </summary>
  19 + public int? Month { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 健康师ID(可选,用于筛选特定健康师)
  23 + /// </summary>
  24 + public string EmployeeId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 健康师姓名/电话(可选,用于模糊搜索)
  28 + /// </summary>
  29 + public string Keyword { get; set; }
  30 + }
  31 +}
  32 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationOutput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation
  4 +{
  5 + /// <summary>
  6 + /// 健康师工资额外计算输出
  7 + /// </summary>
  8 + public class SalaryExtraCalculationOutput
  9 + {
  10 + /// <summary>
  11 + /// 主键ID
  12 + /// </summary>
  13 + public string Id { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 健康师ID
  17 + /// </summary>
  18 + public string EmployeeId { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 健康师姓名
  22 + /// </summary>
  23 + public string EmployeeName { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 健康师电话
  27 + /// </summary>
  28 + public string EmployeePhone { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 年份
  32 + /// </summary>
  33 + public int Year { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 月份
  37 + /// </summary>
  38 + public int Month { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 基础奖励业绩
  42 + /// </summary>
  43 + public decimal BaseRewardPerformance { get; set; }
  44 +
  45 + /// <summary>
  46 + /// 合作奖励业绩
  47 + /// </summary>
  48 + public decimal CooperationRewardPerformance { get; set; }
  49 +
  50 + /// <summary>
  51 + /// 新客业绩
  52 + /// </summary>
  53 + public decimal NewCustomerPerformance { get; set; }
  54 +
  55 + /// <summary>
  56 + /// 新客成交率
  57 + /// </summary>
  58 + public decimal NewCustomerConversionRate { get; set; }
  59 +
  60 + /// <summary>
  61 + /// 升单业绩
  62 + /// </summary>
  63 + public decimal UpgradePerformance { get; set; }
  64 +
  65 + /// <summary>
  66 + /// 升单成交率
  67 + /// </summary>
  68 + public decimal UpgradeConversionRate { get; set; }
  69 +
  70 + /// <summary>
  71 + /// 升单人头数
  72 + /// </summary>
  73 + public decimal UpgradeCustomerCount { get; set; }
  74 +
  75 + /// <summary>
  76 + /// 其他业绩加
  77 + /// </summary>
  78 + public decimal OtherPerformanceAdd { get; set; }
  79 +
  80 + /// <summary>
  81 + /// 其他业绩减
  82 + /// </summary>
  83 + public decimal OtherPerformanceSubtract { get; set; }
  84 + }
  85 +}
  86 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_extra_calculation/LqSalaryExtraCalculationEntity.cs 0 → 100644
  1 +using System;
  2 +using NCC.Common.Const;
  3 +using SqlSugar;
  4 +
  5 +namespace NCC.Extend.Entitys.lq_salary_extra_calculation
  6 +{
  7 + /// <summary>
  8 + /// 健康师工资额外计算表
  9 + /// </summary>
  10 + [SugarTable("lq_salary_extra_calculation")]
  11 + [Tenant(ClaimConst.TENANT_ID)]
  12 + public class LqSalaryExtraCalculationEntity
  13 + {
  14 + /// <summary>
  15 + /// 主键ID
  16 + /// </summary>
  17 + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)]
  18 + public string Id { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 健康师ID
  22 + /// </summary>
  23 + [SugarColumn(ColumnName = "F_EmployeeId")]
  24 + public string EmployeeId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 年份
  28 + /// </summary>
  29 + [SugarColumn(ColumnName = "F_Year")]
  30 + public int Year { get; set; }
  31 +
  32 + /// <summary>
  33 + /// 月份
  34 + /// </summary>
  35 + [SugarColumn(ColumnName = "F_Month")]
  36 + public int Month { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 基础奖励业绩
  40 + /// </summary>
  41 + [SugarColumn(ColumnName = "F_BaseRewardPerformance")]
  42 + public decimal BaseRewardPerformance { get; set; }
  43 +
  44 + /// <summary>
  45 + /// 合作奖励业绩
  46 + /// </summary>
  47 + [SugarColumn(ColumnName = "F_CooperationRewardPerformance")]
  48 + public decimal CooperationRewardPerformance { get; set; }
  49 +
  50 + /// <summary>
  51 + /// 新客业绩
  52 + /// </summary>
  53 + [SugarColumn(ColumnName = "F_NewCustomerPerformance")]
  54 + public decimal NewCustomerPerformance { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 新客成交率
  58 + /// </summary>
  59 + [SugarColumn(ColumnName = "F_NewCustomerConversionRate")]
  60 + public decimal NewCustomerConversionRate { get; set; }
  61 +
  62 + /// <summary>
  63 + /// 升单业绩
  64 + /// </summary>
  65 + [SugarColumn(ColumnName = "F_UpgradePerformance")]
  66 + public decimal UpgradePerformance { get; set; }
  67 +
  68 + /// <summary>
  69 + /// 升单成交率
  70 + /// </summary>
  71 + [SugarColumn(ColumnName = "F_UpgradeConversionRate")]
  72 + public decimal UpgradeConversionRate { get; set; }
  73 +
  74 + /// <summary>
  75 + /// 升单人头数
  76 + /// </summary>
  77 + [SugarColumn(ColumnName = "F_UpgradeCustomerCount")]
  78 + public decimal UpgradeCustomerCount { get; set; }
  79 +
  80 + /// <summary>
  81 + /// 其他业绩加
  82 + /// </summary>
  83 + [SugarColumn(ColumnName = "F_OtherPerformanceAdd")]
  84 + public decimal OtherPerformanceAdd { get; set; }
  85 +
  86 + /// <summary>
  87 + /// 其他业绩减
  88 + /// </summary>
  89 + [SugarColumn(ColumnName = "F_OtherPerformanceSubtract")]
  90 + public decimal OtherPerformanceSubtract { get; set; }
  91 + }
  92 +}
  93 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_statistics/LqSalaryStatisticsEntity.cs
... ... @@ -78,6 +78,30 @@ namespace NCC.Extend.Entitys.lq_salary_statistics
78 78 public decimal CooperationPerformance { get; set; }
79 79  
80 80 /// <summary>
  81 + /// 基础奖励业绩
  82 + /// </summary>
  83 + [SugarColumn(ColumnName = "F_BaseRewardPerformance")]
  84 + public decimal BaseRewardPerformance { get; set; }
  85 +
  86 + /// <summary>
  87 + /// 合作奖励业绩
  88 + /// </summary>
  89 + [SugarColumn(ColumnName = "F_CooperationRewardPerformance")]
  90 + public decimal CooperationRewardPerformance { get; set; }
  91 +
  92 + /// <summary>
  93 + /// 实际基础业绩
  94 + /// </summary>
  95 + [SugarColumn(ColumnName = "F_ActualBasePerformance")]
  96 + public decimal ActualBasePerformance { get; set; }
  97 +
  98 + /// <summary>
  99 + /// 实际合作业绩
  100 + /// </summary>
  101 + [SugarColumn(ColumnName = "F_ActualCooperationPerformance")]
  102 + public decimal ActualCooperationPerformance { get; set; }
  103 +
  104 + /// <summary>
81 105 /// 奖励业绩
82 106 /// </summary>
83 107 [SugarColumn(ColumnName = "F_RewardPerformance")]
... ... @@ -120,6 +144,12 @@ namespace NCC.Extend.Entitys.lq_salary_statistics
120 144 public decimal NewCustomerPoint { get; set; }
121 145  
122 146 /// <summary>
  147 + /// 升单人头数
  148 + /// </summary>
  149 + [SugarColumn(ColumnName = "F_UpgradeCustomerCount")]
  150 + public decimal UpgradeCustomerCount { get; set; }
  151 +
  152 + /// <summary>
123 153 /// 升单业绩
124 154 /// </summary>
125 155 [SugarColumn(ColumnName = "F_UpgradePerformance")]
... ... @@ -132,6 +162,30 @@ namespace NCC.Extend.Entitys.lq_salary_statistics
132 162 public decimal UpgradePoint { get; set; }
133 163  
134 164 /// <summary>
  165 + /// 新客业绩提成金额
  166 + /// </summary>
  167 + [SugarColumn(ColumnName = "F_NewCustomerPerformanceCommission")]
  168 + public decimal NewCustomerPerformanceCommission { get; set; }
  169 +
  170 + /// <summary>
  171 + /// 升单业绩提成金额
  172 + /// </summary>
  173 + [SugarColumn(ColumnName = "F_UpgradePerformanceCommission")]
  174 + public decimal UpgradePerformanceCommission { get; set; }
  175 +
  176 + /// <summary>
  177 + /// 其他业绩加
  178 + /// </summary>
  179 + [SugarColumn(ColumnName = "F_OtherPerformanceAdd")]
  180 + public decimal OtherPerformanceAdd { get; set; }
  181 +
  182 + /// <summary>
  183 + /// 其他业绩减
  184 + /// </summary>
  185 + [SugarColumn(ColumnName = "F_OtherPerformanceSubtract")]
  186 + public decimal OtherPerformanceSubtract { get; set; }
  187 +
  188 + /// <summary>
135 189 /// 消耗
136 190 /// </summary>
137 191 [SugarColumn(ColumnName = "F_Consumption")]
... ... @@ -442,5 +496,29 @@ namespace NCC.Extend.Entitys.lq_salary_statistics
442 496 /// </summary>
443 497 [SugarColumn(ColumnName = "F_UpdateUser")]
444 498 public string UpdateUser { get; set; }
  499 +
  500 + /// <summary>
  501 + /// 是否新店
  502 + /// </summary>
  503 + [SugarColumn(ColumnName = "F_IsNewStore")]
  504 + public string IsNewStore { get; set; }
  505 +
  506 + /// <summary>
  507 + /// 新店保护阶段
  508 + /// </summary>
  509 + [SugarColumn(ColumnName = "F_NewStoreProtectionStage")]
  510 + public int NewStoreProtectionStage { get; set; }
  511 +
  512 + /// <summary>
  513 + /// 门店类型
  514 + /// </summary>
  515 + [SugarColumn(ColumnName = "F_StoreType")]
  516 + public int? StoreType { get; set; }
  517 +
  518 + /// <summary>
  519 + /// 门店类别
  520 + /// </summary>
  521 + [SugarColumn(ColumnName = "F_StoreCategory")]
  522 + public int? StoreCategory { get; set; }
445 523 }
446 524 }
447 525 \ No newline at end of file
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs 0 → 100644
  1 +using Microsoft.AspNetCore.Authorization;
  2 +using Microsoft.AspNetCore.Http;
  3 +using Microsoft.AspNetCore.Mvc;
  4 +using NCC.Common.Enum;
  5 +using NCC.Common.Filter;
  6 +using NCC.Common.Helper;
  7 +using NCC.Dependency;
  8 +using NCC.DynamicApiController;
  9 +using NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation;
  10 +using Yitter.IdGenerator;
  11 +using NCC.Extend.Entitys.lq_salary_extra_calculation;
  12 +using NCC.Extend.Entitys.lq_md_xdbhsj;
  13 +using NCC.System.Entitys.Permission;
  14 +using SqlSugar;
  15 +using System;
  16 +using System.Collections.Generic;
  17 +using System.IO;
  18 +using System.Linq;
  19 +using System.Threading.Tasks;
  20 +using NCC.Common.Core.Manager;
  21 +using NCC.FriendlyException;
  22 +
  23 +namespace NCC.Extend
  24 +{
  25 + /// <summary>
  26 + /// 健康师工资额外计算服务
  27 + /// </summary>
  28 + [ApiDescriptionSettings(Tag = "健康师工资额外计算", Name = "LqSalaryExtraCalculation", Order = 301)]
  29 + [Route("api/Extend/[controller]")]
  30 + public class LqSalaryExtraCalculationService : IDynamicApiController, ITransient
  31 + {
  32 + private readonly ISqlSugarClient _db;
  33 + private readonly IUserManager _userManager;
  34 +
  35 + /// <summary>
  36 + /// 初始化一个<see cref="LqSalaryExtraCalculationService"/>类型的新实例
  37 + /// </summary>
  38 + public LqSalaryExtraCalculationService(ISqlSugarClient db, IUserManager userManager)
  39 + {
  40 + _db = db;
  41 + _userManager = userManager;
  42 + }
  43 +
  44 + /// <summary>
  45 + /// 获取健康师工资额外计算列表
  46 + /// </summary>
  47 + /// <param name="input">查询参数</param>
  48 + /// <returns>健康师工资额外计算分页列表</returns>
  49 + [HttpGet]
  50 + public async Task<PageResult<SalaryExtraCalculationOutput>> GetList([FromQuery] SalaryExtraCalculationInput input)
  51 + {
  52 + var query = _db.Queryable<LqSalaryExtraCalculationEntity>()
  53 + .LeftJoin<UserEntity>((ec, u) => ec.EmployeeId == u.Id);
  54 +
  55 + // 年份筛选
  56 + if (input.Year.HasValue)
  57 + {
  58 + query = query.Where((ec, u) => ec.Year == input.Year.Value);
  59 + }
  60 +
  61 + // 月份筛选
  62 + if (input.Month.HasValue)
  63 + {
  64 + query = query.Where((ec, u) => ec.Month == input.Month.Value);
  65 + }
  66 +
  67 + // 健康师ID筛选
  68 + if (!string.IsNullOrEmpty(input.EmployeeId))
  69 + {
  70 + query = query.Where((ec, u) => ec.EmployeeId == input.EmployeeId);
  71 + }
  72 +
  73 + // 关键词搜索(姓名或电话)
  74 + if (!string.IsNullOrEmpty(input.Keyword))
  75 + {
  76 + query = query.Where((ec, u) => u.RealName.Contains(input.Keyword) || u.MobilePhone.Contains(input.Keyword));
  77 + }
  78 +
  79 + var list = await query.Select((ec, u) => new SalaryExtraCalculationOutput
  80 + {
  81 + Id = ec.Id,
  82 + EmployeeId = ec.EmployeeId,
  83 + EmployeeName = u.RealName,
  84 + EmployeePhone = u.MobilePhone,
  85 + Year = ec.Year,
  86 + Month = ec.Month,
  87 + BaseRewardPerformance = ec.BaseRewardPerformance,
  88 + CooperationRewardPerformance = ec.CooperationRewardPerformance,
  89 + NewCustomerPerformance = ec.NewCustomerPerformance,
  90 + NewCustomerConversionRate = ec.NewCustomerConversionRate,
  91 + UpgradePerformance = ec.UpgradePerformance,
  92 + UpgradeConversionRate = ec.UpgradeConversionRate,
  93 + UpgradeCustomerCount = ec.UpgradeCustomerCount,
  94 + OtherPerformanceAdd = ec.OtherPerformanceAdd,
  95 + OtherPerformanceSubtract = ec.OtherPerformanceSubtract
  96 + })
  97 + .ToPagedListAsync(input.currentPage, input.pageSize);
  98 +
  99 + return PageResult<SalaryExtraCalculationOutput>.SqlSugarPageResult(list);
  100 + }
  101 +
  102 + /// <summary>
  103 + /// 从Excel导入健康师工资额外计算数据
  104 + /// </summary>
  105 + /// <param name="file">Excel文件</param>
  106 + /// <returns>导入结果</returns>
  107 + [HttpPost("ImportFromExcel")]
  108 + public async Task<dynamic> ImportFromExcel(IFormFile file)
  109 + {
  110 + try
  111 + {
  112 + if (file == null || file.Length == 0)
  113 + {
  114 + throw NCCException.Oh("请选择要上传的Excel文件");
  115 + }
  116 +
  117 + // 检查文件格式
  118 + var allowedExtensions = new[] { ".xlsx", ".xls" };
  119 + var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();
  120 + if (!allowedExtensions.Contains(fileExtension))
  121 + {
  122 + throw NCCException.Oh("只支持.xlsx和.xls格式的Excel文件");
  123 + }
  124 +
  125 + var importData = new List<SalaryExtraCalculationImportInput>();
  126 +
  127 + // 保存临时文件
  128 + var tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + Path.GetExtension(file.FileName));
  129 + try
  130 + {
  131 + using (var stream = new FileStream(tempFilePath, FileMode.Create))
  132 + {
  133 + await file.CopyToAsync(stream);
  134 + }
  135 +
  136 + // 使用ExcelImportHelper读取Excel文件
  137 + // 参数说明:0表示第一个工作表,0表示第一行是标题行
  138 + var dataTable = ExcelImportHelper.ToDataTable(tempFilePath, 0, 0);
  139 +
  140 + if (dataTable.Rows.Count == 0)
  141 + {
  142 + throw NCCException.Oh("Excel文件中没有数据行");
  143 + }
  144 +
  145 + // Excel字段顺序:id, 健康师姓名, 健康师电话, 年份, 月份, 基础奖励业绩, 合作奖励业绩, 新客业绩, 新客成交率, 升单业绩, 升单成交率, 升单人头数, 其他业绩加, 其他业绩减
  146 + // 从第1行开始读取数据(跳过标题行)
  147 + for (int i = 1; i < dataTable.Rows.Count; i++)
  148 + {
  149 + try
  150 + {
  151 + var row = dataTable.Rows[i];
  152 + var id = row[0]?.ToString()?.Trim();
  153 + var employeeName = row[1]?.ToString()?.Trim();
  154 + var employeePhone = row[2]?.ToString()?.Trim();
  155 + var yearText = row[3]?.ToString()?.Trim();
  156 + var monthText = row[4]?.ToString()?.Trim();
  157 + var baseRewardPerformanceText = row[5]?.ToString()?.Trim();
  158 + var cooperationRewardPerformanceText = row[6]?.ToString()?.Trim();
  159 + var newCustomerPerformanceText = row[7]?.ToString()?.Trim();
  160 + var newCustomerConversionRateText = row[8]?.ToString()?.Trim();
  161 + var upgradePerformanceText = row[9]?.ToString()?.Trim();
  162 + var upgradeConversionRateText = row[10]?.ToString()?.Trim();
  163 + var upgradeCustomerCountText = row[11]?.ToString()?.Trim();
  164 + var otherPerformanceAddText = row[12]?.ToString()?.Trim();
  165 + var otherPerformanceSubtractText = row[13]?.ToString()?.Trim();
  166 +
  167 + // 跳过空行
  168 + if (string.IsNullOrEmpty(employeeName) && string.IsNullOrEmpty(employeePhone))
  169 + {
  170 + continue;
  171 + }
  172 +
  173 + // 验证必填字段
  174 + if (string.IsNullOrEmpty(employeeName))
  175 + {
  176 + throw new Exception($"第{i + 1}行:健康师姓名不能为空");
  177 + }
  178 + if (string.IsNullOrEmpty(employeePhone))
  179 + {
  180 + throw new Exception($"第{i + 1}行:健康师电话不能为空");
  181 + }
  182 +
  183 + // 解析年份
  184 + if (!int.TryParse(yearText, out int year))
  185 + {
  186 + throw new Exception($"第{i + 1}行:年份格式错误");
  187 + }
  188 +
  189 + // 解析月份
  190 + if (!int.TryParse(monthText, out int month))
  191 + {
  192 + throw new Exception($"第{i + 1}行:月份格式错误");
  193 + }
  194 +
  195 + // 验证月份范围
  196 + if (month < 1 || month > 12)
  197 + {
  198 + throw new Exception($"第{i + 1}行:月份必须在1-12之间");
  199 + }
  200 +
  201 + // 解析数值字段(允许为空,默认为0)
  202 + decimal.TryParse(baseRewardPerformanceText, out decimal baseRewardPerformance);
  203 + decimal.TryParse(cooperationRewardPerformanceText, out decimal cooperationRewardPerformance);
  204 + decimal.TryParse(newCustomerPerformanceText, out decimal newCustomerPerformance);
  205 + decimal.TryParse(newCustomerConversionRateText, out decimal newCustomerConversionRate);
  206 + decimal.TryParse(upgradePerformanceText, out decimal upgradePerformance);
  207 + decimal.TryParse(upgradeConversionRateText, out decimal upgradeConversionRate);
  208 + decimal.TryParse(upgradeCustomerCountText, out decimal upgradeCustomerCount);
  209 + decimal.TryParse(otherPerformanceAddText, out decimal otherPerformanceAdd);
  210 + decimal.TryParse(otherPerformanceSubtractText, out decimal otherPerformanceSubtract);
  211 +
  212 + var item = new SalaryExtraCalculationImportInput
  213 + {
  214 + Id = id,
  215 + EmployeeName = employeeName,
  216 + EmployeePhone = employeePhone,
  217 + Year = year,
  218 + Month = month,
  219 + BaseRewardPerformance = baseRewardPerformance,
  220 + CooperationRewardPerformance = cooperationRewardPerformance,
  221 + NewCustomerPerformance = newCustomerPerformance,
  222 + NewCustomerConversionRate = newCustomerConversionRate,
  223 + UpgradePerformance = upgradePerformance,
  224 + UpgradeConversionRate = upgradeConversionRate,
  225 + UpgradeCustomerCount = upgradeCustomerCount,
  226 + OtherPerformanceAdd = otherPerformanceAdd,
  227 + OtherPerformanceSubtract = otherPerformanceSubtract
  228 + };
  229 +
  230 + importData.Add(item);
  231 + }
  232 + catch (Exception ex)
  233 + {
  234 + throw new Exception($"第{i + 1}行数据解析失败: {ex.Message}");
  235 + }
  236 + }
  237 + }
  238 + finally
  239 + {
  240 + // 清理临时文件
  241 + if (File.Exists(tempFilePath))
  242 + {
  243 + File.Delete(tempFilePath);
  244 + }
  245 + }
  246 +
  247 + if (!importData.Any())
  248 + {
  249 + throw NCCException.Oh("Excel文件中没有有效的数据行");
  250 + }
  251 +
  252 + // 处理导入数据
  253 + return await ProcessImportData(importData);
  254 + }
  255 + catch (Exception ex)
  256 + {
  257 + throw NCCException.Oh($"上传Excel文件导入健康师工资额外计算数据失败: {ex.Message}");
  258 + }
  259 + }
  260 +
  261 + #region 处理导入数据
  262 + /// <summary>
  263 + /// 处理导入数据
  264 + /// </summary>
  265 + /// <param name="importData">导入数据列表</param>
  266 + /// <returns>导入结果</returns>
  267 + private async Task<dynamic> ProcessImportData(List<SalaryExtraCalculationImportInput> importData)
  268 + {
  269 + var successCount = 0;
  270 + var failCount = 0;
  271 + var errorMessages = new List<string>();
  272 + var entitiesToInsert = new List<LqSalaryExtraCalculationEntity>();
  273 + var entitiesToUpdate = new List<LqSalaryExtraCalculationEntity>();
  274 +
  275 + foreach (var item in importData)
  276 + {
  277 + try
  278 + {
  279 + // 1. 根据健康师姓名和电话查找用户ID
  280 + var user = await _db.Queryable<UserEntity>()
  281 + .Where(u => u.RealName == item.EmployeeName && u.MobilePhone == item.EmployeePhone)
  282 + .FirstAsync();
  283 +
  284 + if (user == null)
  285 + {
  286 + errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 不存在");
  287 + failCount++;
  288 + continue;
  289 + }
  290 +
  291 + // 2. 检查是否已存在相同记录(根据健康师ID、年份、月份)
  292 + LqSalaryExtraCalculationEntity existingRecord = null;
  293 +
  294 + // 如果提供了ID,先尝试根据ID查找
  295 + if (!string.IsNullOrEmpty(item.Id))
  296 + {
  297 + existingRecord = await _db.Queryable<LqSalaryExtraCalculationEntity>()
  298 + .Where(x => x.Id == item.Id)
  299 + .FirstAsync();
  300 + }
  301 +
  302 + // 如果没有找到,则根据健康师ID、年份、月份查找
  303 + if (existingRecord == null)
  304 + {
  305 + existingRecord = await _db.Queryable<LqSalaryExtraCalculationEntity>()
  306 + .Where(x => x.EmployeeId == user.Id && x.Year == item.Year && x.Month == item.Month)
  307 + .FirstAsync();
  308 + }
  309 +
  310 + if (existingRecord != null)
  311 + {
  312 + // 更新现有记录
  313 + existingRecord.BaseRewardPerformance = item.BaseRewardPerformance;
  314 + existingRecord.CooperationRewardPerformance = item.CooperationRewardPerformance;
  315 + existingRecord.NewCustomerPerformance = item.NewCustomerPerformance;
  316 + existingRecord.NewCustomerConversionRate = item.NewCustomerConversionRate;
  317 + existingRecord.UpgradePerformance = item.UpgradePerformance;
  318 + existingRecord.UpgradeConversionRate = item.UpgradeConversionRate;
  319 + existingRecord.UpgradeCustomerCount = item.UpgradeCustomerCount;
  320 + existingRecord.OtherPerformanceAdd = item.OtherPerformanceAdd;
  321 + existingRecord.OtherPerformanceSubtract = item.OtherPerformanceSubtract;
  322 + entitiesToUpdate.Add(existingRecord);
  323 + }
  324 + else
  325 + {
  326 + // 创建新记录
  327 + var entity = new LqSalaryExtraCalculationEntity
  328 + {
  329 + Id = YitIdHelper.NextId().ToString(),
  330 + EmployeeId = user.Id,
  331 + Year = item.Year,
  332 + Month = item.Month,
  333 + BaseRewardPerformance = item.BaseRewardPerformance,
  334 + CooperationRewardPerformance = item.CooperationRewardPerformance,
  335 + NewCustomerPerformance = item.NewCustomerPerformance,
  336 + NewCustomerConversionRate = item.NewCustomerConversionRate,
  337 + UpgradePerformance = item.UpgradePerformance,
  338 + UpgradeConversionRate = item.UpgradeConversionRate,
  339 + UpgradeCustomerCount = item.UpgradeCustomerCount,
  340 + OtherPerformanceAdd = item.OtherPerformanceAdd,
  341 + OtherPerformanceSubtract = item.OtherPerformanceSubtract
  342 + };
  343 + entitiesToInsert.Add(entity);
  344 + }
  345 +
  346 + successCount++;
  347 + }
  348 + catch (Exception ex)
  349 + {
  350 + errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 处理失败: {ex.Message}");
  351 + failCount++;
  352 + }
  353 + }
  354 +
  355 + // 批量插入新记录
  356 + if (entitiesToInsert.Any())
  357 + {
  358 + await _db.Insertable(entitiesToInsert).ExecuteCommandAsync();
  359 + }
  360 +
  361 + // 批量更新现有记录
  362 + if (entitiesToUpdate.Any())
  363 + {
  364 + await _db.Updateable(entitiesToUpdate).ExecuteCommandAsync();
  365 + }
  366 +
  367 + var result = new
  368 + {
  369 + successCount,
  370 + failCount,
  371 + totalCount = importData.Count,
  372 + errorMessages = errorMessages.Take(50).ToList() // 最多返回50条错误信息
  373 + };
  374 +
  375 + if (failCount > 0)
  376 + {
  377 + throw NCCException.Oh($"导入完成,成功:{successCount}条,失败:{failCount}条。{string.Join("; ", errorMessages.Take(10))}");
  378 + }
  379 +
  380 + return result;
  381 + }
  382 + #endregion
  383 +
  384 + /// <summary>
  385 + /// 为新店健康师生成模拟数据
  386 + /// </summary>
  387 + /// <param name="year">年份</param>
  388 + /// <param name="month">月份</param>
  389 + /// <returns>生成结果</returns>
  390 + [HttpPost("GenerateMockData")]
  391 + public async Task<dynamic> GenerateMockData(int year, int month)
  392 + {
  393 + try
  394 + {
  395 + // 验证月份范围
  396 + if (month < 1 || month > 12)
  397 + {
  398 + throw NCCException.Oh("月份必须在1-12之间");
  399 + }
  400 +
  401 + var startDate = new DateTime(year, month, 1);
  402 + var endDate = startDate.AddMonths(1).AddDays(-1);
  403 +
  404 + // 1. 查询新店保护信息
  405 + var newStoreProtectionList = await _db.Queryable<LqMdXdbhsjEntity>()
  406 + .Where(x => x.Sfqy == 1)
  407 + .ToListAsync();
  408 +
  409 + // 筛选出在统计月份处于保护期内的门店
  410 + var newStoreIds = newStoreProtectionList
  411 + .Where(x => x.Bhkssj <= endDate && x.Bhjssj >= startDate)
  412 + .Select(x => x.Mdid)
  413 + .Distinct()
  414 + .ToList();
  415 +
  416 + if (!newStoreIds.Any())
  417 + {
  418 + throw NCCException.Oh("当前月份没有新店");
  419 + }
  420 +
  421 + // 2. 查询新店下的健康师
  422 + // 健康师的岗位字段是F_GW,值为"健康师"
  423 + var healthCoaches = await _db.Queryable<UserEntity>()
  424 + .Where(u => newStoreIds.Contains(u.Mdid) && u.Gw == "健康师" && u.EnabledMark == 1 && (u.DeleteMark == null || u.DeleteMark == 0))
  425 + .Select(u => new { u.Id, u.RealName, u.Mdid })
  426 + .ToListAsync();
  427 +
  428 + if (!healthCoaches.Any())
  429 + {
  430 + throw NCCException.Oh("新店下没有健康师");
  431 + }
  432 +
  433 + // 3. 检查是否已存在数据
  434 + var existingEmployeeIds = await _db.Queryable<LqSalaryExtraCalculationEntity>()
  435 + .Where(x => x.Year == year && x.Month == month)
  436 + .Select(x => x.EmployeeId)
  437 + .ToListAsync();
  438 +
  439 + var employeesToProcess = healthCoaches
  440 + .Where(hc => !existingEmployeeIds.Contains(hc.Id))
  441 + .ToList();
  442 +
  443 + if (!employeesToProcess.Any())
  444 + {
  445 + throw NCCException.Oh("所有新店健康师已存在该月份的数据");
  446 + }
  447 +
  448 + // 4. 生成模拟数据
  449 + var random = new Random();
  450 + var entitiesToInsert = new List<LqSalaryExtraCalculationEntity>();
  451 + var successCount = 0;
  452 +
  453 + foreach (var employee in employeesToProcess)
  454 + {
  455 + try
  456 + {
  457 + // 生成金额
  458 + // 基础业绩奖励和合作业绩奖励:0-20000之间
  459 + var baseRewardPerformance = (decimal)(random.NextDouble() * 20000);
  460 + var cooperationRewardPerformance = (decimal)(random.NextDouble() * 20000);
  461 + // 其他金额字段:10000-30000之间
  462 + var newCustomerPerformance = (decimal)(random.NextDouble() * 20000 + 10000);
  463 + var upgradePerformance = (decimal)(random.NextDouble() * 20000 + 10000);
  464 + var otherPerformanceAdd = (decimal)(random.NextDouble() * 20000 + 10000);
  465 + var otherPerformanceSubtract = (decimal)(random.NextDouble() * 20000 + 10000);
  466 +
  467 + // 生成转化率(0-60%之间,转换为0-0.6)
  468 + var newCustomerConversionRate = (decimal)(random.NextDouble() * 0.6);
  469 + var upgradeConversionRate = (decimal)(random.NextDouble() * 0.6);
  470 +
  471 + // 生成升单人头数(0-10之间)
  472 + var upgradeCustomerCount = (decimal)random.Next(0, 11);
  473 +
  474 + var entity = new LqSalaryExtraCalculationEntity
  475 + {
  476 + Id = YitIdHelper.NextId().ToString(),
  477 + EmployeeId = employee.Id,
  478 + Year = year,
  479 + Month = month,
  480 + BaseRewardPerformance = Math.Round(baseRewardPerformance, 2),
  481 + CooperationRewardPerformance = Math.Round(cooperationRewardPerformance, 2),
  482 + NewCustomerPerformance = Math.Round(newCustomerPerformance, 2),
  483 + NewCustomerConversionRate = Math.Round(newCustomerConversionRate, 4),
  484 + UpgradePerformance = Math.Round(upgradePerformance, 2),
  485 + UpgradeConversionRate = Math.Round(upgradeConversionRate, 4),
  486 + UpgradeCustomerCount = upgradeCustomerCount,
  487 + OtherPerformanceAdd = Math.Round(otherPerformanceAdd, 2),
  488 + OtherPerformanceSubtract = Math.Round(otherPerformanceSubtract, 2)
  489 + };
  490 +
  491 + entitiesToInsert.Add(entity);
  492 + successCount++;
  493 + }
  494 + catch
  495 + {
  496 + // 记录错误但继续处理其他健康师
  497 + continue;
  498 + }
  499 + }
  500 +
  501 + // 5. 批量插入数据
  502 + if (entitiesToInsert.Any())
  503 + {
  504 + await _db.Insertable(entitiesToInsert).ExecuteCommandAsync();
  505 + }
  506 +
  507 + return new
  508 + {
  509 + successCount,
  510 + totalCount = employeesToProcess.Count,
  511 + message = $"成功为新店健康师生成 {successCount} 条模拟数据"
  512 + };
  513 + }
  514 + catch (Exception ex)
  515 + {
  516 + throw NCCException.Oh($"生成模拟数据失败: {ex.Message}");
  517 + }
  518 + }
  519 + }
  520 +}
  521 +
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
... ... @@ -16,6 +16,11 @@ using NCC.Extend.Entitys.lq_person_times_record;
16 16 using NCC.Extend.Entitys.lq_salary_statistics;
17 17 using NCC.Extend.Entitys.lq_xh_jksyj;
18 18 using NCC.Extend.Entitys.lq_ycsd_jsj;
  19 +using NCC.Extend.Entitys.lq_mdxx;
  20 +using NCC.Extend.Entitys.lq_hytk_hytk;
  21 +using NCC.Extend.Entitys.lq_md_xdbhsj;
  22 +using NCC.Extend.Entitys.lq_salary_extra_calculation;
  23 +using NCC.System.Entitys.Permission;
19 24 using SqlSugar;
20 25 using System;
21 26 using System.Collections.Generic;
... ... @@ -97,7 +102,15 @@ namespace NCC.Extend
97 102 TotalDeduction = x.TotalDeduction,
98 103 ActualSalary = x.ActualSalary,
99 104 IsLocked = x.IsLocked,
100   - UpdateTime = x.UpdateTime
  105 + UpdateTime = x.UpdateTime,
  106 + IsNewStore = x.IsNewStore,
  107 + NewStoreProtectionStage = x.NewStoreProtectionStage,
  108 + StoreType = x.StoreType,
  109 + StoreCategory = x.StoreCategory,
  110 + ActualBasePerformance = x.ActualBasePerformance,
  111 + ActualCooperationPerformance = x.ActualCooperationPerformance,
  112 + NewCustomerCommission = x.NewCustomerPerformanceCommission,
  113 + UpgradeCommission = x.UpgradePerformanceCommission
101 114 })
102 115 .ToPagedListAsync(input.currentPage, input.pageSize);
103 116  
... ... @@ -127,17 +140,18 @@ namespace NCC.Extend
127 140 // 1.1.1 获取关联的开单记录(用于获取 sfskdd)
128 141 var billingIds = performanceList.Select(x => x.Glkdbh).Distinct().ToList();
129 142 var billingDict = await _db.Queryable<LqKdKdjlbEntity>()
130   - .Where(x => billingIds.Contains(x.Id))
  143 + .Where(x => billingIds.Contains(x.Id) && x.Id != null)
131 144 .ToDictionaryAsync(x => x.Id, x => x.Sfskdd);
132 145  
133 146 // 1.1.2 组合数据
134 147 var performanceData = performanceList.Select(p => new
135 148 {
136   - p.Jks,
  149 + Jks = p.Jkszh, // 使用 Jkszh (账号/ID) 而不是 Jks (姓名)
137 150 p.Jksxm,
138 151 p.StoreId,
139 152 p.Jksyj,
140 153 p.ItemCategory,
  154 + p.PerformanceType, // 新增业绩类型字段
141 155 Sfskdd = billingDict.ContainsKey(p.Glkdbh) ? billingDict[p.Glkdbh] : null
142 156 }).ToList();
143 157  
... ... @@ -161,7 +175,7 @@ namespace NCC.Extend
161 175 var teamList = await _db.Queryable<LqYcsdJsjEntity>()
162 176 .Where(x => teamIds.Contains(x.Id))
163 177 .ToListAsync();
164   - var teamDict = teamList.ToDictionary(x => x.Id, x => x.Jsj);
  178 + var teamDict = teamList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x.Jsj);
165 179  
166 180 // 1.4.2 组合数据
167 181 var teamMembers = teamUserList.Select(user => new
... ... @@ -185,6 +199,55 @@ namespace NCC.Extend
185 199 .Where(x => x.Month == monthStr)
186 200 .ToListAsync();
187 201  
  202 + // 1.6.1 门店总业绩计算 (开单实付 - 退款金额)
  203 + // 开单实付
  204 + var storeBillingList = await _db.Queryable<LqKdKdjlbEntity>()
  205 + .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate.AddDays(1) && x.IsEffective == 1)
  206 + .Select(x => new { x.Djmd, x.Sfyj })
  207 + .ToListAsync();
  208 + var storeBillingDict = storeBillingList
  209 + .Where(x => !string.IsNullOrEmpty(x.Djmd))
  210 + .GroupBy(x => x.Djmd)
  211 + .ToDictionary(g => g.Key, g => g.Sum(x => x.Sfyj));
  212 +
  213 + // 退款金额
  214 + var storeRefundList = await _db.Queryable<LqHytkHytkEntity>()
  215 + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1)
  216 + .Select(x => new { x.Mdbh, x.Tkje })
  217 + .ToListAsync();
  218 + var storeRefundDict = storeRefundList
  219 + .Where(x => !string.IsNullOrEmpty(x.Mdbh))
  220 + .GroupBy(x => x.Mdbh)
  221 + .ToDictionary(g => g.Key, g => g.Sum(x => x.Tkje ?? 0));
  222 +
  223 + // 1.7 门店信息 (lq_mdxx)
  224 + var storeList = await _db.Queryable<LqMdxxEntity>().ToListAsync();
  225 + var storeDict = storeList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x);
  226 +
  227 + // 1.7.1 门店新店保护信息 (lq_md_xdbhsj)
  228 + var newStoreProtectionList = await _db.Queryable<LqMdXdbhsjEntity>()
  229 + .Where(x => x.Sfqy == 1)
  230 + .ToListAsync();
  231 +
  232 + // 构造新店保护查找字典: StoreId -> List<ProtectionInfo>
  233 + // 因为一个门店可能有多个阶段配置,虽然通常是一个时间段,但为了严谨取符合当前月份的配置
  234 + // 逻辑: 统计月份 (startDate ~ endDate) 是否在 bhkssj ~ bhjssj 范围内
  235 + // 只要统计月份与保护期有交集,就算保护期。或者严格一点,统计月份的第一天在保护期内。
  236 + // 这里取: 统计月份的第一天 (startDate) 在保护期内
  237 + var newStoreProtectionDict = newStoreProtectionList
  238 + .Where(x => x.Bhkssj <= startDate && x.Bhjssj >= startDate)
  239 + .GroupBy(x => x.Mdid)
  240 + .ToDictionary(g => g.Key, g => g.First()); // 取第一个匹配的配置
  241 +
  242 + // 1.8 健康师工资额外计算数据 (lq_salary_extra_calculation)
  243 + var extraCalculationList = await _db.Queryable<LqSalaryExtraCalculationEntity>()
  244 + .Where(x => x.Year == year && x.Month == month)
  245 + .ToListAsync();
  246 +
  247 + var extraCalculationDict = extraCalculationList
  248 + .Where(x => !string.IsNullOrEmpty(x.EmployeeId))
  249 + .ToDictionary(x => x.EmployeeId, x => x);
  250 +
188 251 // 2. 聚合每个健康师的数据对象
189 252 var employeeStats = new Dictionary<string, LqSalaryStatisticsEntity>();
190 253  
... ... @@ -197,6 +260,19 @@ namespace NCC.Extend
197 260 .Distinct()
198 261 .ToList();
199 262  
  263 + // 1.8 批量获取员工信息 (BASE_USER + BASE_POSITION)
  264 + // 使用 allEmployeeIds 作为驱动,查询 BASE_USER
  265 + var userList = await _db.Queryable<UserEntity>()
  266 + .Where(x => allEmployeeIds.Contains(x.Id))
  267 + .Select(x => new { x.Id, x.RealName, x.PositionId, x.Mdid })
  268 + .ToListAsync();
  269 +
  270 + var userDict = userList.ToDictionary(x => x.Id, x => x);
  271 +
  272 + var positionIds = userList.Select(x => x.PositionId).Distinct().ToList();
  273 + var positionList = await _db.Queryable<PositionEntity>().Where(x => positionIds.Contains(x.Id)).ToListAsync();
  274 + var positionLookup = positionList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x.FullName);
  275 +
200 276 foreach (var empId in allEmployeeIds)
201 277 {
202 278 var salary = new LqSalaryStatisticsEntity
... ... @@ -209,38 +285,121 @@ namespace NCC.Extend
209 285 IsLocked = 0
210 286 };
211 287  
212   - // 填充基础信息 (姓名、门店)
213   - var perfRecord = performanceData.FirstOrDefault(x => x.Jks == empId);
214   - var consRecord = consumptionList.FirstOrDefault(x => x.Jks == empId);
  288 + // 填充基础信息 (优先从 BASE_USER 获取)
  289 + string userMdid = null;
  290 + if (userDict.ContainsKey(empId))
  291 + {
  292 + var user = userDict[empId];
  293 + salary.EmployeeName = user.RealName;
  294 + userMdid = user.Mdid;
  295 +
  296 + // 岗位
  297 + if (user.PositionId != null && positionLookup.ContainsKey(user.PositionId))
  298 + {
  299 + salary.Position = positionLookup[user.PositionId];
  300 + }
  301 + }
215 302  
216   - if (perfRecord != null)
  303 + // 如果 BASE_USER 没名字,尝试从业务表获取
  304 + if (string.IsNullOrEmpty(salary.EmployeeName))
217 305 {
218   - salary.EmployeeName = perfRecord.Jksxm;
219   - salary.StoreId = perfRecord.StoreId;
  306 + var perfRecord = performanceData.FirstOrDefault(x => x.Jks == empId);
  307 + var consRecord = consumptionList.FirstOrDefault(x => x.Jks == empId);
  308 + if (perfRecord != null) salary.EmployeeName = perfRecord.Jksxm;
  309 + else if (consRecord != null) salary.EmployeeName = consRecord.Jksxm;
220 310 }
221   - else if (consRecord != null)
  311 +
  312 + // 填充门店ID (从业务数据获取,因为 User 表的 OrganizeId 未必是门店)
  313 + var perfStore = performanceData.FirstOrDefault(x => x.Jks == empId && !string.IsNullOrEmpty(x.StoreId));
  314 + var consStore = consumptionList.FirstOrDefault(x => x.Jks == empId && !string.IsNullOrEmpty(x.StoreId));
  315 +
  316 + if (perfStore != null) salary.StoreId = perfStore.StoreId;
  317 + else if (consStore != null) salary.StoreId = consStore.StoreId;
  318 +
  319 + // 如果业务数据没门店,尝试使用 User.Mdid
  320 + if (string.IsNullOrEmpty(salary.StoreId) && !string.IsNullOrEmpty(userMdid))
222 321 {
223   - salary.EmployeeName = consRecord.Jksxm;
224   - salary.StoreId = consRecord.StoreId;
  322 + if (storeDict.ContainsKey(userMdid))
  323 + {
  324 + salary.StoreId = userMdid;
  325 + }
225 326 }
226 327  
227   - // 填充门店名称
228   - if (!string.IsNullOrEmpty(salary.StoreId))
  328 + // 填充门店名称及分类信息
  329 + if (!string.IsNullOrEmpty(salary.StoreId) && storeDict.ContainsKey(salary.StoreId))
  330 + {
  331 + var store = storeDict[salary.StoreId];
  332 + salary.StoreName = store.Dm;
  333 + salary.StoreType = store.StoreType;
  334 + salary.StoreCategory = store.StoreCategory;
  335 + }
  336 +
  337 + // 填充新店保护信息
  338 + if (!string.IsNullOrEmpty(salary.StoreId) && newStoreProtectionDict.ContainsKey(salary.StoreId))
  339 + {
  340 + var protection = newStoreProtectionDict[salary.StoreId];
  341 + salary.IsNewStore = "是";
  342 + salary.NewStoreProtectionStage = protection.Stage;
  343 + }
  344 + else
229 345 {
230   - // 这里简单处理,实际可能需要缓存门店列表
231   - // salary.StoreName = ...
  346 + salary.IsNewStore = "否";
  347 + salary.NewStoreProtectionStage = 0;
232 348 }
233 349  
234 350 // 2.1 计算个人业绩
235 351 var myPerf = performanceData.Where(x => x.Jks == empId).ToList();
236   - salary.BasePerformance = myPerf.Where(x => x.ItemCategory == "基础业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
237   - salary.CooperationPerformance = myPerf.Where(x => x.ItemCategory == "合作业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
  352 + salary.BasePerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "基础业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
  353 + salary.CooperationPerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "合作业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
238 354 salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0"));
239 355  
240 356 // 新客与升单业绩
241 357 salary.NewCustomerPerformance = myPerf.Where(x => x.Sfskdd == "是").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
242 358 salary.UpgradePerformance = myPerf.Where(x => x.Sfskdd == "否").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
243 359  
  360 + // 2.1.1 填充额外计算数据
  361 + if (extraCalculationDict.ContainsKey(empId))
  362 + {
  363 + var extraData = extraCalculationDict[empId];
  364 + salary.BaseRewardPerformance = extraData.BaseRewardPerformance;
  365 + salary.CooperationRewardPerformance = extraData.CooperationRewardPerformance;
  366 + salary.OtherPerformanceAdd = extraData.OtherPerformanceAdd;
  367 + salary.OtherPerformanceSubtract = extraData.OtherPerformanceSubtract;
  368 + salary.UpgradeCustomerCount = extraData.UpgradeCustomerCount;
  369 + salary.NewCustomerConversionRate = extraData.NewCustomerConversionRate;
  370 + }
  371 +
  372 + // 2.1.2 计算实际基础业绩和实际合作业绩
  373 + // 定义新店相关变量,供后续多处使用
  374 + bool isNewStore = salary.IsNewStore == "是";
  375 + int newStoreStage = salary.NewStoreProtectionStage;
  376 +
  377 + // 实际基础业绩 = 基础业绩 - 基础奖励业绩 + 其他业绩加 - 其他业绩减
  378 + decimal actualBasePerformance = salary.BasePerformance
  379 + - salary.BaseRewardPerformance
  380 + + salary.OtherPerformanceAdd
  381 + - salary.OtherPerformanceSubtract;
  382 +
  383 + // 新店额外调整:根据阶段扣除新客业绩或升单业绩
  384 + if (isNewStore)
  385 + {
  386 + if (newStoreStage == 1)
  387 + {
  388 + // 第一阶段:扣除新客业绩
  389 + actualBasePerformance -= salary.NewCustomerPerformance;
  390 + }
  391 + else if (newStoreStage == 2)
  392 + {
  393 + // 第二阶段:扣除升单业绩
  394 + actualBasePerformance -= salary.UpgradePerformance;
  395 + }
  396 + }
  397 +
  398 + salary.ActualBasePerformance = actualBasePerformance;
  399 +
  400 + // 实际合作业绩 = 合作业绩 - 合作奖励业绩
  401 + salary.ActualCooperationPerformance = salary.CooperationPerformance - salary.CooperationRewardPerformance;
  402 +
244 403 // 2.2 计算消耗和项目数
245 404 var myCons = consumptionList.Where(x => x.Jks == empId).ToList();
246 405 salary.Consumption = myCons.Sum(x => x.Jksyj ?? 0);
... ... @@ -258,10 +417,33 @@ namespace NCC.Extend
258 417  
259 418 // 2.5 战队信息 (初始)
260 419 var myTeam = teamMembers.FirstOrDefault(x => x.UserId == empId);
  420 + // 初始判断岗位:如果是战队队长(IsLeader=1)则是顾问,否则是健康师
  421 + // 注意:这里先根据战队设置判断,后续考勤不足21天会降级
  422 + if (myTeam != null && myTeam.IsLeader == 1)
  423 + {
  424 + salary.Position = "顾问";
  425 + }
  426 + else if (string.IsNullOrEmpty(salary.Position)) // 如果BASE_USER没岗位,且不是队长,默认为健康师
  427 + {
  428 + salary.Position = "健康师";
  429 + }
  430 +
261 431 if (myTeam != null)
262 432 {
263 433 salary.GoldTriangleId = myTeam.TeamId;
264   - salary.GoldTriangleTeam = myTeam.TeamName ?? "";
  434 + salary.GoldTriangleTeam = myTeam.TeamName ?? "个人";
  435 + }
  436 + else
  437 + {
  438 + salary.GoldTriangleTeam = "个人";
  439 + }
  440 +
  441 + // 2.6 门店总业绩
  442 + if (!string.IsNullOrEmpty(salary.StoreId))
  443 + {
  444 + decimal billing = storeBillingDict.ContainsKey(salary.StoreId) ? storeBillingDict[salary.StoreId] : 0;
  445 + decimal refund = storeRefundDict.ContainsKey(salary.StoreId) ? storeRefundDict[salary.StoreId] : 0;
  446 + salary.StoreTotalPerformance = billing - refund;
265 447 }
266 448  
267 449 employeeStats[empId] = salary;
... ... @@ -293,11 +475,12 @@ namespace NCC.Extend
293 475 }
294 476 }
295 477  
296   - // 对于无效成员,移除战队标识,视为单人
  478 + // 对于无效成员,移除战队标识,视为单人,并重置岗位为健康师
297 479 foreach (var member in invalidMembers)
298 480 {
299 481 member.GoldTriangleId = null;
300   - member.GoldTriangleTeam = null;
  482 + member.GoldTriangleTeam = "个人";
  483 + member.Position = "健康师"; // 降级为健康师
301 484 }
302 485  
303 486 // 计算有效战队的总业绩
... ... @@ -313,8 +496,12 @@ namespace NCC.Extend
313 496 // 4. 计算薪资 (底薪 & 提成)
314 497 foreach (var salary in employeeStats.Values)
315 498 {
  499 + // 定义新店相关变量,供底薪和提成计算使用
  500 + bool isNewStore = salary.IsNewStore == "是";
  501 + int newStoreStage = salary.NewStoreProtectionStage;
  502 +
316 503 // 4.1 底薪计算
317   - salary.HealthCoachBaseSalary = CalculateBaseSalary(salary.Consumption, salary.ProjectCount);
  504 + salary.HealthCoachBaseSalary = CalculateBaseSalary(salary.Consumption, salary.ProjectCount, isNewStore);
318 505  
319 506 // 4.2 提成计算
320 507 // 单人业绩 <= 6000 无提成
... ... @@ -335,29 +522,60 @@ namespace NCC.Extend
335 522 // 是战队成员
336 523 // 获取战队人数 (注意:这里应该是有效战队人数)
337 524 var teamMemberCount = employeeStats.Values.Count(x => x.GoldTriangleId == salary.GoldTriangleId);
  525 + // 注意:提成点按原始基础业绩计算,不是实际基础业绩
338 526 commissionPoint = GetTeamCommissionPoint(teamMemberCount, salary.TeamPerformance);
339 527 }
340 528 else
341 529 {
342 530 // 单人 (或被剔除出战队)
  531 + // 注意:提成点按原始总业绩计算
343 532 commissionPoint = GetTeamCommissionPoint(1, salary.TotalPerformance);
344 533 }
345 534  
346 535 salary.CommissionPoint = commissionPoint;
347 536  
348   - // 计算基础/合作提成
349   - salary.BasePerformanceCommission = salary.BasePerformance * 0.95m * commissionPoint;
350   - salary.CooperationPerformanceCommission = salary.CooperationPerformance * 0.95m * 0.65m * commissionPoint;
  537 + // 计算基础/合作提成(使用实际业绩)
  538 + salary.BasePerformanceCommission = salary.ActualBasePerformance * 0.95m * commissionPoint;
  539 + salary.CooperationPerformanceCommission = salary.ActualCooperationPerformance * 0.95m * 0.65m * commissionPoint;
  540 +
  541 + // 计算新客转化率提成和升单人头提成(根据新店阶段)
  542 + // isNewStore 和 newStoreStage 已在上面定义
  543 +
  544 + if (isNewStore)
  545 + {
  546 + if (newStoreStage == 1)
  547 + {
  548 + // 第一阶段:计算新客转化率提成
  549 + salary.NewCustomerPerformanceCommission = CalculateNewCustomerConversionCommission(
  550 + salary.NewCustomerPerformance,
  551 + salary.NewCustomerConversionRate);
  552 + }
  553 + else if (newStoreStage == 2)
  554 + {
  555 + // 第二阶段:计算升单人头提成
  556 + salary.UpgradePerformanceCommission = CalculateUpgradeCustomerCommission(
  557 + salary.UpgradePerformance,
  558 + salary.UpgradeCustomerCount);
  559 + }
  560 + // 第三阶段:不计算新客/升单提成
  561 + }
351 562  
352 563 // 计算顾问提成
353 564 // 检查是否是顾问
354   - var isConsultant = teamMembers.Any(x => x.UserId == salary.EmployeeId && x.IsLeader == 1);
355   - if (isConsultant && !string.IsNullOrEmpty(salary.GoldTriangleId))
  565 + // 注意:这里需要重新判断是否是顾问,因为可能被降级了
  566 + if (salary.Position == "顾问" && !string.IsNullOrEmpty(salary.GoldTriangleId))
356 567 {
357   - salary.ConsultantCommission = CalculateConsultantCommission(salary.TeamPerformance, employeeStats.Values.Where(x => x.GoldTriangleId == salary.GoldTriangleId).ToList());
  568 + salary.ConsultantCommission = CalculateConsultantCommission(
  569 + salary.TeamPerformance,
  570 + employeeStats.Values.Where(x => x.GoldTriangleId == salary.GoldTriangleId).ToList(),
  571 + isNewStore);
358 572 }
359 573  
360   - salary.TotalCommission = salary.BasePerformanceCommission + salary.CooperationPerformanceCommission + salary.ConsultantCommission;
  574 + salary.TotalCommission = salary.BasePerformanceCommission
  575 + + salary.CooperationPerformanceCommission
  576 + + salary.ConsultantCommission
  577 + + salary.NewCustomerPerformanceCommission
  578 + + salary.UpgradePerformanceCommission;
361 579 }
362 580  
363 581 // 计算占比
... ... @@ -386,7 +604,7 @@ namespace NCC.Extend
386 604 /// <summary>
387 605 /// 计算底薪
388 606 /// </summary>
389   - private decimal CalculateBaseSalary(decimal consumption, decimal projectCount)
  607 + private decimal CalculateBaseSalary(decimal consumption, decimal projectCount, bool isNewStore)
390 608 {
391 609 // 0星:<1w 或 <96个 -> 1800
392 610 // 1星:>=1w 且 >=96个 -> 2000
... ... @@ -394,6 +612,7 @@ namespace NCC.Extend
394 612 // 3星:>=4w 且 >=156个 -> 2400
395 613  
396 614 // 特殊规则:若消耗或项目数中仅一项未达标(0星),底薪按1星(2000元)计算
  615 + // 新店规则:新店底薪最低为1星(2000元),不满足1星按1星算
397 616  
398 617 int starCons = 0;
399 618 if (consumption >= 40000) starCons = 3;
... ... @@ -413,13 +632,21 @@ namespace NCC.Extend
413 632 finalStar = 1;
414 633 }
415 634  
416   - switch (finalStar)
  635 + decimal baseSalary = finalStar switch
417 636 {
418   - case 3: return 2400;
419   - case 2: return 2200;
420   - case 1: return 2000;
421   - default: return 1800;
  637 + 3 => 2400,
  638 + 2 => 2200,
  639 + 1 => 2000,
  640 + _ => 1800
  641 + };
  642 +
  643 + // 新店保底1星(2000元)
  644 + if (isNewStore && baseSalary < 2000)
  645 + {
  646 + baseSalary = 2000;
422 647 }
  648 +
  649 + return baseSalary;
423 650 }
424 651  
425 652 /// <summary>
... ... @@ -455,32 +682,72 @@ namespace NCC.Extend
455 682 /// <summary>
456 683 /// 计算顾问提成
457 684 /// </summary>
458   - private decimal CalculateConsultantCommission(decimal teamPerformance, List<LqSalaryStatisticsEntity> teamMembers)
  685 + private decimal CalculateConsultantCommission(decimal teamPerformance, List<LqSalaryStatisticsEntity> teamMembers, bool isNewStore)
459 686 {
460 687 // 顾问提成规则:
461 688 // 高级顾问:战队总业绩 ≥ 6万元 且 组员业绩达到40%以上 且 消耗达到6万元 → 团队总业绩0.8%
462 689 // 普通顾问:战队总业绩 ≥ 4万元 且 组员业绩达到30%以上 且 消耗达到4万元 → 团队总业绩0.3%
463 690  
464   - // 这里的“组员业绩达到X%以上”理解为:除顾问外的成员业绩占比?或者每个成员都达标?
465   - // 通常理解为:团队中是否有成员业绩贡献较高,或者团队整体结构健康。
466   - // 假设“组员业绩达到X%”是指:团队中至少有一名成员(非顾问本人?)或者所有成员平均?
467   - // 鉴于规则模糊,这里先简化实现:暂只考核总业绩和消耗。
468   - // 消耗是团队总消耗吗?假设是。
  691 + // 注意:
  692 + // 1. "组员业绩"指除顾问外的其他成员业绩总和
  693 + // 2. 只统计有效战队成员(考勤≥21天,未被剔除的成员)
  694 + // 3. "达到X%以上"指:组员业绩总和 ≥ 团队总业绩 × X%
  695 + // 4. 新店顾问不考核消耗
469 696  
470 697 var teamConsumption = teamMembers.Sum(x => x.Consumption);
471 698  
472   - // 高级顾问
473   - if (teamPerformance >= 60000 && teamConsumption >= 60000)
  699 + // 计算组员(非顾问)业绩总和
  700 + // teamMembers 已经是过滤后的有效成员列表(GoldTriangleId 相同且未被剔除)
  701 + var memberPerformance = teamMembers.Where(x => x.Position != "顾问").Sum(x => x.TotalPerformance);
  702 +
  703 + // 高级顾问:业绩≥6万 且 组员业绩≥40% 且 (新店 或 消耗≥6万)
  704 + if (teamPerformance >= 60000 && memberPerformance >= teamPerformance * 0.4m)
474 705 {
475   - return teamPerformance * 0.008m;
  706 + if (isNewStore || teamConsumption >= 60000)
  707 + {
  708 + return teamPerformance * 0.008m;
  709 + }
476 710 }
477   - // 普通顾问
478   - if (teamPerformance >= 40000 && teamConsumption >= 40000)
  711 +
  712 + // 普通顾问:业绩≥4万 且 组员业绩≥30% 且 (新店 或 消耗≥4万)
  713 + if (teamPerformance >= 40000 && memberPerformance >= teamPerformance * 0.3m)
479 714 {
480   - return teamPerformance * 0.003m;
  715 + if (isNewStore || teamConsumption >= 40000)
  716 + {
  717 + return teamPerformance * 0.003m;
  718 + }
481 719 }
482 720  
483 721 return 0;
484 722 }
  723 +
  724 + /// <summary>
  725 + /// 计算新客转化率提成
  726 + /// </summary>
  727 + private decimal CalculateNewCustomerConversionCommission(decimal newCustomerPerformance, decimal conversionRate)
  728 + {
  729 + decimal commissionRate = 0;
  730 +
  731 + if (conversionRate >= 0.5m) commissionRate = 0.20m;
  732 + else if (conversionRate >= 0.45m) commissionRate = 0.15m;
  733 + else if (conversionRate >= 0.35m) commissionRate = 0.10m;
  734 + else if (conversionRate >= 0) commissionRate = 0.06m;
  735 +
  736 + return newCustomerPerformance * commissionRate;
  737 + }
  738 +
  739 + /// <summary>
  740 + /// 计算升单人头提成
  741 + /// </summary>
  742 + private decimal CalculateUpgradeCustomerCommission(decimal upgradePerformance, decimal upgradeCustomerCount)
  743 + {
  744 + decimal commissionRate = 0;
  745 +
  746 + if (upgradeCustomerCount >= 10) commissionRate = 0.20m;
  747 + else if (upgradeCustomerCount >= 4) commissionRate = 0.10m;
  748 + // 0-4个: 0%
  749 +
  750 + return upgradePerformance * commissionRate;
  751 + }
485 752 }
486 753 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
... ... @@ -4997,5 +4997,7 @@ namespace NCC.Extend.LqStatistics
4997 4997 }
4998 4998 #endregion
4999 4999  
  5000 +
  5001 +
5000 5002 }
5001 5003 }
... ...
sql/创建健康师工资额外计算表.sql 0 → 100644
  1 +-- 创建健康师工资额外计算表
  2 +CREATE TABLE IF NOT EXISTS `lq_salary_extra_calculation` (
  3 + `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID',
  4 + `F_EmployeeId` VARCHAR(50) NULL COMMENT '健康师ID',
  5 + `F_Year` INT NULL COMMENT '年份',
  6 + `F_Month` INT NULL COMMENT '月份',
  7 + `F_BaseRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '基础奖励业绩',
  8 + `F_CooperationRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合作奖励业绩',
  9 + `F_NewCustomerPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '新客业绩',
  10 + `F_NewCustomerConversionRate` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '新客成交率',
  11 + `F_UpgradePerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单业绩',
  12 + `F_UpgradeConversionRate` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '升单成交率',
  13 + PRIMARY KEY (`F_Id`),
  14 + KEY `idx_employee_year_month` (`F_EmployeeId`, `F_Year`, `F_Month`),
  15 + KEY `idx_year_month` (`F_Year`, `F_Month`)
  16 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康师工资额外计算表';
  17 +
... ...
sql/添加健康师工资表其他业绩字段.sql 0 → 100644
  1 +-- 在健康师工资表中添加其他业绩加和其他业绩减字段
  2 +
  3 +-- 添加其他业绩加字段
  4 +ALTER TABLE `lq_salary_statistics`
  5 +ADD COLUMN `F_OtherPerformanceAdd` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩加' AFTER `F_UpgradePerformanceCommission`;
  6 +
  7 +-- 添加其他业绩减字段
  8 +ALTER TABLE `lq_salary_statistics`
  9 +ADD COLUMN `F_OtherPerformanceSubtract` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩减' AFTER `F_OtherPerformanceAdd`;
  10 +
... ...
sql/添加健康师工资表升单人头数和提成金额字段.sql 0 → 100644
  1 +-- 在健康师工资表中添加升单人头数、新客业绩提成金额、升单业绩提成金额字段
  2 +
  3 +-- 添加升单人头数字段(放在F_NewCustomerPoint字段后面)
  4 +ALTER TABLE `lq_salary_statistics`
  5 +ADD COLUMN `F_UpgradeCustomerCount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单人头数' AFTER `F_NewCustomerPoint`;
  6 +
  7 +-- 添加新客业绩提成金额字段
  8 +ALTER TABLE `lq_salary_statistics`
  9 +ADD COLUMN `F_NewCustomerPerformanceCommission` DECIMAL(18,2) DEFAULT 0.00 COMMENT '新客业绩提成金额' AFTER `F_UpgradePoint`;
  10 +
  11 +-- 添加升单业绩提成金额字段
  12 +ALTER TABLE `lq_salary_statistics`
  13 +ADD COLUMN `F_UpgradePerformanceCommission` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单业绩提成金额' AFTER `F_NewCustomerPerformanceCommission`;
  14 +
... ...
sql/添加健康师工资表奖励业绩字段.sql 0 → 100644
  1 +-- 在健康师工资表中添加基础奖励业绩和合作奖励业绩字段
  2 +
  3 +-- 添加基础奖励业绩字段
  4 +ALTER TABLE `lq_salary_statistics`
  5 +ADD COLUMN `F_BaseRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '基础奖励业绩' AFTER `F_CooperationPerformance`;
  6 +
  7 +-- 添加合作奖励业绩字段
  8 +ALTER TABLE `lq_salary_statistics`
  9 +ADD COLUMN `F_CooperationRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合作奖励业绩' AFTER `F_BaseRewardPerformance`;
  10 +
... ...
sql/添加健康师工资表实际业绩字段.sql 0 → 100644
  1 +-- 在健康师工资表中添加实际基础业绩和实际合作业绩字段
  2 +
  3 +-- 添加实际基础业绩字段
  4 +ALTER TABLE `lq_salary_statistics`
  5 +ADD COLUMN `F_ActualBasePerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '实际基础业绩' AFTER `F_CooperationRewardPerformance`;
  6 +
  7 +-- 添加实际合作业绩字段
  8 +ALTER TABLE `lq_salary_statistics`
  9 +ADD COLUMN `F_ActualCooperationPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '实际合作业绩' AFTER `F_ActualBasePerformance`;
  10 +
... ...
sql/添加健康师工资额外计算表其他业绩字段.sql 0 → 100644
  1 +-- 在健康师工资额外计算表中添加其他业绩加和其他业绩减字段
  2 +
  3 +-- 添加其他业绩加字段
  4 +ALTER TABLE `lq_salary_extra_calculation`
  5 +ADD COLUMN `F_OtherPerformanceAdd` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩加' AFTER `F_UpgradeCustomerCount`;
  6 +
  7 +-- 添加其他业绩减字段
  8 +ALTER TABLE `lq_salary_extra_calculation`
  9 +ADD COLUMN `F_OtherPerformanceSubtract` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩减' AFTER `F_OtherPerformanceAdd`;
  10 +
... ...
sql/添加健康师工资额外计算表升单人头数字段.sql 0 → 100644
  1 +-- 在健康师工资额外计算表中添加升单人头数字段
  2 +
  3 +ALTER TABLE `lq_salary_extra_calculation`
  4 +ADD COLUMN `F_UpgradeCustomerCount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单人头数' AFTER `F_UpgradeConversionRate`;
  5 +
... ...