Commit ac25ff124b26024e5debb94b5b3bca78ed99587c
1 parent
b717f998
feat: 添加大项目部老师工资计算、事业部总经理/经理工资计算、健康师额外数据导入修复、OSS URL迁移功能
- 新增大项目部老师工资计算功能(LqMajorProjectTeacherSalaryService) - 新增事业部总经理/经理工资计算功能(LqBusinessUnitManagerSalaryService) - 修复健康师额外数据导入功能,支持列数不足的情况 - 新增OSS URL迁移功能,支持将旧OSS地址替换为新OSS地址 - 优化库存使用申请列表查询,添加审批状态和是否已领取字段 - 优化文件上传服务,统一返回OSS路径
Showing
45 changed files
with
4509 additions
and
70 deletions
excel/健康师额外数据模板.xlsx
0 → 100644
No preview for this file type
netcore/src/Application/NCC.API/appsettings.json
| ... | ... | @@ -189,7 +189,7 @@ |
| 189 | 189 | "NCC_App": { |
| 190 | 190 | "CodeAreasName": "SubDev,Food,Extend,test", |
| 191 | 191 | //系统文件路径(末尾必须带斜杆) |
| 192 | - "SystemPath": "Files/", | |
| 192 | + "SystemPath": "/", | |
| 193 | 193 | //微信公众号允许上传文件类型 |
| 194 | 194 | "MPUploadFileType": "bmp,png,jpeg,jpg,gif,mp3,wma,wav,amr,mp4", |
| 195 | 195 | //微信允许上传文件类型 |
| ... | ... | @@ -213,7 +213,7 @@ |
| 213 | 213 | "AccessKeySecret": "84dpUAlu2eoyFOIEhFGkZlIy45h0B6", |
| 214 | 214 | "Endpoint": "oss-cn-chengdu.aliyuncs.com", |
| 215 | 215 | "Region": "cn-chengdu", |
| 216 | - "CustomDomain": "http://oss.lvqianmeiye.com" | |
| 216 | + "CustomDomain": "https://lvqian-erip.oss-cn-chengdu.aliyuncs.com" | |
| 217 | 217 | }, |
| 218 | 218 | //================== 系统错误邮件报告反馈相关 ============================== --> |
| 219 | 219 | //软件的错误报告 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryInput.cs
0 → 100644
| 1 | +using NCC.Common.Filter; | |
| 2 | +using System; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.LqBusinessUnitManagerSalary | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 事业部总经理/经理工资查询参数 | |
| 8 | + /// </summary> | |
| 9 | + public class BusinessUnitManagerSalaryInput : 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 | + /// 经理类型(0=经理,1=总经理,不传则查询全部) | |
| 23 | + /// </summary> | |
| 24 | + public int? ManagerType { 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/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqBusinessUnitManagerSalary | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 事业部总经理/经理工资输出 | |
| 7 | + /// </summary> | |
| 8 | + public class BusinessUnitManagerSalaryOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string Id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 统计月份 | |
| 17 | + /// </summary> | |
| 18 | + public string StatisticsMonth { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 核算岗位 | |
| 22 | + /// </summary> | |
| 23 | + public string Position { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 员工姓名 | |
| 27 | + /// </summary> | |
| 28 | + public string EmployeeName { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 员工ID | |
| 32 | + /// </summary> | |
| 33 | + public string EmployeeId { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 员工账号 | |
| 37 | + /// </summary> | |
| 38 | + public string EmployeeAccount { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 经理类型(0=经理,1=总经理) | |
| 42 | + /// </summary> | |
| 43 | + public int ManagerType { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 是否离职 | |
| 47 | + /// </summary> | |
| 48 | + public int IsTerminated { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 门店业绩明细(JSON格式) | |
| 52 | + /// </summary> | |
| 53 | + public string StorePerformanceDetail { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 底薪 | |
| 57 | + /// </summary> | |
| 58 | + public decimal BaseSalary { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 提成合计 | |
| 62 | + /// </summary> | |
| 63 | + public decimal TotalCommission { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 在店天数 | |
| 67 | + /// </summary> | |
| 68 | + public decimal WorkingDays { get; set; } | |
| 69 | + | |
| 70 | + /// <summary> | |
| 71 | + /// 请假天数 | |
| 72 | + /// </summary> | |
| 73 | + public decimal LeaveDays { get; set; } | |
| 74 | + | |
| 75 | + /// <summary> | |
| 76 | + /// 核算应发工资 | |
| 77 | + /// </summary> | |
| 78 | + public decimal CalculatedGrossSalary { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 最终应发工资 | |
| 82 | + /// </summary> | |
| 83 | + public decimal FinalGrossSalary { get; set; } | |
| 84 | + | |
| 85 | + /// <summary> | |
| 86 | + /// 当月培训补贴 | |
| 87 | + /// </summary> | |
| 88 | + public decimal MonthlyTrainingSubsidy { get; set; } | |
| 89 | + | |
| 90 | + /// <summary> | |
| 91 | + /// 当月交通补贴 | |
| 92 | + /// </summary> | |
| 93 | + public decimal MonthlyTransportSubsidy { get; set; } | |
| 94 | + | |
| 95 | + /// <summary> | |
| 96 | + /// 上月培训补贴 | |
| 97 | + /// </summary> | |
| 98 | + public decimal LastMonthTrainingSubsidy { get; set; } | |
| 99 | + | |
| 100 | + /// <summary> | |
| 101 | + /// 上月交通补贴 | |
| 102 | + /// </summary> | |
| 103 | + public decimal LastMonthTransportSubsidy { get; set; } | |
| 104 | + | |
| 105 | + /// <summary> | |
| 106 | + /// 补贴合计 | |
| 107 | + /// </summary> | |
| 108 | + public decimal TotalSubsidy { get; set; } | |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 缺卡扣款 | |
| 112 | + /// </summary> | |
| 113 | + public decimal MissingCard { get; set; } | |
| 114 | + | |
| 115 | + /// <summary> | |
| 116 | + /// 迟到扣款 | |
| 117 | + /// </summary> | |
| 118 | + public decimal LateArrival { get; set; } | |
| 119 | + | |
| 120 | + /// <summary> | |
| 121 | + /// 请假扣款 | |
| 122 | + /// </summary> | |
| 123 | + public decimal LeaveDeduction { get; set; } | |
| 124 | + | |
| 125 | + /// <summary> | |
| 126 | + /// 扣社保 | |
| 127 | + /// </summary> | |
| 128 | + public decimal SocialInsuranceDeduction { get; set; } | |
| 129 | + | |
| 130 | + /// <summary> | |
| 131 | + /// 扣除奖励 | |
| 132 | + /// </summary> | |
| 133 | + public decimal RewardDeduction { get; set; } | |
| 134 | + | |
| 135 | + /// <summary> | |
| 136 | + /// 扣住宿费 | |
| 137 | + /// </summary> | |
| 138 | + public decimal AccommodationDeduction { get; set; } | |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 扣学习期费用 | |
| 142 | + /// </summary> | |
| 143 | + public decimal StudyPeriodDeduction { get; set; } | |
| 144 | + | |
| 145 | + /// <summary> | |
| 146 | + /// 扣工作服费用 | |
| 147 | + /// </summary> | |
| 148 | + public decimal WorkClothesDeduction { get; set; } | |
| 149 | + | |
| 150 | + /// <summary> | |
| 151 | + /// 扣款合计 | |
| 152 | + /// </summary> | |
| 153 | + public decimal TotalDeduction { get; set; } | |
| 154 | + | |
| 155 | + /// <summary> | |
| 156 | + /// 发奖金 | |
| 157 | + /// </summary> | |
| 158 | + public decimal Bonus { get; set; } | |
| 159 | + | |
| 160 | + /// <summary> | |
| 161 | + /// 退手机押金 | |
| 162 | + /// </summary> | |
| 163 | + public decimal ReturnPhoneDeposit { get; set; } | |
| 164 | + | |
| 165 | + /// <summary> | |
| 166 | + /// 退住宿押金 | |
| 167 | + /// </summary> | |
| 168 | + public decimal ReturnAccommodationDeposit { get; set; } | |
| 169 | + | |
| 170 | + /// <summary> | |
| 171 | + /// 实发工资 | |
| 172 | + /// </summary> | |
| 173 | + public decimal ActualSalary { get; set; } | |
| 174 | + | |
| 175 | + /// <summary> | |
| 176 | + /// 当月是否发放 | |
| 177 | + /// </summary> | |
| 178 | + public string MonthlyPaymentStatus { get; set; } | |
| 179 | + | |
| 180 | + /// <summary> | |
| 181 | + /// 支付金额 | |
| 182 | + /// </summary> | |
| 183 | + public decimal PaidAmount { get; set; } | |
| 184 | + | |
| 185 | + /// <summary> | |
| 186 | + /// 待支付金额 | |
| 187 | + /// </summary> | |
| 188 | + public decimal PendingAmount { get; set; } | |
| 189 | + | |
| 190 | + /// <summary> | |
| 191 | + /// 补发上月 | |
| 192 | + /// </summary> | |
| 193 | + public decimal LastMonthSupplement { get; set; } | |
| 194 | + | |
| 195 | + /// <summary> | |
| 196 | + /// 当月支付总额 | |
| 197 | + /// </summary> | |
| 198 | + public decimal MonthlyTotalPayment { get; set; } | |
| 199 | + | |
| 200 | + /// <summary> | |
| 201 | + /// 是否锁定 | |
| 202 | + /// </summary> | |
| 203 | + public int IsLocked { get; set; } | |
| 204 | + | |
| 205 | + /// <summary> | |
| 206 | + /// 更新时间 | |
| 207 | + /// </summary> | |
| 208 | + public DateTime UpdateTime { get; set; } | |
| 209 | + } | |
| 210 | +} | |
| 211 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs
| ... | ... | @@ -111,5 +111,15 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsage |
| 111 | 111 | /// 使用批次ID(同一批次申请的使用记录使用相同的批次ID) |
| 112 | 112 | /// </summary> |
| 113 | 113 | public string usageBatchId { get; set; } |
| 114 | + | |
| 115 | + /// <summary> | |
| 116 | + /// 审批状态(待审批/审批中/已通过/未通过/已退回),通过usageBatchId关联申请表获取 | |
| 117 | + /// </summary> | |
| 118 | + public string approvalStatus { get; set; } | |
| 119 | + | |
| 120 | + /// <summary> | |
| 121 | + /// 是否已领取(1-已领取,0-未领取),通过usageBatchId关联申请表获取 | |
| 122 | + /// </summary> | |
| 123 | + public int? isReceived { get; set; } | |
| 114 | 124 | } |
| 115 | 125 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryInput.cs
0 → 100644
| 1 | +using NCC.Common.Filter; | |
| 2 | +using System; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.LqMajorProjectTeacherSalary | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 大项目部老师工资查询参数 | |
| 8 | + /// </summary> | |
| 9 | + public class MajorProjectTeacherSalaryInput : 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 StoreId { 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/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqMajorProjectTeacherSalary | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 大项目部老师工资输出 | |
| 7 | + /// </summary> | |
| 8 | + public class MajorProjectTeacherSalaryOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string Id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 统计月份 | |
| 17 | + /// </summary> | |
| 18 | + public string StatisticsMonth { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店ID | |
| 22 | + /// </summary> | |
| 23 | + public string StoreId { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 门店名称 | |
| 27 | + /// </summary> | |
| 28 | + public string StoreName { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 核算岗位 | |
| 32 | + /// </summary> | |
| 33 | + public string Position { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 员工姓名 | |
| 37 | + /// </summary> | |
| 38 | + public string EmployeeName { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 员工ID | |
| 42 | + /// </summary> | |
| 43 | + public string EmployeeId { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 员工账号 | |
| 47 | + /// </summary> | |
| 48 | + public string EmployeeAccount { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 开单业绩 | |
| 52 | + /// </summary> | |
| 53 | + public decimal OrderAchievement { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 消耗业绩 | |
| 57 | + /// </summary> | |
| 58 | + public decimal ConsumeAchievement { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 退卡业绩 | |
| 62 | + /// </summary> | |
| 63 | + public decimal RefundAchievement { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 总业绩 | |
| 67 | + /// </summary> | |
| 68 | + public decimal TotalPerformance { get; set; } | |
| 69 | + | |
| 70 | + /// <summary> | |
| 71 | + /// 底薪 | |
| 72 | + /// </summary> | |
| 73 | + public decimal BaseSalary { get; set; } | |
| 74 | + | |
| 75 | + /// <summary> | |
| 76 | + /// 业绩提成比例 | |
| 77 | + /// </summary> | |
| 78 | + public decimal PerformanceCommissionRate { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 业绩提成金额 | |
| 82 | + /// </summary> | |
| 83 | + public decimal PerformanceCommissionAmount { get; set; } | |
| 84 | + | |
| 85 | + /// <summary> | |
| 86 | + /// 提成合计 | |
| 87 | + /// </summary> | |
| 88 | + public decimal TotalCommission { get; set; } | |
| 89 | + | |
| 90 | + /// <summary> | |
| 91 | + /// 手工费 | |
| 92 | + /// </summary> | |
| 93 | + public decimal HandworkFee { get; set; } | |
| 94 | + | |
| 95 | + /// <summary> | |
| 96 | + /// 在店天数 | |
| 97 | + /// </summary> | |
| 98 | + public decimal WorkingDays { get; set; } | |
| 99 | + | |
| 100 | + /// <summary> | |
| 101 | + /// 请假天数 | |
| 102 | + /// </summary> | |
| 103 | + public decimal LeaveDays { get; set; } | |
| 104 | + | |
| 105 | + /// <summary> | |
| 106 | + /// 车补 | |
| 107 | + /// </summary> | |
| 108 | + public decimal TransportationAllowance { get; set; } | |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 少休费 | |
| 112 | + /// </summary> | |
| 113 | + public decimal LessRest { get; set; } | |
| 114 | + | |
| 115 | + /// <summary> | |
| 116 | + /// 全勤奖 | |
| 117 | + /// </summary> | |
| 118 | + public decimal FullAttendance { get; set; } | |
| 119 | + | |
| 120 | + /// <summary> | |
| 121 | + /// 核算应发工资 | |
| 122 | + /// </summary> | |
| 123 | + public decimal CalculatedGrossSalary { get; set; } | |
| 124 | + | |
| 125 | + /// <summary> | |
| 126 | + /// 保底工资 | |
| 127 | + /// </summary> | |
| 128 | + public decimal GuaranteedSalary { get; set; } | |
| 129 | + | |
| 130 | + /// <summary> | |
| 131 | + /// 保底请假扣款 | |
| 132 | + /// </summary> | |
| 133 | + public decimal GuaranteedLeaveDeduction { get; set; } | |
| 134 | + | |
| 135 | + /// <summary> | |
| 136 | + /// 保底底薪 | |
| 137 | + /// </summary> | |
| 138 | + public decimal GuaranteedBaseSalary { get; set; } | |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 保底补差 | |
| 142 | + /// </summary> | |
| 143 | + public decimal GuaranteedSupplement { get; set; } | |
| 144 | + | |
| 145 | + /// <summary> | |
| 146 | + /// 最终应发工资 | |
| 147 | + /// </summary> | |
| 148 | + public decimal FinalGrossSalary { get; set; } | |
| 149 | + | |
| 150 | + /// <summary> | |
| 151 | + /// 当月培训补贴 | |
| 152 | + /// </summary> | |
| 153 | + public decimal MonthlyTrainingSubsidy { get; set; } | |
| 154 | + | |
| 155 | + /// <summary> | |
| 156 | + /// 当月交通补贴 | |
| 157 | + /// </summary> | |
| 158 | + public decimal MonthlyTransportSubsidy { get; set; } | |
| 159 | + | |
| 160 | + /// <summary> | |
| 161 | + /// 上月培训补贴 | |
| 162 | + /// </summary> | |
| 163 | + public decimal LastMonthTrainingSubsidy { get; set; } | |
| 164 | + | |
| 165 | + /// <summary> | |
| 166 | + /// 上月交通补贴 | |
| 167 | + /// </summary> | |
| 168 | + public decimal LastMonthTransportSubsidy { get; set; } | |
| 169 | + | |
| 170 | + /// <summary> | |
| 171 | + /// 补贴合计 | |
| 172 | + /// </summary> | |
| 173 | + public decimal TotalSubsidy { get; set; } | |
| 174 | + | |
| 175 | + /// <summary> | |
| 176 | + /// 缺卡扣款 | |
| 177 | + /// </summary> | |
| 178 | + public decimal MissingCard { get; set; } | |
| 179 | + | |
| 180 | + /// <summary> | |
| 181 | + /// 迟到扣款 | |
| 182 | + /// </summary> | |
| 183 | + public decimal LateArrival { get; set; } | |
| 184 | + | |
| 185 | + /// <summary> | |
| 186 | + /// 请假扣款 | |
| 187 | + /// </summary> | |
| 188 | + public decimal LeaveDeduction { get; set; } | |
| 189 | + | |
| 190 | + /// <summary> | |
| 191 | + /// 扣社保 | |
| 192 | + /// </summary> | |
| 193 | + public decimal SocialInsuranceDeduction { get; set; } | |
| 194 | + | |
| 195 | + /// <summary> | |
| 196 | + /// 扣除奖励 | |
| 197 | + /// </summary> | |
| 198 | + public decimal RewardDeduction { get; set; } | |
| 199 | + | |
| 200 | + /// <summary> | |
| 201 | + /// 扣住宿费 | |
| 202 | + /// </summary> | |
| 203 | + public decimal AccommodationDeduction { get; set; } | |
| 204 | + | |
| 205 | + /// <summary> | |
| 206 | + /// 扣学习期费用 | |
| 207 | + /// </summary> | |
| 208 | + public decimal StudyPeriodDeduction { get; set; } | |
| 209 | + | |
| 210 | + /// <summary> | |
| 211 | + /// 扣工作服费用 | |
| 212 | + /// </summary> | |
| 213 | + public decimal WorkClothesDeduction { get; set; } | |
| 214 | + | |
| 215 | + /// <summary> | |
| 216 | + /// 扣款合计 | |
| 217 | + /// </summary> | |
| 218 | + public decimal TotalDeduction { get; set; } | |
| 219 | + | |
| 220 | + /// <summary> | |
| 221 | + /// 发奖金 | |
| 222 | + /// </summary> | |
| 223 | + public decimal Bonus { get; set; } | |
| 224 | + | |
| 225 | + /// <summary> | |
| 226 | + /// 退手机押金 | |
| 227 | + /// </summary> | |
| 228 | + public decimal ReturnPhoneDeposit { get; set; } | |
| 229 | + | |
| 230 | + /// <summary> | |
| 231 | + /// 退住宿押金 | |
| 232 | + /// </summary> | |
| 233 | + public decimal ReturnAccommodationDeposit { get; set; } | |
| 234 | + | |
| 235 | + /// <summary> | |
| 236 | + /// 实发工资 | |
| 237 | + /// </summary> | |
| 238 | + public decimal ActualSalary { get; set; } | |
| 239 | + | |
| 240 | + /// <summary> | |
| 241 | + /// 当月是否发放 | |
| 242 | + /// </summary> | |
| 243 | + public string MonthlyPaymentStatus { get; set; } | |
| 244 | + | |
| 245 | + /// <summary> | |
| 246 | + /// 支付金额 | |
| 247 | + /// </summary> | |
| 248 | + public decimal PaidAmount { get; set; } | |
| 249 | + | |
| 250 | + /// <summary> | |
| 251 | + /// 待支付金额 | |
| 252 | + /// </summary> | |
| 253 | + public decimal PendingAmount { get; set; } | |
| 254 | + | |
| 255 | + /// <summary> | |
| 256 | + /// 补发上月 | |
| 257 | + /// </summary> | |
| 258 | + public decimal LastMonthSupplement { get; set; } | |
| 259 | + | |
| 260 | + /// <summary> | |
| 261 | + /// 当月支付总额 | |
| 262 | + /// </summary> | |
| 263 | + public decimal MonthlyTotalPayment { get; set; } | |
| 264 | + | |
| 265 | + /// <summary> | |
| 266 | + /// 是否锁定 | |
| 267 | + /// </summary> | |
| 268 | + public int IsLocked { get; set; } | |
| 269 | + | |
| 270 | + /// <summary> | |
| 271 | + /// 是否离职 | |
| 272 | + /// </summary> | |
| 273 | + public int IsTerminated { get; set; } | |
| 274 | + | |
| 275 | + /// <summary> | |
| 276 | + /// 更新时间 | |
| 277 | + /// </summary> | |
| 278 | + public DateTime UpdateTime { get; set; } | |
| 279 | + | |
| 280 | + /// <summary> | |
| 281 | + /// 门店类型 | |
| 282 | + /// </summary> | |
| 283 | + public int? StoreType { get; set; } | |
| 284 | + | |
| 285 | + /// <summary> | |
| 286 | + /// 门店类别 | |
| 287 | + /// </summary> | |
| 288 | + public int? StoreCategory { get; set; } | |
| 289 | + | |
| 290 | + /// <summary> | |
| 291 | + /// 是否新店 | |
| 292 | + /// </summary> | |
| 293 | + public string IsNewStore { get; set; } | |
| 294 | + | |
| 295 | + /// <summary> | |
| 296 | + /// 新店保护阶段 | |
| 297 | + /// </summary> | |
| 298 | + public int NewStoreProtectionStage { get; set; } | |
| 299 | + } | |
| 300 | +} | |
| 301 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentCrInput.cs
| ... | ... | @@ -34,6 +34,11 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment |
| 34 | 34 | public string teacherId { get; set; } |
| 35 | 35 | |
| 36 | 36 | /// <summary> |
| 37 | + /// 教育部老师用户ID | |
| 38 | + /// </summary> | |
| 39 | + public string educationTeacherId { get; set; } | |
| 40 | + | |
| 41 | + /// <summary> | |
| 37 | 42 | /// 备注说明 |
| 38 | 43 | /// </summary> |
| 39 | 44 | public string remark { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentInfoOutput.cs
| ... | ... | @@ -43,6 +43,16 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment |
| 43 | 43 | public string teacherName { get; set; } |
| 44 | 44 | |
| 45 | 45 | /// <summary> |
| 46 | + /// 教育部老师用户ID | |
| 47 | + /// </summary> | |
| 48 | + public string educationTeacherId { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 教育部老师姓名 | |
| 52 | + /// </summary> | |
| 53 | + public string educationTeacherName { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 46 | 56 | /// 备注说明 |
| 47 | 57 | /// </summary> |
| 48 | 58 | public string remark { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListOutput.cs
| ... | ... | @@ -43,6 +43,16 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment |
| 43 | 43 | public string teacherName { get; set; } |
| 44 | 44 | |
| 45 | 45 | /// <summary> |
| 46 | + /// 教育部老师用户ID | |
| 47 | + /// </summary> | |
| 48 | + public string educationTeacherId { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 教育部老师姓名 | |
| 52 | + /// </summary> | |
| 53 | + public string educationTeacherName { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 46 | 56 | /// 备注说明 |
| 47 | 57 | /// </summary> |
| 48 | 58 | public string remark { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListQueryInput.cs
| ... | ... | @@ -26,5 +26,10 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment |
| 26 | 26 | /// 大项目部老师用户ID |
| 27 | 27 | /// </summary> |
| 28 | 28 | public string teacherId { get; set; } |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 教育部老师用户ID | |
| 32 | + /// </summary> | |
| 33 | + public string educationTeacherId { get; set; } | |
| 29 | 34 | } |
| 30 | 35 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentUpInput.cs
| ... | ... | @@ -40,6 +40,11 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment |
| 40 | 40 | public string teacherId { get; set; } |
| 41 | 41 | |
| 42 | 42 | /// <summary> |
| 43 | + /// 教育部老师用户ID | |
| 44 | + /// </summary> | |
| 45 | + public string educationTeacherId { get; set; } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 43 | 48 | /// 备注说明 |
| 44 | 49 | /// </summary> |
| 45 | 50 | public string remark { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_business_unit_manager_salary_statistics/LqBusinessUnitManagerSalaryStatisticsEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_business_unit_manager_salary_statistics | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 事业部总经理/经理工资统计表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_business_unit_manager_salary_statistics")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqBusinessUnitManagerSalaryStatisticsEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键ID | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 统计月份(YYYYMM) | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "F_StatisticsMonth", Length = 6)] | |
| 24 | + public string StatisticsMonth { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 核算岗位 | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "F_Position")] | |
| 30 | + public string Position { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 员工姓名 | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_EmployeeName")] | |
| 36 | + public string EmployeeName { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 员工ID | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_EmployeeId")] | |
| 42 | + public string EmployeeId { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 员工账号 | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_EmployeeAccount")] | |
| 48 | + public string EmployeeAccount { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 经理类型(0=经理,1=总经理) | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "F_ManagerType")] | |
| 54 | + public int ManagerType { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 是否离职(0=在职,1=离职) | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "F_IsTerminated")] | |
| 60 | + public int IsTerminated { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 门店业绩明细(JSON格式) | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "F_StorePerformanceDetail", ColumnDataType = "TEXT")] | |
| 66 | + public string StorePerformanceDetail { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 底薪金额(固定4000元) | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "F_BaseSalary")] | |
| 72 | + public decimal BaseSalary { get; set; } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 提成合计(所有门店提成金额汇总) | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "F_TotalCommission")] | |
| 78 | + public decimal TotalCommission { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 在店天数 | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "F_WorkingDays")] | |
| 84 | + public decimal WorkingDays { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 请假天数 | |
| 88 | + /// </summary> | |
| 89 | + [SugarColumn(ColumnName = "F_LeaveDays")] | |
| 90 | + public decimal LeaveDays { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 核算应发工资(底薪 + 提成合计) | |
| 94 | + /// </summary> | |
| 95 | + [SugarColumn(ColumnName = "F_CalculatedGrossSalary")] | |
| 96 | + public decimal CalculatedGrossSalary { get; set; } | |
| 97 | + | |
| 98 | + /// <summary> | |
| 99 | + /// 最终应发工资 | |
| 100 | + /// </summary> | |
| 101 | + [SugarColumn(ColumnName = "F_FinalGrossSalary")] | |
| 102 | + public decimal FinalGrossSalary { get; set; } | |
| 103 | + | |
| 104 | + /// <summary> | |
| 105 | + /// 当月培训补贴 | |
| 106 | + /// </summary> | |
| 107 | + [SugarColumn(ColumnName = "F_MonthlyTrainingSubsidy")] | |
| 108 | + public decimal MonthlyTrainingSubsidy { get; set; } | |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 当月交通补贴 | |
| 112 | + /// </summary> | |
| 113 | + [SugarColumn(ColumnName = "F_MonthlyTransportSubsidy")] | |
| 114 | + public decimal MonthlyTransportSubsidy { get; set; } | |
| 115 | + | |
| 116 | + /// <summary> | |
| 117 | + /// 上月培训补贴 | |
| 118 | + /// </summary> | |
| 119 | + [SugarColumn(ColumnName = "F_LastMonthTrainingSubsidy")] | |
| 120 | + public decimal LastMonthTrainingSubsidy { get; set; } | |
| 121 | + | |
| 122 | + /// <summary> | |
| 123 | + /// 上月交通补贴 | |
| 124 | + /// </summary> | |
| 125 | + [SugarColumn(ColumnName = "F_LastMonthTransportSubsidy")] | |
| 126 | + public decimal LastMonthTransportSubsidy { get; set; } | |
| 127 | + | |
| 128 | + /// <summary> | |
| 129 | + /// 补贴合计 | |
| 130 | + /// </summary> | |
| 131 | + [SugarColumn(ColumnName = "F_TotalSubsidy")] | |
| 132 | + public decimal TotalSubsidy { get; set; } | |
| 133 | + | |
| 134 | + /// <summary> | |
| 135 | + /// 缺卡扣款 | |
| 136 | + /// </summary> | |
| 137 | + [SugarColumn(ColumnName = "F_MissingCard")] | |
| 138 | + public decimal MissingCard { get; set; } | |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 迟到扣款 | |
| 142 | + /// </summary> | |
| 143 | + [SugarColumn(ColumnName = "F_LateArrival")] | |
| 144 | + public decimal LateArrival { get; set; } | |
| 145 | + | |
| 146 | + /// <summary> | |
| 147 | + /// 请假扣款 | |
| 148 | + /// </summary> | |
| 149 | + [SugarColumn(ColumnName = "F_LeaveDeduction")] | |
| 150 | + public decimal LeaveDeduction { get; set; } | |
| 151 | + | |
| 152 | + /// <summary> | |
| 153 | + /// 扣社保 | |
| 154 | + /// </summary> | |
| 155 | + [SugarColumn(ColumnName = "F_SocialInsuranceDeduction")] | |
| 156 | + public decimal SocialInsuranceDeduction { get; set; } | |
| 157 | + | |
| 158 | + /// <summary> | |
| 159 | + /// 扣除奖励 | |
| 160 | + /// </summary> | |
| 161 | + [SugarColumn(ColumnName = "F_RewardDeduction")] | |
| 162 | + public decimal RewardDeduction { get; set; } | |
| 163 | + | |
| 164 | + /// <summary> | |
| 165 | + /// 扣住宿费 | |
| 166 | + /// </summary> | |
| 167 | + [SugarColumn(ColumnName = "F_AccommodationDeduction")] | |
| 168 | + public decimal AccommodationDeduction { get; set; } | |
| 169 | + | |
| 170 | + /// <summary> | |
| 171 | + /// 扣学习期费用 | |
| 172 | + /// </summary> | |
| 173 | + [SugarColumn(ColumnName = "F_StudyPeriodDeduction")] | |
| 174 | + public decimal StudyPeriodDeduction { get; set; } | |
| 175 | + | |
| 176 | + /// <summary> | |
| 177 | + /// 扣工作服费用 | |
| 178 | + /// </summary> | |
| 179 | + [SugarColumn(ColumnName = "F_WorkClothesDeduction")] | |
| 180 | + public decimal WorkClothesDeduction { get; set; } | |
| 181 | + | |
| 182 | + /// <summary> | |
| 183 | + /// 扣款合计 | |
| 184 | + /// </summary> | |
| 185 | + [SugarColumn(ColumnName = "F_TotalDeduction")] | |
| 186 | + public decimal TotalDeduction { get; set; } | |
| 187 | + | |
| 188 | + /// <summary> | |
| 189 | + /// 发奖金 | |
| 190 | + /// </summary> | |
| 191 | + [SugarColumn(ColumnName = "F_Bonus")] | |
| 192 | + public decimal Bonus { get; set; } | |
| 193 | + | |
| 194 | + /// <summary> | |
| 195 | + /// 退手机押金 | |
| 196 | + /// </summary> | |
| 197 | + [SugarColumn(ColumnName = "F_ReturnPhoneDeposit")] | |
| 198 | + public decimal ReturnPhoneDeposit { get; set; } | |
| 199 | + | |
| 200 | + /// <summary> | |
| 201 | + /// 退住宿押金 | |
| 202 | + /// </summary> | |
| 203 | + [SugarColumn(ColumnName = "F_ReturnAccommodationDeposit")] | |
| 204 | + public decimal ReturnAccommodationDeposit { get; set; } | |
| 205 | + | |
| 206 | + /// <summary> | |
| 207 | + /// 实发工资 | |
| 208 | + /// </summary> | |
| 209 | + [SugarColumn(ColumnName = "F_ActualSalary")] | |
| 210 | + public decimal ActualSalary { get; set; } | |
| 211 | + | |
| 212 | + /// <summary> | |
| 213 | + /// 当月是否发放 | |
| 214 | + /// </summary> | |
| 215 | + [SugarColumn(ColumnName = "F_MonthlyPaymentStatus")] | |
| 216 | + public string MonthlyPaymentStatus { get; set; } | |
| 217 | + | |
| 218 | + /// <summary> | |
| 219 | + /// 支付金额 | |
| 220 | + /// </summary> | |
| 221 | + [SugarColumn(ColumnName = "F_PaidAmount")] | |
| 222 | + public decimal PaidAmount { get; set; } | |
| 223 | + | |
| 224 | + /// <summary> | |
| 225 | + /// 待支付金额 | |
| 226 | + /// </summary> | |
| 227 | + [SugarColumn(ColumnName = "F_PendingAmount")] | |
| 228 | + public decimal PendingAmount { get; set; } | |
| 229 | + | |
| 230 | + /// <summary> | |
| 231 | + /// 补发上月 | |
| 232 | + /// </summary> | |
| 233 | + [SugarColumn(ColumnName = "F_LastMonthSupplement")] | |
| 234 | + public decimal LastMonthSupplement { get; set; } | |
| 235 | + | |
| 236 | + /// <summary> | |
| 237 | + /// 当月支付总额 | |
| 238 | + /// </summary> | |
| 239 | + [SugarColumn(ColumnName = "F_MonthlyTotalPayment")] | |
| 240 | + public decimal MonthlyTotalPayment { get; set; } | |
| 241 | + | |
| 242 | + /// <summary> | |
| 243 | + /// 是否锁定(0=未锁定,1=已锁定) | |
| 244 | + /// </summary> | |
| 245 | + [SugarColumn(ColumnName = "F_IsLocked")] | |
| 246 | + public int IsLocked { get; set; } | |
| 247 | + | |
| 248 | + /// <summary> | |
| 249 | + /// 创建时间 | |
| 250 | + /// </summary> | |
| 251 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 252 | + public DateTime CreateTime { get; set; } | |
| 253 | + | |
| 254 | + /// <summary> | |
| 255 | + /// 更新时间 | |
| 256 | + /// </summary> | |
| 257 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 258 | + public DateTime UpdateTime { get; set; } | |
| 259 | + | |
| 260 | + /// <summary> | |
| 261 | + /// 创建人 | |
| 262 | + /// </summary> | |
| 263 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 264 | + public string CreateUser { get; set; } | |
| 265 | + | |
| 266 | + /// <summary> | |
| 267 | + /// 更新人 | |
| 268 | + /// </summary> | |
| 269 | + [SugarColumn(ColumnName = "F_UpdateUser")] | |
| 270 | + public string UpdateUser { get; set; } | |
| 271 | + } | |
| 272 | +} | |
| 273 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_jksyj/LqHytkJksyjEntity.cs
| ... | ... | @@ -143,5 +143,11 @@ namespace NCC.Extend.Entitys.lq_hytk_jksyj |
| 143 | 143 | /// </summary> |
| 144 | 144 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 145 | 145 | public string PerformanceType { get; set; } |
| 146 | + | |
| 147 | + /// <summary> | |
| 148 | + /// 科美类型(来源:lq_xmzl.F_BeautyType) | |
| 149 | + /// </summary> | |
| 150 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 151 | + public string BeautyType { get; set; } | |
| 146 | 152 | } |
| 147 | 153 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_kjbsyj/LqHytkKjbsyjEntity.cs
| ... | ... | @@ -138,5 +138,11 @@ namespace NCC.Extend.Entitys.lq_hytk_kjbsyj |
| 138 | 138 | /// </summary> |
| 139 | 139 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 140 | 140 | public string PerformanceType { get; set; } |
| 141 | + | |
| 142 | + /// <summary> | |
| 143 | + /// 科美类型(来源:lq_xmzl.F_BeautyType) | |
| 144 | + /// </summary> | |
| 145 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 146 | + public string BeautyType { get; set; } | |
| 141 | 147 | } |
| 142 | 148 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_mx/LqHytkMxEntity.cs
| ... | ... | @@ -126,5 +126,11 @@ namespace NCC.Extend.Entitys.lq_hytk_mx |
| 126 | 126 | /// </summary> |
| 127 | 127 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 128 | 128 | public string PerformanceType { get; set; } |
| 129 | + | |
| 130 | + /// <summary> | |
| 131 | + /// 科美类型(来源:lq_xmzl.F_BeautyType) | |
| 132 | + /// </summary> | |
| 133 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 134 | + public string BeautyType { get; set; } | |
| 129 | 135 | } |
| 130 | 136 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_jksyj/LqKdJksyjEntity.cs
| ... | ... | @@ -106,5 +106,11 @@ namespace NCC.Extend.Entitys.lq_kd_jksyj |
| 106 | 106 | /// </summary> |
| 107 | 107 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 108 | 108 | public string PerformanceType { get; set; } |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 科美类型(来源:lq_xmzl.F_BeautyType) | |
| 112 | + /// </summary> | |
| 113 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 114 | + public string BeautyType { get; set; } | |
| 109 | 115 | } |
| 110 | 116 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs
| ... | ... | @@ -106,5 +106,11 @@ namespace NCC.Extend.Entitys.lq_kd_kjbsyj |
| 106 | 106 | /// </summary> |
| 107 | 107 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 108 | 108 | public string PerformanceType { get; set; } |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 科美类型(来源:lq_xmzl.F_BeautyType) | |
| 112 | + /// </summary> | |
| 113 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 114 | + public string BeautyType { get; set; } | |
| 109 | 115 | } |
| 110 | 116 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_pxmx/LqKdPxmxEntity.cs
| ... | ... | @@ -119,5 +119,12 @@ namespace NCC.Extend.Entitys.lq_kd_pxmx |
| 119 | 119 | /// </summary> |
| 120 | 120 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 121 | 121 | public string PerformanceType { get; set; } |
| 122 | + | |
| 123 | + /// <summary> | |
| 124 | + /// 科美类型(来源:lq_xmzl.F_BeautyType) | |
| 125 | + /// </summary> | |
| 126 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 127 | + public string BeautyType { get; set; } | |
| 128 | + | |
| 122 | 129 | } |
| 123 | 130 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_major_project_teacher_salary_statistics/LqMajorProjectTeacherSalaryStatisticsEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_major_project_teacher_salary_statistics | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 大项目部老师工资统计表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_major_project_teacher_salary_statistics")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqMajorProjectTeacherSalaryStatisticsEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键ID | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 统计月份(YYYYMM) | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "F_StatisticsMonth", Length = 6)] | |
| 24 | + public string StatisticsMonth { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 门店ID | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "F_StoreId")] | |
| 30 | + public string StoreId { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 门店名称 | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_StoreName")] | |
| 36 | + public string StoreName { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 核算岗位 | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_Position")] | |
| 42 | + public string Position { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 员工姓名 | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_EmployeeName")] | |
| 48 | + public string EmployeeName { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 员工ID | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "F_EmployeeId")] | |
| 54 | + public string EmployeeId { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 员工账号 | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "F_EmployeeAccount")] | |
| 60 | + public string EmployeeAccount { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 开单业绩 | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "F_OrderAchievement", DecimalDigits = 2)] | |
| 66 | + public decimal OrderAchievement { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 消耗业绩 | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "F_ConsumeAchievement", DecimalDigits = 2)] | |
| 72 | + public decimal ConsumeAchievement { get; set; } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 退卡业绩 | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "F_RefundAchievement", DecimalDigits = 2)] | |
| 78 | + public decimal RefundAchievement { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 总业绩(开单业绩 + 消耗业绩 + 退卡业绩) | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "F_TotalPerformance", DecimalDigits = 2)] | |
| 84 | + public decimal TotalPerformance { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 底薪金额(固定3000元) | |
| 88 | + /// </summary> | |
| 89 | + [SugarColumn(ColumnName = "F_BaseSalary", DecimalDigits = 2)] | |
| 90 | + public decimal BaseSalary { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 业绩提成比例(百分比,如2.00表示2%,0.00表示无提成) | |
| 94 | + /// </summary> | |
| 95 | + [SugarColumn(ColumnName = "F_PerformanceCommissionRate", DecimalDigits = 4)] | |
| 96 | + public decimal PerformanceCommissionRate { get; set; } | |
| 97 | + | |
| 98 | + /// <summary> | |
| 99 | + /// 业绩提成金额(总业绩 × 业绩提成比例,阶梯式计算) | |
| 100 | + /// </summary> | |
| 101 | + [SugarColumn(ColumnName = "F_PerformanceCommissionAmount", DecimalDigits = 2)] | |
| 102 | + public decimal PerformanceCommissionAmount { get; set; } | |
| 103 | + | |
| 104 | + /// <summary> | |
| 105 | + /// 提成合计(业绩提成金额) | |
| 106 | + /// </summary> | |
| 107 | + [SugarColumn(ColumnName = "F_TotalCommission", DecimalDigits = 2)] | |
| 108 | + public decimal TotalCommission { get; set; } | |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 手工费 | |
| 112 | + /// </summary> | |
| 113 | + [SugarColumn(ColumnName = "F_HandworkFee", DecimalDigits = 2)] | |
| 114 | + public decimal HandworkFee { get; set; } | |
| 115 | + | |
| 116 | + /// <summary> | |
| 117 | + /// 车补 | |
| 118 | + /// </summary> | |
| 119 | + [SugarColumn(ColumnName = "F_TransportationAllowance", DecimalDigits = 2)] | |
| 120 | + public decimal TransportationAllowance { get; set; } | |
| 121 | + | |
| 122 | + /// <summary> | |
| 123 | + /// 少休费 | |
| 124 | + /// </summary> | |
| 125 | + [SugarColumn(ColumnName = "F_LessRest", DecimalDigits = 2)] | |
| 126 | + public decimal LessRest { get; set; } | |
| 127 | + | |
| 128 | + /// <summary> | |
| 129 | + /// 全勤奖 | |
| 130 | + /// </summary> | |
| 131 | + [SugarColumn(ColumnName = "F_FullAttendance", DecimalDigits = 2)] | |
| 132 | + public decimal FullAttendance { get; set; } | |
| 133 | + | |
| 134 | + /// <summary> | |
| 135 | + /// 在店天数 | |
| 136 | + /// </summary> | |
| 137 | + [SugarColumn(ColumnName = "F_WorkingDays", DecimalDigits = 2)] | |
| 138 | + public decimal WorkingDays { get; set; } | |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 请假天数 | |
| 142 | + /// </summary> | |
| 143 | + [SugarColumn(ColumnName = "F_LeaveDays", DecimalDigits = 2)] | |
| 144 | + public decimal LeaveDays { get; set; } | |
| 145 | + | |
| 146 | + /// <summary> | |
| 147 | + /// 核算应发工资(底薪 + 提成合计 + 其他收入) | |
| 148 | + /// </summary> | |
| 149 | + [SugarColumn(ColumnName = "F_CalculatedGrossSalary", DecimalDigits = 2)] | |
| 150 | + public decimal CalculatedGrossSalary { get; set; } | |
| 151 | + | |
| 152 | + /// <summary> | |
| 153 | + /// 保底工资 | |
| 154 | + /// </summary> | |
| 155 | + [SugarColumn(ColumnName = "F_GuaranteedSalary", DecimalDigits = 2)] | |
| 156 | + public decimal GuaranteedSalary { get; set; } | |
| 157 | + | |
| 158 | + /// <summary> | |
| 159 | + /// 保底请假扣款 | |
| 160 | + /// </summary> | |
| 161 | + [SugarColumn(ColumnName = "F_GuaranteedLeaveDeduction", DecimalDigits = 2)] | |
| 162 | + public decimal GuaranteedLeaveDeduction { get; set; } | |
| 163 | + | |
| 164 | + /// <summary> | |
| 165 | + /// 保底底薪 | |
| 166 | + /// </summary> | |
| 167 | + [SugarColumn(ColumnName = "F_GuaranteedBaseSalary", DecimalDigits = 2)] | |
| 168 | + public decimal GuaranteedBaseSalary { get; set; } | |
| 169 | + | |
| 170 | + /// <summary> | |
| 171 | + /// 保底补差 | |
| 172 | + /// </summary> | |
| 173 | + [SugarColumn(ColumnName = "F_GuaranteedSupplement", DecimalDigits = 2)] | |
| 174 | + public decimal GuaranteedSupplement { get; set; } | |
| 175 | + | |
| 176 | + /// <summary> | |
| 177 | + /// 最终应发工资(取核算应发工资和保底工资的较大值) | |
| 178 | + /// </summary> | |
| 179 | + [SugarColumn(ColumnName = "F_FinalGrossSalary", DecimalDigits = 2)] | |
| 180 | + public decimal FinalGrossSalary { get; set; } | |
| 181 | + | |
| 182 | + /// <summary> | |
| 183 | + /// 当月培训补贴 | |
| 184 | + /// </summary> | |
| 185 | + [SugarColumn(ColumnName = "F_MonthlyTrainingSubsidy", DecimalDigits = 2)] | |
| 186 | + public decimal MonthlyTrainingSubsidy { get; set; } | |
| 187 | + | |
| 188 | + /// <summary> | |
| 189 | + /// 当月交通补贴 | |
| 190 | + /// </summary> | |
| 191 | + [SugarColumn(ColumnName = "F_MonthlyTransportSubsidy", DecimalDigits = 2)] | |
| 192 | + public decimal MonthlyTransportSubsidy { get; set; } | |
| 193 | + | |
| 194 | + /// <summary> | |
| 195 | + /// 上月培训补贴 | |
| 196 | + /// </summary> | |
| 197 | + [SugarColumn(ColumnName = "F_LastMonthTrainingSubsidy", DecimalDigits = 2)] | |
| 198 | + public decimal LastMonthTrainingSubsidy { get; set; } | |
| 199 | + | |
| 200 | + /// <summary> | |
| 201 | + /// 上月交通补贴 | |
| 202 | + /// </summary> | |
| 203 | + [SugarColumn(ColumnName = "F_LastMonthTransportSubsidy", DecimalDigits = 2)] | |
| 204 | + public decimal LastMonthTransportSubsidy { get; set; } | |
| 205 | + | |
| 206 | + /// <summary> | |
| 207 | + /// 补贴合计 | |
| 208 | + /// </summary> | |
| 209 | + [SugarColumn(ColumnName = "F_TotalSubsidy", DecimalDigits = 2)] | |
| 210 | + public decimal TotalSubsidy { get; set; } | |
| 211 | + | |
| 212 | + /// <summary> | |
| 213 | + /// 缺卡扣款 | |
| 214 | + /// </summary> | |
| 215 | + [SugarColumn(ColumnName = "F_MissingCard", DecimalDigits = 2)] | |
| 216 | + public decimal MissingCard { get; set; } | |
| 217 | + | |
| 218 | + /// <summary> | |
| 219 | + /// 迟到扣款 | |
| 220 | + /// </summary> | |
| 221 | + [SugarColumn(ColumnName = "F_LateArrival", DecimalDigits = 2)] | |
| 222 | + public decimal LateArrival { get; set; } | |
| 223 | + | |
| 224 | + /// <summary> | |
| 225 | + /// 请假扣款 | |
| 226 | + /// </summary> | |
| 227 | + [SugarColumn(ColumnName = "F_LeaveDeduction", DecimalDigits = 2)] | |
| 228 | + public decimal LeaveDeduction { get; set; } | |
| 229 | + | |
| 230 | + /// <summary> | |
| 231 | + /// 扣社保 | |
| 232 | + /// </summary> | |
| 233 | + [SugarColumn(ColumnName = "F_SocialInsuranceDeduction", DecimalDigits = 2)] | |
| 234 | + public decimal SocialInsuranceDeduction { get; set; } | |
| 235 | + | |
| 236 | + /// <summary> | |
| 237 | + /// 扣除奖励 | |
| 238 | + /// </summary> | |
| 239 | + [SugarColumn(ColumnName = "F_RewardDeduction", DecimalDigits = 2)] | |
| 240 | + public decimal RewardDeduction { get; set; } | |
| 241 | + | |
| 242 | + /// <summary> | |
| 243 | + /// 扣住宿费 | |
| 244 | + /// </summary> | |
| 245 | + [SugarColumn(ColumnName = "F_AccommodationDeduction", DecimalDigits = 2)] | |
| 246 | + public decimal AccommodationDeduction { get; set; } | |
| 247 | + | |
| 248 | + /// <summary> | |
| 249 | + /// 扣学习期费用 | |
| 250 | + /// </summary> | |
| 251 | + [SugarColumn(ColumnName = "F_StudyPeriodDeduction", DecimalDigits = 2)] | |
| 252 | + public decimal StudyPeriodDeduction { get; set; } | |
| 253 | + | |
| 254 | + /// <summary> | |
| 255 | + /// 扣工作服费用 | |
| 256 | + /// </summary> | |
| 257 | + [SugarColumn(ColumnName = "F_WorkClothesDeduction", DecimalDigits = 2)] | |
| 258 | + public decimal WorkClothesDeduction { get; set; } | |
| 259 | + | |
| 260 | + /// <summary> | |
| 261 | + /// 扣款合计 | |
| 262 | + /// </summary> | |
| 263 | + [SugarColumn(ColumnName = "F_TotalDeduction", DecimalDigits = 2)] | |
| 264 | + public decimal TotalDeduction { get; set; } | |
| 265 | + | |
| 266 | + /// <summary> | |
| 267 | + /// 发奖金 | |
| 268 | + /// </summary> | |
| 269 | + [SugarColumn(ColumnName = "F_Bonus", DecimalDigits = 2)] | |
| 270 | + public decimal Bonus { get; set; } | |
| 271 | + | |
| 272 | + /// <summary> | |
| 273 | + /// 退手机押金 | |
| 274 | + /// </summary> | |
| 275 | + [SugarColumn(ColumnName = "F_ReturnPhoneDeposit", DecimalDigits = 2)] | |
| 276 | + public decimal ReturnPhoneDeposit { get; set; } | |
| 277 | + | |
| 278 | + /// <summary> | |
| 279 | + /// 退住宿押金 | |
| 280 | + /// </summary> | |
| 281 | + [SugarColumn(ColumnName = "F_ReturnAccommodationDeposit", DecimalDigits = 2)] | |
| 282 | + public decimal ReturnAccommodationDeposit { get; set; } | |
| 283 | + | |
| 284 | + /// <summary> | |
| 285 | + /// 实发工资(最终应发工资 - 扣款合计 + 补贴合计 + 奖金) | |
| 286 | + /// </summary> | |
| 287 | + [SugarColumn(ColumnName = "F_ActualSalary", DecimalDigits = 2)] | |
| 288 | + public decimal ActualSalary { get; set; } | |
| 289 | + | |
| 290 | + /// <summary> | |
| 291 | + /// 当月是否发放 | |
| 292 | + /// </summary> | |
| 293 | + [SugarColumn(ColumnName = "F_MonthlyPaymentStatus")] | |
| 294 | + public string MonthlyPaymentStatus { get; set; } | |
| 295 | + | |
| 296 | + /// <summary> | |
| 297 | + /// 支付金额 | |
| 298 | + /// </summary> | |
| 299 | + [SugarColumn(ColumnName = "F_PaidAmount", DecimalDigits = 2)] | |
| 300 | + public decimal PaidAmount { get; set; } | |
| 301 | + | |
| 302 | + /// <summary> | |
| 303 | + /// 待支付金额 | |
| 304 | + /// </summary> | |
| 305 | + [SugarColumn(ColumnName = "F_PendingAmount", DecimalDigits = 2)] | |
| 306 | + public decimal PendingAmount { get; set; } | |
| 307 | + | |
| 308 | + /// <summary> | |
| 309 | + /// 补发上月 | |
| 310 | + /// </summary> | |
| 311 | + [SugarColumn(ColumnName = "F_LastMonthSupplement", DecimalDigits = 2)] | |
| 312 | + public decimal LastMonthSupplement { get; set; } | |
| 313 | + | |
| 314 | + /// <summary> | |
| 315 | + /// 当月支付总额 | |
| 316 | + /// </summary> | |
| 317 | + [SugarColumn(ColumnName = "F_MonthlyTotalPayment", DecimalDigits = 2)] | |
| 318 | + public decimal MonthlyTotalPayment { get; set; } | |
| 319 | + | |
| 320 | + /// <summary> | |
| 321 | + /// 是否锁定(0未锁定,1已锁定) | |
| 322 | + /// </summary> | |
| 323 | + [SugarColumn(ColumnName = "F_IsLocked")] | |
| 324 | + public int IsLocked { get; set; } | |
| 325 | + | |
| 326 | + /// <summary> | |
| 327 | + /// 是否离职(0=在职,1=离职) | |
| 328 | + /// </summary> | |
| 329 | + [SugarColumn(ColumnName = "F_IsTerminated")] | |
| 330 | + public int IsTerminated { get; set; } | |
| 331 | + | |
| 332 | + /// <summary> | |
| 333 | + /// 创建时间 | |
| 334 | + /// </summary> | |
| 335 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 336 | + public DateTime CreateTime { get; set; } | |
| 337 | + | |
| 338 | + /// <summary> | |
| 339 | + /// 更新时间 | |
| 340 | + /// </summary> | |
| 341 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 342 | + public DateTime UpdateTime { get; set; } | |
| 343 | + | |
| 344 | + /// <summary> | |
| 345 | + /// 创建人 | |
| 346 | + /// </summary> | |
| 347 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 348 | + public string CreateUser { get; set; } | |
| 349 | + | |
| 350 | + /// <summary> | |
| 351 | + /// 更新人 | |
| 352 | + /// </summary> | |
| 353 | + [SugarColumn(ColumnName = "F_UpdateUser")] | |
| 354 | + public string UpdateUser { get; set; } | |
| 355 | + | |
| 356 | + /// <summary> | |
| 357 | + /// 是否新店 | |
| 358 | + /// </summary> | |
| 359 | + [SugarColumn(ColumnName = "F_IsNewStore")] | |
| 360 | + public string IsNewStore { get; set; } | |
| 361 | + | |
| 362 | + /// <summary> | |
| 363 | + /// 新店保护阶段 | |
| 364 | + /// </summary> | |
| 365 | + [SugarColumn(ColumnName = "F_NewStoreProtectionStage")] | |
| 366 | + public int NewStoreProtectionStage { get; set; } | |
| 367 | + | |
| 368 | + /// <summary> | |
| 369 | + /// 门店类型 | |
| 370 | + /// </summary> | |
| 371 | + [SugarColumn(ColumnName = "F_StoreType")] | |
| 372 | + public int? StoreType { get; set; } | |
| 373 | + | |
| 374 | + /// <summary> | |
| 375 | + /// 门店类别 | |
| 376 | + /// </summary> | |
| 377 | + [SugarColumn(ColumnName = "F_StoreCategory")] | |
| 378 | + public int? StoreCategory { get; set; } | |
| 379 | + } | |
| 380 | +} | |
| 381 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_md_major_project_teacher_assignment/LqMdMajorProjectTeacherAssignmentEntity.cs
| ... | ... | @@ -42,6 +42,12 @@ namespace NCC.Extend.Entitys.lq_md_major_project_teacher_assignment |
| 42 | 42 | public string TeacherId { get; set; } |
| 43 | 43 | |
| 44 | 44 | /// <summary> |
| 45 | + /// 教育部老师用户ID | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_EducationTeacherId")] | |
| 48 | + public string EducationTeacherId { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 45 | 51 | /// 备注说明 |
| 46 | 52 | /// </summary> |
| 47 | 53 | [SugarColumn(ColumnName = "F_Remark")] | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_jksyj/LqXhJksyjEntity.cs
| ... | ... | @@ -154,5 +154,11 @@ namespace NCC.Extend.Entitys.lq_xh_jksyj |
| 154 | 154 | /// </summary> |
| 155 | 155 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 156 | 156 | public string PerformanceType { get; set; } |
| 157 | + | |
| 158 | + /// <summary> | |
| 159 | + /// 科美类型(来源:lq_xmzl.F_BeautyType) | |
| 160 | + /// </summary> | |
| 161 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 162 | + public string BeautyType { get; set; } | |
| 157 | 163 | } |
| 158 | 164 | } |
| 159 | 165 | \ No newline at end of file | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_kjbsyj/LqXhKjbsyjEntity.cs
| ... | ... | @@ -136,5 +136,11 @@ namespace NCC.Extend.Entitys.lq_xh_kjbsyj |
| 136 | 136 | /// </summary> |
| 137 | 137 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 138 | 138 | public string PerformanceType { get; set; } |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 科美类型(来源:lq_xmzl.F_BeautyType) | |
| 142 | + /// </summary> | |
| 143 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 144 | + public string BeautyType { get; set; } | |
| 139 | 145 | } |
| 140 | 146 | } |
| 141 | 147 | \ No newline at end of file | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_pxmx/LqXhPxmxEntity.cs
| ... | ... | @@ -112,5 +112,11 @@ namespace NCC.Extend.Entitys.lq_xh_pxmx |
| 112 | 112 | /// </summary> |
| 113 | 113 | [SugarColumn(ColumnName = "F_PerformanceType")] |
| 114 | 114 | public string PerformanceType { get; set; } |
| 115 | + | |
| 116 | + /// <summary> | |
| 117 | + /// 科技部归类 | |
| 118 | + /// </summary> | |
| 119 | + [SugarColumn(ColumnName = "F_BeautyType")] | |
| 120 | + public string BeautyType { get; set; } | |
| 115 | 121 | } |
| 116 | 122 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/FileUrlMigrationService.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | +using System.Linq; | |
| 4 | +using System.Text.RegularExpressions; | |
| 5 | +using System.Threading.Tasks; | |
| 6 | +using Microsoft.AspNetCore.Mvc; | |
| 7 | +using Microsoft.Extensions.Logging; | |
| 8 | +using NCC.Common.Extension; | |
| 9 | +using NCC.Dependency; | |
| 10 | +using NCC.DynamicApiController; | |
| 11 | +using NCC.Extend.Entitys.lq_kd_kdjlb; | |
| 12 | +using NCC.FriendlyException; | |
| 13 | +using Newtonsoft.Json; | |
| 14 | +using Newtonsoft.Json.Linq; | |
| 15 | +using SqlSugar; | |
| 16 | + | |
| 17 | +namespace NCC.Extend | |
| 18 | +{ | |
| 19 | + /// <summary> | |
| 20 | + /// 文件URL迁移服务 - 将本地文件路径转换为OSS路径 | |
| 21 | + /// </summary> | |
| 22 | + [ApiDescriptionSettings(Tag = "数据迁移", Name = "FileUrlMigration", Order = 999)] | |
| 23 | + [Route("api/Extend/[controller]")] | |
| 24 | + public class FileUrlMigrationService : IDynamicApiController, ITransient | |
| 25 | + { | |
| 26 | + private readonly ISqlSugarRepository<LqKdKdjlbEntity> _repository; | |
| 27 | + private readonly SqlSugarScope _db; | |
| 28 | + private readonly ILogger<FileUrlMigrationService> _logger; | |
| 29 | + | |
| 30 | + private const string OSS_BASE_URL = "http://oss.lvqianmeiye.com/Files/SystemFile"; | |
| 31 | + private const string OLD_URL_PREFIX = "/api/File/Image/annexpic/"; | |
| 32 | + private const string OLD_OSS_URL = "http://oss.lvqianmeiye.com"; | |
| 33 | + private const string NEW_OSS_URL = "https://lvqian-erip.oss-cn-chengdu.aliyuncs.com"; | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 初始化一个<see cref="FileUrlMigrationService"/>类型的新实例 | |
| 37 | + /// </summary> | |
| 38 | + /// <param name="repository">SqlSugar仓储</param> | |
| 39 | + /// <param name="logger">日志记录器</param> | |
| 40 | + public FileUrlMigrationService(ISqlSugarRepository<LqKdKdjlbEntity> repository, ILogger<FileUrlMigrationService> logger) | |
| 41 | + { | |
| 42 | + _repository = repository; | |
| 43 | + _db = _repository.Context; | |
| 44 | + _logger = logger; | |
| 45 | + } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 迁移所有表的文件URL路径 | |
| 49 | + /// </summary> | |
| 50 | + /// <remarks> | |
| 51 | + /// 将数据库中的文件URL从本地路径格式转换为OSS路径格式 | |
| 52 | + /// | |
| 53 | + /// 处理的表和字段: | |
| 54 | + /// 1. lq_kd_kdjlb: scwj, hyqz, F_FIleUrl | |
| 55 | + /// 2. lq_xh_feedback: F_BeforeImage, F_AfterImage | |
| 56 | + /// 3. lq_hytk_hytk: F_FileUrl, F_SignatureFile | |
| 57 | + /// 4. lq_purchase_records: F_Attachment | |
| 58 | + /// | |
| 59 | + /// URL转换规则: | |
| 60 | + /// 原格式:/api/File/Image/annexpic/文件名 | |
| 61 | + /// 新格式:http://oss.lvqianmeiye.com/Files/SystemFile/文件名 | |
| 62 | + /// </remarks> | |
| 63 | + /// <returns>迁移结果统计</returns> | |
| 64 | + [HttpPost("MigrateAllFileUrls")] | |
| 65 | + public async Task<dynamic> MigrateAllFileUrls() | |
| 66 | + { | |
| 67 | + var startTime = DateTime.Now; | |
| 68 | + _logger.LogInformation("开始迁移文件URL路径..."); | |
| 69 | + | |
| 70 | + var totalResults = new | |
| 71 | + { | |
| 72 | + lq_kd_kdjlb_scwj = await MigrateTableField("lq_kd_kdjlb", "scwj", "F_Id"), | |
| 73 | + lq_kd_kdjlb_hyqz = await MigrateTableField("lq_kd_kdjlb", "hyqz", "F_Id"), | |
| 74 | + lq_kd_kdjlb_F_FIleUrl = await MigrateTableField("lq_kd_kdjlb", "F_FIleUrl", "F_Id"), | |
| 75 | + lq_xh_feedback_F_BeforeImage = await MigrateTableField("lq_xh_feedback", "F_BeforeImage", "F_Id"), | |
| 76 | + lq_xh_feedback_F_AfterImage = await MigrateTableField("lq_xh_feedback", "F_AfterImage", "F_Id"), | |
| 77 | + lq_hytk_hytk_F_FileUrl = await MigrateTableField("lq_hytk_hytk", "F_FileUrl", "F_Id"), | |
| 78 | + lq_hytk_hytk_F_SignatureFile = await MigrateTableField("lq_hytk_hytk", "F_SignatureFile", "F_Id"), | |
| 79 | + lq_purchase_records_F_Attachment = await MigrateTableField("lq_purchase_records", "F_Attachment", "F_Id") | |
| 80 | + }; | |
| 81 | + | |
| 82 | + var endTime = DateTime.Now; | |
| 83 | + var duration = (endTime - startTime).TotalSeconds; | |
| 84 | + | |
| 85 | + // 统计总数 | |
| 86 | + var totalProcessed = 0; | |
| 87 | + var totalUpdated = 0; | |
| 88 | + var totalErrors = 0; | |
| 89 | + | |
| 90 | + var resultsList = new List<MigrationResult> | |
| 91 | + { | |
| 92 | + totalResults.lq_kd_kdjlb_scwj, | |
| 93 | + totalResults.lq_kd_kdjlb_hyqz, | |
| 94 | + totalResults.lq_kd_kdjlb_F_FIleUrl, | |
| 95 | + totalResults.lq_xh_feedback_F_BeforeImage, | |
| 96 | + totalResults.lq_xh_feedback_F_AfterImage, | |
| 97 | + totalResults.lq_hytk_hytk_F_FileUrl, | |
| 98 | + totalResults.lq_hytk_hytk_F_SignatureFile, | |
| 99 | + totalResults.lq_purchase_records_F_Attachment | |
| 100 | + }; | |
| 101 | + | |
| 102 | + foreach (var result in resultsList) | |
| 103 | + { | |
| 104 | + totalProcessed += result.TotalProcessed; | |
| 105 | + totalUpdated += result.TotalUpdated; | |
| 106 | + totalErrors += result.TotalErrors; | |
| 107 | + } | |
| 108 | + | |
| 109 | + _logger.LogInformation($"文件URL迁移完成,耗时:{duration:F2}秒,总处理:{totalProcessed},总更新:{totalUpdated},总错误:{totalErrors}"); | |
| 110 | + | |
| 111 | + return new | |
| 112 | + { | |
| 113 | + success = true, | |
| 114 | + duration = $"{duration:F2}秒", | |
| 115 | + summary = new | |
| 116 | + { | |
| 117 | + totalProcessed, | |
| 118 | + totalUpdated, | |
| 119 | + totalErrors | |
| 120 | + }, | |
| 121 | + details = totalResults | |
| 122 | + }; | |
| 123 | + } | |
| 124 | + | |
| 125 | + /// <summary> | |
| 126 | + /// 迁移指定表的指定字段 | |
| 127 | + /// </summary> | |
| 128 | + private async Task<MigrationResult> MigrateTableField(string tableName, string fieldName, string primaryKey) | |
| 129 | + { | |
| 130 | + var result = new MigrationResult | |
| 131 | + { | |
| 132 | + TableName = tableName, | |
| 133 | + FieldName = fieldName | |
| 134 | + }; | |
| 135 | + | |
| 136 | + try | |
| 137 | + { | |
| 138 | + _logger.LogInformation($"开始处理表 {tableName}.{fieldName}"); | |
| 139 | + | |
| 140 | + // 查询需要处理的数据 | |
| 141 | + var sql = $@" | |
| 142 | + SELECT {primaryKey} as Id, {fieldName} as FieldValue | |
| 143 | + FROM {tableName} | |
| 144 | + WHERE {fieldName} IS NOT NULL | |
| 145 | + AND {fieldName} != '' | |
| 146 | + AND {fieldName} != '[]' | |
| 147 | + AND {fieldName} LIKE '%{OLD_URL_PREFIX}%'"; | |
| 148 | + | |
| 149 | + var records = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 150 | + result.TotalProcessed = records.Count; | |
| 151 | + | |
| 152 | + if (records.Count == 0) | |
| 153 | + { | |
| 154 | + _logger.LogInformation($"表 {tableName}.{fieldName} 没有需要处理的数据"); | |
| 155 | + return result; | |
| 156 | + } | |
| 157 | + | |
| 158 | + _logger.LogInformation($"表 {tableName}.{fieldName} 找到 {records.Count} 条需要处理的数据"); | |
| 159 | + | |
| 160 | + // 分批处理,每批100条 | |
| 161 | + const int batchSize = 100; | |
| 162 | + var totalBatches = (int)Math.Ceiling((double)records.Count / batchSize); | |
| 163 | + | |
| 164 | + for (int batchIndex = 0; batchIndex < totalBatches; batchIndex++) | |
| 165 | + { | |
| 166 | + var batch = records.Skip(batchIndex * batchSize).Take(batchSize).ToList(); | |
| 167 | + var updateList = new List<(string id, string newValue)>(); | |
| 168 | + | |
| 169 | + foreach (var record in batch) | |
| 170 | + { | |
| 171 | + try | |
| 172 | + { | |
| 173 | + var id = record.Id?.ToString(); | |
| 174 | + var fieldValue = record.FieldValue?.ToString(); | |
| 175 | + | |
| 176 | + if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(fieldValue)) | |
| 177 | + continue; | |
| 178 | + | |
| 179 | + var newValue = ConvertFileUrlJson(fieldValue); | |
| 180 | + if (newValue != null && newValue != fieldValue) | |
| 181 | + { | |
| 182 | + updateList.Add((id, newValue)); | |
| 183 | + } | |
| 184 | + } | |
| 185 | + catch (Exception ex) | |
| 186 | + { | |
| 187 | + result.TotalErrors++; | |
| 188 | + _logger.LogError(ex, $"处理记录时出错,表:{tableName},字段:{fieldName},记录ID:{record.Id}"); | |
| 189 | + } | |
| 190 | + } | |
| 191 | + | |
| 192 | + // 批量更新 | |
| 193 | + if (updateList.Any()) | |
| 194 | + { | |
| 195 | + try | |
| 196 | + { | |
| 197 | + _db.BeginTran(); | |
| 198 | + foreach (var (id, newValue) in updateList) | |
| 199 | + { | |
| 200 | + // 使用参数化查询防止SQL注入 | |
| 201 | + var updateSql = $"UPDATE `{tableName}` SET `{fieldName}` = @Value WHERE `{primaryKey}` = @Id"; | |
| 202 | + await _db.Ado.ExecuteCommandAsync(updateSql, new { Value = newValue, Id = id }); | |
| 203 | + } | |
| 204 | + _db.CommitTran(); | |
| 205 | + result.TotalUpdated += updateList.Count; | |
| 206 | + _logger.LogInformation($"表 {tableName}.{fieldName} 批次 {batchIndex + 1} 成功更新 {updateList.Count} 条记录"); | |
| 207 | + } | |
| 208 | + catch (Exception ex) | |
| 209 | + { | |
| 210 | + _db.RollbackTran(); | |
| 211 | + result.TotalErrors += updateList.Count; | |
| 212 | + _logger.LogError(ex, $"批量更新时出错,表:{tableName},字段:{fieldName},批次:{batchIndex + 1}"); | |
| 213 | + } | |
| 214 | + } | |
| 215 | + | |
| 216 | + _logger.LogInformation($"表 {tableName}.{fieldName} 批次 {batchIndex + 1}/{totalBatches} 处理完成"); | |
| 217 | + } | |
| 218 | + | |
| 219 | + _logger.LogInformation($"表 {tableName}.{fieldName} 处理完成,处理:{result.TotalProcessed},更新:{result.TotalUpdated},错误:{result.TotalErrors}"); | |
| 220 | + } | |
| 221 | + catch (Exception ex) | |
| 222 | + { | |
| 223 | + _logger.LogError(ex, $"处理表 {tableName}.{fieldName} 时发生错误"); | |
| 224 | + result.TotalErrors = result.TotalProcessed; | |
| 225 | + } | |
| 226 | + | |
| 227 | + return result; | |
| 228 | + } | |
| 229 | + | |
| 230 | + /// <summary> | |
| 231 | + /// 转换文件URL JSON字符串 | |
| 232 | + /// </summary> | |
| 233 | + private string ConvertFileUrlJson(string jsonString) | |
| 234 | + { | |
| 235 | + try | |
| 236 | + { | |
| 237 | + if (string.IsNullOrWhiteSpace(jsonString) || jsonString.Trim() == "[]") | |
| 238 | + return jsonString; | |
| 239 | + | |
| 240 | + // 解析JSON数组 | |
| 241 | + var jsonArray = JArray.Parse(jsonString); | |
| 242 | + bool hasChanges = false; | |
| 243 | + | |
| 244 | + foreach (var item in jsonArray) | |
| 245 | + { | |
| 246 | + if (item is JObject obj && obj["url"] != null) | |
| 247 | + { | |
| 248 | + var url = obj["url"].ToString(); | |
| 249 | + if (url.StartsWith(OLD_URL_PREFIX)) | |
| 250 | + { | |
| 251 | + // 提取文件名 | |
| 252 | + var fileName = url.Substring(OLD_URL_PREFIX.Length); | |
| 253 | + // 构建新的OSS URL | |
| 254 | + var newUrl = $"{OSS_BASE_URL}/{fileName}"; | |
| 255 | + obj["url"] = newUrl; | |
| 256 | + hasChanges = true; | |
| 257 | + } | |
| 258 | + } | |
| 259 | + } | |
| 260 | + | |
| 261 | + return hasChanges ? jsonArray.ToString(Formatting.None) : jsonString; | |
| 262 | + } | |
| 263 | + catch (Exception ex) | |
| 264 | + { | |
| 265 | + _logger.LogError(ex, $"转换JSON时出错:{jsonString}"); | |
| 266 | + return jsonString; // 转换失败时返回原值 | |
| 267 | + } | |
| 268 | + } | |
| 269 | + | |
| 270 | + /// <summary> | |
| 271 | + /// 迁移所有表的OSS URL(将旧OSS地址替换为新OSS地址) | |
| 272 | + /// </summary> | |
| 273 | + /// <remarks> | |
| 274 | + /// 将数据库中的文件URL从旧OSS地址格式转换为新OSS地址格式 | |
| 275 | + /// | |
| 276 | + /// 处理的表和字段: | |
| 277 | + /// 1. lq_kd_kdjlb: scwj, hyqz, F_FIleUrl | |
| 278 | + /// 2. lq_xh_feedback: F_BeforeImage, F_AfterImage | |
| 279 | + /// 3. lq_hytk_hytk: F_FileUrl, F_SignatureFile | |
| 280 | + /// 4. lq_purchase_records: F_Attachment | |
| 281 | + /// | |
| 282 | + /// URL转换规则: | |
| 283 | + /// 原格式:http://oss.lvqianmeiye.com/... | |
| 284 | + /// 新格式:https://lvqian-erip.oss-cn-chengdu.aliyuncs.com/... | |
| 285 | + /// </remarks> | |
| 286 | + /// <returns>迁移结果统计</returns> | |
| 287 | + [HttpPost("MigrateOssUrls")] | |
| 288 | + public async Task<dynamic> MigrateOssUrls() | |
| 289 | + { | |
| 290 | + var startTime = DateTime.Now; | |
| 291 | + _logger.LogInformation("开始迁移OSS URL路径..."); | |
| 292 | + | |
| 293 | + var totalResults = new | |
| 294 | + { | |
| 295 | + lq_kd_kdjlb_scwj = await MigrateOssUrlInTableField("lq_kd_kdjlb", "scwj", "F_Id"), | |
| 296 | + lq_kd_kdjlb_hyqz = await MigrateOssUrlInTableField("lq_kd_kdjlb", "hyqz", "F_Id"), | |
| 297 | + lq_kd_kdjlb_F_FIleUrl = await MigrateOssUrlInTableField("lq_kd_kdjlb", "F_FIleUrl", "F_Id"), | |
| 298 | + lq_xh_feedback_F_BeforeImage = await MigrateOssUrlInTableField("lq_xh_feedback", "F_BeforeImage", "F_Id"), | |
| 299 | + lq_xh_feedback_F_AfterImage = await MigrateOssUrlInTableField("lq_xh_feedback", "F_AfterImage", "F_Id"), | |
| 300 | + lq_hytk_hytk_F_FileUrl = await MigrateOssUrlInTableField("lq_hytk_hytk", "F_FileUrl", "F_Id"), | |
| 301 | + lq_hytk_hytk_F_SignatureFile = await MigrateOssUrlInTableField("lq_hytk_hytk", "F_SignatureFile", "F_Id"), | |
| 302 | + lq_purchase_records_F_Attachment = await MigrateOssUrlInTableField("lq_purchase_records", "F_Attachment", "F_Id") | |
| 303 | + }; | |
| 304 | + | |
| 305 | + var endTime = DateTime.Now; | |
| 306 | + var duration = (endTime - startTime).TotalSeconds; | |
| 307 | + | |
| 308 | + // 统计总数 | |
| 309 | + var totalProcessed = 0; | |
| 310 | + var totalUpdated = 0; | |
| 311 | + var totalErrors = 0; | |
| 312 | + | |
| 313 | + var resultsList = new List<MigrationResult> | |
| 314 | + { | |
| 315 | + totalResults.lq_kd_kdjlb_scwj, | |
| 316 | + totalResults.lq_kd_kdjlb_hyqz, | |
| 317 | + totalResults.lq_kd_kdjlb_F_FIleUrl, | |
| 318 | + totalResults.lq_xh_feedback_F_BeforeImage, | |
| 319 | + totalResults.lq_xh_feedback_F_AfterImage, | |
| 320 | + totalResults.lq_hytk_hytk_F_FileUrl, | |
| 321 | + totalResults.lq_hytk_hytk_F_SignatureFile, | |
| 322 | + totalResults.lq_purchase_records_F_Attachment | |
| 323 | + }; | |
| 324 | + | |
| 325 | + foreach (var result in resultsList) | |
| 326 | + { | |
| 327 | + totalProcessed += result.TotalProcessed; | |
| 328 | + totalUpdated += result.TotalUpdated; | |
| 329 | + totalErrors += result.TotalErrors; | |
| 330 | + } | |
| 331 | + | |
| 332 | + _logger.LogInformation($"OSS URL迁移完成,耗时:{duration:F2}秒,总处理:{totalProcessed},总更新:{totalUpdated},总错误:{totalErrors}"); | |
| 333 | + | |
| 334 | + return new | |
| 335 | + { | |
| 336 | + success = true, | |
| 337 | + duration = $"{duration:F2}秒", | |
| 338 | + summary = new | |
| 339 | + { | |
| 340 | + totalProcessed, | |
| 341 | + totalUpdated, | |
| 342 | + totalErrors | |
| 343 | + }, | |
| 344 | + details = totalResults | |
| 345 | + }; | |
| 346 | + } | |
| 347 | + | |
| 348 | + /// <summary> | |
| 349 | + /// 迁移指定表的指定字段中的OSS URL | |
| 350 | + /// </summary> | |
| 351 | + private async Task<MigrationResult> MigrateOssUrlInTableField(string tableName, string fieldName, string primaryKey) | |
| 352 | + { | |
| 353 | + var result = new MigrationResult | |
| 354 | + { | |
| 355 | + TableName = tableName, | |
| 356 | + FieldName = fieldName | |
| 357 | + }; | |
| 358 | + | |
| 359 | + try | |
| 360 | + { | |
| 361 | + _logger.LogInformation($"开始处理表 {tableName}.{fieldName} 的OSS URL迁移"); | |
| 362 | + | |
| 363 | + // 查询需要处理的数据(包含旧OSS URL的记录) | |
| 364 | + var sql = $@" | |
| 365 | + SELECT {primaryKey} as Id, {fieldName} as FieldValue | |
| 366 | + FROM {tableName} | |
| 367 | + WHERE {fieldName} IS NOT NULL | |
| 368 | + AND {fieldName} != '' | |
| 369 | + AND {fieldName} != '[]' | |
| 370 | + AND {fieldName} LIKE '%{OLD_OSS_URL}%'"; | |
| 371 | + | |
| 372 | + var records = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 373 | + result.TotalProcessed = records.Count; | |
| 374 | + | |
| 375 | + if (records.Count == 0) | |
| 376 | + { | |
| 377 | + _logger.LogInformation($"表 {tableName}.{fieldName} 没有需要处理的数据"); | |
| 378 | + return result; | |
| 379 | + } | |
| 380 | + | |
| 381 | + _logger.LogInformation($"表 {tableName}.{fieldName} 找到 {records.Count} 条需要处理的数据"); | |
| 382 | + | |
| 383 | + // 分批处理,每批100条 | |
| 384 | + const int batchSize = 100; | |
| 385 | + var totalBatches = (int)Math.Ceiling((double)records.Count / batchSize); | |
| 386 | + | |
| 387 | + for (int batchIndex = 0; batchIndex < totalBatches; batchIndex++) | |
| 388 | + { | |
| 389 | + var batch = records.Skip(batchIndex * batchSize).Take(batchSize).ToList(); | |
| 390 | + var updateList = new List<(string id, string newValue)>(); | |
| 391 | + | |
| 392 | + foreach (var record in batch) | |
| 393 | + { | |
| 394 | + try | |
| 395 | + { | |
| 396 | + var id = record.Id?.ToString(); | |
| 397 | + var fieldValue = record.FieldValue?.ToString(); | |
| 398 | + | |
| 399 | + if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(fieldValue)) | |
| 400 | + continue; | |
| 401 | + | |
| 402 | + var newValue = ReplaceOssUrlInJson(fieldValue); | |
| 403 | + if (newValue != null && newValue != fieldValue) | |
| 404 | + { | |
| 405 | + updateList.Add((id, newValue)); | |
| 406 | + } | |
| 407 | + } | |
| 408 | + catch (Exception ex) | |
| 409 | + { | |
| 410 | + result.TotalErrors++; | |
| 411 | + _logger.LogError(ex, $"处理记录时出错,表:{tableName},字段:{fieldName},记录ID:{record.Id}"); | |
| 412 | + } | |
| 413 | + } | |
| 414 | + | |
| 415 | + // 批量更新 | |
| 416 | + if (updateList.Any()) | |
| 417 | + { | |
| 418 | + try | |
| 419 | + { | |
| 420 | + _db.BeginTran(); | |
| 421 | + foreach (var (id, newValue) in updateList) | |
| 422 | + { | |
| 423 | + // 使用参数化查询防止SQL注入 | |
| 424 | + var updateSql = $"UPDATE `{tableName}` SET `{fieldName}` = @Value WHERE `{primaryKey}` = @Id"; | |
| 425 | + await _db.Ado.ExecuteCommandAsync(updateSql, new { Value = newValue, Id = id }); | |
| 426 | + } | |
| 427 | + _db.CommitTran(); | |
| 428 | + result.TotalUpdated += updateList.Count; | |
| 429 | + _logger.LogInformation($"表 {tableName}.{fieldName} 批次 {batchIndex + 1} 成功更新 {updateList.Count} 条记录"); | |
| 430 | + } | |
| 431 | + catch (Exception ex) | |
| 432 | + { | |
| 433 | + _db.RollbackTran(); | |
| 434 | + result.TotalErrors += updateList.Count; | |
| 435 | + _logger.LogError(ex, $"批量更新时出错,表:{tableName},字段:{fieldName},批次:{batchIndex + 1}"); | |
| 436 | + } | |
| 437 | + } | |
| 438 | + | |
| 439 | + _logger.LogInformation($"表 {tableName}.{fieldName} 批次 {batchIndex + 1}/{totalBatches} 处理完成"); | |
| 440 | + } | |
| 441 | + | |
| 442 | + _logger.LogInformation($"表 {tableName}.{fieldName} 处理完成,处理:{result.TotalProcessed},更新:{result.TotalUpdated},错误:{result.TotalErrors}"); | |
| 443 | + } | |
| 444 | + catch (Exception ex) | |
| 445 | + { | |
| 446 | + _logger.LogError(ex, $"处理表 {tableName}.{fieldName} 时发生错误"); | |
| 447 | + result.TotalErrors = result.TotalProcessed; | |
| 448 | + } | |
| 449 | + | |
| 450 | + return result; | |
| 451 | + } | |
| 452 | + | |
| 453 | + /// <summary> | |
| 454 | + /// 替换JSON字符串中的OSS URL | |
| 455 | + /// </summary> | |
| 456 | + private string ReplaceOssUrlInJson(string jsonString) | |
| 457 | + { | |
| 458 | + try | |
| 459 | + { | |
| 460 | + if (string.IsNullOrWhiteSpace(jsonString) || jsonString.Trim() == "[]") | |
| 461 | + return jsonString; | |
| 462 | + | |
| 463 | + // 如果包含旧OSS URL,直接替换 | |
| 464 | + if (jsonString.Contains(OLD_OSS_URL)) | |
| 465 | + { | |
| 466 | + var newJsonString = jsonString.Replace(OLD_OSS_URL, NEW_OSS_URL); | |
| 467 | + | |
| 468 | + // 如果是JSON数组格式,需要解析并替换 | |
| 469 | + try | |
| 470 | + { | |
| 471 | + var jsonArray = JArray.Parse(jsonString); | |
| 472 | + bool hasChanges = false; | |
| 473 | + | |
| 474 | + foreach (var item in jsonArray) | |
| 475 | + { | |
| 476 | + if (item is JObject obj && obj["url"] != null) | |
| 477 | + { | |
| 478 | + var url = obj["url"].ToString(); | |
| 479 | + if (url.Contains(OLD_OSS_URL)) | |
| 480 | + { | |
| 481 | + obj["url"] = url.Replace(OLD_OSS_URL, NEW_OSS_URL); | |
| 482 | + hasChanges = true; | |
| 483 | + } | |
| 484 | + } | |
| 485 | + } | |
| 486 | + | |
| 487 | + return hasChanges ? jsonArray.ToString(Formatting.None) : jsonString; | |
| 488 | + } | |
| 489 | + catch | |
| 490 | + { | |
| 491 | + // 如果不是JSON格式,直接替换字符串 | |
| 492 | + return newJsonString; | |
| 493 | + } | |
| 494 | + } | |
| 495 | + | |
| 496 | + return jsonString; | |
| 497 | + } | |
| 498 | + catch (Exception ex) | |
| 499 | + { | |
| 500 | + _logger.LogError(ex, $"替换OSS URL时出错:{jsonString}"); | |
| 501 | + return jsonString; // 转换失败时返回原值 | |
| 502 | + } | |
| 503 | + } | |
| 504 | + | |
| 505 | + /// <summary> | |
| 506 | + /// 迁移结果 | |
| 507 | + /// </summary> | |
| 508 | + private class MigrationResult | |
| 509 | + { | |
| 510 | + public string TableName { get; set; } | |
| 511 | + public string FieldName { get; set; } | |
| 512 | + public int TotalProcessed { get; set; } | |
| 513 | + public int TotalUpdated { get; set; } | |
| 514 | + public int TotalErrors { get; set; } | |
| 515 | + } | |
| 516 | + } | |
| 517 | +} | |
| 518 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqBusinessUnitManagerSalaryService.cs
0 → 100644
| 1 | +using Microsoft.AspNetCore.Authorization; | |
| 2 | +using Microsoft.AspNetCore.Mvc; | |
| 3 | +using NCC.Common.Filter; | |
| 4 | +using NCC.Common.Helper; | |
| 5 | +using NCC.Dependency; | |
| 6 | +using NCC.DynamicApiController; | |
| 7 | +using NCC.Extend.Entitys.Dto.LqBusinessUnitManagerSalary; | |
| 8 | +using NCC.Extend.Entitys.lq_attendance_summary; | |
| 9 | +using NCC.Extend.Entitys.lq_hytk_hytk; | |
| 10 | +using NCC.Extend.Entitys.lq_kd_kdjlb; | |
| 11 | +using NCC.Extend.Entitys.lq_md_general_manager_lifeline; | |
| 12 | +using NCC.Extend.Entitys.lq_md_target; | |
| 13 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 14 | +using NCC.Extend.Entitys.lq_business_unit_manager_salary_statistics; | |
| 15 | +using NCC.System.Entitys.Permission; | |
| 16 | +using SqlSugar; | |
| 17 | +using System; | |
| 18 | +using System.Collections.Generic; | |
| 19 | +using System.Linq; | |
| 20 | +using System.Threading.Tasks; | |
| 21 | +using Yitter.IdGenerator; | |
| 22 | + | |
| 23 | +namespace NCC.Extend | |
| 24 | +{ | |
| 25 | + /// <summary> | |
| 26 | + /// 事业部总经理/经理薪酬服务 | |
| 27 | + /// </summary> | |
| 28 | + [ApiDescriptionSettings(Tag = "事业部总经理/经理薪酬服务", Name = "LqBusinessUnitManagerSalary", Order = 304)] | |
| 29 | + [Route("api/Extend/[controller]")] | |
| 30 | + public class LqBusinessUnitManagerSalaryService : IDynamicApiController, ITransient | |
| 31 | + { | |
| 32 | + private readonly ISqlSugarClient _db; | |
| 33 | + | |
| 34 | + /// <summary> | |
| 35 | + /// 初始化一个<see cref="LqBusinessUnitManagerSalaryService"/>类型的新实例 | |
| 36 | + /// </summary> | |
| 37 | + public LqBusinessUnitManagerSalaryService(ISqlSugarClient db) | |
| 38 | + { | |
| 39 | + _db = db; | |
| 40 | + } | |
| 41 | + | |
| 42 | + /// <summary> | |
| 43 | + /// 获取事业部总经理/经理工资列表 | |
| 44 | + /// </summary> | |
| 45 | + /// <param name="input">查询参数</param> | |
| 46 | + /// <returns>事业部总经理/经理工资分页列表</returns> | |
| 47 | + [HttpGet("business-unit-manager")] | |
| 48 | + public async Task<dynamic> GetBusinessUnitManagerSalaryList([FromQuery] BusinessUnitManagerSalaryInput input) | |
| 49 | + { | |
| 50 | + var monthStr = $"{input.Year}{input.Month:D2}"; | |
| 51 | + | |
| 52 | + // 1. 检查当月是否已生成工资数据 | |
| 53 | + var exists = await _db.Queryable<LqBusinessUnitManagerSalaryStatisticsEntity>() | |
| 54 | + .AnyAsync(x => x.StatisticsMonth == monthStr); | |
| 55 | + | |
| 56 | + // 2. 如果没有数据,则进行计算 | |
| 57 | + if (!exists) | |
| 58 | + { | |
| 59 | + await CalculateBusinessUnitManagerSalary(input.Year, input.Month); | |
| 60 | + } | |
| 61 | + | |
| 62 | + // 3. 查询数据 | |
| 63 | + var query = _db.Queryable<LqBusinessUnitManagerSalaryStatisticsEntity>() | |
| 64 | + .Where(x => x.StatisticsMonth == monthStr); | |
| 65 | + | |
| 66 | + if (input.ManagerType.HasValue) | |
| 67 | + { | |
| 68 | + query = query.Where(x => x.ManagerType == input.ManagerType.Value); | |
| 69 | + } | |
| 70 | + | |
| 71 | + if (!string.IsNullOrEmpty(input.Keyword)) | |
| 72 | + { | |
| 73 | + query = query.Where(x => x.EmployeeName.Contains(input.Keyword) || x.EmployeeAccount.Contains(input.Keyword)); | |
| 74 | + } | |
| 75 | + | |
| 76 | + var list = await query.Select(x => new BusinessUnitManagerSalaryOutput | |
| 77 | + { | |
| 78 | + Id = x.Id, | |
| 79 | + StatisticsMonth = x.StatisticsMonth, | |
| 80 | + Position = x.Position, | |
| 81 | + EmployeeName = x.EmployeeName, | |
| 82 | + EmployeeId = x.EmployeeId, | |
| 83 | + EmployeeAccount = x.EmployeeAccount, | |
| 84 | + ManagerType = x.ManagerType, | |
| 85 | + IsTerminated = x.IsTerminated, | |
| 86 | + StorePerformanceDetail = x.StorePerformanceDetail, | |
| 87 | + BaseSalary = x.BaseSalary, | |
| 88 | + TotalCommission = x.TotalCommission, | |
| 89 | + WorkingDays = x.WorkingDays, | |
| 90 | + LeaveDays = x.LeaveDays, | |
| 91 | + CalculatedGrossSalary = x.CalculatedGrossSalary, | |
| 92 | + FinalGrossSalary = x.FinalGrossSalary, | |
| 93 | + MonthlyTrainingSubsidy = x.MonthlyTrainingSubsidy, | |
| 94 | + MonthlyTransportSubsidy = x.MonthlyTransportSubsidy, | |
| 95 | + LastMonthTrainingSubsidy = x.LastMonthTrainingSubsidy, | |
| 96 | + LastMonthTransportSubsidy = x.LastMonthTransportSubsidy, | |
| 97 | + TotalSubsidy = x.TotalSubsidy, | |
| 98 | + MissingCard = x.MissingCard, | |
| 99 | + LateArrival = x.LateArrival, | |
| 100 | + LeaveDeduction = x.LeaveDeduction, | |
| 101 | + SocialInsuranceDeduction = x.SocialInsuranceDeduction, | |
| 102 | + RewardDeduction = x.RewardDeduction, | |
| 103 | + AccommodationDeduction = x.AccommodationDeduction, | |
| 104 | + StudyPeriodDeduction = x.StudyPeriodDeduction, | |
| 105 | + WorkClothesDeduction = x.WorkClothesDeduction, | |
| 106 | + TotalDeduction = x.TotalDeduction, | |
| 107 | + Bonus = x.Bonus, | |
| 108 | + ReturnPhoneDeposit = x.ReturnPhoneDeposit, | |
| 109 | + ReturnAccommodationDeposit = x.ReturnAccommodationDeposit, | |
| 110 | + ActualSalary = x.ActualSalary, | |
| 111 | + MonthlyPaymentStatus = x.MonthlyPaymentStatus, | |
| 112 | + PaidAmount = x.PaidAmount, | |
| 113 | + PendingAmount = x.PendingAmount, | |
| 114 | + LastMonthSupplement = x.LastMonthSupplement, | |
| 115 | + MonthlyTotalPayment = x.MonthlyTotalPayment, | |
| 116 | + IsLocked = x.IsLocked, | |
| 117 | + UpdateTime = x.UpdateTime | |
| 118 | + }) | |
| 119 | + .ToPagedListAsync(input.currentPage, input.pageSize); | |
| 120 | + | |
| 121 | + return PageResult<BusinessUnitManagerSalaryOutput>.SqlSugarPageResult(list); | |
| 122 | + } | |
| 123 | + | |
| 124 | + /// <summary> | |
| 125 | + /// 计算事业部总经理/经理工资 | |
| 126 | + /// </summary> | |
| 127 | + /// <param name="year">年份</param> | |
| 128 | + /// <param name="month">月份</param> | |
| 129 | + /// <returns></returns> | |
| 130 | + [HttpPost("calculate/business-unit-manager")] | |
| 131 | + public async Task CalculateBusinessUnitManagerSalary(int year, int month) | |
| 132 | + { | |
| 133 | + var startDate = new DateTime(year, month, 1); | |
| 134 | + var endDate = startDate.AddMonths(1).AddDays(-1); | |
| 135 | + var monthStr = $"{year}{month:D2}"; | |
| 136 | + | |
| 137 | + // 1. 获取基础数据 | |
| 138 | + | |
| 139 | + // 1.1 获取总经理/经理归属信息(从lq_md_general_manager_lifeline表) | |
| 140 | + var lifelineList = await _db.Queryable<LqMdGeneralManagerLifelineEntity>() | |
| 141 | + .Where(x => x.Month == monthStr) | |
| 142 | + .ToListAsync(); | |
| 143 | + | |
| 144 | + if (!lifelineList.Any()) | |
| 145 | + { | |
| 146 | + // 如果没有归属信息,直接返回 | |
| 147 | + return; | |
| 148 | + } | |
| 149 | + | |
| 150 | + // 1.2 获取所有不重复的总经理/经理ID(确保所有总经理/经理都被计算) | |
| 151 | + var allManagerIds = lifelineList | |
| 152 | + .Where(x => !string.IsNullOrEmpty(x.GeneralManagerId)) | |
| 153 | + .Select(x => x.GeneralManagerId) | |
| 154 | + .Distinct() | |
| 155 | + .ToList(); | |
| 156 | + | |
| 157 | + // 1.3 按总经理/经理ID分组,获取每个总经理/经理管理的门店 | |
| 158 | + var managerStoreDict = lifelineList | |
| 159 | + .Where(x => !string.IsNullOrEmpty(x.GeneralManagerId) && !string.IsNullOrEmpty(x.StoreId)) | |
| 160 | + .GroupBy(x => x.GeneralManagerId) | |
| 161 | + .ToDictionary(g => g.Key, g => g.Select(x => x.StoreId).Distinct().ToList()); | |
| 162 | + | |
| 163 | + // 1.4 门店信息 (lq_mdxx) | |
| 164 | + var storeList = await _db.Queryable<LqMdxxEntity>().ToListAsync(); | |
| 165 | + var storeDict = storeList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x); | |
| 166 | + | |
| 167 | + // 1.5 门店生命线信息 (lq_md_target) | |
| 168 | + var targetList = await _db.Queryable<LqMdTargetEntity>() | |
| 169 | + .Where(x => x.Month == monthStr) | |
| 170 | + .ToListAsync(); | |
| 171 | + var storeLifelineDict = targetList | |
| 172 | + .Where(x => !string.IsNullOrEmpty(x.StoreId)) | |
| 173 | + .ToDictionary(x => x.StoreId, x => x.StoreLifeline); | |
| 174 | + | |
| 175 | + // 1.6 门店总业绩计算 (开单实付 - 退卡金额) | |
| 176 | + // 开单实付(从lq_kd_kdjlb表统计sfyj字段) | |
| 177 | + var storeBillingList = await _db.Queryable<LqKdKdjlbEntity>() | |
| 178 | + .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate.AddDays(1) && x.IsEffective == 1) | |
| 179 | + .Select(x => new { x.Djmd, x.Sfyj }) | |
| 180 | + .ToListAsync(); | |
| 181 | + var storeBillingDict = storeBillingList | |
| 182 | + .Where(x => !string.IsNullOrEmpty(x.Djmd)) | |
| 183 | + .GroupBy(x => x.Djmd) | |
| 184 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.Sfyj)); | |
| 185 | + | |
| 186 | + // 退卡金额(从lq_hytk_hytk表统计,使用F_ActualRefundAmount,如果没有则使用tkje) | |
| 187 | + var storeRefundList = await _db.Queryable<LqHytkHytkEntity>() | |
| 188 | + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1) | |
| 189 | + .Select(x => new { x.Md, x.ActualRefundAmount, x.Tkje }) | |
| 190 | + .ToListAsync(); | |
| 191 | + var storeRefundDict = storeRefundList | |
| 192 | + .Where(x => !string.IsNullOrEmpty(x.Md)) | |
| 193 | + .GroupBy(x => x.Md) | |
| 194 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.ActualRefundAmount ?? x.Tkje ?? 0)); | |
| 195 | + | |
| 196 | + // 1.7 考勤数据 (lq_attendance_summary) | |
| 197 | + var attendanceList = await _db.Queryable<LqAttendanceSummaryEntity>() | |
| 198 | + .Where(x => x.Year == year && x.Month == month && x.IsEffective == 1) | |
| 199 | + .ToListAsync(); | |
| 200 | + var attendanceDict = attendanceList.ToDictionary(x => x.UserId, x => x); | |
| 201 | + | |
| 202 | + // 1.8 获取员工信息 (BASE_USER) | |
| 203 | + var userList = await _db.Queryable<UserEntity>() | |
| 204 | + .Where(x => allManagerIds.Contains(x.Id)) | |
| 205 | + .Select(x => new { x.Id, x.RealName, x.Account, x.IsOnJob }) | |
| 206 | + .ToListAsync(); | |
| 207 | + var userDict = userList.ToDictionary(x => x.Id, x => x); | |
| 208 | + | |
| 209 | + // 2. 按总经理/经理聚合数据 | |
| 210 | + var managerStats = new Dictionary<string, LqBusinessUnitManagerSalaryStatisticsEntity>(); | |
| 211 | + | |
| 212 | + foreach (var managerId in allManagerIds) | |
| 213 | + { | |
| 214 | + if (string.IsNullOrEmpty(managerId)) | |
| 215 | + { | |
| 216 | + continue; | |
| 217 | + } | |
| 218 | + | |
| 219 | + // 获取该总经理/经理的信息 | |
| 220 | + var managerLifeline = lifelineList.FirstOrDefault(x => x.GeneralManagerId == managerId); | |
| 221 | + if (managerLifeline == null) | |
| 222 | + { | |
| 223 | + continue; | |
| 224 | + } | |
| 225 | + | |
| 226 | + // 2.1 创建工资统计对象 | |
| 227 | + var salary = new LqBusinessUnitManagerSalaryStatisticsEntity | |
| 228 | + { | |
| 229 | + Id = YitIdHelper.NextId().ToString(), | |
| 230 | + StatisticsMonth = monthStr, | |
| 231 | + EmployeeId = managerId, | |
| 232 | + ManagerType = managerLifeline.ManagerType, | |
| 233 | + Position = managerLifeline.ManagerType == 1 ? "总经理" : "经理", | |
| 234 | + IsTerminated = 0, | |
| 235 | + CreateTime = DateTime.Now, | |
| 236 | + UpdateTime = DateTime.Now, | |
| 237 | + IsLocked = 0 | |
| 238 | + }; | |
| 239 | + | |
| 240 | + // 2.2 填充员工信息 | |
| 241 | + if (userDict.ContainsKey(managerId)) | |
| 242 | + { | |
| 243 | + var user = userDict[managerId]; | |
| 244 | + salary.EmployeeName = user.RealName ?? ""; | |
| 245 | + salary.EmployeeAccount = user.Account ?? ""; | |
| 246 | + salary.IsTerminated = user.IsOnJob == 0 ? 1 : 0; | |
| 247 | + } | |
| 248 | + | |
| 249 | + // 2.3 考勤数据 | |
| 250 | + var attendance = attendanceDict.ContainsKey(managerId) ? attendanceDict[managerId] : null; | |
| 251 | + salary.WorkingDays = attendance?.WorkDays ?? 0; | |
| 252 | + salary.LeaveDays = attendance?.LeaveDays ?? 0; | |
| 253 | + | |
| 254 | + // 2.4 计算底薪(固定4000元) | |
| 255 | + salary.BaseSalary = 4000m; | |
| 256 | + | |
| 257 | + // 2.5 遍历该总经理/经理管理的每个门店,计算提成 | |
| 258 | + var storePerformanceDetails = new List<StorePerformanceDetail>(); | |
| 259 | + decimal totalCommission = 0m; | |
| 260 | + | |
| 261 | + // 获取该总经理/经理管理的门店列表(如果没有管理的门店,则为空列表) | |
| 262 | + var managedStores = managerStoreDict.ContainsKey(managerId) ? managerStoreDict[managerId] : new List<string>(); | |
| 263 | + foreach (var storeId in managedStores) | |
| 264 | + { | |
| 265 | + if (string.IsNullOrEmpty(storeId)) | |
| 266 | + { | |
| 267 | + continue; | |
| 268 | + } | |
| 269 | + | |
| 270 | + // 获取该门店的提成阶梯设置 | |
| 271 | + var storeLifelineSetting = lifelineList.FirstOrDefault(x => x.StoreId == storeId && x.GeneralManagerId == managerId); | |
| 272 | + if (storeLifelineSetting == null) | |
| 273 | + { | |
| 274 | + continue; | |
| 275 | + } | |
| 276 | + | |
| 277 | + // 获取门店信息 | |
| 278 | + var storeName = storeDict.ContainsKey(storeId) ? storeDict[storeId].Dm ?? "" : ""; | |
| 279 | + | |
| 280 | + // 获取门店生命线(提成门槛) | |
| 281 | + if (!storeLifelineDict.ContainsKey(storeId)) | |
| 282 | + { | |
| 283 | + // 门店生命线未设置,跳过该门店 | |
| 284 | + storePerformanceDetails.Add(new StorePerformanceDetail | |
| 285 | + { | |
| 286 | + StoreId = storeId, | |
| 287 | + StoreName = storeName, | |
| 288 | + StoreLifeline = 0, | |
| 289 | + BillingPerformance = 0, | |
| 290 | + RefundPerformance = 0, | |
| 291 | + StorePerformance = 0, | |
| 292 | + ReachedLifeline = false, | |
| 293 | + CommissionAmount = 0, | |
| 294 | + CalculationDetail = "门店生命线未设置,无法计算提成" | |
| 295 | + }); | |
| 296 | + continue; | |
| 297 | + } | |
| 298 | + | |
| 299 | + var storeLifeline = storeLifelineDict[storeId]; | |
| 300 | + if (storeLifeline <= 0) | |
| 301 | + { | |
| 302 | + // 门店生命线未设置或为0,跳过该门店 | |
| 303 | + storePerformanceDetails.Add(new StorePerformanceDetail | |
| 304 | + { | |
| 305 | + StoreId = storeId, | |
| 306 | + StoreName = storeName, | |
| 307 | + StoreLifeline = 0, | |
| 308 | + BillingPerformance = 0, | |
| 309 | + RefundPerformance = 0, | |
| 310 | + StorePerformance = 0, | |
| 311 | + ReachedLifeline = false, | |
| 312 | + CommissionAmount = 0, | |
| 313 | + CalculationDetail = "门店生命线未设置或为0,无法计算提成" | |
| 314 | + }); | |
| 315 | + continue; | |
| 316 | + } | |
| 317 | + | |
| 318 | + // 获取门店业绩 | |
| 319 | + var billing = storeBillingDict.ContainsKey(storeId) ? storeBillingDict[storeId] : 0; | |
| 320 | + var refund = storeRefundDict.ContainsKey(storeId) ? storeRefundDict[storeId] : 0; | |
| 321 | + var storePerformance = billing - refund; | |
| 322 | + | |
| 323 | + // 判断是否达到门店生命线 | |
| 324 | + var reachedLifeline = storePerformance >= storeLifeline; | |
| 325 | + | |
| 326 | + // 计算提成 | |
| 327 | + decimal commissionAmount = 0m; | |
| 328 | + string calculationDetail = ""; | |
| 329 | + | |
| 330 | + if (reachedLifeline) | |
| 331 | + { | |
| 332 | + // 达到门店生命线,使用提成阶梯计算提成(分段累进) | |
| 333 | + var commissionResult = CalculateStoreCommission(storePerformance, storeLifelineSetting); | |
| 334 | + commissionAmount = commissionResult.Amount; | |
| 335 | + calculationDetail = commissionResult.Detail; | |
| 336 | + | |
| 337 | + totalCommission += commissionAmount; | |
| 338 | + } | |
| 339 | + else | |
| 340 | + { | |
| 341 | + calculationDetail = $"业绩{storePerformance:N2}元,未达到门店生命线{storeLifeline:N2}元,无提成"; | |
| 342 | + } | |
| 343 | + | |
| 344 | + // 添加到门店业绩明细 | |
| 345 | + storePerformanceDetails.Add(new StorePerformanceDetail | |
| 346 | + { | |
| 347 | + StoreId = storeId, | |
| 348 | + StoreName = storeName, | |
| 349 | + StoreLifeline = storeLifeline, | |
| 350 | + BillingPerformance = billing, | |
| 351 | + RefundPerformance = refund, | |
| 352 | + StorePerformance = storePerformance, | |
| 353 | + ReachedLifeline = reachedLifeline, | |
| 354 | + Lifeline1 = storeLifelineSetting.Lifeline1, | |
| 355 | + CommissionRate1 = storeLifelineSetting.CommissionRate1, | |
| 356 | + Lifeline2 = storeLifelineSetting.Lifeline2, | |
| 357 | + CommissionRate2 = storeLifelineSetting.CommissionRate2, | |
| 358 | + Lifeline3 = storeLifelineSetting.Lifeline3, | |
| 359 | + CommissionRate3 = storeLifelineSetting.CommissionRate3, | |
| 360 | + CommissionAmount = commissionAmount, | |
| 361 | + CalculationDetail = calculationDetail | |
| 362 | + }); | |
| 363 | + } | |
| 364 | + | |
| 365 | + // 2.6 保存门店业绩明细(JSON格式) | |
| 366 | + salary.StorePerformanceDetail = storePerformanceDetails.ToJson(); | |
| 367 | + | |
| 368 | + // 2.7 提成合计 | |
| 369 | + salary.TotalCommission = totalCommission; | |
| 370 | + | |
| 371 | + // 2.8 计算应发工资 | |
| 372 | + salary.CalculatedGrossSalary = salary.BaseSalary + salary.TotalCommission; | |
| 373 | + salary.FinalGrossSalary = salary.CalculatedGrossSalary; | |
| 374 | + | |
| 375 | + // 2.9 初始化其他字段(默认值为0) | |
| 376 | + salary.MonthlyTrainingSubsidy = 0; | |
| 377 | + salary.MonthlyTransportSubsidy = 0; | |
| 378 | + salary.LastMonthTrainingSubsidy = 0; | |
| 379 | + salary.LastMonthTransportSubsidy = 0; | |
| 380 | + salary.TotalSubsidy = 0; | |
| 381 | + salary.MissingCard = 0; | |
| 382 | + salary.LateArrival = 0; | |
| 383 | + salary.LeaveDeduction = 0; | |
| 384 | + salary.SocialInsuranceDeduction = 0; | |
| 385 | + salary.RewardDeduction = 0; | |
| 386 | + salary.AccommodationDeduction = 0; | |
| 387 | + salary.StudyPeriodDeduction = 0; | |
| 388 | + salary.WorkClothesDeduction = 0; | |
| 389 | + salary.TotalDeduction = 0; | |
| 390 | + salary.Bonus = 0; | |
| 391 | + salary.ReturnPhoneDeposit = 0; | |
| 392 | + salary.ReturnAccommodationDeposit = 0; | |
| 393 | + salary.ActualSalary = salary.FinalGrossSalary - salary.TotalDeduction + salary.TotalSubsidy + salary.Bonus; | |
| 394 | + salary.MonthlyPaymentStatus = "未发放"; | |
| 395 | + salary.PaidAmount = 0; | |
| 396 | + salary.PendingAmount = salary.ActualSalary; | |
| 397 | + salary.LastMonthSupplement = 0; | |
| 398 | + salary.MonthlyTotalPayment = 0; | |
| 399 | + | |
| 400 | + managerStats[managerId] = salary; | |
| 401 | + } | |
| 402 | + | |
| 403 | + // 3. 保存数据 | |
| 404 | + if (managerStats.Any()) | |
| 405 | + { | |
| 406 | + // 先删除当月旧数据 (防止重复) | |
| 407 | + await _db.Deleteable<LqBusinessUnitManagerSalaryStatisticsEntity>() | |
| 408 | + .Where(x => x.StatisticsMonth == monthStr) | |
| 409 | + .ExecuteCommandAsync(); | |
| 410 | + | |
| 411 | + await _db.Insertable(managerStats.Values.ToList()).ExecuteCommandAsync(); | |
| 412 | + } | |
| 413 | + } | |
| 414 | + | |
| 415 | + /// <summary> | |
| 416 | + /// 计算门店提成(分段累进) | |
| 417 | + /// </summary> | |
| 418 | + /// <param name="storePerformance">门店业绩</param> | |
| 419 | + /// <param name="lifelineSetting">提成阶梯设置</param> | |
| 420 | + /// <returns>提成金额和计算说明</returns> | |
| 421 | + private (decimal Amount, string Detail) CalculateStoreCommission(decimal storePerformance, LqMdGeneralManagerLifelineEntity lifelineSetting) | |
| 422 | + { | |
| 423 | + // 验证提成阶梯1和提成比例1必须设置 | |
| 424 | + if (lifelineSetting.Lifeline1 <= 0 || lifelineSetting.CommissionRate1 <= 0) | |
| 425 | + { | |
| 426 | + return (0m, "提成阶梯1或提成比例1未设置,无法计算提成"); | |
| 427 | + } | |
| 428 | + | |
| 429 | + decimal commissionAmount = 0m; | |
| 430 | + string detail = ""; | |
| 431 | + | |
| 432 | + var lifeline1 = lifelineSetting.Lifeline1; | |
| 433 | + var rate1 = lifelineSetting.CommissionRate1; | |
| 434 | + var lifeline2 = lifelineSetting.Lifeline2 ?? 0; | |
| 435 | + var rate2 = lifelineSetting.CommissionRate2 ?? 0; | |
| 436 | + var lifeline3 = lifelineSetting.Lifeline3 ?? 0; | |
| 437 | + var rate3 = lifelineSetting.CommissionRate3 ?? 0; | |
| 438 | + | |
| 439 | + // 分段累进计算 | |
| 440 | + if (storePerformance <= lifeline1) | |
| 441 | + { | |
| 442 | + // 业绩 ≤ 提成阶梯1 | |
| 443 | + commissionAmount = storePerformance * (rate1 / 100m); | |
| 444 | + detail = $"业绩{storePerformance:N2}元,≤ 提成阶梯1({lifeline1:N2}元),提成 = {storePerformance:N2} × {rate1}% = {commissionAmount:N2}元"; | |
| 445 | + } | |
| 446 | + else if (lifeline2 > 0 && storePerformance <= lifeline2) | |
| 447 | + { | |
| 448 | + // 提成阶梯1 < 业绩 ≤ 提成阶梯2 | |
| 449 | + var part1 = lifeline1 * (rate1 / 100m); | |
| 450 | + var part2 = (storePerformance - lifeline1) * (rate2 / 100m); | |
| 451 | + commissionAmount = part1 + part2; | |
| 452 | + detail = $"业绩{storePerformance:N2}元,> 提成阶梯1({lifeline1:N2}元) 且 ≤ 提成阶梯2({lifeline2:N2}元),提成 = {lifeline1:N2} × {rate1}% + ({storePerformance:N2} - {lifeline1:N2}) × {rate2}% = {part1:N2} + {part2:N2} = {commissionAmount:N2}元"; | |
| 453 | + } | |
| 454 | + else if (lifeline3 > 0 && storePerformance <= lifeline3) | |
| 455 | + { | |
| 456 | + // 提成阶梯2 < 业绩 ≤ 提成阶梯3 | |
| 457 | + var part1 = lifeline1 * (rate1 / 100m); | |
| 458 | + var part2 = (lifeline2 - lifeline1) * (rate2 / 100m); | |
| 459 | + var part3 = (storePerformance - lifeline2) * (rate3 / 100m); | |
| 460 | + commissionAmount = part1 + part2 + part3; | |
| 461 | + detail = $"业绩{storePerformance:N2}元,> 提成阶梯2({lifeline2:N2}元) 且 ≤ 提成阶梯3({lifeline3:N2}元),提成 = {lifeline1:N2} × {rate1}% + ({lifeline2:N2} - {lifeline1:N2}) × {rate2}% + ({storePerformance:N2} - {lifeline2:N2}) × {rate3}% = {part1:N2} + {part2:N2} + {part3:N2} = {commissionAmount:N2}元"; | |
| 462 | + } | |
| 463 | + else if (lifeline3 > 0) | |
| 464 | + { | |
| 465 | + // 业绩 > 提成阶梯3 | |
| 466 | + var part1 = lifeline1 * (rate1 / 100m); | |
| 467 | + var part2 = (lifeline2 - lifeline1) * (rate2 / 100m); | |
| 468 | + var part3 = (lifeline3 - lifeline2) * (rate3 / 100m); | |
| 469 | + var part4 = (storePerformance - lifeline3) * (rate3 / 100m); | |
| 470 | + commissionAmount = part1 + part2 + part3 + part4; | |
| 471 | + detail = $"业绩{storePerformance:N2}元,> 提成阶梯3({lifeline3:N2}元),提成 = {lifeline1:N2} × {rate1}% + ({lifeline2:N2} - {lifeline1:N2}) × {rate2}% + ({lifeline3:N2} - {lifeline2:N2}) × {rate3}% + ({storePerformance:N2} - {lifeline3:N2}) × {rate3}% = {part1:N2} + {part2:N2} + {part3:N2} + {part4:N2} = {commissionAmount:N2}元"; | |
| 472 | + } | |
| 473 | + else if (lifeline2 > 0) | |
| 474 | + { | |
| 475 | + // 提成阶梯3未设置,业绩 > 提成阶梯2,按提成比例2计算超出部分 | |
| 476 | + var part1 = lifeline1 * (rate1 / 100m); | |
| 477 | + var part2 = (storePerformance - lifeline1) * (rate2 / 100m); | |
| 478 | + commissionAmount = part1 + part2; | |
| 479 | + detail = $"业绩{storePerformance:N2}元,> 提成阶梯2({lifeline2:N2}元),提成阶梯3未设置,提成 = {lifeline1:N2} × {rate1}% + ({storePerformance:N2} - {lifeline1:N2}) × {rate2}% = {part1:N2} + {part2:N2} = {commissionAmount:N2}元"; | |
| 480 | + } | |
| 481 | + else | |
| 482 | + { | |
| 483 | + // 只有提成阶梯1,业绩 > 提成阶梯1,按提成比例1计算 | |
| 484 | + commissionAmount = storePerformance * (rate1 / 100m); | |
| 485 | + detail = $"业绩{storePerformance:N2}元,> 提成阶梯1({lifeline1:N2}元),提成阶梯2未设置,提成 = {storePerformance:N2} × {rate1}% = {commissionAmount:N2}元"; | |
| 486 | + } | |
| 487 | + | |
| 488 | + return (commissionAmount, detail); | |
| 489 | + } | |
| 490 | + | |
| 491 | + /// <summary> | |
| 492 | + /// 门店业绩明细(用于JSON序列化) | |
| 493 | + /// </summary> | |
| 494 | + private class StorePerformanceDetail | |
| 495 | + { | |
| 496 | + public string StoreId { get; set; } | |
| 497 | + public string StoreName { get; set; } | |
| 498 | + public decimal StoreLifeline { get; set; } | |
| 499 | + public decimal BillingPerformance { get; set; } | |
| 500 | + public decimal RefundPerformance { get; set; } | |
| 501 | + public decimal StorePerformance { get; set; } | |
| 502 | + public bool ReachedLifeline { get; set; } | |
| 503 | + public decimal Lifeline1 { get; set; } | |
| 504 | + public decimal CommissionRate1 { get; set; } | |
| 505 | + public decimal? Lifeline2 { get; set; } | |
| 506 | + public decimal? CommissionRate2 { get; set; } | |
| 507 | + public decimal? Lifeline3 { get; set; } | |
| 508 | + public decimal? CommissionRate3 { get; set; } | |
| 509 | + public decimal CommissionAmount { get; set; } | |
| 510 | + public string CalculationDetail { get; set; } | |
| 511 | + } | |
| 512 | + } | |
| 513 | +} | |
| 514 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
| ... | ... | @@ -355,6 +355,7 @@ namespace NCC.Extend.LqHytkHytk |
| 355 | 355 | IsEffective = StatusEnum.有效.GetHashCode(), |
| 356 | 356 | ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), |
| 357 | 357 | PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", |
| 358 | + BeautyType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), | |
| 358 | 359 | }; |
| 359 | 360 | allMxEntities.Add(lqHytkMxEntity); |
| 360 | 361 | |
| ... | ... | @@ -384,7 +385,8 @@ namespace NCC.Extend.LqHytkHytk |
| 384 | 385 | ItemId = lqHytkMxEntity.Px, |
| 385 | 386 | StoreId = newEntity.Md, |
| 386 | 387 | ItemName = lqHytkMxEntity.Pxmc, |
| 387 | - PerformanceType = lqHytkMxEntity.PerformanceType | |
| 388 | + PerformanceType = lqHytkMxEntity.PerformanceType, | |
| 389 | + BeautyType = lqHytkMxEntity.BeautyType, | |
| 388 | 390 | } |
| 389 | 391 | ); |
| 390 | 392 | } |
| ... | ... | @@ -416,7 +418,8 @@ namespace NCC.Extend.LqHytkHytk |
| 416 | 418 | ItemId = lqHytkMxEntity.Px, |
| 417 | 419 | StoreId = newEntity.Md, |
| 418 | 420 | ItemName = lqHytkMxEntity.Pxmc, |
| 419 | - PerformanceType = lqHytkMxEntity.PerformanceType | |
| 421 | + PerformanceType = lqHytkMxEntity.PerformanceType, | |
| 422 | + BeautyType = lqHytkMxEntity.BeautyType, | |
| 420 | 423 | } |
| 421 | 424 | ); |
| 422 | 425 | } |
| ... | ... | @@ -517,6 +520,7 @@ namespace NCC.Extend.LqHytkHytk |
| 517 | 520 | TotalPrice = item.F_TotalPrice ?? (item.pxjg * (item.F_ProjectNumber ?? 1)), |
| 518 | 521 | ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), |
| 519 | 522 | PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", |
| 523 | + BeautyType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), | |
| 520 | 524 | }; |
| 521 | 525 | allMxEntities.Add(lqHytkMxEntity); |
| 522 | 526 | |
| ... | ... | @@ -545,7 +549,8 @@ namespace NCC.Extend.LqHytkHytk |
| 545 | 549 | ItemId = lqHytkMxEntity.Px, |
| 546 | 550 | StoreId = entity.Md, |
| 547 | 551 | ItemName = lqHytkMxEntity.Pxmc, |
| 548 | - PerformanceType = lqHytkMxEntity.PerformanceType | |
| 552 | + PerformanceType = lqHytkMxEntity.PerformanceType, | |
| 553 | + BeautyType = lqHytkMxEntity.BeautyType, | |
| 549 | 554 | } |
| 550 | 555 | ); |
| 551 | 556 | } |
| ... | ... | @@ -575,7 +580,8 @@ namespace NCC.Extend.LqHytkHytk |
| 575 | 580 | ItemId = lqHytkMxEntity.Px, |
| 576 | 581 | StoreId = entity.Md, |
| 577 | 582 | ItemName = lqHytkMxEntity.Pxmc, |
| 578 | - PerformanceType = lqHytkMxEntity.PerformanceType | |
| 583 | + PerformanceType = lqHytkMxEntity.PerformanceType, | |
| 584 | + BeautyType = lqHytkMxEntity.BeautyType, | |
| 579 | 585 | } |
| 580 | 586 | ); |
| 581 | 587 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
| ... | ... | @@ -538,7 +538,7 @@ namespace NCC.Extend |
| 538 | 538 | { |
| 539 | 539 | var sidx = input.sidx == null ? "id" : input.sidx; |
| 540 | 540 | |
| 541 | - // 查询使用记录信息,关联产品表 | |
| 541 | + // 查询使用记录信息,关联产品表(使用Queryable<T1, T2>语法,与GetBatchInfoAsync保持一致) | |
| 542 | 542 | var data = await _db.Queryable<LqInventoryUsageEntity, LqProductEntity>((u, product) => u.ProductId == product.Id) |
| 543 | 543 | .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (u, product) => u.ProductId == input.ProductId) |
| 544 | 544 | .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (u, product) => u.StoreId == input.StoreId) |
| ... | ... | @@ -562,6 +562,8 @@ namespace NCC.Extend |
| 562 | 562 | totalAmount = u.TotalAmount, // 总价(单价 × 数量) |
| 563 | 563 | relatedConsumeId = u.RelatedConsumeId, |
| 564 | 564 | usageBatchId = u.UsageBatchId, |
| 565 | + approvalStatus = null, // 审批状态(稍后在内存中补充) | |
| 566 | + isReceived = null, // 是否已领取(稍后在内存中补充) | |
| 565 | 567 | createUser = u.CreateUser, |
| 566 | 568 | createUserName = "", |
| 567 | 569 | createTime = u.CreateTime, |
| ... | ... | @@ -574,6 +576,35 @@ namespace NCC.Extend |
| 574 | 576 | .OrderBy(sidx + " " + input.sort) |
| 575 | 577 | .ToPagedListAsync(input.currentPage, input.pageSize); |
| 576 | 578 | |
| 579 | + // 补充审批状态和是否已领取信息(通过usageBatchId关联申请表) | |
| 580 | + if (data.list.Any()) | |
| 581 | + { | |
| 582 | + var batchIds = data.list.Where(x => !string.IsNullOrEmpty(x.usageBatchId)) | |
| 583 | + .Select(x => x.usageBatchId) | |
| 584 | + .Distinct() | |
| 585 | + .ToList(); | |
| 586 | + | |
| 587 | + if (batchIds.Any()) | |
| 588 | + { | |
| 589 | + var applicationDict = await _db.Queryable<LqInventoryUsageApplicationEntity>() | |
| 590 | + .Where(x => batchIds.Contains(x.UsageBatchId) && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 591 | + .Select(x => new { x.UsageBatchId, x.ApprovalStatus, x.IsReceived }) | |
| 592 | + .ToListAsync(); | |
| 593 | + | |
| 594 | + var applicationLookup = applicationDict.ToDictionary(x => x.UsageBatchId, x => new { x.ApprovalStatus, x.IsReceived }); | |
| 595 | + | |
| 596 | + foreach (var item in data.list) | |
| 597 | + { | |
| 598 | + if (!string.IsNullOrEmpty(item.usageBatchId) && applicationLookup.ContainsKey(item.usageBatchId)) | |
| 599 | + { | |
| 600 | + var appInfo = applicationLookup[item.usageBatchId]; | |
| 601 | + item.approvalStatus = appInfo.ApprovalStatus; | |
| 602 | + item.isReceived = appInfo.IsReceived; | |
| 603 | + } | |
| 604 | + } | |
| 605 | + } | |
| 606 | + } | |
| 607 | + | |
| 577 | 608 | // 补充用户信息 |
| 578 | 609 | var userIds = data.list.SelectMany(x => new[] { x.createUser, x.updateUser }) |
| 579 | 610 | .Where(x => !string.IsNullOrEmpty(x)) | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
| ... | ... | @@ -934,6 +934,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 934 | 934 | ActivityId = input.activityId, |
| 935 | 935 | ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), |
| 936 | 936 | PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", |
| 937 | + BeautyType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), | |
| 937 | 938 | }; |
| 938 | 939 | allPxmxEntities.Add(lqKdPxmxEntity); |
| 939 | 940 | // 收集该品项关联的健康师业绩 |
| ... | ... | @@ -960,7 +961,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 960 | 961 | ItemId = lqKdPxmxEntity.Px, |
| 961 | 962 | ItemName = lqKdPxmxEntity.Pxmc, |
| 962 | 963 | StoreId = entity.Djmd, |
| 963 | - PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", | |
| 964 | + PerformanceType = lqKdPxmxEntity.PerformanceType, | |
| 965 | + BeautyType = lqKdPxmxEntity.BeautyType, | |
| 964 | 966 | }); |
| 965 | 967 | } |
| 966 | 968 | } |
| ... | ... | @@ -986,7 +988,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 986 | 988 | ItemId = lqKdPxmxEntity.Px, |
| 987 | 989 | ItemName = lqKdPxmxEntity.Pxmc, |
| 988 | 990 | StoreId = entity.Djmd, |
| 989 | - PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", | |
| 991 | + PerformanceType = lqKdPxmxEntity.PerformanceType, | |
| 992 | + BeautyType = lqKdPxmxEntity.BeautyType, | |
| 990 | 993 | } |
| 991 | 994 | ); |
| 992 | 995 | } |
| ... | ... | @@ -1249,7 +1252,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 1249 | 1252 | ItemId = item.ItemId, |
| 1250 | 1253 | IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效 |
| 1251 | 1254 | CreateTime = DateTime.Now, // 设置创建时间 |
| 1252 | - ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync(), | |
| 1255 | + ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync() | |
| 1253 | 1256 | }; |
| 1254 | 1257 | allDeductEntities.Add(lqKdDeductEntity); |
| 1255 | 1258 | } |
| ... | ... | @@ -1277,6 +1280,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 1277 | 1280 | ActivityId = input.activityId, |
| 1278 | 1281 | ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), |
| 1279 | 1282 | PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", |
| 1283 | + BeautyType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), | |
| 1280 | 1284 | }; |
| 1281 | 1285 | allPxmxEntities.Add(lqKdPxmxEntity); |
| 1282 | 1286 | |
| ... | ... | @@ -1304,6 +1308,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 1304 | 1308 | StoreId = entity.Djmd, |
| 1305 | 1309 | ItemName = lqKdPxmxEntity.Pxmc, |
| 1306 | 1310 | PerformanceType = lqKdPxmxEntity.PerformanceType, |
| 1311 | + BeautyType = lqKdPxmxEntity.BeautyType, | |
| 1307 | 1312 | }); |
| 1308 | 1313 | } |
| 1309 | 1314 | } |
| ... | ... | @@ -1330,6 +1335,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 1330 | 1335 | StoreId = entity.Djmd, |
| 1331 | 1336 | ItemName = lqKdPxmxEntity.Pxmc, |
| 1332 | 1337 | PerformanceType = lqKdPxmxEntity.PerformanceType, |
| 1338 | + BeautyType = lqKdPxmxEntity.BeautyType, | |
| 1333 | 1339 | }); |
| 1334 | 1340 | } |
| 1335 | 1341 | } |
| ... | ... | @@ -2878,6 +2884,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 2878 | 2884 | IsEffective = StatusEnum.有效.GetHashCode(), |
| 2879 | 2885 | ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Qt2).FirstAsync(), |
| 2880 | 2886 | PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Fl3).FirstAsync() ?? "", |
| 2887 | + BeautyType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.BeautyType).FirstAsync(), | |
| 2881 | 2888 | }; |
| 2882 | 2889 | refundMxEntities.Add(refundMxEntity); |
| 2883 | 2890 | var refundKdyjEntities = _db.Queryable<LqKdJksyjEntity>().Where(p => p.Kdpxid == item.BillingItemId).ToList(); |
| ... | ... | @@ -2913,7 +2920,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 2913 | 2920 | ItemId = refundMxEntity.Px, |
| 2914 | 2921 | StoreId = refundEntity.Md, |
| 2915 | 2922 | ItemName = refundMxEntity.Pxmc, |
| 2916 | - PerformanceType = refundMxEntity.PerformanceType | |
| 2923 | + PerformanceType = refundMxEntity.PerformanceType, | |
| 2924 | + BeautyType = refundMxEntity.BeautyType, | |
| 2917 | 2925 | }); |
| 2918 | 2926 | } |
| 2919 | 2927 | //查询科技部老师的业绩 |
| ... | ... | @@ -2941,7 +2949,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 2941 | 2949 | ItemId = refundMxEntity.Px, |
| 2942 | 2950 | StoreId = refundEntity.Md, |
| 2943 | 2951 | ItemName = refundMxEntity.Pxmc, |
| 2944 | - PerformanceType = refundMxEntity.PerformanceType | |
| 2952 | + PerformanceType = refundMxEntity.PerformanceType, | |
| 2953 | + BeautyType = refundMxEntity.BeautyType, | |
| 2945 | 2954 | }); |
| 2946 | 2955 | } |
| 2947 | 2956 | } |
| ... | ... | @@ -3025,6 +3034,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 3025 | 3034 | Remark = $"从会员 {fromMember.Khmc} 转入", |
| 3026 | 3035 | ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Qt2).FirstAsync(), |
| 3027 | 3036 | PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Fl3).FirstAsync() ?? "", |
| 3037 | + BeautyType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.BeautyType).FirstAsync(), | |
| 3028 | 3038 | }; |
| 3029 | 3039 | billingPxmxEntities.Add(billingPxmxEntity); |
| 3030 | 3040 | |
| ... | ... | @@ -3056,7 +3066,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 3056 | 3066 | ItemId = billingPxmxEntity.Px, |
| 3057 | 3067 | StoreId = billingEntity.Djmd, |
| 3058 | 3068 | ItemName = billingPxmxEntity.Pxmc, |
| 3059 | - PerformanceType = billingPxmxEntity.PerformanceType | |
| 3069 | + PerformanceType = billingPxmxEntity.PerformanceType, | |
| 3070 | + BeautyType = billingPxmxEntity.BeautyType, | |
| 3060 | 3071 | }); |
| 3061 | 3072 | } |
| 3062 | 3073 | } |
| ... | ... | @@ -3087,7 +3098,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 3087 | 3098 | ItemId = billingPxmxEntity.Px, |
| 3088 | 3099 | StoreId = billingEntity.Djmd, |
| 3089 | 3100 | ItemName = billingPxmxEntity.Pxmc, |
| 3090 | - PerformanceType = billingPxmxEntity.PerformanceType | |
| 3101 | + PerformanceType = billingPxmxEntity.PerformanceType, | |
| 3102 | + BeautyType = billingPxmxEntity.BeautyType, | |
| 3091 | 3103 | }); |
| 3092 | 3104 | } |
| 3093 | 3105 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqMajorProjectTeacherSalaryService.cs
0 → 100644
| 1 | +using Microsoft.AspNetCore.Authorization; | |
| 2 | +using Microsoft.AspNetCore.Mvc; | |
| 3 | +using NCC.Common.Filter; | |
| 4 | +using NCC.Common.Helper; | |
| 5 | +using NCC.Dependency; | |
| 6 | +using NCC.DynamicApiController; | |
| 7 | +using NCC.Extend.Entitys.Dto.LqMajorProjectTeacherSalary; | |
| 8 | +using NCC.Extend.Entitys.lq_attendance_summary; | |
| 9 | +using NCC.Extend.Entitys.lq_hytk_hytk; | |
| 10 | +using NCC.Extend.Entitys.lq_kd_kdjlb; | |
| 11 | +using NCC.Extend.Entitys.lq_md_major_project_teacher_assignment; | |
| 12 | +using NCC.Extend.Entitys.lq_md_xdbhsj; | |
| 13 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 14 | +using NCC.Extend.Entitys.lq_major_project_teacher_salary_statistics; | |
| 15 | +using NCC.System.Entitys.Permission; | |
| 16 | +using SqlSugar; | |
| 17 | +using System; | |
| 18 | +using System.Collections.Generic; | |
| 19 | +using System.Linq; | |
| 20 | +using System.Threading.Tasks; | |
| 21 | +using Yitter.IdGenerator; | |
| 22 | + | |
| 23 | +namespace NCC.Extend | |
| 24 | +{ | |
| 25 | + /// <summary> | |
| 26 | + /// 大项目部老师薪酬服务 | |
| 27 | + /// </summary> | |
| 28 | + [ApiDescriptionSettings(Tag = "大项目部老师薪酬服务", Name = "LqMajorProjectTeacherSalary", Order = 303)] | |
| 29 | + [Route("api/Extend/[controller]")] | |
| 30 | + public class LqMajorProjectTeacherSalaryService : IDynamicApiController, ITransient | |
| 31 | + { | |
| 32 | + private readonly ISqlSugarClient _db; | |
| 33 | + | |
| 34 | + /// <summary> | |
| 35 | + /// 初始化一个<see cref="LqMajorProjectTeacherSalaryService"/>类型的新实例 | |
| 36 | + /// </summary> | |
| 37 | + public LqMajorProjectTeacherSalaryService(ISqlSugarClient db) | |
| 38 | + { | |
| 39 | + _db = db; | |
| 40 | + } | |
| 41 | + | |
| 42 | + /// <summary> | |
| 43 | + /// 获取大项目部老师工资列表 | |
| 44 | + /// </summary> | |
| 45 | + /// <param name="input">查询参数</param> | |
| 46 | + /// <returns>大项目部老师工资分页列表</returns> | |
| 47 | + [HttpGet("major-project-teacher")] | |
| 48 | + public async Task<dynamic> GetMajorProjectTeacherSalaryList([FromQuery] MajorProjectTeacherSalaryInput input) | |
| 49 | + { | |
| 50 | + var monthStr = $"{input.Year}{input.Month:D2}"; | |
| 51 | + | |
| 52 | + // 1. 检查当月是否已生成工资数据 | |
| 53 | + var exists = await _db.Queryable<LqMajorProjectTeacherSalaryStatisticsEntity>() | |
| 54 | + .AnyAsync(x => x.StatisticsMonth == monthStr); | |
| 55 | + | |
| 56 | + // 2. 如果没有数据,则进行计算 | |
| 57 | + if (!exists) | |
| 58 | + { | |
| 59 | + await CalculateMajorProjectTeacherSalary(input.Year, input.Month); | |
| 60 | + } | |
| 61 | + | |
| 62 | + // 3. 查询数据 | |
| 63 | + var query = _db.Queryable<LqMajorProjectTeacherSalaryStatisticsEntity>() | |
| 64 | + .Where(x => x.StatisticsMonth == monthStr); | |
| 65 | + | |
| 66 | + if (!string.IsNullOrEmpty(input.StoreId)) | |
| 67 | + { | |
| 68 | + query = query.Where(x => x.StoreId == input.StoreId); | |
| 69 | + } | |
| 70 | + | |
| 71 | + if (!string.IsNullOrEmpty(input.Keyword)) | |
| 72 | + { | |
| 73 | + query = query.Where(x => x.EmployeeName.Contains(input.Keyword) || x.EmployeeAccount.Contains(input.Keyword)); | |
| 74 | + } | |
| 75 | + | |
| 76 | + var list = await query.Select(x => new MajorProjectTeacherSalaryOutput | |
| 77 | + { | |
| 78 | + Id = x.Id, | |
| 79 | + StatisticsMonth = x.StatisticsMonth, | |
| 80 | + StoreId = x.StoreId, | |
| 81 | + StoreName = x.StoreName, | |
| 82 | + Position = x.Position, | |
| 83 | + EmployeeName = x.EmployeeName, | |
| 84 | + EmployeeId = x.EmployeeId, | |
| 85 | + EmployeeAccount = x.EmployeeAccount, | |
| 86 | + OrderAchievement = x.OrderAchievement, | |
| 87 | + ConsumeAchievement = x.ConsumeAchievement, | |
| 88 | + RefundAchievement = x.RefundAchievement, | |
| 89 | + TotalPerformance = x.TotalPerformance, | |
| 90 | + BaseSalary = x.BaseSalary, | |
| 91 | + PerformanceCommissionRate = x.PerformanceCommissionRate, | |
| 92 | + PerformanceCommissionAmount = x.PerformanceCommissionAmount, | |
| 93 | + TotalCommission = x.TotalCommission, | |
| 94 | + HandworkFee = x.HandworkFee, | |
| 95 | + WorkingDays = x.WorkingDays, | |
| 96 | + LeaveDays = x.LeaveDays, | |
| 97 | + TransportationAllowance = x.TransportationAllowance, | |
| 98 | + LessRest = x.LessRest, | |
| 99 | + FullAttendance = x.FullAttendance, | |
| 100 | + CalculatedGrossSalary = x.CalculatedGrossSalary, | |
| 101 | + GuaranteedSalary = x.GuaranteedSalary, | |
| 102 | + GuaranteedLeaveDeduction = x.GuaranteedLeaveDeduction, | |
| 103 | + GuaranteedBaseSalary = x.GuaranteedBaseSalary, | |
| 104 | + GuaranteedSupplement = x.GuaranteedSupplement, | |
| 105 | + FinalGrossSalary = x.FinalGrossSalary, | |
| 106 | + MonthlyTrainingSubsidy = x.MonthlyTrainingSubsidy, | |
| 107 | + MonthlyTransportSubsidy = x.MonthlyTransportSubsidy, | |
| 108 | + LastMonthTrainingSubsidy = x.LastMonthTrainingSubsidy, | |
| 109 | + LastMonthTransportSubsidy = x.LastMonthTransportSubsidy, | |
| 110 | + TotalSubsidy = x.TotalSubsidy, | |
| 111 | + MissingCard = x.MissingCard, | |
| 112 | + LateArrival = x.LateArrival, | |
| 113 | + LeaveDeduction = x.LeaveDeduction, | |
| 114 | + SocialInsuranceDeduction = x.SocialInsuranceDeduction, | |
| 115 | + RewardDeduction = x.RewardDeduction, | |
| 116 | + AccommodationDeduction = x.AccommodationDeduction, | |
| 117 | + StudyPeriodDeduction = x.StudyPeriodDeduction, | |
| 118 | + WorkClothesDeduction = x.WorkClothesDeduction, | |
| 119 | + TotalDeduction = x.TotalDeduction, | |
| 120 | + Bonus = x.Bonus, | |
| 121 | + ReturnPhoneDeposit = x.ReturnPhoneDeposit, | |
| 122 | + ReturnAccommodationDeposit = x.ReturnAccommodationDeposit, | |
| 123 | + ActualSalary = x.ActualSalary, | |
| 124 | + MonthlyPaymentStatus = x.MonthlyPaymentStatus, | |
| 125 | + PaidAmount = x.PaidAmount, | |
| 126 | + PendingAmount = x.PendingAmount, | |
| 127 | + LastMonthSupplement = x.LastMonthSupplement, | |
| 128 | + MonthlyTotalPayment = x.MonthlyTotalPayment, | |
| 129 | + IsLocked = x.IsLocked, | |
| 130 | + IsTerminated = x.IsTerminated, | |
| 131 | + UpdateTime = x.UpdateTime, | |
| 132 | + StoreType = x.StoreType, | |
| 133 | + StoreCategory = x.StoreCategory, | |
| 134 | + IsNewStore = x.IsNewStore, | |
| 135 | + NewStoreProtectionStage = x.NewStoreProtectionStage | |
| 136 | + }) | |
| 137 | + .ToPagedListAsync(input.currentPage, input.pageSize); | |
| 138 | + | |
| 139 | + return PageResult<MajorProjectTeacherSalaryOutput>.SqlSugarPageResult(list); | |
| 140 | + } | |
| 141 | + | |
| 142 | + /// <summary> | |
| 143 | + /// 计算大项目部老师工资 | |
| 144 | + /// </summary> | |
| 145 | + /// <param name="year">年份</param> | |
| 146 | + /// <param name="month">月份</param> | |
| 147 | + /// <returns></returns> | |
| 148 | + [HttpPost("calculate/major-project-teacher")] | |
| 149 | + public async Task CalculateMajorProjectTeacherSalary(int year, int month) | |
| 150 | + { | |
| 151 | + var startDate = new DateTime(year, month, 1); | |
| 152 | + var endDate = startDate.AddMonths(1).AddDays(-1); | |
| 153 | + var monthStr = $"{year}{month:D2}"; | |
| 154 | + var yearStr = year.ToString(); | |
| 155 | + var monthStr2 = month.ToString("D2"); | |
| 156 | + | |
| 157 | + // 1. 获取基础数据 | |
| 158 | + | |
| 159 | + // 1.1 获取大项目部老师归属信息(从lq_md_major_project_teacher_assignment表) | |
| 160 | + var assignmentList = await _db.Queryable<LqMdMajorProjectTeacherAssignmentEntity>() | |
| 161 | + .Where(x => x.Year == yearStr && x.Month == monthStr2) | |
| 162 | + .ToListAsync(); | |
| 163 | + | |
| 164 | + if (!assignmentList.Any()) | |
| 165 | + { | |
| 166 | + // 如果没有归属信息,直接返回 | |
| 167 | + return; | |
| 168 | + } | |
| 169 | + | |
| 170 | + // 1.2 门店信息 (lq_mdxx) | |
| 171 | + var storeList = await _db.Queryable<LqMdxxEntity>().ToListAsync(); | |
| 172 | + var storeDict = storeList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x); | |
| 173 | + | |
| 174 | + // 1.3 门店新店保护信息 (lq_md_xdbhsj) | |
| 175 | + var newStoreProtectionList = await _db.Queryable<LqMdXdbhsjEntity>() | |
| 176 | + .Where(x => x.Sfqy == 1) | |
| 177 | + .ToListAsync(); | |
| 178 | + | |
| 179 | + var newStoreProtectionDict = newStoreProtectionList | |
| 180 | + .Where(x => x.Bhkssj <= startDate && x.Bhjssj >= startDate) | |
| 181 | + .GroupBy(x => x.Mdid) | |
| 182 | + .ToDictionary(g => g.Key, g => g.First()); | |
| 183 | + | |
| 184 | + // 1.4 门店总业绩计算 (开单实付 - 退卡金额) | |
| 185 | + // 开单实付(从lq_kd_kdjlb表统计sfyj字段) | |
| 186 | + var storeBillingList = await _db.Queryable<LqKdKdjlbEntity>() | |
| 187 | + .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate.AddDays(1) && x.IsEffective == 1) | |
| 188 | + .Select(x => new { x.Djmd, x.Sfyj }) | |
| 189 | + .ToListAsync(); | |
| 190 | + var storeBillingDict = storeBillingList | |
| 191 | + .Where(x => !string.IsNullOrEmpty(x.Djmd)) | |
| 192 | + .GroupBy(x => x.Djmd) | |
| 193 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.Sfyj)); | |
| 194 | + | |
| 195 | + // 退卡金额(从lq_hytk_hytk表统计,使用F_ActualRefundAmount,如果没有则使用tkje) | |
| 196 | + var storeRefundList = await _db.Queryable<LqHytkHytkEntity>() | |
| 197 | + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1) | |
| 198 | + .Select(x => new { x.Md, x.ActualRefundAmount, x.Tkje }) | |
| 199 | + .ToListAsync(); | |
| 200 | + var storeRefundDict = storeRefundList | |
| 201 | + .Where(x => !string.IsNullOrEmpty(x.Md)) | |
| 202 | + .GroupBy(x => x.Md) | |
| 203 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.ActualRefundAmount ?? x.Tkje ?? 0)); | |
| 204 | + | |
| 205 | + // 1.5 考勤数据 (lq_attendance_summary) | |
| 206 | + var attendanceList = await _db.Queryable<LqAttendanceSummaryEntity>() | |
| 207 | + .Where(x => x.Year == year && x.Month == month && x.IsEffective == 1) | |
| 208 | + .ToListAsync(); | |
| 209 | + var attendanceDict = attendanceList.ToDictionary(x => x.UserId, x => x); | |
| 210 | + | |
| 211 | + // 1.6 获取员工信息 (BASE_USER) | |
| 212 | + var teacherIds = assignmentList.Select(x => x.TeacherId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); | |
| 213 | + var userList = await _db.Queryable<UserEntity>() | |
| 214 | + .Where(x => teacherIds.Contains(x.Id)) | |
| 215 | + .Select(x => new { x.Id, x.RealName, x.Account, x.IsOnJob }) | |
| 216 | + .ToListAsync(); | |
| 217 | + var userDict = userList.ToDictionary(x => x.Id, x => x); | |
| 218 | + | |
| 219 | + // 2. 按大项目部老师聚合数据 | |
| 220 | + var teacherStats = new Dictionary<string, LqMajorProjectTeacherSalaryStatisticsEntity>(); | |
| 221 | + | |
| 222 | + foreach (var assignment in assignmentList) | |
| 223 | + { | |
| 224 | + if (string.IsNullOrEmpty(assignment.TeacherId) || string.IsNullOrEmpty(assignment.StoreId)) | |
| 225 | + { | |
| 226 | + continue; | |
| 227 | + } | |
| 228 | + | |
| 229 | + var teacherId = assignment.TeacherId; | |
| 230 | + var storeId = assignment.StoreId; | |
| 231 | + | |
| 232 | + // 如果该老师已经处理过(可能有多条归属记录),则合并数据 | |
| 233 | + if (teacherStats.ContainsKey(teacherId)) | |
| 234 | + { | |
| 235 | + // 如果同一个老师归属于多个门店,需要合并业绩 | |
| 236 | + var existingSalary = teacherStats[teacherId]; | |
| 237 | + | |
| 238 | + // 获取该门店的业绩 | |
| 239 | + var additionalBilling = storeBillingDict.ContainsKey(storeId) ? storeBillingDict[storeId] : 0; | |
| 240 | + var additionalRefund = storeRefundDict.ContainsKey(storeId) ? storeRefundDict[storeId] : 0; | |
| 241 | + var additionalStorePerformance = additionalBilling - additionalRefund; | |
| 242 | + | |
| 243 | + // 累加业绩 | |
| 244 | + existingSalary.OrderAchievement += additionalBilling; | |
| 245 | + existingSalary.RefundAchievement += additionalRefund; | |
| 246 | + existingSalary.TotalPerformance += additionalStorePerformance; | |
| 247 | + | |
| 248 | + continue; | |
| 249 | + } | |
| 250 | + | |
| 251 | + // 2.1 创建工资统计对象 | |
| 252 | + var salary = new LqMajorProjectTeacherSalaryStatisticsEntity | |
| 253 | + { | |
| 254 | + Id = YitIdHelper.NextId().ToString(), | |
| 255 | + StatisticsMonth = monthStr, | |
| 256 | + EmployeeId = teacherId, | |
| 257 | + Position = "大项目部老师", | |
| 258 | + IsTerminated = 0, | |
| 259 | + CreateTime = DateTime.Now, | |
| 260 | + UpdateTime = DateTime.Now, | |
| 261 | + IsLocked = 0 | |
| 262 | + }; | |
| 263 | + | |
| 264 | + // 2.2 填充员工信息 | |
| 265 | + if (userDict.ContainsKey(teacherId)) | |
| 266 | + { | |
| 267 | + var user = userDict[teacherId]; | |
| 268 | + salary.EmployeeName = user.RealName ?? ""; | |
| 269 | + salary.EmployeeAccount = user.Account ?? ""; | |
| 270 | + salary.IsTerminated = user.IsOnJob == 0 ? 1 : 0; | |
| 271 | + } | |
| 272 | + | |
| 273 | + // 2.3 填充门店信息 | |
| 274 | + if (storeDict.ContainsKey(storeId)) | |
| 275 | + { | |
| 276 | + var store = storeDict[storeId]; | |
| 277 | + salary.StoreId = storeId; | |
| 278 | + salary.StoreName = store.Dm ?? ""; | |
| 279 | + salary.StoreType = store.StoreType; | |
| 280 | + salary.StoreCategory = store.StoreCategory; | |
| 281 | + } | |
| 282 | + else | |
| 283 | + { | |
| 284 | + salary.StoreId = storeId; | |
| 285 | + salary.StoreName = ""; | |
| 286 | + } | |
| 287 | + | |
| 288 | + // 2.4 新店保护信息 | |
| 289 | + if (!string.IsNullOrEmpty(salary.StoreId) && newStoreProtectionDict.ContainsKey(salary.StoreId)) | |
| 290 | + { | |
| 291 | + var protection = newStoreProtectionDict[salary.StoreId]; | |
| 292 | + salary.IsNewStore = "是"; | |
| 293 | + salary.NewStoreProtectionStage = protection.Stage; | |
| 294 | + } | |
| 295 | + else | |
| 296 | + { | |
| 297 | + salary.IsNewStore = "否"; | |
| 298 | + salary.NewStoreProtectionStage = 0; | |
| 299 | + } | |
| 300 | + | |
| 301 | + // 2.5 统计门店总业绩(开单业绩 - 退卡业绩) | |
| 302 | + var billing = storeBillingDict.ContainsKey(storeId) ? storeBillingDict[storeId] : 0; | |
| 303 | + var refund = storeRefundDict.ContainsKey(storeId) ? storeRefundDict[storeId] : 0; | |
| 304 | + var storeTotalPerformance = billing - refund; | |
| 305 | + | |
| 306 | + salary.OrderAchievement = billing; // 开单业绩(门店开单实付) | |
| 307 | + salary.RefundAchievement = refund; // 退卡业绩(门店退卡金额) | |
| 308 | + salary.ConsumeAchievement = 0; // 大项目部老师不统计消耗业绩 | |
| 309 | + salary.TotalPerformance = storeTotalPerformance; // 门店总业绩(开单 - 退卡) | |
| 310 | + | |
| 311 | + // 2.6 考勤数据 | |
| 312 | + var attendance = attendanceDict.ContainsKey(teacherId) ? attendanceDict[teacherId] : null; | |
| 313 | + salary.WorkingDays = attendance?.WorkDays ?? 0; | |
| 314 | + salary.LeaveDays = attendance?.LeaveDays ?? 0; | |
| 315 | + | |
| 316 | + // 3. 工资计算 | |
| 317 | + if (salary.IsTerminated == 1) | |
| 318 | + { | |
| 319 | + // 离职员工特殊处理(待确认规则) | |
| 320 | + salary.BaseSalary = 0; | |
| 321 | + salary.PerformanceCommissionRate = 0; | |
| 322 | + salary.PerformanceCommissionAmount = 0; | |
| 323 | + salary.TotalCommission = 0; | |
| 324 | + } | |
| 325 | + else | |
| 326 | + { | |
| 327 | + // 在职员工正常计算 | |
| 328 | + | |
| 329 | + // 3.1 计算底薪(固定3000元) | |
| 330 | + salary.BaseSalary = 3000m; | |
| 331 | + | |
| 332 | + // 3.2 计算业绩提成(阶梯式) | |
| 333 | + var performanceCommissionResult = CalculatePerformanceCommission(salary.TotalPerformance); | |
| 334 | + salary.PerformanceCommissionRate = performanceCommissionResult.Rate; | |
| 335 | + salary.PerformanceCommissionAmount = performanceCommissionResult.Amount; | |
| 336 | + | |
| 337 | + // 3.3 提成合计(等于业绩提成金额) | |
| 338 | + salary.TotalCommission = salary.PerformanceCommissionAmount; | |
| 339 | + } | |
| 340 | + | |
| 341 | + // 3.4 初始化其他字段(默认值为0) | |
| 342 | + salary.HandworkFee = 0; | |
| 343 | + salary.TransportationAllowance = 0; | |
| 344 | + salary.LessRest = 0; | |
| 345 | + salary.FullAttendance = 0; | |
| 346 | + salary.CalculatedGrossSalary = salary.BaseSalary + salary.TotalCommission + salary.HandworkFee; | |
| 347 | + salary.GuaranteedSalary = 0; | |
| 348 | + salary.GuaranteedLeaveDeduction = 0; | |
| 349 | + salary.GuaranteedBaseSalary = 0; | |
| 350 | + salary.GuaranteedSupplement = 0; | |
| 351 | + salary.FinalGrossSalary = salary.CalculatedGrossSalary; | |
| 352 | + salary.MonthlyTrainingSubsidy = 0; | |
| 353 | + salary.MonthlyTransportSubsidy = 0; | |
| 354 | + salary.LastMonthTrainingSubsidy = 0; | |
| 355 | + salary.LastMonthTransportSubsidy = 0; | |
| 356 | + salary.TotalSubsidy = 0; | |
| 357 | + salary.MissingCard = 0; | |
| 358 | + salary.LateArrival = 0; | |
| 359 | + salary.LeaveDeduction = 0; | |
| 360 | + salary.SocialInsuranceDeduction = 0; | |
| 361 | + salary.RewardDeduction = 0; | |
| 362 | + salary.AccommodationDeduction = 0; | |
| 363 | + salary.StudyPeriodDeduction = 0; | |
| 364 | + salary.WorkClothesDeduction = 0; | |
| 365 | + salary.TotalDeduction = 0; | |
| 366 | + salary.Bonus = 0; | |
| 367 | + salary.ReturnPhoneDeposit = 0; | |
| 368 | + salary.ReturnAccommodationDeposit = 0; | |
| 369 | + salary.ActualSalary = salary.FinalGrossSalary - salary.TotalDeduction + salary.TotalSubsidy + salary.Bonus; | |
| 370 | + salary.MonthlyPaymentStatus = ""; | |
| 371 | + salary.PaidAmount = 0; | |
| 372 | + salary.PendingAmount = salary.ActualSalary; | |
| 373 | + salary.LastMonthSupplement = 0; | |
| 374 | + salary.MonthlyTotalPayment = 0; | |
| 375 | + | |
| 376 | + teacherStats[teacherId] = salary; | |
| 377 | + } | |
| 378 | + | |
| 379 | + // 4. 重新计算合并后的业绩提成(如果同一个老师归属于多个门店) | |
| 380 | + foreach (var salary in teacherStats.Values) | |
| 381 | + { | |
| 382 | + if (salary.IsTerminated == 0) | |
| 383 | + { | |
| 384 | + // 重新计算业绩提成(基于合并后的总业绩) | |
| 385 | + var performanceCommissionResult = CalculatePerformanceCommission(salary.TotalPerformance); | |
| 386 | + salary.PerformanceCommissionRate = performanceCommissionResult.Rate; | |
| 387 | + salary.PerformanceCommissionAmount = performanceCommissionResult.Amount; | |
| 388 | + salary.TotalCommission = salary.PerformanceCommissionAmount; | |
| 389 | + | |
| 390 | + // 重新计算应发工资 | |
| 391 | + salary.CalculatedGrossSalary = salary.BaseSalary + salary.TotalCommission + salary.HandworkFee; | |
| 392 | + salary.FinalGrossSalary = salary.CalculatedGrossSalary; | |
| 393 | + salary.ActualSalary = salary.FinalGrossSalary - salary.TotalDeduction + salary.TotalSubsidy + salary.Bonus; | |
| 394 | + salary.PendingAmount = salary.ActualSalary; | |
| 395 | + } | |
| 396 | + } | |
| 397 | + | |
| 398 | + // 5. 保存数据 | |
| 399 | + if (teacherStats.Any()) | |
| 400 | + { | |
| 401 | + // 先删除当月旧数据 (防止重复) | |
| 402 | + await _db.Deleteable<LqMajorProjectTeacherSalaryStatisticsEntity>() | |
| 403 | + .Where(x => x.StatisticsMonth == monthStr) | |
| 404 | + .ExecuteCommandAsync(); | |
| 405 | + | |
| 406 | + await _db.Insertable(teacherStats.Values.ToList()).ExecuteCommandAsync(); | |
| 407 | + } | |
| 408 | + } | |
| 409 | + | |
| 410 | + /// <summary> | |
| 411 | + /// 计算业绩提成(阶梯式) | |
| 412 | + /// </summary> | |
| 413 | + /// <param name="totalPerformance">总业绩(门店总业绩)</param> | |
| 414 | + /// <returns>提成比例和金额</returns> | |
| 415 | + private (decimal Rate, decimal Amount) CalculatePerformanceCommission(decimal totalPerformance) | |
| 416 | + { | |
| 417 | + if (totalPerformance <= 200000m) | |
| 418 | + { | |
| 419 | + // ≤ 20万 → 0%(无提成) | |
| 420 | + return (0m, 0m); | |
| 421 | + } | |
| 422 | + else if (totalPerformance <= 1000000m) | |
| 423 | + { | |
| 424 | + // > 20万 且 ≤ 100万 → 2% | |
| 425 | + return (2m, totalPerformance * 0.02m); | |
| 426 | + } | |
| 427 | + else | |
| 428 | + { | |
| 429 | + // > 100万 → 2.5% | |
| 430 | + return (2.5m, totalPerformance * 0.025m); | |
| 431 | + } | |
| 432 | + } | |
| 433 | + } | |
| 434 | +} | |
| 435 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqMdMajorProjectTeacherAssignmentService.cs
| ... | ... | @@ -63,6 +63,8 @@ namespace NCC.Extend |
| 63 | 63 | month = it.Month, |
| 64 | 64 | teacherId = it.TeacherId, |
| 65 | 65 | teacherName = SqlFunc.Subqueryable<UserEntity>().Where(x => x.Id == it.TeacherId).Select(x => x.RealName), |
| 66 | + educationTeacherId = it.EducationTeacherId, | |
| 67 | + educationTeacherName = SqlFunc.Subqueryable<UserEntity>().Where(x => x.Id == it.EducationTeacherId).Select(x => x.RealName), | |
| 66 | 68 | remark = it.Remark, |
| 67 | 69 | createTime = it.CreateTime, |
| 68 | 70 | createUserId = it.CreateUserId, |
| ... | ... | @@ -91,6 +93,7 @@ namespace NCC.Extend |
| 91 | 93 | .WhereIF(!string.IsNullOrEmpty(input.year), p => p.Year == input.year) |
| 92 | 94 | .WhereIF(!string.IsNullOrEmpty(input.month), p => p.Month == input.month) |
| 93 | 95 | .WhereIF(!string.IsNullOrEmpty(input.teacherId), p => p.TeacherId == input.teacherId) |
| 96 | + .WhereIF(!string.IsNullOrEmpty(input.educationTeacherId), p => p.EducationTeacherId == input.educationTeacherId) | |
| 94 | 97 | .Select(it => new LqMdMajorProjectTeacherAssignmentListOutput |
| 95 | 98 | { |
| 96 | 99 | id = it.Id, |
| ... | ... | @@ -100,6 +103,8 @@ namespace NCC.Extend |
| 100 | 103 | month = it.Month, |
| 101 | 104 | teacherId = it.TeacherId, |
| 102 | 105 | teacherName = SqlFunc.Subqueryable<UserEntity>().Where(x => x.Id == it.TeacherId).Select(x => x.RealName), |
| 106 | + educationTeacherId = it.EducationTeacherId, | |
| 107 | + educationTeacherName = SqlFunc.Subqueryable<UserEntity>().Where(x => x.Id == it.EducationTeacherId).Select(x => x.RealName), | |
| 103 | 108 | remark = it.Remark, |
| 104 | 109 | createTime = it.CreateTime, |
| 105 | 110 | createUserId = it.CreateUserId, |
| ... | ... | @@ -212,6 +217,7 @@ namespace NCC.Extend |
| 212 | 217 | entity.Year = input.year; |
| 213 | 218 | entity.Month = input.month; |
| 214 | 219 | entity.TeacherId = input.teacherId; |
| 220 | + entity.EducationTeacherId = input.educationTeacherId; | |
| 215 | 221 | entity.Remark = input.remark; |
| 216 | 222 | entity.UpdateTime = DateTime.Now; |
| 217 | 223 | entity.UpdateUserId = userInfo.userId; |
| ... | ... | @@ -327,6 +333,7 @@ namespace NCC.Extend |
| 327 | 333 | Year = targetYearStr, |
| 328 | 334 | Month = targetMonthStr, |
| 329 | 335 | TeacherId = x.TeacherId, |
| 336 | + EducationTeacherId = x.EducationTeacherId, | |
| 330 | 337 | Remark = x.Remark, |
| 331 | 338 | CreateTime = DateTime.Now, |
| 332 | 339 | CreateUserId = _userManager.UserId, | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs
| ... | ... | @@ -152,20 +152,30 @@ namespace NCC.Extend |
| 152 | 152 | try |
| 153 | 153 | { |
| 154 | 154 | var row = dataTable.Rows[i]; |
| 155 | - var id = row[0]?.ToString()?.Trim(); | |
| 156 | - var employeeName = row[1]?.ToString()?.Trim(); | |
| 157 | - var employeePhone = row[2]?.ToString()?.Trim(); | |
| 158 | - var yearText = row[3]?.ToString()?.Trim(); | |
| 159 | - var monthText = row[4]?.ToString()?.Trim(); | |
| 160 | - var baseRewardPerformanceText = row[5]?.ToString()?.Trim(); | |
| 161 | - var cooperationRewardPerformanceText = row[6]?.ToString()?.Trim(); | |
| 162 | - var newCustomerPerformanceText = row[7]?.ToString()?.Trim(); | |
| 163 | - var newCustomerConversionRateText = row[8]?.ToString()?.Trim(); | |
| 164 | - var upgradePerformanceText = row[9]?.ToString()?.Trim(); | |
| 165 | - var upgradeConversionRateText = row[10]?.ToString()?.Trim(); | |
| 166 | - var upgradeCustomerCountText = row[11]?.ToString()?.Trim(); | |
| 167 | - var otherPerformanceAddText = row[12]?.ToString()?.Trim(); | |
| 168 | - var otherPerformanceSubtractText = row[13]?.ToString()?.Trim(); | |
| 155 | + // 安全获取列值,如果列不存在则返回空字符串 | |
| 156 | + var GetColumnValue = new Func<int, string>((colIndex) => | |
| 157 | + { | |
| 158 | + if (colIndex < row.ItemArray.Length) | |
| 159 | + { | |
| 160 | + return row[colIndex]?.ToString()?.Trim() ?? ""; | |
| 161 | + } | |
| 162 | + return ""; | |
| 163 | + }); | |
| 164 | + | |
| 165 | + var id = GetColumnValue(0); | |
| 166 | + var employeeName = GetColumnValue(1); | |
| 167 | + var employeePhone = GetColumnValue(2); | |
| 168 | + var yearText = GetColumnValue(3); | |
| 169 | + var monthText = GetColumnValue(4); | |
| 170 | + var baseRewardPerformanceText = GetColumnValue(5); | |
| 171 | + var cooperationRewardPerformanceText = GetColumnValue(6); | |
| 172 | + var newCustomerPerformanceText = GetColumnValue(7); | |
| 173 | + var newCustomerConversionRateText = GetColumnValue(8); | |
| 174 | + var upgradePerformanceText = GetColumnValue(9); | |
| 175 | + var upgradeConversionRateText = GetColumnValue(10); | |
| 176 | + var upgradeCustomerCountText = GetColumnValue(11); | |
| 177 | + var otherPerformanceAddText = GetColumnValue(12); | |
| 178 | + var otherPerformanceSubtractText = GetColumnValue(13); | |
| 169 | 179 | |
| 170 | 180 | // 跳过空行 |
| 171 | 181 | if (string.IsNullOrEmpty(employeeName) && string.IsNullOrEmpty(employeePhone)) |
| ... | ... | @@ -279,30 +289,67 @@ namespace NCC.Extend |
| 279 | 289 | { |
| 280 | 290 | try |
| 281 | 291 | { |
| 282 | - // 1. 根据健康师姓名和电话查找用户ID | |
| 283 | - var user = await _db.Queryable<UserEntity>() | |
| 284 | - .Where(u => u.RealName == item.EmployeeName && u.MobilePhone == item.EmployeePhone) | |
| 285 | - .FirstAsync(); | |
| 286 | - | |
| 287 | - if (user == null) | |
| 288 | - { | |
| 289 | - errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 不存在"); | |
| 290 | - failCount++; | |
| 291 | - continue; | |
| 292 | - } | |
| 293 | - | |
| 294 | - // 2. 检查是否已存在相同记录(根据健康师ID、年份、月份) | |
| 292 | + // 1. 查找用户ID(优先使用ID,否则通过姓名和电话查找) | |
| 293 | + UserEntity user = null; | |
| 295 | 294 | LqSalaryExtraCalculationEntity existingRecord = null; |
| 296 | 295 | |
| 297 | - // 如果提供了ID,先尝试根据ID查找 | |
| 296 | + // 如果提供了ID,先尝试根据ID查找现有记录,获取EmployeeId | |
| 298 | 297 | if (!string.IsNullOrEmpty(item.Id)) |
| 299 | 298 | { |
| 300 | 299 | existingRecord = await _db.Queryable<LqSalaryExtraCalculationEntity>() |
| 301 | 300 | .Where(x => x.Id == item.Id) |
| 302 | 301 | .FirstAsync(); |
| 302 | + | |
| 303 | + if (existingRecord != null) | |
| 304 | + { | |
| 305 | + // 通过EmployeeId查找用户 | |
| 306 | + user = await _db.Queryable<UserEntity>() | |
| 307 | + .Where(u => u.Id == existingRecord.EmployeeId) | |
| 308 | + .FirstAsync(); | |
| 309 | + } | |
| 310 | + } | |
| 311 | + | |
| 312 | + // 如果还没有找到用户,通过姓名和电话查找 | |
| 313 | + if (user == null) | |
| 314 | + { | |
| 315 | + // 处理姓名:去除"A"前缀 | |
| 316 | + var cleanName = item.EmployeeName; | |
| 317 | + if (cleanName.StartsWith("A")) | |
| 318 | + { | |
| 319 | + cleanName = cleanName.Substring(1).Trim(); | |
| 320 | + } | |
| 321 | + | |
| 322 | + // 处理电话:如果电话是"无"或空,只按姓名查找 | |
| 323 | + var phoneIsEmpty = string.IsNullOrWhiteSpace(item.EmployeePhone) || | |
| 324 | + item.EmployeePhone.Trim() == "无" || | |
| 325 | + item.EmployeePhone.Trim() == "00000000" || | |
| 326 | + item.EmployeePhone.Trim().Length < 8; // 排除明显无效的电话 | |
| 327 | + | |
| 328 | + if (phoneIsEmpty) | |
| 329 | + { | |
| 330 | + // 只按姓名查找(支持原姓名和去除前缀后的姓名) | |
| 331 | + user = await _db.Queryable<UserEntity>() | |
| 332 | + .Where(u => u.RealName == item.EmployeeName || u.RealName == cleanName) | |
| 333 | + .FirstAsync(); | |
| 334 | + } | |
| 335 | + else | |
| 336 | + { | |
| 337 | + // 同时匹配姓名和电话(支持原姓名和去除前缀后的姓名) | |
| 338 | + user = await _db.Queryable<UserEntity>() | |
| 339 | + .Where(u => (u.RealName == item.EmployeeName || u.RealName == cleanName) && | |
| 340 | + u.MobilePhone == item.EmployeePhone) | |
| 341 | + .FirstAsync(); | |
| 342 | + } | |
| 343 | + } | |
| 344 | + | |
| 345 | + if (user == null) | |
| 346 | + { | |
| 347 | + errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 不存在"); | |
| 348 | + failCount++; | |
| 349 | + continue; | |
| 303 | 350 | } |
| 304 | 351 | |
| 305 | - // 如果没有找到,则根据健康师ID、年份、月份查找 | |
| 352 | + // 2. 如果还没有找到现有记录,则根据健康师ID、年份、月份查找 | |
| 306 | 353 | if (existingRecord == null) |
| 307 | 354 | { |
| 308 | 355 | existingRecord = await _db.Queryable<LqSalaryExtraCalculationEntity>() | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
| ... | ... | @@ -939,6 +939,7 @@ namespace NCC.Extend.LqXhHyhk |
| 939 | 939 | IsEffective = StatusEnum.有效.GetHashCode(), |
| 940 | 940 | ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), |
| 941 | 941 | PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", |
| 942 | + BeautyType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), | |
| 942 | 943 | }; |
| 943 | 944 | allPxmxEntities.Add(lqXhPxmxEntity); |
| 944 | 945 | |
| ... | ... | @@ -973,7 +974,8 @@ namespace NCC.Extend.LqXhHyhk |
| 973 | 974 | ItemId = lqXhPxmxEntity.Px, |
| 974 | 975 | StoreId = entity.Md, |
| 975 | 976 | ItemName = lqXhPxmxEntity.Pxmc, |
| 976 | - PerformanceType = lqXhPxmxEntity.PerformanceType | |
| 977 | + PerformanceType = lqXhPxmxEntity.PerformanceType, | |
| 978 | + BeautyType = lqXhPxmxEntity.BeautyType, | |
| 977 | 979 | } |
| 978 | 980 | ); |
| 979 | 981 | } |
| ... | ... | @@ -1013,7 +1015,8 @@ namespace NCC.Extend.LqXhHyhk |
| 1013 | 1015 | ItemId = lqXhPxmxEntity.Px, |
| 1014 | 1016 | StoreId = entity.Md, |
| 1015 | 1017 | ItemName = lqXhPxmxEntity.Pxmc, |
| 1016 | - PerformanceType = lqXhPxmxEntity.PerformanceType | |
| 1018 | + PerformanceType = lqXhPxmxEntity.PerformanceType, | |
| 1019 | + BeautyType = lqXhPxmxEntity.BeautyType, | |
| 1017 | 1020 | } |
| 1018 | 1021 | ); |
| 1019 | 1022 | } |
| ... | ... | @@ -1317,6 +1320,7 @@ namespace NCC.Extend.LqXhHyhk |
| 1317 | 1320 | ProjectNumber = (decimal)((item.projectNumber ?? 0) + (entity.OvertimeCoefficient * (item.projectNumber ?? 0))), |
| 1318 | 1321 | ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), |
| 1319 | 1322 | PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", |
| 1323 | + BeautyType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), | |
| 1320 | 1324 | }; |
| 1321 | 1325 | allPxmxEntities.Add(lqXhPxmxEntity); |
| 1322 | 1326 | |
| ... | ... | @@ -1351,7 +1355,8 @@ namespace NCC.Extend.LqXhHyhk |
| 1351 | 1355 | ItemId = lqXhPxmxEntity.Px, |
| 1352 | 1356 | StoreId = entity.Md, |
| 1353 | 1357 | ItemName = lqXhPxmxEntity.Pxmc, |
| 1354 | - PerformanceType = lqXhPxmxEntity.PerformanceType | |
| 1358 | + PerformanceType = lqXhPxmxEntity.PerformanceType, | |
| 1359 | + BeautyType = lqXhPxmxEntity.BeautyType, | |
| 1355 | 1360 | } |
| 1356 | 1361 | ); |
| 1357 | 1362 | } |
| ... | ... | @@ -1390,7 +1395,8 @@ namespace NCC.Extend.LqXhHyhk |
| 1390 | 1395 | ItemId = lqXhPxmxEntity.Px, |
| 1391 | 1396 | StoreId = entity.Md, |
| 1392 | 1397 | ItemName = lqXhPxmxEntity.Pxmc, |
| 1393 | - PerformanceType = lqXhPxmxEntity.PerformanceType | |
| 1398 | + PerformanceType = lqXhPxmxEntity.PerformanceType, | |
| 1399 | + BeautyType = lqXhPxmxEntity.BeautyType, | |
| 1394 | 1400 | }); |
| 1395 | 1401 | } |
| 1396 | 1402 | } | ... | ... |
netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs
| 1 | 1 | using System; |
| 2 | +using System.Globalization; | |
| 2 | 3 | using System.IO; |
| 3 | 4 | using System.Linq; |
| 4 | 5 | using System.Text; |
| ... | ... | @@ -64,24 +65,33 @@ namespace NCC.System.Service.Common |
| 64 | 65 | if (!this.AllowFileType(fileType, type)) |
| 65 | 66 | throw NCCException.Oh(ErrorCode.D1800); |
| 66 | 67 | var _filePath = GetPathByType(type); |
| 67 | - var _fileName = DateTime.Now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + Path.GetExtension(file.FileName); | |
| 68 | + var now = DateTime.Now; | |
| 69 | + var _fileName = now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + Path.GetExtension(file.FileName); | |
| 68 | 70 | |
| 69 | - // annexpic 类型强制使用阿里云OSS存储 | |
| 71 | + // annexpic 类型强制使用阿里云OSS存储,并按天生成文件夹(不包含Files/SystemFile前缀) | |
| 70 | 72 | string forceStoreType = type == "annexpic" ? "aliyun-oss" : null; |
| 71 | - await UploadFileByType(file, _filePath, _fileName, forceStoreType); | |
| 73 | + string uploadFilePath = _filePath; | |
| 74 | + if (type == "annexpic") | |
| 75 | + { | |
| 76 | + // 按天生成文件夹:yyyy/MM/dd(直接使用日期文件夹,不包含Files/SystemFile前缀) | |
| 77 | + var dateFolder = now.ToString("yyyy/MM/dd"); | |
| 78 | + uploadFilePath = dateFolder; | |
| 79 | + } | |
| 80 | + await UploadFileByType(file, uploadFilePath, _fileName, forceStoreType); | |
| 72 | 81 | |
| 73 | 82 | // 如果是annexpic类型且使用阿里云OSS,返回OSS的完整访问地址 |
| 74 | 83 | string fileUrl; |
| 75 | 84 | if (type == "annexpic") |
| 76 | 85 | { |
| 77 | - fileUrl = await GetOSSAccessUrl(_filePath, _fileName); | |
| 86 | + fileUrl = await GetOSSAccessUrl(uploadFilePath, _fileName); | |
| 78 | 87 | } |
| 79 | 88 | else |
| 80 | 89 | { |
| 81 | 90 | fileUrl = string.Format("/api/File/Image/{0}/{1}", type, _fileName); |
| 82 | 91 | } |
| 83 | 92 | |
| 84 | - return new { name = _fileName, url = fileUrl }; | |
| 93 | + // 返回格式与数据库保存格式一致:name(原始文件名), fileId(生成的文件名), url(OSS地址) | |
| 94 | + return new { name = file.FileName, fileId = _fileName, url = fileUrl }; | |
| 85 | 95 | } |
| 86 | 96 | |
| 87 | 97 | /// <summary> |
| ... | ... | @@ -94,7 +104,40 @@ namespace NCC.System.Service.Common |
| 94 | 104 | [AllowAnonymous] |
| 95 | 105 | public async Task<IActionResult> GetImg(string type, string fileName) |
| 96 | 106 | { |
| 97 | - var filePath = Path.Combine(GetPathByType(type), fileName.Replace("@", ".")); | |
| 107 | + var basePath = GetPathByType(type); | |
| 108 | + var actualFileName = fileName.Replace("@", "."); | |
| 109 | + | |
| 110 | + // annexpic 类型需要从文件名中提取日期来构建路径(不包含Files/SystemFile前缀) | |
| 111 | + string filePath; | |
| 112 | + if (type == "annexpic") | |
| 113 | + { | |
| 114 | + // 文件名格式:yyyyMMdd_xxx.ext,提取日期部分 | |
| 115 | + if (actualFileName.Length >= 8) | |
| 116 | + { | |
| 117 | + var datePart = actualFileName.Substring(0, 8); // 前8位是日期 | |
| 118 | + if (DateTime.TryParseExact(datePart, "yyyyMMdd", null, DateTimeStyles.None, out DateTime fileDate)) | |
| 119 | + { | |
| 120 | + // 构建日期文件夹路径:yyyy/MM/dd(直接使用日期文件夹,不包含Files/SystemFile前缀) | |
| 121 | + var dateFolder = fileDate.ToString("yyyy/MM/dd"); | |
| 122 | + filePath = $"{dateFolder}/{actualFileName}"; | |
| 123 | + } | |
| 124 | + else | |
| 125 | + { | |
| 126 | + // 如果无法解析日期,使用原路径(兼容旧文件) | |
| 127 | + filePath = Path.Combine(basePath, actualFileName); | |
| 128 | + } | |
| 129 | + } | |
| 130 | + else | |
| 131 | + { | |
| 132 | + // 文件名长度不足,使用原路径(兼容旧文件) | |
| 133 | + filePath = Path.Combine(basePath, actualFileName); | |
| 134 | + } | |
| 135 | + } | |
| 136 | + else | |
| 137 | + { | |
| 138 | + filePath = Path.Combine(basePath, actualFileName); | |
| 139 | + } | |
| 140 | + | |
| 98 | 141 | // annexpic 类型强制使用阿里云OSS存储 |
| 99 | 142 | string forceStoreType = type == "annexpic" ? "aliyun-oss" : null; |
| 100 | 143 | return await DownloadFileByType(filePath, fileName, forceStoreType); |
| ... | ... | @@ -193,12 +236,46 @@ namespace NCC.System.Service.Common |
| 193 | 236 | var fileName = paramsList.Count > 1 ? paramsList[1] : ""; |
| 194 | 237 | string type = paramsList.Count > 2 ? paramsList[2] : ""; |
| 195 | 238 | string exname = paramsList.Count > 3 ? paramsList[3] : ""; |
| 196 | - var filePath = Path.Combine(GetPathByType(type), fileName.Replace("@", ".")); | |
| 239 | + | |
| 240 | + var basePath = GetPathByType(type); | |
| 241 | + var actualFileName = fileName.Replace("@", "."); | |
| 242 | + | |
| 243 | + // annexpic 类型需要从文件名中提取日期来构建路径(不包含Files/SystemFile前缀) | |
| 244 | + string filePath; | |
| 245 | + if (type == "annexpic") | |
| 246 | + { | |
| 247 | + // 文件名格式:yyyyMMdd_xxx.ext,提取日期部分 | |
| 248 | + if (actualFileName.Length >= 8) | |
| 249 | + { | |
| 250 | + var datePart = actualFileName.Substring(0, 8); // 前8位是日期 | |
| 251 | + if (DateTime.TryParseExact(datePart, "yyyyMMdd", null, DateTimeStyles.None, out DateTime fileDate)) | |
| 252 | + { | |
| 253 | + // 构建日期文件夹路径:yyyy/MM/dd(直接使用日期文件夹,不包含Files/SystemFile前缀) | |
| 254 | + var dateFolder = fileDate.ToString("yyyy/MM/dd"); | |
| 255 | + filePath = $"{dateFolder}/{actualFileName}"; | |
| 256 | + } | |
| 257 | + else | |
| 258 | + { | |
| 259 | + // 如果无法解析日期,使用原路径(兼容旧文件) | |
| 260 | + filePath = Path.Combine(basePath, actualFileName); | |
| 261 | + } | |
| 262 | + } | |
| 263 | + else | |
| 264 | + { | |
| 265 | + // 文件名长度不足,使用原路径(兼容旧文件) | |
| 266 | + filePath = Path.Combine(basePath, actualFileName); | |
| 267 | + } | |
| 268 | + } | |
| 269 | + else | |
| 270 | + { | |
| 271 | + filePath = Path.Combine(basePath, actualFileName); | |
| 272 | + } | |
| 273 | + | |
| 197 | 274 | var fileDownloadName = exname; |
| 198 | 275 | if (fileDownloadName.IsNullOrWhiteSpace() || fileDownloadName.Split('.').Length < 2) |
| 199 | 276 | fileDownloadName = Path.GetFileName(filePath); |
| 200 | 277 | if (fileDownloadName.IsNullOrWhiteSpace()) |
| 201 | - fileDownloadName = fileName.Replace(GetPathByType(type), ""); | |
| 278 | + fileDownloadName = fileName.Replace(basePath, ""); | |
| 202 | 279 | // annexpic 类型强制使用阿里云OSS存储 |
| 203 | 280 | string forceStoreType = type == "annexpic" ? "aliyun-oss" : null; |
| 204 | 281 | return await DownloadFileByType(filePath, fileDownloadName, forceStoreType); |
| ... | ... | @@ -365,10 +442,21 @@ namespace NCC.System.Service.Common |
| 365 | 442 | |
| 366 | 443 | // 使用Uri对象来解析和替换域名,保留查询参数(签名信息) |
| 367 | 444 | var originalUri = new Uri(urlString); |
| 445 | + | |
| 446 | + // 提取域名(去掉协议和端口号) | |
| 447 | + var domainHost = domain.Replace("https://", "").Replace("http://", "").TrimEnd('/'); | |
| 448 | + // 如果域名中包含端口号,去掉端口号 | |
| 449 | + var colonIndex = domainHost.IndexOf(':'); | |
| 450 | + if (colonIndex > 0) | |
| 451 | + { | |
| 452 | + domainHost = domainHost.Substring(0, colonIndex); | |
| 453 | + } | |
| 454 | + | |
| 368 | 455 | var customUri = new UriBuilder(originalUri) |
| 369 | 456 | { |
| 370 | 457 | Scheme = domain.StartsWith("https://") ? "https" : "http", |
| 371 | - Host = domain.Replace("https://", "").Replace("http://", "").TrimEnd('/'), | |
| 458 | + Host = domainHost, | |
| 459 | + Port = -1, // 使用默认端口(http:80, https:443),不显示在URL中 | |
| 372 | 460 | // 确保保留查询参数(签名信息) |
| 373 | 461 | Query = originalUri.Query |
| 374 | 462 | }; |
| ... | ... | @@ -626,29 +714,54 @@ namespace NCC.System.Service.Common |
| 626 | 714 | throw NCCException.Oh($"不支持的图片格式: {imageFormat}"); |
| 627 | 715 | } |
| 628 | 716 | |
| 629 | - // 生成文件名 | |
| 630 | - var fileName = GenerateImageFileName(input.FileName, imageFormat); | |
| 631 | - | |
| 632 | 717 | // 获取存储路径 |
| 633 | 718 | var imageType = string.IsNullOrEmpty(input.ImageType) ? "temporary" : input.ImageType; |
| 634 | - var filePath = GetPathByType(imageType); | |
| 635 | 719 | |
| 636 | - // 确保目录存在 | |
| 637 | - if (!Directory.Exists(filePath)) | |
| 720 | + // 所有类型都上传到阿里云OSS存储 | |
| 721 | + string uploadFilePath; | |
| 722 | + string fileName; | |
| 723 | + var now = DateTime.Now; | |
| 724 | + | |
| 725 | + if (imageType == "annexpic") | |
| 726 | + { | |
| 727 | + // 生成文件名(格式与 Uploader 一致:yyyyMMdd_xxx.ext) | |
| 728 | + fileName = now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + "." + imageFormat; | |
| 729 | + // 按天生成文件夹:yyyy/MM/dd(直接使用日期文件夹,不包含Files/SystemFile前缀) | |
| 730 | + var dateFolder = now.ToString("yyyy/MM/dd"); | |
| 731 | + uploadFilePath = dateFolder; | |
| 732 | + } | |
| 733 | + else | |
| 734 | + { | |
| 735 | + // 生成文件名 | |
| 736 | + fileName = GenerateImageFileName(input.FileName, imageFormat); | |
| 737 | + // 获取原始路径,用于OSS存储 | |
| 738 | + var originalPath = GetPathByType(imageType).TrimEnd('/').TrimEnd('\\'); | |
| 739 | + // 按天生成文件夹:yyyy/MM/dd,并保留原始路径结构 | |
| 740 | + var dateFolder = now.ToString("yyyy/MM/dd"); | |
| 741 | + uploadFilePath = $"{originalPath}/{dateFolder}"; | |
| 742 | + } | |
| 743 | + | |
| 744 | + // 上传到OSS | |
| 745 | + var bucketName = KeyVariable.BucketName; | |
| 746 | + var ossPath = $"{uploadFilePath.TrimEnd('/').TrimEnd('\\')}/{fileName}"; | |
| 747 | + using (var stream = new MemoryStream(imageData)) | |
| 638 | 748 | { |
| 639 | - Directory.CreateDirectory(filePath); | |
| 749 | + await _oSSServiceFactory.Create("aliyun").PutObjectAsync(bucketName, ossPath, stream); | |
| 640 | 750 | } |
| 641 | 751 | |
| 642 | - // 保存图片文件 | |
| 643 | - var fullPath = Path.Combine(filePath, fileName); | |
| 644 | - await File.WriteAllBytesAsync(fullPath, imageData); | |
| 752 | + // 获取OSS的完整访问地址 | |
| 753 | + string accessUrl = await GetOSSAccessUrl(uploadFilePath, fileName); | |
| 645 | 754 | |
| 646 | - // 生成访问URL | |
| 647 | - var accessUrl = $"/api/File/Image/{imageType}/{fileName}"; | |
| 755 | + // 返回格式与数据库保存格式一致:name(原始文件名), fileId(生成的文件名), url(OSS地址) | |
| 756 | + // 对于Base64上传,如果没有提供原始文件名,使用生成的文件名作为name | |
| 757 | + var originalFileName = string.IsNullOrEmpty(input.FileName) | |
| 758 | + ? fileName | |
| 759 | + : $"{input.FileName}.{imageFormat}"; | |
| 648 | 760 | |
| 649 | 761 | return new |
| 650 | 762 | { |
| 651 | - name = fileName, | |
| 763 | + name = originalFileName, | |
| 764 | + fileId = fileName, | |
| 652 | 765 | url = accessUrl, |
| 653 | 766 | fileSize = imageData.Length, |
| 654 | 767 | imageFormat = imageFormat.ToUpper(), | ... | ... |
sql/创建事业部总经理经理工资统计表.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 创建事业部总经理/经理工资统计表 | |
| 3 | +-- 功能:存储事业部总经理/经理每月的工资计算数据,包括底薪、业绩提成、扣款、补贴、奖金、支付等信息 | |
| 4 | +-- 创建时间:2025年 | |
| 5 | +-- ============================================ | |
| 6 | + | |
| 7 | +-- 删除表(如果存在) | |
| 8 | +DROP TABLE IF EXISTS lq_business_unit_manager_salary_statistics; | |
| 9 | + | |
| 10 | +-- ============================================ | |
| 11 | +-- 创建事业部总经理/经理工资统计表 | |
| 12 | +-- ============================================ | |
| 13 | +CREATE TABLE lq_business_unit_manager_salary_statistics ( | |
| 14 | + -- 主键 | |
| 15 | + F_Id VARCHAR(50) NOT NULL COMMENT '主键ID', | |
| 16 | + | |
| 17 | + -- 一、基础信息字段 | |
| 18 | + F_StatisticsMonth VARCHAR(6) NOT NULL COMMENT '统计月份(YYYYMM格式)', | |
| 19 | + F_Position VARCHAR(50) NOT NULL COMMENT '核算岗位(总经理/经理)', | |
| 20 | + F_EmployeeName VARCHAR(100) NOT NULL COMMENT '员工姓名', | |
| 21 | + F_EmployeeId VARCHAR(50) NOT NULL COMMENT '员工ID', | |
| 22 | + F_EmployeeAccount VARCHAR(100) NULL COMMENT '员工账号', | |
| 23 | + F_ManagerType INT NOT NULL COMMENT '经理类型(0=经理,1=总经理)', | |
| 24 | + F_IsTerminated INT NOT NULL DEFAULT 0 COMMENT '是否离职(0=在职,1=离职)', | |
| 25 | + | |
| 26 | + -- 二、管理的门店信息(JSON格式) | |
| 27 | + F_StorePerformanceDetail TEXT NULL COMMENT '门店业绩明细(JSON格式,记录每个门店的业绩和提成详情)', | |
| 28 | + | |
| 29 | + -- 三、底薪相关字段 | |
| 30 | + F_BaseSalary DECIMAL(18,2) NOT NULL DEFAULT 4000.00 COMMENT '底薪金额(固定4000元)', | |
| 31 | + | |
| 32 | + -- 四、提成相关字段 | |
| 33 | + F_TotalCommission DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '提成合计(所有门店提成金额汇总)', | |
| 34 | + | |
| 35 | + -- 五、考勤相关字段 | |
| 36 | + F_WorkingDays DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '在店天数', | |
| 37 | + F_LeaveDays DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '请假天数', | |
| 38 | + | |
| 39 | + -- 六、工资计算字段 | |
| 40 | + F_CalculatedGrossSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '核算应发工资(底薪 + 提成合计)', | |
| 41 | + F_FinalGrossSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '最终应发工资(等于核算应发工资)', | |
| 42 | + | |
| 43 | + -- 七、补贴相关字段 | |
| 44 | + F_MonthlyTrainingSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月培训补贴', | |
| 45 | + F_MonthlyTransportSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月交通补贴', | |
| 46 | + F_LastMonthTrainingSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '上月培训补贴', | |
| 47 | + F_LastMonthTransportSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '上月交通补贴', | |
| 48 | + F_TotalSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '补贴合计', | |
| 49 | + | |
| 50 | + -- 八、扣款相关字段 | |
| 51 | + F_MissingCard DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '缺卡扣款', | |
| 52 | + F_LateArrival DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '迟到扣款', | |
| 53 | + F_LeaveDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '请假扣款', | |
| 54 | + F_SocialInsuranceDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣社保', | |
| 55 | + F_RewardDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣除奖励', | |
| 56 | + F_AccommodationDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣住宿费', | |
| 57 | + F_StudyPeriodDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣学习期费用', | |
| 58 | + F_WorkClothesDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣工作服费用', | |
| 59 | + F_TotalDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣款合计', | |
| 60 | + | |
| 61 | + -- 九、奖金相关字段 | |
| 62 | + F_Bonus DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '发奖金', | |
| 63 | + F_ReturnPhoneDeposit DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退手机押金', | |
| 64 | + F_ReturnAccommodationDeposit DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退住宿押金', | |
| 65 | + | |
| 66 | + -- 十、支付相关字段 | |
| 67 | + F_ActualSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '实发工资(最终应发工资 - 扣款合计 + 补贴合计 + 奖金)', | |
| 68 | + F_MonthlyPaymentStatus VARCHAR(20) NOT NULL DEFAULT '未发放' COMMENT '当月是否发放(已发放/未发放/部分发放)', | |
| 69 | + F_PaidAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '支付金额', | |
| 70 | + F_PendingAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '待支付金额', | |
| 71 | + F_LastMonthSupplement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '补发上月', | |
| 72 | + F_MonthlyTotalPayment DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月支付总额', | |
| 73 | + | |
| 74 | + -- 十一、系统字段 | |
| 75 | + F_IsLocked INT NOT NULL DEFAULT 0 COMMENT '是否锁定(0=未锁定,1=已锁定)', | |
| 76 | + F_CreateTime DATETIME NOT NULL COMMENT '创建时间', | |
| 77 | + F_UpdateTime DATETIME NOT NULL COMMENT '更新时间', | |
| 78 | + F_CreateUser VARCHAR(50) NULL COMMENT '创建人', | |
| 79 | + F_UpdateUser VARCHAR(50) NULL COMMENT '更新人', | |
| 80 | + | |
| 81 | + -- 主键约束 | |
| 82 | + PRIMARY KEY (F_Id), | |
| 83 | + | |
| 84 | + -- 唯一索引:确保同一员工同一月份只有一条记录 | |
| 85 | + UNIQUE KEY `uk_employee_month` (F_EmployeeId, F_StatisticsMonth), | |
| 86 | + | |
| 87 | + -- 普通索引 | |
| 88 | + KEY `idx_statistics_month` (F_StatisticsMonth), | |
| 89 | + KEY `idx_employee_id` (F_EmployeeId), | |
| 90 | + KEY `idx_employee_account` (F_EmployeeAccount), | |
| 91 | + KEY `idx_manager_type` (F_ManagerType), | |
| 92 | + KEY `idx_is_terminated` (F_IsTerminated), | |
| 93 | + KEY `idx_create_time` (F_CreateTime) | |
| 94 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='事业部总经理/经理工资统计表'; | |
| 95 | + | |
| 96 | +-- ============================================ | |
| 97 | +-- 表结构说明 | |
| 98 | +-- ============================================ | |
| 99 | +/* | |
| 100 | +表名:lq_business_unit_manager_salary_statistics(事业部总经理/经理工资统计表) | |
| 101 | + | |
| 102 | +功能说明: | |
| 103 | +1. 存储事业部总经理/经理每月的工资计算数据 | |
| 104 | +2. 包括底薪、业绩提成、扣款、补贴、奖金、支付等信息 | |
| 105 | +3. 支持按员工、月份查询 | |
| 106 | +4. 记录管理的门店汇总信息 | |
| 107 | + | |
| 108 | +主要字段说明: | |
| 109 | +- F_BaseSalary:底薪(固定4000元) | |
| 110 | +- F_StorePerformanceDetail:门店业绩明细(JSON格式,记录每个门店的业绩和提成详情) | |
| 111 | +- F_TotalCommission:提成合计(所有门店提成金额汇总) | |
| 112 | + | |
| 113 | +数据来源: | |
| 114 | +- 总经理/经理归属:lq_md_general_manager_lifeline 表(通过F_GeneralManagerId和F_Month获取) | |
| 115 | +- 门店生命线:lq_md_target 表的 F_StoreLifeline 字段 | |
| 116 | +- 提成阶梯:lq_md_general_manager_lifeline 表的 F_Lifeline1/2/3 和 F_CommissionRate1/2/3 | |
| 117 | +- 开单业绩:lq_kd_kdjlb 表的 sfyj 字段(按门店统计) | |
| 118 | +- 退卡业绩:lq_hytk_hytk 表的 F_ActualRefundAmount 或 tkje 字段(按门店统计) | |
| 119 | + | |
| 120 | +计算公式: | |
| 121 | +- 门店总业绩 = 开单业绩 - 退卡业绩 | |
| 122 | +- 提成计算逻辑: | |
| 123 | + 1. 判断是否达到提成门槛:门店业绩 ≥ 门店生命线? | |
| 124 | + - 如果否 → 该门店提成 = 0 | |
| 125 | + - 如果是 → 继续计算提成 | |
| 126 | + 2. 如果达到门槛,使用提成阶梯计算提成(分段累进): | |
| 127 | + - 业绩 ≤ 提成阶梯1:提成 = 业绩 × 提成比例1 | |
| 128 | + - 提成阶梯1 < 业绩 ≤ 提成阶梯2:提成 = 提成阶梯1 × 提成比例1 + (业绩 - 提成阶梯1) × 提成比例2 | |
| 129 | + - 提成阶梯2 < 业绩 ≤ 提成阶梯3:提成 = 提成阶梯1 × 提成比例1 + (提成阶梯2 - 提成阶梯1) × 提成比例2 + (业绩 - 提成阶梯2) × 提成比例3 | |
| 130 | + - 业绩 > 提成阶梯3:提成 = 提成阶梯1 × 提成比例1 + (提成阶梯2 - 提成阶梯1) × 提成比例2 + (提成阶梯3 - 提成阶梯2) × 提成比例3 + (业绩 - 提成阶梯3) × 提成比例3 | |
| 131 | +- 总提成 = SUM(各门店提成金额) | |
| 132 | +- 核算应发工资 = 底薪(4000) + 总提成 | |
| 133 | +- 最终应发工资 = 核算应发工资 | |
| 134 | +- 实发工资 = 最终应发工资 - 扣款合计 + 补贴合计 + 奖金 | |
| 135 | + | |
| 136 | +门店业绩明细JSON格式示例: | |
| 137 | +[ | |
| 138 | + { | |
| 139 | + "storeId": "门店ID", | |
| 140 | + "storeName": "门店名称", | |
| 141 | + "storeLifeline": 300000.00, | |
| 142 | + "billingPerformance": 400000.00, | |
| 143 | + "refundPerformance": 50000.00, | |
| 144 | + "storePerformance": 350000.00, | |
| 145 | + "reachedLifeline": true, | |
| 146 | + "lifeline1": 350000.00, | |
| 147 | + "commissionRate1": 1.00, | |
| 148 | + "lifeline2": 400000.00, | |
| 149 | + "commissionRate2": 1.50, | |
| 150 | + "lifeline3": 450000.00, | |
| 151 | + "commissionRate3": 2.00, | |
| 152 | + "commissionAmount": 3500.00, | |
| 153 | + "calculationDetail": "业绩350000元,达到门店生命线300000元,使用提成阶梯1计算:350000 × 1.0% = 3500元" | |
| 154 | + }, | |
| 155 | + { | |
| 156 | + "storeId": "门店ID2", | |
| 157 | + "storeName": "门店名称2", | |
| 158 | + "storeLifeline": 250000.00, | |
| 159 | + "billingPerformance": 220000.00, | |
| 160 | + "refundPerformance": 20000.00, | |
| 161 | + "storePerformance": 200000.00, | |
| 162 | + "reachedLifeline": false, | |
| 163 | + "commissionAmount": 0.00, | |
| 164 | + "calculationDetail": "业绩200000元,未达到门店生命线250000元,无提成" | |
| 165 | + } | |
| 166 | +] | |
| 167 | + | |
| 168 | +JSON字段说明: | |
| 169 | +- storeId:门店ID | |
| 170 | +- storeName:门店名称 | |
| 171 | +- storeLifeline:门店生命线(从lq_md_target表获取) | |
| 172 | +- billingPerformance:门店开单业绩 | |
| 173 | +- refundPerformance:门店退卡业绩 | |
| 174 | +- storePerformance:门店总业绩(开单业绩 - 退卡业绩) | |
| 175 | +- reachedLifeline:是否达到门店生命线(true/false) | |
| 176 | +- lifeline1/2/3:提成阶梯1/2/3(从lq_md_general_manager_lifeline表获取) | |
| 177 | +- commissionRate1/2/3:提成比例1/2/3(%) | |
| 178 | +- commissionAmount:该门店的提成金额 | |
| 179 | +- calculationDetail:计算说明(文字描述) | |
| 180 | + | |
| 181 | +索引说明: | |
| 182 | +- 主键索引:F_Id | |
| 183 | +- 唯一索引:F_EmployeeId + F_StatisticsMonth(确保同一员工同一月份只有一条记录) | |
| 184 | +- 普通索引: | |
| 185 | + - F_StatisticsMonth:按月份查询 | |
| 186 | + - F_EmployeeId:按员工查询 | |
| 187 | + - F_ManagerType:按经理类型查询(总经理/经理) | |
| 188 | + - F_CreateTime:按创建时间查询 | |
| 189 | + | |
| 190 | +数据校验要求: | |
| 191 | +1. 底薪固定为4000元 | |
| 192 | +2. 门店生命线必须设置,未设置应报错 | |
| 193 | +3. 提成阶梯1和提成比例1必须设置,未设置应报错 | |
| 194 | +4. 提成必须按照阶梯式计算(分段累进) | |
| 195 | +5. 如果门店业绩 < 门店生命线,则该门店提成为0 | |
| 196 | +6. 总经理和经理的计算规则相同 | |
| 197 | +*/ | |
| 198 | + | ... | ... |
sql/创建大项目部老师工资统计表.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 创建大项目部老师工资统计表 | |
| 3 | +-- 功能:存储大项目部老师每月的工资计算数据,包括底薪、业绩提成、扣款、补贴、奖金、支付等信息 | |
| 4 | +-- 创建时间:2025年 | |
| 5 | +-- ============================================ | |
| 6 | + | |
| 7 | +-- 删除表(如果存在) | |
| 8 | +DROP TABLE IF EXISTS lq_major_project_teacher_salary_statistics; | |
| 9 | + | |
| 10 | +-- ============================================ | |
| 11 | +-- 创建大项目部老师工资统计表 | |
| 12 | +-- ============================================ | |
| 13 | +CREATE TABLE lq_major_project_teacher_salary_statistics ( | |
| 14 | + -- 主键 | |
| 15 | + F_Id VARCHAR(50) NOT NULL COMMENT '主键ID', | |
| 16 | + | |
| 17 | + -- 一、基础信息字段 | |
| 18 | + F_StatisticsMonth VARCHAR(6) NOT NULL COMMENT '统计月份(YYYYMM格式)', | |
| 19 | + F_StoreId VARCHAR(50) NOT NULL COMMENT '门店ID', | |
| 20 | + F_StoreName VARCHAR(200) NOT NULL COMMENT '门店名称', | |
| 21 | + F_Position VARCHAR(50) NOT NULL DEFAULT '大项目部老师' COMMENT '核算岗位(固定为"大项目部老师")', | |
| 22 | + F_EmployeeName VARCHAR(100) NOT NULL COMMENT '员工姓名', | |
| 23 | + F_EmployeeId VARCHAR(50) NOT NULL COMMENT '员工ID', | |
| 24 | + F_EmployeeAccount VARCHAR(100) NULL COMMENT '员工账号', | |
| 25 | + F_StoreType INT NULL COMMENT '门店类型(200平/旗舰店)', | |
| 26 | + F_StoreCategory INT NULL COMMENT '门店分类(1=A类,2=B类,3=C类)', | |
| 27 | + F_IsNewStore VARCHAR(10) NULL COMMENT '是否新店(是/否)', | |
| 28 | + F_NewStoreProtectionStage INT NOT NULL DEFAULT 0 COMMENT '新店保护阶段(0/1/2)', | |
| 29 | + | |
| 30 | + -- 二、业绩相关字段 | |
| 31 | + F_OrderAchievement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '开单业绩(从lq_kd_kjbsyj表统计,根据归属表关联)', | |
| 32 | + F_ConsumeAchievement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '消耗业绩(从lq_xh_kjbsyj表统计,根据归属表关联)', | |
| 33 | + F_RefundAchievement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退卡业绩(从lq_hytk_kjbsyj表统计,根据归属表关联)', | |
| 34 | + F_TotalPerformance DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '总业绩(开单业绩 + 消耗业绩 + 退卡业绩,用于业绩提成计算)', | |
| 35 | + | |
| 36 | + -- 三、底薪相关字段 | |
| 37 | + F_BaseSalary DECIMAL(18,2) NOT NULL DEFAULT 3000.00 COMMENT '底薪金额(固定3000元)', | |
| 38 | + | |
| 39 | + -- 四、业绩提成相关字段 | |
| 40 | + F_PerformanceCommissionRate DECIMAL(18,4) NOT NULL DEFAULT 0.0000 COMMENT '业绩提成比例(百分比,如2.00表示2%,0.00表示无提成)', | |
| 41 | + F_PerformanceCommissionAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '业绩提成金额(总业绩 × 业绩提成比例,阶梯式计算)', | |
| 42 | + F_TotalCommission DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '提成合计(业绩提成金额)', | |
| 43 | + | |
| 44 | + -- 五、其他收入字段 | |
| 45 | + F_HandworkFee DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '手工费', | |
| 46 | + F_TransportationAllowance DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '车补', | |
| 47 | + F_LessRest DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '少休费', | |
| 48 | + F_FullAttendance DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '全勤奖', | |
| 49 | + | |
| 50 | + -- 六、考勤相关字段 | |
| 51 | + F_WorkingDays DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '在店天数', | |
| 52 | + F_LeaveDays DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '请假天数', | |
| 53 | + | |
| 54 | + -- 七、工资计算字段 | |
| 55 | + F_CalculatedGrossSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '核算应发工资(底薪 + 提成合计 + 其他收入)', | |
| 56 | + F_GuaranteedSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '保底工资', | |
| 57 | + F_GuaranteedLeaveDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '保底请假扣款', | |
| 58 | + F_GuaranteedBaseSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '保底底薪', | |
| 59 | + F_GuaranteedSupplement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '保底补差', | |
| 60 | + F_FinalGrossSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '最终应发工资(取核算应发工资和保底工资的较大值)', | |
| 61 | + | |
| 62 | + -- 八、补贴相关字段 | |
| 63 | + F_MonthlyTrainingSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月培训补贴', | |
| 64 | + F_MonthlyTransportSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月交通补贴', | |
| 65 | + F_LastMonthTrainingSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '上月培训补贴', | |
| 66 | + F_LastMonthTransportSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '上月交通补贴', | |
| 67 | + F_TotalSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '补贴合计', | |
| 68 | + | |
| 69 | + -- 九、扣款相关字段 | |
| 70 | + F_MissingCard DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '缺卡扣款', | |
| 71 | + F_LateArrival DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '迟到扣款', | |
| 72 | + F_LeaveDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '请假扣款', | |
| 73 | + F_SocialInsuranceDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣社保', | |
| 74 | + F_RewardDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣除奖励', | |
| 75 | + F_AccommodationDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣住宿费', | |
| 76 | + F_StudyPeriodDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣学习期费用', | |
| 77 | + F_WorkClothesDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣工作服费用', | |
| 78 | + F_TotalDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣款合计', | |
| 79 | + | |
| 80 | + -- 十、奖金相关字段 | |
| 81 | + F_Bonus DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '发奖金', | |
| 82 | + F_ReturnPhoneDeposit DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退手机押金', | |
| 83 | + F_ReturnAccommodationDeposit DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退住宿押金', | |
| 84 | + | |
| 85 | + -- 十一、支付相关字段 | |
| 86 | + F_ActualSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '实发工资(最终应发工资 - 扣款合计 + 补贴合计 + 奖金)', | |
| 87 | + F_MonthlyPaymentStatus VARCHAR(20) NOT NULL DEFAULT '未发放' COMMENT '当月是否发放(已发放/未发放/部分发放)', | |
| 88 | + F_PaidAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '支付金额', | |
| 89 | + F_PendingAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '待支付金额', | |
| 90 | + F_LastMonthSupplement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '补发上月', | |
| 91 | + F_MonthlyTotalPayment DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月支付总额', | |
| 92 | + | |
| 93 | + -- 十二、系统字段 | |
| 94 | + F_IsLocked INT NOT NULL DEFAULT 0 COMMENT '是否锁定(0=未锁定,1=已锁定)', | |
| 95 | + F_IsTerminated INT NOT NULL DEFAULT 0 COMMENT '是否离职(0=在职,1=离职)', | |
| 96 | + F_CreateTime DATETIME NOT NULL COMMENT '创建时间', | |
| 97 | + F_UpdateTime DATETIME NOT NULL COMMENT '更新时间', | |
| 98 | + F_CreateUser VARCHAR(50) NULL COMMENT '创建人', | |
| 99 | + F_UpdateUser VARCHAR(50) NULL COMMENT '更新人', | |
| 100 | + | |
| 101 | + -- 主键约束 | |
| 102 | + PRIMARY KEY (F_Id), | |
| 103 | + | |
| 104 | + -- 唯一索引:确保同一员工同一月份只有一条记录 | |
| 105 | + UNIQUE KEY `uk_employee_month` (F_EmployeeId, F_StatisticsMonth), | |
| 106 | + | |
| 107 | + -- 普通索引 | |
| 108 | + KEY `idx_store_id` (F_StoreId), | |
| 109 | + KEY `idx_statistics_month` (F_StatisticsMonth), | |
| 110 | + KEY `idx_employee_id` (F_EmployeeId), | |
| 111 | + KEY `idx_employee_account` (F_EmployeeAccount), | |
| 112 | + KEY `idx_store_category` (F_StoreCategory), | |
| 113 | + KEY `idx_is_terminated` (F_IsTerminated), | |
| 114 | + KEY `idx_create_time` (F_CreateTime) | |
| 115 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='大项目部老师工资统计表'; | |
| 116 | + | |
| 117 | +-- ============================================ | |
| 118 | +-- 表结构说明 | |
| 119 | +-- ============================================ | |
| 120 | +/* | |
| 121 | +表名:lq_major_project_teacher_salary_statistics(大项目部老师工资统计表) | |
| 122 | + | |
| 123 | +功能说明: | |
| 124 | +1. 存储大项目部老师每月的工资计算数据 | |
| 125 | +2. 包括底薪、业绩提成、扣款、补贴、奖金、支付等信息 | |
| 126 | +3. 支持按门店、员工、月份查询 | |
| 127 | + | |
| 128 | +主要字段说明: | |
| 129 | +- F_BaseSalary:底薪(固定3000元) | |
| 130 | +- F_TotalPerformance:总业绩(开单业绩 + 消耗业绩 + 退卡业绩),用于计算业绩提成 | |
| 131 | +- F_PerformanceCommissionRate:业绩提成比例(阶梯式:≤20万无提成,>20万且≤100万为2%,>100万为2.5%) | |
| 132 | +- F_PerformanceCommissionAmount:业绩提成金额(总业绩 × 业绩提成比例) | |
| 133 | +- F_TotalCommission:提成合计(等于业绩提成金额) | |
| 134 | + | |
| 135 | +数据来源: | |
| 136 | +- 老师归属:lq_md_major_project_teacher_assignment 表(每个月归属不一样) | |
| 137 | +- 开单业绩:lq_kd_kjbsyj 表(根据归属表关联) | |
| 138 | +- 消耗业绩:lq_xh_kjbsyj 表(根据归属表关联) | |
| 139 | +- 退卡业绩:lq_hytk_kjbsyj 表(根据归属表关联) | |
| 140 | + | |
| 141 | +计算公式: | |
| 142 | +- 总业绩 = 开单业绩 + 消耗业绩 + 退卡业绩 | |
| 143 | +- 业绩提成计算(阶梯式): | |
| 144 | + * 总业绩 ≤ 20万:无提成(0%) | |
| 145 | + * 总业绩 > 20万 且 ≤ 100万:2% | |
| 146 | + * 总业绩 > 100万:2.5% | |
| 147 | +- 提成金额 = 总业绩 × 对应提成比例 | |
| 148 | +- 核算应发工资 = 底薪(3000) + 提成合计 + 其他收入 | |
| 149 | +- 最终应发工资 = MAX(核算应发工资, 保底工资) | |
| 150 | +- 实发工资 = 最终应发工资 - 扣款合计 + 补贴合计 + 奖金 | |
| 151 | + | |
| 152 | +索引说明: | |
| 153 | +- 主键索引:F_Id | |
| 154 | +- 唯一索引:F_EmployeeId + F_StatisticsMonth(确保同一员工同一月份只有一条记录) | |
| 155 | +- 普通索引: | |
| 156 | + - F_StoreId:按门店查询 | |
| 157 | + - F_StatisticsMonth:按月份查询 | |
| 158 | + - F_EmployeeId:按员工查询 | |
| 159 | + - F_StoreCategory:按门店分类查询 | |
| 160 | + - F_CreateTime:按创建时间查询 | |
| 161 | + | |
| 162 | +数据校验要求: | |
| 163 | +1. 底薪固定为3000元 | |
| 164 | +2. 业绩提成必须按照阶梯式计算 | |
| 165 | +3. 总业绩必须从归属表关联的业绩数据中统计 | |
| 166 | +*/ | |
| 167 | + | ... | ... |
sql/创建库存使用申请审批流程表.sql
sql/同步BeautyType字段数据.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 同步 F_BeautyType 字段数据 | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:从 lq_xmzl 表的 F_BeautyType 字段同步数据到其他相关表 | |
| 5 | +-- | |
| 6 | +-- 关联关系: | |
| 7 | +-- 1. lq_kd_pxmx: 通过 px 字段关联到 lq_xmzl.F_Id | |
| 8 | +-- 2. lq_kd_jksyj: 通过 F_kdpxid 关联到 lq_kd_pxmx,或通过 F_ItemId 直接关联到 lq_xmzl | |
| 9 | +-- 3. lq_kd_kjbsyj: 通过 F_kdpxid 关联到 lq_kd_pxmx,或通过 F_ItemId 直接关联到 lq_xmzl | |
| 10 | +-- 4. lq_hytk_mx: 通过 px 字段关联到 lq_xmzl.F_Id | |
| 11 | +-- 5. lq_hytk_jksyj: 通过 F_tkpxid 关联到 lq_hytk_mx,或通过 F_ItemId 直接关联到 lq_xmzl | |
| 12 | +-- 6. lq_hytk_kjbsyj: 通过 F_tkpxid 关联到 lq_hytk_mx,或通过 F_ItemId 直接关联到 lq_xmzl | |
| 13 | +-- 7. lq_xh_pxmx: 通过 px 字段关联到 lq_xmzl.F_Id | |
| 14 | +-- 8. lq_xh_jksyj: 通过 F_kdpxid 关联到 lq_xh_pxmx,或通过 F_ItemId 直接关联到 lq_xmzl | |
| 15 | +-- 9. lq_xh_kjbsyj: 通过 F_hkpxid 关联到 lq_xh_pxmx,或通过 F_ItemId 直接关联到 lq_xmzl | |
| 16 | +-- | |
| 17 | +-- 注意事项: | |
| 18 | +-- - 优先使用 F_ItemId 直接关联(如果存在且有效) | |
| 19 | +-- - 如果 F_ItemId 为空,则通过关联表间接关联 | |
| 20 | +-- - 只更新 F_BeautyType 为 NULL 或空字符串的记录 | |
| 21 | + | |
| 22 | +-- ============================================ | |
| 23 | +-- 1. 同步 lq_kd_pxmx(开单品项明细表) | |
| 24 | +-- ============================================ | |
| 25 | +UPDATE lq_kd_pxmx t1 | |
| 26 | +INNER JOIN lq_xmzl t2 ON t1.px = t2.F_Id | |
| 27 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 28 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 29 | + AND t2.F_BeautyType != '' | |
| 30 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 31 | + | |
| 32 | +-- ============================================ | |
| 33 | +-- 2. 同步 lq_kd_jksyj(开单健康师业绩表) | |
| 34 | +-- ============================================ | |
| 35 | +-- 方式1:通过 F_ItemId 直接关联(优先) | |
| 36 | +UPDATE lq_kd_jksyj t1 | |
| 37 | +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id | |
| 38 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 39 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 40 | + AND t2.F_BeautyType != '' | |
| 41 | + AND t1.F_ItemId IS NOT NULL | |
| 42 | + AND t1.F_ItemId != '' | |
| 43 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 44 | + | |
| 45 | +-- 方式2:通过 F_kdpxid 关联到 lq_kd_pxmx,再关联到 lq_xmzl | |
| 46 | +UPDATE lq_kd_jksyj t1 | |
| 47 | +INNER JOIN lq_kd_pxmx t2 ON t1.F_kdpxid = t2.F_Id | |
| 48 | +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id | |
| 49 | +SET t1.F_BeautyType = t3.F_BeautyType | |
| 50 | +WHERE t3.F_BeautyType IS NOT NULL | |
| 51 | + AND t3.F_BeautyType != '' | |
| 52 | + AND t1.F_kdpxid IS NOT NULL | |
| 53 | + AND t1.F_kdpxid != '' | |
| 54 | + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') | |
| 55 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 56 | + | |
| 57 | +-- ============================================ | |
| 58 | +-- 3. 同步 lq_kd_kjbsyj(开单科技部老师业绩表) | |
| 59 | +-- ============================================ | |
| 60 | +-- 方式1:通过 F_ItemId 直接关联(优先) | |
| 61 | +UPDATE lq_kd_kjbsyj t1 | |
| 62 | +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id | |
| 63 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 64 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 65 | + AND t2.F_BeautyType != '' | |
| 66 | + AND t1.F_ItemId IS NOT NULL | |
| 67 | + AND t1.F_ItemId != '' | |
| 68 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 69 | + | |
| 70 | +-- 方式2:通过 F_kdpxid 关联到 lq_kd_pxmx,再关联到 lq_xmzl | |
| 71 | +UPDATE lq_kd_kjbsyj t1 | |
| 72 | +INNER JOIN lq_kd_pxmx t2 ON t1.F_kdpxid = t2.F_Id | |
| 73 | +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id | |
| 74 | +SET t1.F_BeautyType = t3.F_BeautyType | |
| 75 | +WHERE t3.F_BeautyType IS NOT NULL | |
| 76 | + AND t3.F_BeautyType != '' | |
| 77 | + AND t1.F_kdpxid IS NOT NULL | |
| 78 | + AND t1.F_kdpxid != '' | |
| 79 | + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') | |
| 80 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 81 | + | |
| 82 | +-- ============================================ | |
| 83 | +-- 4. 同步 lq_hytk_mx(退卡品项明细表) | |
| 84 | +-- ============================================ | |
| 85 | +UPDATE lq_hytk_mx t1 | |
| 86 | +INNER JOIN lq_xmzl t2 ON t1.px = t2.F_Id | |
| 87 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 88 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 89 | + AND t2.F_BeautyType != '' | |
| 90 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 91 | + | |
| 92 | +-- ============================================ | |
| 93 | +-- 5. 同步 lq_hytk_jksyj(退卡健康师业绩表) | |
| 94 | +-- ============================================ | |
| 95 | +-- 方式1:通过 F_ItemId 直接关联(优先) | |
| 96 | +UPDATE lq_hytk_jksyj t1 | |
| 97 | +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id | |
| 98 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 99 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 100 | + AND t2.F_BeautyType != '' | |
| 101 | + AND t1.F_ItemId IS NOT NULL | |
| 102 | + AND t1.F_ItemId != '' | |
| 103 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 104 | + | |
| 105 | +-- 方式2:通过 F_tkpxid 关联到 lq_hytk_mx,再关联到 lq_xmzl | |
| 106 | +UPDATE lq_hytk_jksyj t1 | |
| 107 | +INNER JOIN lq_hytk_mx t2 ON t1.F_tkpxid = t2.F_Id | |
| 108 | +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id | |
| 109 | +SET t1.F_BeautyType = t3.F_BeautyType | |
| 110 | +WHERE t3.F_BeautyType IS NOT NULL | |
| 111 | + AND t3.F_BeautyType != '' | |
| 112 | + AND t1.F_tkpxid IS NOT NULL | |
| 113 | + AND t1.F_tkpxid != '' | |
| 114 | + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') | |
| 115 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 116 | + | |
| 117 | +-- ============================================ | |
| 118 | +-- 6. 同步 lq_hytk_kjbsyj(退卡科技部老师业绩表) | |
| 119 | +-- ============================================ | |
| 120 | +-- 方式1:通过 F_ItemId 直接关联(优先) | |
| 121 | +UPDATE lq_hytk_kjbsyj t1 | |
| 122 | +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id | |
| 123 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 124 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 125 | + AND t2.F_BeautyType != '' | |
| 126 | + AND t1.F_ItemId IS NOT NULL | |
| 127 | + AND t1.F_ItemId != '' | |
| 128 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 129 | + | |
| 130 | +-- 方式2:通过 F_tkpxid 关联到 lq_hytk_mx,再关联到 lq_xmzl | |
| 131 | +UPDATE lq_hytk_kjbsyj t1 | |
| 132 | +INNER JOIN lq_hytk_mx t2 ON t1.F_tkpxid = t2.F_Id | |
| 133 | +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id | |
| 134 | +SET t1.F_BeautyType = t3.F_BeautyType | |
| 135 | +WHERE t3.F_BeautyType IS NOT NULL | |
| 136 | + AND t3.F_BeautyType != '' | |
| 137 | + AND t1.F_tkpxid IS NOT NULL | |
| 138 | + AND t1.F_tkpxid != '' | |
| 139 | + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') | |
| 140 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 141 | + | |
| 142 | +-- ============================================ | |
| 143 | +-- 7. 同步 lq_xh_pxmx(耗卡品项明细表) | |
| 144 | +-- ============================================ | |
| 145 | +UPDATE lq_xh_pxmx t1 | |
| 146 | +INNER JOIN lq_xmzl t2 ON t1.px = t2.F_Id | |
| 147 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 148 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 149 | + AND t2.F_BeautyType != '' | |
| 150 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 151 | + | |
| 152 | +-- ============================================ | |
| 153 | +-- 8. 同步 lq_xh_jksyj(耗卡健康师业绩表) | |
| 154 | +-- ============================================ | |
| 155 | +-- 方式1:通过 F_ItemId 直接关联(优先) | |
| 156 | +UPDATE lq_xh_jksyj t1 | |
| 157 | +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id | |
| 158 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 159 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 160 | + AND t2.F_BeautyType != '' | |
| 161 | + AND t1.F_ItemId IS NOT NULL | |
| 162 | + AND t1.F_ItemId != '' | |
| 163 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 164 | + | |
| 165 | +-- 方式2:通过 F_kdpxid 关联到 lq_xh_pxmx,再关联到 lq_xmzl | |
| 166 | +UPDATE lq_xh_jksyj t1 | |
| 167 | +INNER JOIN lq_xh_pxmx t2 ON t1.F_kdpxid = t2.F_Id | |
| 168 | +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id | |
| 169 | +SET t1.F_BeautyType = t3.F_BeautyType | |
| 170 | +WHERE t3.F_BeautyType IS NOT NULL | |
| 171 | + AND t3.F_BeautyType != '' | |
| 172 | + AND t1.F_kdpxid IS NOT NULL | |
| 173 | + AND t1.F_kdpxid != '' | |
| 174 | + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') | |
| 175 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 176 | + | |
| 177 | +-- ============================================ | |
| 178 | +-- 9. 同步 lq_xh_kjbsyj(耗卡科技部老师业绩表) | |
| 179 | +-- ============================================ | |
| 180 | +-- 方式1:通过 F_ItemId 直接关联(优先) | |
| 181 | +UPDATE lq_xh_kjbsyj t1 | |
| 182 | +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id | |
| 183 | +SET t1.F_BeautyType = t2.F_BeautyType | |
| 184 | +WHERE t2.F_BeautyType IS NOT NULL | |
| 185 | + AND t2.F_BeautyType != '' | |
| 186 | + AND t1.F_ItemId IS NOT NULL | |
| 187 | + AND t1.F_ItemId != '' | |
| 188 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 189 | + | |
| 190 | +-- 方式2:通过 F_hkpxid 关联到 lq_xh_pxmx,再关联到 lq_xmzl | |
| 191 | +UPDATE lq_xh_kjbsyj t1 | |
| 192 | +INNER JOIN lq_xh_pxmx t2 ON t1.F_hkpxid = t2.F_Id | |
| 193 | +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id | |
| 194 | +SET t1.F_BeautyType = t3.F_BeautyType | |
| 195 | +WHERE t3.F_BeautyType IS NOT NULL | |
| 196 | + AND t3.F_BeautyType != '' | |
| 197 | + AND t1.F_hkpxid IS NOT NULL | |
| 198 | + AND t1.F_hkpxid != '' | |
| 199 | + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') | |
| 200 | + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); | |
| 201 | + | |
| 202 | +-- ============================================ | |
| 203 | +-- 10. 验证同步结果 | |
| 204 | +-- ============================================ | |
| 205 | +-- 查看各表同步的数据统计 | |
| 206 | +-- SELECT | |
| 207 | +-- 'lq_kd_pxmx' AS table_name, | |
| 208 | +-- COUNT(*) AS total_count, | |
| 209 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 210 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 211 | +-- FROM lq_kd_pxmx | |
| 212 | +-- UNION ALL | |
| 213 | +-- SELECT | |
| 214 | +-- 'lq_kd_jksyj' AS table_name, | |
| 215 | +-- COUNT(*) AS total_count, | |
| 216 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 217 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 218 | +-- FROM lq_kd_jksyj | |
| 219 | +-- UNION ALL | |
| 220 | +-- SELECT | |
| 221 | +-- 'lq_kd_kjbsyj' AS table_name, | |
| 222 | +-- COUNT(*) AS total_count, | |
| 223 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 224 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 225 | +-- FROM lq_kd_kjbsyj | |
| 226 | +-- UNION ALL | |
| 227 | +-- SELECT | |
| 228 | +-- 'lq_hytk_mx' AS table_name, | |
| 229 | +-- COUNT(*) AS total_count, | |
| 230 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 231 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 232 | +-- FROM lq_hytk_mx | |
| 233 | +-- UNION ALL | |
| 234 | +-- SELECT | |
| 235 | +-- 'lq_hytk_jksyj' AS table_name, | |
| 236 | +-- COUNT(*) AS total_count, | |
| 237 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 238 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 239 | +-- FROM lq_hytk_jksyj | |
| 240 | +-- UNION ALL | |
| 241 | +-- SELECT | |
| 242 | +-- 'lq_hytk_kjbsyj' AS table_name, | |
| 243 | +-- COUNT(*) AS total_count, | |
| 244 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 245 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 246 | +-- FROM lq_hytk_kjbsyj | |
| 247 | +-- UNION ALL | |
| 248 | +-- SELECT | |
| 249 | +-- 'lq_xh_pxmx' AS table_name, | |
| 250 | +-- COUNT(*) AS total_count, | |
| 251 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 252 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 253 | +-- FROM lq_xh_pxmx | |
| 254 | +-- UNION ALL | |
| 255 | +-- SELECT | |
| 256 | +-- 'lq_xh_jksyj' AS table_name, | |
| 257 | +-- COUNT(*) AS total_count, | |
| 258 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 259 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 260 | +-- FROM lq_xh_jksyj | |
| 261 | +-- UNION ALL | |
| 262 | +-- SELECT | |
| 263 | +-- 'lq_xh_kjbsyj' AS table_name, | |
| 264 | +-- COUNT(*) AS total_count, | |
| 265 | +-- COUNT(F_BeautyType) AS beauty_type_count, | |
| 266 | +-- COUNT(*) - COUNT(F_BeautyType) AS null_count | |
| 267 | +-- FROM lq_xh_kjbsyj; | |
| 268 | + | ... | ... |
sql/添加教育部老师字段.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 为门店大项目部老师归属设置表添加教育部老师字段 | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:在 lq_md_major_project_teacher_assignment 表中添加教育部老师字段 | |
| 5 | +-- | |
| 6 | +-- 字段说明: | |
| 7 | +-- F_EducationTeacherId:教育部老师用户ID,用于存储教育部老师的归属设置 | |
| 8 | +-- | |
| 9 | +-- 业务含义: | |
| 10 | +-- - 一个门店在一个月份可以同时有大项目部老师和教育部老师 | |
| 11 | +-- - 教育部老师ID关联BASE_USER.F_Id | |
| 12 | +-- | |
| 13 | +-- 注意事项: | |
| 14 | +-- - 字段类型为VARCHAR(50),允许为NULL(因为历史数据可能没有教育部老师) | |
| 15 | +-- - 字段位置:放在 F_TeacherId 字段之后 | |
| 16 | + | |
| 17 | +-- ============================================ | |
| 18 | +-- 添加教育部老师字段 | |
| 19 | +-- ============================================ | |
| 20 | +ALTER TABLE lq_md_major_project_teacher_assignment | |
| 21 | +ADD COLUMN F_EducationTeacherId VARCHAR(50) NULL COMMENT '教育部老师用户ID' AFTER F_TeacherId; | |
| 22 | + | |
| 23 | +-- ============================================ | |
| 24 | +-- 创建索引(可选,如果需要按教育部老师查询) | |
| 25 | +-- ============================================ | |
| 26 | +CREATE INDEX idx_education_teacher_id ON lq_md_major_project_teacher_assignment(F_EducationTeacherId); | |
| 27 | + | |
| 28 | +-- ============================================ | |
| 29 | +-- 验证字段创建 | |
| 30 | +-- ============================================ | |
| 31 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 32 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 33 | +-- WHERE TABLE_NAME = 'lq_md_major_project_teacher_assignment' | |
| 34 | +-- AND COLUMN_NAME = 'F_EducationTeacherId'; | |
| 35 | + | ... | ... |
sql/添加科技部归类字段.sql
sql/重命名科技部归类字段为BeautyType.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 重命名科技部归类字段为 F_BeautyType | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:将已创建的 F_KjbCategory 字段重命名为 F_BeautyType,以便与 lq_xmzl 表的字段名统一 | |
| 5 | +-- | |
| 6 | +-- 字段说明: | |
| 7 | +-- F_BeautyType:科美类型,用于存储品项的科美类型信息(与 lq_xmzl.F_BeautyType 统一) | |
| 8 | +-- | |
| 9 | +-- 业务含义: | |
| 10 | +-- - 科美类型用于对品项进行分类统计和分析 | |
| 11 | +-- - 分类值来源于项目资料表(lq_xmzl)的 F_BeautyType 字段 | |
| 12 | +-- | |
| 13 | +-- 注意事项: | |
| 14 | +-- - 此脚本用于重命名已创建的字段 | |
| 15 | +-- - 字段类型保持不变:VARCHAR(50),允许为NULL | |
| 16 | +-- - 字段位置保持不变:在 F_PerformanceType 字段之后 | |
| 17 | + | |
| 18 | +-- ============================================ | |
| 19 | +-- 1. lq_kd_pxmx(开单品项明细表) - 重命名字段 | |
| 20 | +-- ============================================ | |
| 21 | +ALTER TABLE lq_kd_pxmx | |
| 22 | +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; | |
| 23 | + | |
| 24 | +-- ============================================ | |
| 25 | +-- 2. lq_kd_jksyj(开单健康师业绩表) - 重命名字段 | |
| 26 | +-- ============================================ | |
| 27 | +ALTER TABLE lq_kd_jksyj | |
| 28 | +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; | |
| 29 | + | |
| 30 | +-- ============================================ | |
| 31 | +-- 3. lq_kd_kjbsyj(开单科技部老师业绩表) - 重命名字段 | |
| 32 | +-- ============================================ | |
| 33 | +ALTER TABLE lq_kd_kjbsyj | |
| 34 | +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; | |
| 35 | + | |
| 36 | +-- ============================================ | |
| 37 | +-- 4. lq_hytk_mx(退卡品项明细表) - 重命名字段 | |
| 38 | +-- ============================================ | |
| 39 | +ALTER TABLE lq_hytk_mx | |
| 40 | +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; | |
| 41 | + | |
| 42 | +-- ============================================ | |
| 43 | +-- 5. lq_hytk_jksyj(退卡健康师业绩表) - 重命名字段 | |
| 44 | +-- ============================================ | |
| 45 | +ALTER TABLE lq_hytk_jksyj | |
| 46 | +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; | |
| 47 | + | |
| 48 | +-- ============================================ | |
| 49 | +-- 6. lq_hytk_kjbsyj(退卡科技部老师业绩表) - 重命名字段 | |
| 50 | +-- ============================================ | |
| 51 | +ALTER TABLE lq_hytk_kjbsyj | |
| 52 | +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; | |
| 53 | + | |
| 54 | +-- ============================================ | |
| 55 | +-- 7. lq_xh_pxmx(耗卡品项明细表) - 添加字段 | |
| 56 | +-- ============================================ | |
| 57 | +-- 注意:如果字段已存在会报错,可以忽略;如果存在 F_KjbCategory 需要先重命名 | |
| 58 | +ALTER TABLE lq_xh_pxmx | |
| 59 | +ADD COLUMN F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)' AFTER F_PerformanceType; | |
| 60 | + | |
| 61 | +-- ============================================ | |
| 62 | +-- 8. lq_xh_jksyj(耗卡健康师业绩表) - 添加字段 | |
| 63 | +-- ============================================ | |
| 64 | +-- 注意:如果字段已存在会报错,可以忽略;如果存在 F_KjbCategory 需要先重命名 | |
| 65 | +ALTER TABLE lq_xh_jksyj | |
| 66 | +ADD COLUMN F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)' AFTER F_PerformanceType; | |
| 67 | + | |
| 68 | +-- ============================================ | |
| 69 | +-- 9. lq_xh_kjbsyj(耗卡科技部老师业绩表) - 添加字段 | |
| 70 | +-- ============================================ | |
| 71 | +-- 注意:如果字段已存在会报错,可以忽略;如果存在 F_KjbCategory 需要先重命名 | |
| 72 | +ALTER TABLE lq_xh_kjbsyj | |
| 73 | +ADD COLUMN F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)' AFTER F_PerformanceType; | |
| 74 | + | |
| 75 | +-- ============================================ | |
| 76 | +-- 10. 验证字段重命名 | |
| 77 | +-- ============================================ | |
| 78 | +-- 验证 lq_kd_pxmx 表 | |
| 79 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 80 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 81 | +-- WHERE TABLE_NAME = 'lq_kd_pxmx' | |
| 82 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 83 | + | |
| 84 | +-- 验证 lq_kd_jksyj 表 | |
| 85 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 86 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 87 | +-- WHERE TABLE_NAME = 'lq_kd_jksyj' | |
| 88 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 89 | + | |
| 90 | +-- 验证 lq_kd_kjbsyj 表 | |
| 91 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 92 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 93 | +-- WHERE TABLE_NAME = 'lq_kd_kjbsyj' | |
| 94 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 95 | + | |
| 96 | +-- 验证 lq_hytk_mx 表 | |
| 97 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 98 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 99 | +-- WHERE TABLE_NAME = 'lq_hytk_mx' | |
| 100 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 101 | + | |
| 102 | +-- 验证 lq_hytk_jksyj 表 | |
| 103 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 104 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 105 | +-- WHERE TABLE_NAME = 'lq_hytk_jksyj' | |
| 106 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 107 | + | |
| 108 | +-- 验证 lq_hytk_kjbsyj 表 | |
| 109 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 110 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 111 | +-- WHERE TABLE_NAME = 'lq_hytk_kjbsyj' | |
| 112 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 113 | + | |
| 114 | +-- 验证 lq_xh_pxmx 表 | |
| 115 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 116 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 117 | +-- WHERE TABLE_NAME = 'lq_xh_pxmx' | |
| 118 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 119 | + | |
| 120 | +-- 验证 lq_xh_jksyj 表 | |
| 121 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 122 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 123 | +-- WHERE TABLE_NAME = 'lq_xh_jksyj' | |
| 124 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 125 | + | |
| 126 | +-- 验证 lq_xh_kjbsyj 表 | |
| 127 | +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT | |
| 128 | +-- FROM INFORMATION_SCHEMA.COLUMNS | |
| 129 | +-- WHERE TABLE_NAME = 'lq_xh_kjbsyj' | |
| 130 | +-- AND COLUMN_NAME = 'F_BeautyType'; | |
| 131 | + | ... | ... |
事业部总经理经理工资计算规则梳理.md
0 → 100644
| 1 | +# 事业部总经理/经理工资计算规则梳理 | |
| 2 | + | |
| 3 | +## 📋 目录 | |
| 4 | +- [概述](#概述) | |
| 5 | +- [计算规则](#计算规则) | |
| 6 | +- [数据来源](#数据来源) | |
| 7 | +- [归属规则](#归属规则) | |
| 8 | +- [计算流程](#计算流程) | |
| 9 | + | |
| 10 | +--- | |
| 11 | + | |
| 12 | +## 📋 概述 | |
| 13 | + | |
| 14 | +事业部总经理/经理工资由以下几个部分组成: | |
| 15 | +1. **底薪**:固定4000元 | |
| 16 | +2. **提成**:根据管理的门店业绩,使用阶梯式提成计算(基于门店总业绩) | |
| 17 | + | |
| 18 | +**重要说明**: | |
| 19 | +- 每个总经理/经理都会管理多个门店 | |
| 20 | +- 提成计算基于门店总业绩(开单业绩 - 退卡业绩) | |
| 21 | +- 总经理和经理的计算规则相同 | |
| 22 | +- 必须先达到门店生命线才能计算提成 | |
| 23 | + | |
| 24 | +--- | |
| 25 | + | |
| 26 | +## 💰 计算规则 | |
| 27 | + | |
| 28 | +### 1. 底薪规则 | |
| 29 | + | |
| 30 | +**固定底薪**:4000元 | |
| 31 | + | |
| 32 | +- 无论业绩多少,底薪固定为4000元 | |
| 33 | +- 不设档位,不设条件 | |
| 34 | +- 不设考核扣款(与店长、主任不同) | |
| 35 | + | |
| 36 | +--- | |
| 37 | + | |
| 38 | +### 2. 提成规则(阶梯式) | |
| 39 | + | |
| 40 | +**提成计算方式**:根据管理的门店业绩,使用阶梯式提成计算 | |
| 41 | + | |
| 42 | +#### 2.1 提成门槛:门店生命线 | |
| 43 | + | |
| 44 | +**重要概念**:门店生命线是提成的**门槛条件**,不是提成阶梯 | |
| 45 | + | |
| 46 | +- **数据来源**:`lq_md_target` 表的 `F_StoreLifeline` 字段 | |
| 47 | +- **判断条件**: | |
| 48 | + - 如果门店业绩 < 门店生命线 → **无提成** | |
| 49 | + - 如果门店业绩 ≥ 门店生命线 → **可以计算提成** | |
| 50 | + | |
| 51 | +**重要说明**: | |
| 52 | +- 门店生命线是**必须设置的**,未设置应报错 | |
| 53 | +- 门店生命线是门店级别的指标,用于判断是否达到提成门槛 | |
| 54 | + | |
| 55 | +#### 2.2 提成阶梯设置 | |
| 56 | + | |
| 57 | +**数据来源**:`lq_md_general_manager_lifeline` 表 | |
| 58 | + | |
| 59 | +每个门店都有**三个等级的提成阶梯**,每个等级对应不同的提成比例: | |
| 60 | + | |
| 61 | +| 等级 | 生命线字段 | 提成比例字段 | 说明 | | |
| 62 | +|------|-----------|-------------|------| | |
| 63 | +| **等级1** | `F_Lifeline1` | `F_CommissionRate1` | 第一级提成阶梯(必填) | | |
| 64 | +| **等级2** | `F_Lifeline2` | `F_CommissionRate2` | 第二级提成阶梯(可选) | | |
| 65 | +| **等级3** | `F_Lifeline3` | `F_CommissionRate3` | 第三级提成阶梯(可选) | | |
| 66 | + | |
| 67 | +**重要说明**: | |
| 68 | +- 提成阶梯1和提成比例1为必填项 | |
| 69 | +- 提成阶梯2、3和对应的提成比例为可选项 | |
| 70 | +- 每个门店的提成阶梯设置可能不同 | |
| 71 | +- 提成阶梯由总经理在系统中配置 | |
| 72 | + | |
| 73 | +**注意**:提成阶梯(`lq_md_general_manager_lifeline`)和门店生命线(`lq_md_target.F_StoreLifeline`)是**两个不同的概念**: | |
| 74 | +- **门店生命线**:判断是否达到提成门槛 | |
| 75 | +- **提成阶梯**:计算提成金额的阶梯 | |
| 76 | + | |
| 77 | +#### 2.3 阶梯式提成计算规则 | |
| 78 | + | |
| 79 | +**前提条件**:门店业绩必须 ≥ 门店生命线,否则无提成 | |
| 80 | + | |
| 81 | +**计算逻辑**:根据门店业绩落在哪个提成阶梯区间,使用对应的提成比例计算(分段累进) | |
| 82 | + | |
| 83 | +**示例**(假设某门店的设置): | |
| 84 | +- 门店生命线(`lq_md_target.F_StoreLifeline`)= 300,000元 | |
| 85 | +- 提成阶梯1 = 350,000元,提成比例1 = 1.0% | |
| 86 | +- 提成阶梯2 = 400,000元,提成比例2 = 1.5% | |
| 87 | +- 提成阶梯3 = 450,000元,提成比例3 = 2.0% | |
| 88 | + | |
| 89 | +**计算规则**(分段累进): | |
| 90 | + | |
| 91 | +1. **业绩 < 门店生命线**: | |
| 92 | + - 提成 = 0(无提成) | |
| 93 | + - 示例:业绩 = 280,000元 → 提成 = 0 | |
| 94 | + | |
| 95 | +2. **门店生命线 ≤ 业绩 ≤ 提成阶梯1**: | |
| 96 | + - 提成 = 业绩 × 提成比例1 | |
| 97 | + - 示例:业绩 = 320,000元 → 提成 = 320,000 × 1.0% = 3,200元 | |
| 98 | + | |
| 99 | +3. **提成阶梯1 < 业绩 ≤ 提成阶梯2**: | |
| 100 | + - 提成 = 提成阶梯1 × 提成比例1 + (业绩 - 提成阶梯1) × 提成比例2 | |
| 101 | + - 示例:业绩 = 380,000元 → 提成 = 350,000 × 1.0% + (380,000 - 350,000) × 1.5% = 3,500 + 450 = 3,950元 | |
| 102 | + | |
| 103 | +4. **提成阶梯2 < 业绩 ≤ 提成阶梯3**: | |
| 104 | + - 提成 = 提成阶梯1 × 提成比例1 + (提成阶梯2 - 提成阶梯1) × 提成比例2 + (业绩 - 提成阶梯2) × 提成比例3 | |
| 105 | + - 示例:业绩 = 420,000元 → 提成 = 350,000 × 1.0% + (400,000 - 350,000) × 1.5% + (420,000 - 400,000) × 2.0% = 3,500 + 750 + 400 = 4,650元 | |
| 106 | + | |
| 107 | +5. **业绩 > 提成阶梯3**: | |
| 108 | + - 提成 = 提成阶梯1 × 提成比例1 + (提成阶梯2 - 提成阶梯1) × 提成比例2 + (提成阶梯3 - 提成阶梯2) × 提成比例3 + (业绩 - 提成阶梯3) × 提成比例3 | |
| 109 | + - 示例:业绩 = 500,000元 → 提成 = 350,000 × 1.0% + (400,000 - 350,000) × 1.5% + (450,000 - 400,000) × 2.0% + (500,000 - 450,000) × 2.0% = 3,500 + 750 + 1,000 + 1,000 = 6,250元 | |
| 110 | + | |
| 111 | +**注意**: | |
| 112 | +- 如果提成阶梯2或提成阶梯3未设置(为NULL或0),则只使用提成阶梯1计算 | |
| 113 | +- 如果提成阶梯2设置但提成阶梯3未设置,则业绩超过提成阶梯2的部分按提成比例2计算 | |
| 114 | + | |
| 115 | +#### 2.4 多门店提成汇总 | |
| 116 | + | |
| 117 | +**计算方式**: | |
| 118 | +- 总经理/经理管理的所有门店的提成分别计算 | |
| 119 | +- 将所有门店的提成金额汇总,得到总提成金额 | |
| 120 | + | |
| 121 | +**计算公式**: | |
| 122 | +``` | |
| 123 | +总提成 = SUM(各门店提成金额) | |
| 124 | +``` | |
| 125 | + | |
| 126 | +--- | |
| 127 | + | |
| 128 | +## 📊 数据来源 | |
| 129 | + | |
| 130 | +### 1. 总经理/经理列表和门店归属 | |
| 131 | + | |
| 132 | +**数据来源表**:`lq_md_general_manager_lifeline`(总经理门店生命线设置表) | |
| 133 | + | |
| 134 | +**关键字段**: | |
| 135 | +- `F_GeneralManagerId`:总经理/经理用户ID | |
| 136 | +- `F_ManagerType`:经理类型(0=经理,1=总经理) | |
| 137 | +- `F_StoreId`:门店ID | |
| 138 | +- `F_Month`:月份(YYYYMM格式) | |
| 139 | + | |
| 140 | +**查询方式**: | |
| 141 | +1. **获取当月所有的经理和总经理**: | |
| 142 | + ```sql | |
| 143 | + SELECT DISTINCT F_GeneralManagerId, F_ManagerType | |
| 144 | + FROM lq_md_general_manager_lifeline | |
| 145 | + WHERE F_Month = 'YYYYMM' | |
| 146 | + ``` | |
| 147 | + | |
| 148 | +2. **获取总经理/经理管理的门店**: | |
| 149 | + ```sql | |
| 150 | + SELECT F_StoreId | |
| 151 | + FROM lq_md_general_manager_lifeline | |
| 152 | + WHERE F_Month = 'YYYYMM' | |
| 153 | + AND F_GeneralManagerId = '用户ID' | |
| 154 | + ``` | |
| 155 | + | |
| 156 | +**重要说明**: | |
| 157 | +- 通过 `lq_md_general_manager_lifeline` 表可以获取当月所有的经理和总经理 | |
| 158 | +- 通过 `lq_md_general_manager_lifeline` 表可以看到当月总经理和经理管理的门店 | |
| 159 | +- 每个门店在每个月可以设置一个总经理/经理的提成阶梯 | |
| 160 | + | |
| 161 | +### 2. 门店生命线(提成门槛) | |
| 162 | + | |
| 163 | +**数据来源表**:`lq_md_target`(门店目标表) | |
| 164 | + | |
| 165 | +**关键字段**: | |
| 166 | +- `F_StoreId`:门店ID | |
| 167 | +- `F_Month`:月份(YYYYMM格式) | |
| 168 | +- `F_StoreLifeline`:门店生命线(**必须设置**) | |
| 169 | + | |
| 170 | +**查询条件**: | |
| 171 | +- 按门店ID和月份查询:`F_StoreId = '门店ID' AND F_Month = 'YYYYMM'` | |
| 172 | + | |
| 173 | +**重要说明**: | |
| 174 | +- 门店生命线是**必须设置的**,未设置应报错 | |
| 175 | +- 门店生命线用于判断是否达到提成门槛 | |
| 176 | +- 如果门店业绩 < 门店生命线,则无提成 | |
| 177 | + | |
| 178 | +### 3. 提成阶梯设置 | |
| 179 | + | |
| 180 | +**数据来源表**:`lq_md_general_manager_lifeline`(总经理门店生命线设置表) | |
| 181 | + | |
| 182 | +**关键字段**: | |
| 183 | +- `F_StoreId`:门店ID | |
| 184 | +- `F_Month`:月份(YYYYMM格式) | |
| 185 | +- `F_GeneralManagerId`:总经理/经理用户ID | |
| 186 | +- `F_Lifeline1`:第一级提成阶梯(必填) | |
| 187 | +- `F_CommissionRate1`:第一级提成比例(%,必填) | |
| 188 | +- `F_Lifeline2`:第二级提成阶梯(可选) | |
| 189 | +- `F_CommissionRate2`:第二级提成比例(%,可选) | |
| 190 | +- `F_Lifeline3`:第三级提成阶梯(可选) | |
| 191 | +- `F_CommissionRate3`:第三级提成比例(%,可选) | |
| 192 | + | |
| 193 | +**查询条件**: | |
| 194 | +- 按门店ID、月份、总经理/经理ID查询 | |
| 195 | +- 如果未找到提成阶梯设置,则无法计算提成(应报错或跳过该门店) | |
| 196 | + | |
| 197 | +### 4. 门店总业绩 | |
| 198 | + | |
| 199 | +**定义**:门店总业绩 = 开单业绩 - 退卡业绩 | |
| 200 | + | |
| 201 | +**数据来源表及字段**: | |
| 202 | + | |
| 203 | +| 业绩类型 | 数据表 | 字段 | 说明 | | |
| 204 | +|---------|--------|------|------| | |
| 205 | +| **开单业绩** | `lq_kd_kdjlb` | `sfyj` | 门店开单实付金额 | | |
| 206 | +| **退卡业绩** | `lq_hytk_hytk` | `F_ActualRefundAmount` 或 `tkje` | 门店退卡金额 | | |
| 207 | + | |
| 208 | +**计算方式**: | |
| 209 | +```sql | |
| 210 | +门店总业绩 = SUM(门店开单实付金额) - SUM(门店退卡金额) | |
| 211 | +``` | |
| 212 | + | |
| 213 | +**过滤条件**: | |
| 214 | +- 所有表记录必须满足:`F_IsEffective = 1`(有效记录) | |
| 215 | +- 按统计月份(YYYYMM格式)过滤时间范围 | |
| 216 | +- 按门店ID(`djmd` 或 `md`)过滤 | |
| 217 | + | |
| 218 | +**重要说明**: | |
| 219 | +- **确认**:提成计算基于门店总业绩(开单 - 退卡) | |
| 220 | + | |
| 221 | +--- | |
| 222 | + | |
| 223 | +## 👥 归属规则 | |
| 224 | + | |
| 225 | +### 1. 总经理/经理与门店的关联 | |
| 226 | + | |
| 227 | +**关联方式**: | |
| 228 | +- 通过 `lq_md_general_manager_lifeline` 表关联 | |
| 229 | +- 每个门店在每个月可以设置一个总经理/经理的提成阶梯 | |
| 230 | +- 同一个总经理/经理可以管理多个门店 | |
| 231 | + | |
| 232 | +**重要说明**: | |
| 233 | +- 通过 `lq_md_general_manager_lifeline` 表可以获取当月所有的经理和总经理 | |
| 234 | +- 通过 `lq_md_general_manager_lifeline` 表可以看到当月总经理和经理管理的门店 | |
| 235 | + | |
| 236 | +### 2. 门店生命线和提成阶梯的关系 | |
| 237 | + | |
| 238 | +**两个不同的概念**: | |
| 239 | + | |
| 240 | +1. **门店生命线**(`lq_md_target.F_StoreLifeline`): | |
| 241 | + - 用于判断是否达到提成门槛 | |
| 242 | + - 如果门店业绩 < 门店生命线,则无提成 | |
| 243 | + - 如果门店业绩 ≥ 门店生命线,则可以计算提成 | |
| 244 | + | |
| 245 | +2. **提成阶梯**(`lq_md_general_manager_lifeline` 表的 Lifeline1/2/3): | |
| 246 | + - 用于计算提成金额的阶梯 | |
| 247 | + - 只有在达到门店生命线的前提下,才使用提成阶梯计算提成 | |
| 248 | + | |
| 249 | +**重要说明**: | |
| 250 | +- 门店生命线是**必须设置的**,未设置应报错 | |
| 251 | +- 提成阶梯1和提成比例1是必填项,未设置应报错 | |
| 252 | +- 提成阶梯2、3和对应的提成比例为可选项 | |
| 253 | + | |
| 254 | +--- | |
| 255 | + | |
| 256 | +## 🔄 计算流程 | |
| 257 | + | |
| 258 | +### 1. 数据准备 | |
| 259 | + | |
| 260 | +1. **获取总经理/经理列表和门店归属**: | |
| 261 | + - 从 `lq_md_general_manager_lifeline` 表获取当月所有的经理和总经理 | |
| 262 | + - 按总经理/经理ID分组,得到每个总经理/经理管理的门店列表 | |
| 263 | + | |
| 264 | +2. **获取门店生命线**: | |
| 265 | + - 从 `lq_md_target` 表获取每个门店的生命线(`F_StoreLifeline`) | |
| 266 | + - 门店生命线是必须设置的,未设置应报错 | |
| 267 | + | |
| 268 | +3. **获取提成阶梯设置**: | |
| 269 | + - 从 `lq_md_general_manager_lifeline` 表获取每个门店的提成阶梯设置 | |
| 270 | + - 提成阶梯1和提成比例1是必填项,未设置应报错 | |
| 271 | + | |
| 272 | +4. **获取门店总业绩**: | |
| 273 | + - 从 `lq_kd_kdjlb` 表统计每个门店的开单业绩(`sfyj`) | |
| 274 | + - 从 `lq_hytk_hytk` 表统计每个门店的退卡业绩(`F_ActualRefundAmount` 或 `tkje`) | |
| 275 | + - 计算每个门店的总业绩 = 开单业绩 - 退卡业绩 | |
| 276 | + | |
| 277 | +### 2. 工资计算 | |
| 278 | + | |
| 279 | +**遍历每个总经理/经理**: | |
| 280 | + | |
| 281 | +1. **初始化工资统计对象**: | |
| 282 | + - 底薪 = 4000元 | |
| 283 | + - 总提成 = 0 | |
| 284 | + | |
| 285 | +2. **遍历该总经理/经理管理的每个门店**: | |
| 286 | + | |
| 287 | + a. **判断是否达到提成门槛**: | |
| 288 | + - 获取该门店的生命线(`lq_md_target.F_StoreLifeline`) | |
| 289 | + - 获取该门店的总业绩 | |
| 290 | + - 如果门店业绩 < 门店生命线 → 该门店提成 = 0,跳过 | |
| 291 | + - 如果门店业绩 ≥ 门店生命线 → 继续计算提成 | |
| 292 | + | |
| 293 | + b. **计算该门店的提成**(如果达到门槛): | |
| 294 | + - 获取该门店的提成阶梯设置(`lq_md_general_manager_lifeline`) | |
| 295 | + - 根据门店业绩和提成阶梯,使用分段累进方式计算提成金额 | |
| 296 | + - 累加到总提成 | |
| 297 | + | |
| 298 | +3. **计算最终工资**: | |
| 299 | + - 应发工资 = 底薪 + 总提成 | |
| 300 | + | |
| 301 | +### 3. 数据保存 | |
| 302 | + | |
| 303 | +- 将计算结果保存到工资统计表中(待创建表结构) | |
| 304 | +- 如果已存在当月数据,则更新;否则插入新数据 | |
| 305 | + | |
| 306 | +--- | |
| 307 | + | |
| 308 | +## 📝 注意事项 | |
| 309 | + | |
| 310 | +1. **数据一致性**: | |
| 311 | + - 门店业绩计算逻辑必须与其他统计接口保持一致 | |
| 312 | + - 门店生命线必须与门店目标表保持一致 | |
| 313 | + | |
| 314 | +2. **数据校验**: | |
| 315 | + - 门店生命线(`lq_md_target.F_StoreLifeline`)必须设置,未设置应报错 | |
| 316 | + - 提成阶梯1(`lq_md_general_manager_lifeline.F_Lifeline1`)必须设置,未设置应报错 | |
| 317 | + - 提成比例1(`lq_md_general_manager_lifeline.F_CommissionRate1`)必须设置,未设置应报错 | |
| 318 | + - 如果门店未在 `lq_md_general_manager_lifeline` 表中设置,则无法计算该门店的提成 | |
| 319 | + | |
| 320 | +3. **边界情况**: | |
| 321 | + - 如果门店没有业绩数据,业绩为0,未达到门店生命线,提成为0 | |
| 322 | + - 如果门店业绩 < 门店生命线,提成为0 | |
| 323 | + - 如果总经理/经理没有管理的门店,总提成为0,应发工资 = 底薪(4000元) | |
| 324 | + - 如果提成阶梯2或提成阶梯3未设置,则只使用提成阶梯1计算 | |
| 325 | + | |
| 326 | +4. **计算精度**: | |
| 327 | + - 涉及金额计算时,建议保留2位小数 | |
| 328 | + - 提成比例以百分比形式存储(如:1.0表示1%) | |
| 329 | + | |
| 330 | +5. **总经理和经理**: | |
| 331 | + - 总经理和经理的计算规则相同 | |
| 332 | + - 都使用门店生命线来判断是否达到提成门槛 | |
| 333 | + - 都使用 `lq_md_general_manager_lifeline` 表的提成阶梯来计算提成 | |
| 334 | + | |
| 335 | +6. **保底工资**: | |
| 336 | + - 暂时不考虑保底工资规则 | |
| 337 | + | |
| 338 | +--- | |
| 339 | + | |
| 340 | +## 📋 参考文档 | |
| 341 | + | |
| 342 | +- [项目信息-薪酬规则与名词解释.md](./项目信息-薪酬规则与名词解释.md) | |
| 343 | +- [店长工资计算规则梳理.md](./店长工资计算规则梳理.md) | |
| 344 | +- [主任工资计算规则梳理.md](./主任工资计算规则梳理.md) | |
| 345 | +- [大项目部老师工资计算规则梳理.md](./大项目部老师工资计算规则梳理.md) | |
| 346 | + | |
| 347 | +--- | |
| 348 | + | |
| 349 | +**最后更新时间**:2025年1月 | |
| 350 | + | ... | ... |
大项目部老师工资计算规则梳理.md
0 → 100644
| 1 | +# 大项目部老师工资计算规则梳理 | |
| 2 | + | |
| 3 | +## 📋 目录 | |
| 4 | +- [计算规则](#计算规则) | |
| 5 | +- [数据来源](#数据来源) | |
| 6 | +- [归属规则](#归属规则) | |
| 7 | +- [计算流程](#计算流程) | |
| 8 | + | |
| 9 | +--- | |
| 10 | + | |
| 11 | +## 💰 计算规则 | |
| 12 | + | |
| 13 | +### 1. 底薪规则 | |
| 14 | + | |
| 15 | +**固定底薪**:3000元 | |
| 16 | + | |
| 17 | +- 无论业绩多少,底薪固定为3000元 | |
| 18 | +- 不设档位,不设条件 | |
| 19 | + | |
| 20 | +--- | |
| 21 | + | |
| 22 | +### 2. 业绩提成规则(阶梯式) | |
| 23 | + | |
| 24 | +业绩提成基于**总业绩**计算,采用阶梯式方式: | |
| 25 | + | |
| 26 | +| 总业绩范围 | 提成比例 | 说明 | | |
| 27 | +|-----------|---------|------| | |
| 28 | +| ≤ 20万 | 0% | 无提成 | | |
| 29 | +| > 20万 且 ≤ 100万 | 2% | 按2%计算提成 | | |
| 30 | +| > 100万 | 2.5% | 按2.5%计算提成 | | |
| 31 | + | |
| 32 | +**计算说明**: | |
| 33 | +- 提成金额 = 总业绩 × 对应提成比例 | |
| 34 | +- 采用阶梯式计算,不同区间按不同比例计算 | |
| 35 | +- **注意**:不是分段累进,而是整个总业绩按对应比例计算 | |
| 36 | + | |
| 37 | +**示例**: | |
| 38 | +- 总业绩 = 15万 → 提成 = 0(无提成) | |
| 39 | +- 总业绩 = 50万 → 提成 = 50万 × 2% = 1万 | |
| 40 | +- 总业绩 = 120万 → 提成 = 120万 × 2.5% = 3万 | |
| 41 | + | |
| 42 | +--- | |
| 43 | + | |
| 44 | +## 📊 数据来源 | |
| 45 | + | |
| 46 | +### 总业绩(TotalPerformance) | |
| 47 | + | |
| 48 | +**定义**:门店总业绩 = 开单业绩 - 退卡业绩 | |
| 49 | + | |
| 50 | +**重要说明**: | |
| 51 | +- **大项目部老师计算的是门店总业绩,不是个人业绩** | |
| 52 | +- 大项目部老师和科技部老师是两个不同的岗位 | |
| 53 | +- 根据归属表 `lq_md_major_project_teacher_assignment` 确定该老师在某个月归属于哪个门店 | |
| 54 | +- 然后统计该门店在该月的总业绩(开单业绩 - 退卡业绩) | |
| 55 | + | |
| 56 | +**数据来源表及字段**: | |
| 57 | + | |
| 58 | +| 业绩类型 | 数据表 | 字段 | 说明 | | |
| 59 | +|---------|--------|------|------| | |
| 60 | +| **开单业绩** | `lq_kd_kdjlb` | `sfyj` | 门店开单实付金额(根据归属表关联的门店ID统计) | | |
| 61 | +| **退卡业绩** | `lq_hytk_hytk` | `F_ActualRefundAmount` 或 `tkje` | 门店退卡金额(根据归属表关联的门店ID统计) | | |
| 62 | + | |
| 63 | +**计算方式**: | |
| 64 | +```sql | |
| 65 | +门店总业绩 = SUM(门店开单实付金额) - SUM(门店退卡金额) | |
| 66 | +``` | |
| 67 | + | |
| 68 | +**过滤条件**: | |
| 69 | +- 所有表记录必须满足:`F_IsEffective = 1`(有效记录) | |
| 70 | +- 按统计月份(YYYYMM格式)过滤时间范围 | |
| 71 | +- **关键**:根据 `lq_md_major_project_teacher_assignment` 归属表关联 | |
| 72 | + - 关联条件:`lq_md_major_project_teacher_assignment.F_TeacherId = 大项目部老师ID` | |
| 73 | + - 关联条件:`lq_md_major_project_teacher_assignment.F_Year = 统计年份` | |
| 74 | + - 关联条件:`lq_md_major_project_teacher_assignment.F_Month = 统计月份` | |
| 75 | + - 关联条件:`lq_md_major_project_teacher_assignment.F_StoreId = 门店ID` | |
| 76 | + - 然后统计该门店在该月的开单业绩和退卡业绩 | |
| 77 | + | |
| 78 | +**注意**: | |
| 79 | +- 大项目部老师的归属每个月可能不一样 | |
| 80 | +- 需要根据归属表来确定该老师在该月份归属于哪个门店 | |
| 81 | +- **统计的是门店的总业绩,不是个人业绩** | |
| 82 | +- 如果同一个老师在某个月归属于多个门店,需要合并多个门店的业绩 | |
| 83 | + | |
| 84 | +--- | |
| 85 | + | |
| 86 | +## 🔗 归属规则 | |
| 87 | + | |
| 88 | +### 归属表:`lq_md_major_project_teacher_assignment` | |
| 89 | + | |
| 90 | +**表结构**: | |
| 91 | +- `F_Id`:主键ID | |
| 92 | +- `F_StoreId`:门店ID | |
| 93 | +- `F_Year`:年份(YYYY格式) | |
| 94 | +- `F_Month`:月份(MM格式) | |
| 95 | +- `F_TeacherId`:大项目部老师用户ID | |
| 96 | +- `F_EducationTeacherId`:教育部老师用户ID(可选) | |
| 97 | +- `F_Remark`:备注说明 | |
| 98 | + | |
| 99 | +**归属规则**: | |
| 100 | +- 每个大项目部老师每个月可能归属于不同的门店 | |
| 101 | +- 归属信息存储在 `lq_md_major_project_teacher_assignment` 表中 | |
| 102 | +- 统计工资时,需要根据该表来确定: | |
| 103 | + 1. 该老师在该月份归属于哪个门店 | |
| 104 | + 2. 该门店的业绩数据中,哪些属于该老师 | |
| 105 | + | |
| 106 | +**关联逻辑**: | |
| 107 | +1. 根据 `F_Year` 和 `F_Month` 确定统计月份 | |
| 108 | +2. 根据 `F_TeacherId` 确定大项目部老师 | |
| 109 | +3. 根据 `F_StoreId` 确定归属门店 | |
| 110 | +4. 统计该门店在该月份的业绩数据(开单、消耗、退卡) | |
| 111 | +5. 根据业绩表中的老师ID字段关联到该大项目部老师 | |
| 112 | + | |
| 113 | +**业绩关联方式**(待确认): | |
| 114 | +- 开单业绩:`lq_kd_kjbsyj` 表中的 `kjblszh` 或 `kjbls` 字段 | |
| 115 | +- 消耗业绩:`lq_xh_kjbsyj` 表中的 `kjblszh` 或 `kjbls` 字段 | |
| 116 | +- 退卡业绩:`lq_hytk_kjbsyj` 表中的 `kjblszh` 或 `kjbls` 字段 | |
| 117 | + | |
| 118 | +**注意**: | |
| 119 | +- 需要确认大项目部老师的业绩是如何关联的 | |
| 120 | +- 可能需要通过 `BASE_USER` 表的账号或ID来关联 | |
| 121 | +- 或者通过其他关联字段来确定 | |
| 122 | + | |
| 123 | +--- | |
| 124 | + | |
| 125 | +## ⚠️ 特殊规则 | |
| 126 | + | |
| 127 | +### 离职员工规则 | |
| 128 | + | |
| 129 | +**适用条件**: | |
| 130 | +- 员工状态:`BASE_USER.F_IsOnJob = 0`(离职) | |
| 131 | + | |
| 132 | +**计算规则**: | |
| 133 | +- 离职员工的工资计算规则待确认 | |
| 134 | +- 可能需要特殊处理(如:只计算在职期间的业绩) | |
| 135 | + | |
| 136 | +--- | |
| 137 | + | |
| 138 | +## 📝 计算流程 | |
| 139 | + | |
| 140 | +### 1. 数据准备阶段 | |
| 141 | + | |
| 142 | +1. **获取归属信息** | |
| 143 | + - 从 `lq_md_major_project_teacher_assignment` 表查询指定月份的所有归属记录 | |
| 144 | + - 按 `F_TeacherId` 和 `F_StoreId` 分组 | |
| 145 | + | |
| 146 | +2. **获取业绩数据** | |
| 147 | + - 开单业绩:从 `lq_kd_kjbsyj` 表统计 | |
| 148 | + - 消耗业绩:从 `lq_xh_kjbsyj` 表统计 | |
| 149 | + - 退卡业绩:从 `lq_hytk_kjbsyj` 表统计 | |
| 150 | + - 按统计月份过滤时间范围 | |
| 151 | + - 按归属门店和老师ID关联 | |
| 152 | + | |
| 153 | +3. **获取员工信息** | |
| 154 | + - 从 `BASE_USER` 表获取员工基本信息 | |
| 155 | + - 包括:姓名、账号、门店信息等 | |
| 156 | + | |
| 157 | +### 2. 业绩统计阶段 | |
| 158 | + | |
| 159 | +1. **计算开单业绩** | |
| 160 | + - 根据归属表关联的开单业绩数据 | |
| 161 | + - 按大项目部老师ID汇总 | |
| 162 | + | |
| 163 | +2. **计算消耗业绩** | |
| 164 | + - 根据归属表关联的消耗业绩数据 | |
| 165 | + - 按大项目部老师ID汇总 | |
| 166 | + | |
| 167 | +3. **计算退卡业绩** | |
| 168 | + - 根据归属表关联的退卡业绩数据 | |
| 169 | + - 按大项目部老师ID汇总 | |
| 170 | + | |
| 171 | +4. **计算总业绩** | |
| 172 | + - 总业绩 = 开单业绩 + 消耗业绩 + 退卡业绩 | |
| 173 | + | |
| 174 | +### 3. 工资计算阶段 | |
| 175 | + | |
| 176 | +1. **计算底薪** | |
| 177 | + - 底薪 = 3000元(固定) | |
| 178 | + | |
| 179 | +2. **计算业绩提成** | |
| 180 | + - 判断总业绩范围: | |
| 181 | + - ≤ 20万:提成比例 = 0% | |
| 182 | + - > 20万 且 ≤ 100万:提成比例 = 2% | |
| 183 | + - > 100万:提成比例 = 2.5% | |
| 184 | + - 提成金额 = 总业绩 × 提成比例 | |
| 185 | + | |
| 186 | +3. **计算其他收入** | |
| 187 | + - 手工费、车补、少休费、全勤奖等(默认0) | |
| 188 | + | |
| 189 | +4. **计算核算应发工资** | |
| 190 | + - 核算应发工资 = 底薪 + 提成合计 + 其他收入 | |
| 191 | + | |
| 192 | +5. **计算最终应发工资** | |
| 193 | + - 最终应发工资 = MAX(核算应发工资, 保底工资) | |
| 194 | + - 如果没有保底工资,则等于核算应发工资 | |
| 195 | + | |
| 196 | +6. **计算实发工资** | |
| 197 | + - 实发工资 = 最终应发工资 - 扣款合计 + 补贴合计 + 奖金 | |
| 198 | + | |
| 199 | +### 4. 数据保存阶段 | |
| 200 | + | |
| 201 | +1. **保存工资统计记录** | |
| 202 | + - 保存到 `lq_major_project_teacher_salary_statistics` 表 | |
| 203 | + - 确保同一员工同一月份只有一条记录(唯一索引) | |
| 204 | + | |
| 205 | +2. **更新状态** | |
| 206 | + - 设置 `F_IsLocked = 0`(未锁定) | |
| 207 | + - 设置 `F_IsTerminated`(根据员工状态) | |
| 208 | + | |
| 209 | +--- | |
| 210 | + | |
| 211 | +## ❓ 待确认问题 | |
| 212 | + | |
| 213 | +1. **业绩关联方式** | |
| 214 | + - 大项目部老师的业绩是如何关联的? | |
| 215 | + - 是通过 `BASE_USER` 表的账号或ID来关联吗? | |
| 216 | + - 还是通过其他字段来确定? | |
| 217 | + | |
| 218 | +2. **归属门店的业绩统计** | |
| 219 | + - 如果一个大项目部老师在某个月归属于某个门店,那么: | |
| 220 | + - 该门店的所有业绩都算他的吗? | |
| 221 | + - 还是只统计该门店中他参与的业绩? | |
| 222 | + | |
| 223 | +3. **离职员工处理** | |
| 224 | + - 离职员工的工资计算规则是什么? | |
| 225 | + - 是否需要特殊处理? | |
| 226 | + | |
| 227 | +4. **保底工资** | |
| 228 | + - 大项目部老师是否有保底工资? | |
| 229 | + - 保底工资的计算规则是什么? | |
| 230 | + | |
| 231 | +--- | |
| 232 | + | |
| 233 | +## 📌 总结 | |
| 234 | + | |
| 235 | +### 核心规则 | |
| 236 | +1. **底薪**:固定3000元 | |
| 237 | +2. **业绩提成**:阶梯式计算 | |
| 238 | + - ≤ 20万:0% | |
| 239 | + - > 20万 且 ≤ 100万:2% | |
| 240 | + - > 100万:2.5% | |
| 241 | + | |
| 242 | +### 关键点 | |
| 243 | +1. **归属表关联**:需要根据 `lq_md_major_project_teacher_assignment` 表来确定归属关系 | |
| 244 | +2. **业绩统计**:需要根据归属表关联的业绩数据来统计 | |
| 245 | +3. **月份归属**:每个月的归属可能不一样,需要按月统计 | |
| 246 | + | |
| 247 | +### 下一步 | |
| 248 | +1. 确认业绩关联方式 | |
| 249 | +2. 确认归属门店的业绩统计规则 | |
| 250 | +3. 确认离职员工处理规则 | |
| 251 | +4. 确认保底工资规则 | |
| 252 | +5. 实现计算逻辑 | |
| 253 | + | ... | ... |