Commit a78c0ae8c55c3de6aee30ea290908ab4154f4409

Authored by “wangming”
1 parent 5e629136

feat: 退卡服务添加健康师和科技部老师筛选功能,返回品项明细和业绩数据;开单服务添加健康师和科技部老师筛选及业绩返回;科技部老师统计添加手工费统计;修复Ge…

…tTechTeacherStatistics方法错误;WeChatBot配置化
netcore/src/Application/NCC.API/appsettings.json
@@ -181,6 +181,11 @@ @@ -181,6 +181,11 @@
181 "scope": "snsapi_userinfo" 181 "scope": "snsapi_userinfo"
182 } 182 }
183 }, 183 },
  184 + "WeChatBot": {
  185 + "BotApiUrl": "http://wx.lvqianmeiye.com/api/Bot/send-text",
  186 + "WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=496f1add-122b-43fc-9e38-0ca79c48b33f",
  187 + "WebhookUrlTest": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6f8686ec-5011-4c1d-bae9-d82a2a2f4d83"
  188 + },
184 "NCC_App": { 189 "NCC_App": {
185 "CodeAreasName": "SubDev,Food,Extend,test", 190 "CodeAreasName": "SubDev,Food,Extend,test",
186 //系统文件路径(末尾必须带斜杆) 191 //系统文件路径(末尾必须带斜杆)
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/LqHytkHytkListOutput.cs
1 using System; 1 using System;
  2 +using System.Collections.Generic;
  3 +using NCC.Extend.Entitys.Dto.LqHytkMx;
  4 +using NCC.Extend.Entitys.Dto.LqHytkJksyj;
  5 +using NCC.Extend.Entitys.Dto.LqHytkKjbsyj;
2 6
3 namespace NCC.Extend.Entitys.Dto.LqHytkHytk 7 namespace NCC.Extend.Entitys.Dto.LqHytkHytk
4 { 8 {
@@ -102,5 +106,19 @@ namespace NCC.Extend.Entitys.Dto.LqHytkHytk @@ -102,5 +106,19 @@ namespace NCC.Extend.Entitys.Dto.LqHytkHytk
102 /// </summary> 106 /// </summary>
103 public string cancelRemark { get; set; } 107 public string cancelRemark { get; set; }
104 108
  109 + /// <summary>
  110 + /// 退卡品项明细列表
  111 + /// </summary>
  112 + public List<LqHytkMxInfoOutput> lqHytkMxList { get; set; }
  113 +
  114 + /// <summary>
  115 + /// 健康师业绩列表
  116 + /// </summary>
  117 + public List<LqHytkJksyjInfoOutput> lqHytkJksyjList { get; set; }
  118 +
  119 + /// <summary>
  120 + /// 科技部老师业绩列表
  121 + /// </summary>
  122 + public List<LqHytkKjbsyjInfoOutput> lqHytkKjbsyjList { get; set; }
105 } 123 }
106 } 124 }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/LqHytkHytkListQueryInput.cs
@@ -93,5 +93,14 @@ namespace NCC.Extend.Entitys.Dto.LqHytkHytk @@ -93,5 +93,14 @@ namespace NCC.Extend.Entitys.Dto.LqHytkHytk
93 /// </summary> 93 /// </summary>
94 public int isEffective { get; set; } = 0; 94 public int isEffective { get; set; } = 0;
95 95
  96 + /// <summary>
  97 + /// 健康师ID(可选,传入后只返回该健康师参与的退卡记录)
  98 + /// </summary>
  99 + public string jksId { get; set; }
  100 +
  101 + /// <summary>
  102 + /// 科技部老师ID(可选,传入后只返回该老师参与的退卡记录)
  103 + /// </summary>
  104 + public string kjblsId { get; set; }
96 } 105 }
97 } 106 }
98 \ No newline at end of file 107 \ No newline at end of file
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs
@@ -41,5 +41,10 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics @@ -41,5 +41,10 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
41 /// 耗卡品项次数 41 /// 耗卡品项次数
42 /// </summary> 42 /// </summary>
43 public int ConsumeItemCount { get; set; } 43 public int ConsumeItemCount { get; set; }
  44 +
  45 + /// <summary>
  46 + /// 消耗手工费(耗卡手工费)
  47 + /// </summary>
  48 + public decimal ConsumeLaborCost { get; set; }
44 } 49 }
45 } 50 }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqXhHyhk/MemberRemainingItemsOutput.cs
@@ -25,6 +25,7 @@ namespace NCC.Extend.Entitys.Dto.LqXhHyhk @@ -25,6 +25,7 @@ namespace NCC.Extend.Entitys.Dto.LqXhHyhk
25 public string MemberName { get; set; } 25 public string MemberName { get; set; }
26 26
27 /// <summary> 27 /// <summary>
  28 + /// <summary>
28 /// 剩余品项列表 29 /// 剩余品项列表
29 /// </summary> 30 /// </summary>
30 /// <remarks>按剩余数量降序排列的品项列表</remarks> 31 /// <remarks>按剩余数量降序排列的品项列表</remarks>
@@ -71,6 +72,12 @@ namespace NCC.Extend.Entitys.Dto.LqXhHyhk @@ -71,6 +72,12 @@ namespace NCC.Extend.Entitys.Dto.LqXhHyhk
71 public string SourceType { get; set; } 72 public string SourceType { get; set; }
72 73
73 /// <summary> 74 /// <summary>
  75 + /// 备注
  76 + /// </summary>
  77 + /// <example>备注</example>
  78 + public string Remark { get; set; }
  79 +
  80 + /// <summary>
74 /// 总购买数量 81 /// 总购买数量
75 /// </summary> 82 /// </summary>
76 /// <remarks>该品项的总购买次数</remarks> 83 /// <remarks>该品项的总购买次数</remarks>
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
@@ -70,8 +70,35 @@ namespace NCC.Extend.LqHytkHytk @@ -70,8 +70,35 @@ namespace NCC.Extend.LqHytkHytk
70 /// <summary> 70 /// <summary>
71 /// 获取退卡信息列表 71 /// 获取退卡信息列表
72 /// </summary> 72 /// </summary>
  73 + /// <remarks>
  74 + /// 获取退卡记录列表,支持根据健康师ID或科技部老师ID筛选,返回品项明细、健康师业绩和科技部老师业绩
  75 + ///
  76 + /// 示例请求:
  77 + /// ```json
  78 + /// GET /api/Extend/LqHytkHytk?jksId=健康师ID&amp;kjblsId=科技部老师ID&amp;currentPage=1&amp;pageSize=10
  79 + /// ```
  80 + ///
  81 + /// 参数说明:
  82 + /// - jksId: 健康师ID(可选,传入后只返回该健康师参与的退卡记录)
  83 + /// - kjblsId: 科技部老师ID(可选,传入后只返回该老师参与的退卡记录)
  84 + /// - id: 退卡编号(可选)
  85 + /// - md: 门店ID(可选)
  86 + /// - hy: 会员ID(可选)
  87 + /// - tksj: 退卡时间(可选,格式:yyyy-MM-dd,yyyy-MM-dd)
  88 + /// - currentPage: 当前页码(必填)
  89 + /// - pageSize: 每页数量(必填)
  90 + ///
  91 + /// 返回数据说明:
  92 + /// - 退卡基本信息:id、门店信息、会员信息、退卡金额、退卡时间等
  93 + /// - lqHytkMxList: 退卡品项明细列表,每个明细包含品项信息、退款金额、项目次数等
  94 + /// - lqHytkJksyjList: 健康师业绩列表,每个业绩包含健康师信息、业绩金额、手工费、退卡品项次数等
  95 + /// - lqHytkKjbsyjList: 科技部老师业绩列表,每个业绩包含科技部老师信息、业绩金额、手工费、退卡品项次数等
  96 + /// </remarks>
73 /// <param name="input">查询参数</param> 97 /// <param name="input">查询参数</param>
74 - /// <returns></returns> 98 + /// <returns>分页的退卡记录列表,包含退卡基本信息、品项明细、健康师业绩、科技部老师业绩</returns>
  99 + /// <response code="200">成功返回退卡列表</response>
  100 + /// <response code="400">参数错误</response>
  101 + /// <response code="500">服务器内部错误</response>
75 [HttpGet("")] 102 [HttpGet("")]
76 public async Task<dynamic> GetList([FromQuery] LqHytkHytkListQueryInput input) 103 public async Task<dynamic> GetList([FromQuery] LqHytkHytkListQueryInput input)
77 { 104 {
@@ -79,7 +106,48 @@ namespace NCC.Extend.LqHytkHytk @@ -79,7 +106,48 @@ namespace NCC.Extend.LqHytkHytk
79 List<string> queryTksj = input.tksj != null ? input.tksj.Split(',').ToObeject<List<string>>() : null; 106 List<string> queryTksj = input.tksj != null ? input.tksj.Split(',').ToObeject<List<string>>() : null;
80 DateTime? startTksj = queryTksj != null ? Ext.GetDateTime(queryTksj.First()) : null; 107 DateTime? startTksj = queryTksj != null ? Ext.GetDateTime(queryTksj.First()) : null;
81 DateTime? endTksj = queryTksj != null ? Ext.GetDateTime(queryTksj.Last()) : null; 108 DateTime? endTksj = queryTksj != null ? Ext.GetDateTime(queryTksj.Last()) : null;
82 - var data = await _db.Queryable<LqHytkHytkEntity>() 109 +
  110 + // 根据是否传入健康师ID或科技部老师ID,动态构建查询
  111 + ISugarQueryable<LqHytkHytkEntity> baseQuery = null;
  112 +
  113 + if (!string.IsNullOrEmpty(input.jksId) && !string.IsNullOrEmpty(input.kjblsId))
  114 + {
  115 + // 同时传入健康师ID和科技部老师ID,需要同时关联两个业绩表(不过滤有效性)
  116 + baseQuery = _db.Queryable<LqHytkJksyjEntity, LqHytkKjbsyjEntity, LqHytkHytkEntity>(
  117 + (jksyj, kjbsyj, hytk) => jksyj.Gltkbh == hytk.Id && kjbsyj.Gltkbh == hytk.Id)
  118 + .Where((jksyj, kjbsyj, hytk) => jksyj.Jkszh == input.jksId)
  119 + .Where((jksyj, kjbsyj, hytk) => kjbsyj.Kjblszh == input.kjblsId)
  120 + .Select((jksyj, kjbsyj, hytk) => hytk)
  121 + .Distinct()
  122 + .MergeTable();
  123 + }
  124 + else if (!string.IsNullOrEmpty(input.jksId))
  125 + {
  126 + // 只传入健康师ID,关联健康师业绩表(不过滤有效性)
  127 + baseQuery = _db.Queryable<LqHytkJksyjEntity, LqHytkHytkEntity>(
  128 + (jksyj, hytk) => jksyj.Gltkbh == hytk.Id)
  129 + .Where((jksyj, hytk) => jksyj.Jkszh == input.jksId)
  130 + .Select((jksyj, hytk) => hytk)
  131 + .Distinct()
  132 + .MergeTable();
  133 + }
  134 + else if (!string.IsNullOrEmpty(input.kjblsId))
  135 + {
  136 + // 只传入科技部老师ID,关联科技部老师业绩表(不过滤有效性)
  137 + baseQuery = _db.Queryable<LqHytkKjbsyjEntity, LqHytkHytkEntity>(
  138 + (kjbsyj, hytk) => kjbsyj.Gltkbh == hytk.Id)
  139 + .Where((kjbsyj, hytk) => kjbsyj.Kjblszh == input.kjblsId)
  140 + .Select((kjbsyj, hytk) => hytk)
  141 + .Distinct()
  142 + .MergeTable();
  143 + }
  144 + else
  145 + {
  146 + // 没有传入健康师ID或科技部老师ID,使用原来的查询逻辑
  147 + baseQuery = _db.Queryable<LqHytkHytkEntity>();
  148 + }
  149 +
  150 + var data = await baseQuery
83 .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id)) 151 .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id))
84 .WhereIF(!string.IsNullOrEmpty(input.md), p => p.Md.Equals(input.md)) 152 .WhereIF(!string.IsNullOrEmpty(input.md), p => p.Md.Equals(input.md))
85 .WhereIF(!string.IsNullOrEmpty(input.mdbh), p => p.Mdbh.Contains(input.mdbh)) 153 .WhereIF(!string.IsNullOrEmpty(input.mdbh), p => p.Mdbh.Contains(input.mdbh))
@@ -123,6 +191,104 @@ namespace NCC.Extend.LqHytkHytk @@ -123,6 +191,104 @@ namespace NCC.Extend.LqHytkHytk
123 .MergeTable() 191 .MergeTable()
124 .OrderBy(sidx + " " + input.sort) 192 .OrderBy(sidx + " " + input.sort)
125 .ToPagedListAsync(input.currentPage, input.pageSize); 193 .ToPagedListAsync(input.currentPage, input.pageSize);
  194 +
  195 + // 获取当前页的退卡记录ID列表
  196 + var refundIds = data.list.Select(x => x.id).ToList();
  197 +
  198 + // 批量查询品项明细(不过滤有效性,返回所有记录)
  199 + var itemDetails = new List<LqHytkMxInfoOutput>();
  200 + if (refundIds.Any())
  201 + {
  202 + itemDetails = await _db.Queryable<LqHytkMxEntity>()
  203 + .Where(x => refundIds.Contains(x.RefundInfoId))
  204 + .Select(x => new LqHytkMxInfoOutput
  205 + {
  206 + id = x.Id,
  207 + refundInfoId = x.RefundInfoId,
  208 + billingItemId = x.BillingItemId,
  209 + px = x.Px,
  210 + pxmc = x.Pxmc,
  211 + pxjg = x.Pxjg,
  212 + tkje = x.Tkje,
  213 + projectNumber = x.ProjectNumber,
  214 + isEffective = x.IsEffective,
  215 + sourceType = x.SourceType,
  216 + totalPrice = x.TotalPrice
  217 + })
  218 + .ToListAsync();
  219 + }
  220 +
  221 + // 批量查询健康师业绩(性能优化:一次性查询所有退卡的健康师业绩,不过滤有效性)
  222 + var jksyjList = new List<LqHytkJksyjInfoOutput>();
  223 + if (refundIds.Any())
  224 + {
  225 + jksyjList = await _db.Queryable<LqHytkJksyjEntity>()
  226 + .Where(x => refundIds.Contains(x.Gltkbh))
  227 + .Select(x => new LqHytkJksyjInfoOutput
  228 + {
  229 + id = x.Id,
  230 + gltkbh = x.Gltkbh,
  231 + jks = x.Jks,
  232 + jksxm = x.Jksxm,
  233 + jkszh = x.Jkszh,
  234 + jksyj = x.Jksyj,
  235 + tksj = x.Tksj,
  236 + F_jsjid = x.F_jsjid,
  237 + F_tkpxid = x.F_tkpxid,
  238 + F_LaborCost = x.F_LaborCost,
  239 + F_tkpxNumber = x.F_tkpxNumber
  240 + })
  241 + .ToListAsync();
  242 + }
  243 +
  244 + // 批量查询科技部老师业绩(性能优化:一次性查询所有退卡的科技部老师业绩,不过滤有效性)
  245 + var kjbsyjList = new List<LqHytkKjbsyjInfoOutput>();
  246 + if (refundIds.Any())
  247 + {
  248 + kjbsyjList = await _db.Queryable<LqHytkKjbsyjEntity>()
  249 + .Where(x => refundIds.Contains(x.Gltkbh))
  250 + .Select(x => new LqHytkKjbsyjInfoOutput
  251 + {
  252 + id = x.Id,
  253 + gltkbh = x.Gltkbh,
  254 + kjbls = x.Kjbls,
  255 + kjblsxm = x.Kjblsxm,
  256 + kjblszh = x.Kjblszh,
  257 + kjblsyj = x.Kjblsyj,
  258 + tksj = x.Tksj,
  259 + F_tkpxid = x.F_tkpxid,
  260 + F_LaborCost = x.F_LaborCost,
  261 + F_tkpxNumber = x.F_tkpxNumber
  262 + })
  263 + .ToListAsync();
  264 + }
  265 +
  266 + // 按退卡ID分组品项明细
  267 + var itemDetailsGrouped = itemDetails.GroupBy(x => x.refundInfoId)
  268 + .ToDictionary(g => g.Key, g => g.ToList());
  269 +
  270 + // 按退卡ID分组健康师业绩
  271 + var jksyjGrouped = jksyjList.GroupBy(x => x.gltkbh)
  272 + .ToDictionary(g => g.Key, g => g.ToList());
  273 +
  274 + // 按退卡ID分组科技部老师业绩
  275 + var kjbsyjGrouped = kjbsyjList.GroupBy(x => x.gltkbh)
  276 + .ToDictionary(g => g.Key, g => g.ToList());
  277 +
  278 + // 为每个退卡记录分配品项明细、健康师业绩和科技部老师业绩
  279 + foreach (var item in data.list)
  280 + {
  281 + item.lqHytkMxList = itemDetailsGrouped.ContainsKey(item.id)
  282 + ? itemDetailsGrouped[item.id]
  283 + : new List<LqHytkMxInfoOutput>();
  284 + item.lqHytkJksyjList = jksyjGrouped.ContainsKey(item.id)
  285 + ? jksyjGrouped[item.id]
  286 + : new List<LqHytkJksyjInfoOutput>();
  287 + item.lqHytkKjbsyjList = kjbsyjGrouped.ContainsKey(item.id)
  288 + ? kjbsyjGrouped[item.id]
  289 + : new List<LqHytkKjbsyjInfoOutput>();
  290 + }
  291 +
126 return PageResult<LqHytkHytkListOutput>.SqlSugarPageResult(data); 292 return PageResult<LqHytkHytkListOutput>.SqlSugarPageResult(data);
127 } 293 }
128 #endregion 294 #endregion
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
@@ -454,10 +454,8 @@ namespace NCC.Extend.LqKdKdjlb @@ -454,10 +454,8 @@ namespace NCC.Extend.LqKdKdjlb
454 { 454 {
455 throw NCCException.Oh("健康师ID不能为空"); 455 throw NCCException.Oh("健康师ID不能为空");
456 } 456 }
457 -  
458 var sidx = input.sidx == null ? "kdrq" : input.sidx; 457 var sidx = input.sidx == null ? "kdrq" : input.sidx;
459 var sort = string.IsNullOrEmpty(input.sort) ? "DESC" : input.sort; 458 var sort = string.IsNullOrEmpty(input.sort) ? "DESC" : input.sort;
460 -  
461 // 解析时间范围 459 // 解析时间范围
462 DateTime? startDate = null; 460 DateTime? startDate = null;
463 DateTime? endDate = null; 461 DateTime? endDate = null;
@@ -476,8 +474,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -476,8 +474,7 @@ namespace NCC.Extend.LqKdKdjlb
476 } 474 }
477 475
478 // 通过健康师业绩表关联查询开单记录 476 // 通过健康师业绩表关联查询开单记录
479 - var data = await _db.Queryable<LqKdJksyjEntity, LqKdKdjlbEntity>(  
480 - (jksyj, kdjlb) => jksyj.Glkdbh == kdjlb.Id) 477 + var data = await _db.Queryable<LqKdJksyjEntity, LqKdKdjlbEntity>((jksyj, kdjlb) => jksyj.Glkdbh == kdjlb.Id)
481 .Where((jksyj, kdjlb) => jksyj.Jkszh == input.jksId) 478 .Where((jksyj, kdjlb) => jksyj.Jkszh == input.jksId)
482 .WhereIF(input.isEffective != 0, (jksyj, kdjlb) => jksyj.IsEffective == input.isEffective && kdjlb.IsEffective == input.isEffective) 479 .WhereIF(input.isEffective != 0, (jksyj, kdjlb) => jksyj.IsEffective == input.isEffective && kdjlb.IsEffective == input.isEffective)
483 .WhereIF(input.isEffective == 0, (jksyj, kdjlb) => jksyj.IsEffective == StatusEnum.有效.GetHashCode() && kdjlb.IsEffective == StatusEnum.有效.GetHashCode()) 480 .WhereIF(input.isEffective == 0, (jksyj, kdjlb) => jksyj.IsEffective == StatusEnum.有效.GetHashCode() && kdjlb.IsEffective == StatusEnum.有效.GetHashCode())
netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
@@ -551,7 +551,8 @@ namespace NCC.Extend.LqKhxx @@ -551,7 +551,8 @@ namespace NCC.Extend.LqKhxx
551 x.Pxmc, 551 x.Pxmc,
552 x.Pxjg, 552 x.Pxjg,
553 x.SourceType, 553 x.SourceType,
554 - x.ProjectNumber 554 + x.ProjectNumber,
  555 + x.Remark
555 }) 556 })
556 .ToListAsync(); 557 .ToListAsync();
557 558
@@ -600,6 +601,7 @@ namespace NCC.Extend.LqKhxx @@ -600,6 +601,7 @@ namespace NCC.Extend.LqKhxx
600 ItemPrice = item.Pxjg, 601 ItemPrice = item.Pxjg,
601 SourceType = item.SourceType, 602 SourceType = item.SourceType,
602 TotalPurchased = item.ProjectNumber, 603 TotalPurchased = item.ProjectNumber,
  604 + Remark = item.Remark,
603 ConsumedCount = consumedData.FirstOrDefault(c => c.BillingItemId == item.Id)?.TotalConsumed ?? 0, 605 ConsumedCount = consumedData.FirstOrDefault(c => c.BillingItemId == item.Id)?.TotalConsumed ?? 0,
604 RefundedCount = refundedData.FirstOrDefault(r => r.BillingItemId == item.Id)?.TotalRefunded ?? 0, 606 RefundedCount = refundedData.FirstOrDefault(r => r.BillingItemId == item.Id)?.TotalRefunded ?? 0,
605 DeductCount = deductData.FirstOrDefault(d => d.BillingItemId == item.Id)?.TotalDeduct ?? 0, 607 DeductCount = deductData.FirstOrDefault(d => d.BillingItemId == item.Id)?.TotalDeduct ?? 0,
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -831,8 +831,40 @@ namespace NCC.Extend.LqStatistics @@ -831,8 +831,40 @@ namespace NCC.Extend.LqStatistics
831 /// <summary> 831 /// <summary>
832 /// 获取科技部老师业绩统计 832 /// 获取科技部老师业绩统计
833 /// </summary> 833 /// </summary>
  834 + /// <remarks>
  835 + /// 统计科技部老师的开单业绩、消耗业绩、手工费等相关数据
  836 + ///
  837 + /// 示例请求:
  838 + /// ```json
  839 + /// {
  840 + /// "startDate": "2025-01-01T00:00:00",
  841 + /// "endDate": "2025-01-31T23:59:59",
  842 + /// "teacherId": "科技部老师ID(可选)",
  843 + /// "teacherName": "科技部老师姓名(可选)"
  844 + /// }
  845 + /// ```
  846 + ///
  847 + /// 参数说明:
  848 + /// - startDate: 开始日期(可选)
  849 + /// - endDate: 结束日期(可选)
  850 + /// - teacherId: 科技部老师ID(可选)
  851 + /// - teacherName: 科技部老师姓名(可选)
  852 + ///
  853 + /// 返回数据说明:
  854 + /// - DepartmentName: 部门名称(固定为"科技部")
  855 + /// - TeacherName: 老师姓名
  856 + /// - ConsumeProjectCount: 消耗项目数
  857 + /// - ConsumeAchievement: 消耗业绩
  858 + /// - OrderAchievement: 开单业绩(开卡业绩)
  859 + /// - OrderItemCount: 开单品项次数
  860 + /// - ConsumeItemCount: 耗卡品项次数
  861 + /// - ConsumeLaborCost: 消耗手工费(耗卡手工费)
  862 + /// </remarks>
834 /// <param name="input">查询参数</param> 863 /// <param name="input">查询参数</param>
835 - /// <returns>科技部老师业绩统计结果</returns> 864 + /// <returns>科技部老师业绩统计结果列表,包含开单业绩、消耗业绩、手工费等数据</returns>
  865 + /// <response code="200">成功返回统计数据</response>
  866 + /// <response code="400">参数错误</response>
  867 + /// <response code="500">服务器内部错误</response>
836 [HttpPost("GetTechTeacherStatistics")] 868 [HttpPost("GetTechTeacherStatistics")]
837 [AllowAnonymous] 869 [AllowAnonymous]
838 public async Task<List<TechTeacherSimpleStatisticsOutput>> GetTechTeacherStatistics(TechTeacherStatisticsInput input) 870 public async Task<List<TechTeacherSimpleStatisticsOutput>> GetTechTeacherStatistics(TechTeacherStatisticsInput input)
@@ -852,10 +884,21 @@ namespace NCC.Extend.LqStatistics @@ -852,10 +884,21 @@ namespace NCC.Extend.LqStatistics
852 // 2. 获取业绩流水数据 884 // 2. 获取业绩流水数据
853 var flowQuery = _db.Queryable<VTechTeacherFlowEntity>(); 885 var flowQuery = _db.Queryable<VTechTeacherFlowEntity>();
854 886
855 - // 老师过滤 887 + // 老师过滤(支持通过ID或账号匹配)
856 if (!string.IsNullOrEmpty(input.TeacherId)) 888 if (!string.IsNullOrEmpty(input.TeacherId))
857 { 889 {
858 - flowQuery = flowQuery.Where(x => x.TeacherId == input.TeacherId); 890 + // 先通过用户ID查询账号,然后同时匹配ID和账号
  891 + var teacherAccount = allTeachers.FirstOrDefault(t => t.TeacherId == input.TeacherId)?.TeacherAccount;
  892 + if (!string.IsNullOrEmpty(teacherAccount))
  893 + {
  894 + // 同时匹配ID和账号(因为视图中的teacher_id可能是ID或账号)
  895 + flowQuery = flowQuery.Where(x => x.TeacherId == input.TeacherId || x.TeacherAccount == teacherAccount);
  896 + }
  897 + else
  898 + {
  899 + // 如果找不到账号,只匹配ID
  900 + flowQuery = flowQuery.Where(x => x.TeacherId == input.TeacherId);
  901 + }
859 } 902 }
860 903
861 if (!string.IsNullOrEmpty(input.TeacherName)) 904 if (!string.IsNullOrEmpty(input.TeacherName))
@@ -876,23 +919,25 @@ namespace NCC.Extend.LqStatistics @@ -876,23 +919,25 @@ namespace NCC.Extend.LqStatistics
876 919
877 var flowRecords = await flowQuery.ToListAsync(); 920 var flowRecords = await flowQuery.ToListAsync();
878 921
879 - // 3. 按老师分组统计业绩数据 922 + // 3. 按老师账号分组统计业绩数据(包括耗卡手工费)
  923 + // 注意:视图中的teacher_id是kjbls,teacher_account是kjblszh,使用账号来匹配更准确
880 var teacherStatsDict = flowRecords 924 var teacherStatsDict = flowRecords
881 .GroupBy(x => new 925 .GroupBy(x => new
882 { 926 {
883 - x.TeacherId,  
884 - x.TeacherName,  
885 - x.TeacherAccount, 927 + TeacherAccount = x.TeacherAccount ?? string.Empty,
  928 + TeacherId = x.TeacherId ?? string.Empty,
  929 + TeacherName = x.TeacherName ?? string.Empty,
886 }) 930 })
887 .ToDictionary( 931 .ToDictionary(
888 - g => g.Key, 932 + g => g.Key.TeacherAccount ?? string.Empty,
889 g => new 933 g => new
890 { 934 {
891 - ConsumeProjectCount = (int)g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ProjectCount), 935 + ConsumeProjectCount = (int)(g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ProjectCount)),
892 ConsumeAchievement = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.Achievement), 936 ConsumeAchievement = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.Achievement),
893 OrderAchievement = g.Where(x => x.BusinessType == "开卡").Sum(x => x.Achievement), 937 OrderAchievement = g.Where(x => x.BusinessType == "开卡").Sum(x => x.Achievement),
894 OrderItemCount = g.Where(x => x.BusinessType == "开卡").Sum(x => x.ItemCount), 938 OrderItemCount = g.Where(x => x.BusinessType == "开卡").Sum(x => x.ItemCount),
895 ConsumeItemCount = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ItemCount), 939 ConsumeItemCount = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ItemCount),
  940 + ConsumeLaborCost = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.LaborCost),
896 } 941 }
897 ); 942 );
898 943
@@ -908,13 +953,10 @@ namespace NCC.Extend.LqStatistics @@ -908,13 +953,10 @@ namespace NCC.Extend.LqStatistics
908 if (!string.IsNullOrEmpty(input.TeacherName) && !teacher.TeacherName.Contains(input.TeacherName)) 953 if (!string.IsNullOrEmpty(input.TeacherName) && !teacher.TeacherName.Contains(input.TeacherName))
909 continue; 954 continue;
910 955
  956 + // 使用账号来匹配业绩数据(因为视图中的teacher_account是kjblszh)
  957 + var teacherAccount = teacher.TeacherAccount ?? string.Empty;
911 var stats = teacherStatsDict.GetValueOrDefault( 958 var stats = teacherStatsDict.GetValueOrDefault(
912 - new  
913 - {  
914 - TeacherId = teacher.TeacherId,  
915 - TeacherName = teacher.TeacherName,  
916 - TeacherAccount = teacher.TeacherAccount,  
917 - }, 959 + teacherAccount,
918 new 960 new
919 { 961 {
920 ConsumeProjectCount = 0, 962 ConsumeProjectCount = 0,
@@ -922,6 +964,7 @@ namespace NCC.Extend.LqStatistics @@ -922,6 +964,7 @@ namespace NCC.Extend.LqStatistics
922 OrderAchievement = 0m, 964 OrderAchievement = 0m,
923 OrderItemCount = 0, 965 OrderItemCount = 0,
924 ConsumeItemCount = 0, 966 ConsumeItemCount = 0,
  967 + ConsumeLaborCost = 0m,
925 } 968 }
926 ); 969 );
927 970
@@ -934,6 +977,7 @@ namespace NCC.Extend.LqStatistics @@ -934,6 +977,7 @@ namespace NCC.Extend.LqStatistics
934 OrderAchievement = stats.OrderAchievement, 977 OrderAchievement = stats.OrderAchievement,
935 OrderItemCount = stats.OrderItemCount, 978 OrderItemCount = stats.OrderItemCount,
936 ConsumeItemCount = stats.ConsumeItemCount, 979 ConsumeItemCount = stats.ConsumeItemCount,
  980 + ConsumeLaborCost = stats.ConsumeLaborCost,
937 }; 981 };
938 982
939 result.Add(teacherStats); 983 result.Add(teacherStats);
@@ -3672,6 +3716,5 @@ namespace NCC.Extend.LqStatistics @@ -3672,6 +3716,5 @@ namespace NCC.Extend.LqStatistics
3672 } 3716 }
3673 3717
3674 #endregion 3718 #endregion
3675 -  
3676 } 3719 }
3677 } 3720 }
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
@@ -1043,12 +1043,18 @@ namespace NCC.Extend.LqXhHyhk @@ -1043,12 +1043,18 @@ namespace NCC.Extend.LqXhHyhk
1043 Kjblsyj = ikjbs_tem.kjblsyj, 1043 Kjblsyj = ikjbs_tem.kjblsyj,
1044 Yjsj = DateTime.Now, 1044 Yjsj = DateTime.Now,
1045 Hkpxid = lqXhPxmxEntity.Id, 1045 Hkpxid = lqXhPxmxEntity.Id,
  1046 + // OriginalHdpxNumber = ikjbs_tem.hdpxNumber,
  1047 + // OvertimeHdpxNumber = (decimal)(entity.OvertimeCoefficient * (ikjbs_tem.hdpxNumber ?? 0)),
  1048 + // HdpxNumber = (decimal)((ikjbs_tem.hdpxNumber ?? 0) + (entity.OvertimeCoefficient * (ikjbs_tem.hdpxNumber ?? 0))),
1046 OriginalHdpxNumber = ikjbs_tem.hdpxNumber, 1049 OriginalHdpxNumber = ikjbs_tem.hdpxNumber,
1047 - OvertimeHdpxNumber = (decimal)(entity.OvertimeCoefficient * (ikjbs_tem.hdpxNumber ?? 0)),  
1048 - HdpxNumber = (decimal)((ikjbs_tem.hdpxNumber ?? 0) + (entity.OvertimeCoefficient * (ikjbs_tem.hdpxNumber ?? 0))), 1050 + OvertimeHdpxNumber = 0,
  1051 + HdpxNumber = ikjbs_tem.hdpxNumber,
  1052 + // OriginalLaborCost = ikjbs_tem.laborCost,
  1053 + // OvertimeLaborCost = (decimal)(entity.OvertimeCoefficient * (ikjbs_tem.laborCost ?? 0)),
  1054 + // LaborCost = (decimal)((ikjbs_tem.laborCost ?? 0) + (entity.OvertimeCoefficient * (ikjbs_tem.laborCost ?? 0))),
1049 OriginalLaborCost = ikjbs_tem.laborCost, 1055 OriginalLaborCost = ikjbs_tem.laborCost,
1050 - OvertimeLaborCost = (decimal)(entity.OvertimeCoefficient * (ikjbs_tem.laborCost ?? 0)),  
1051 - LaborCost = (decimal)((ikjbs_tem.laborCost ?? 0) + (entity.OvertimeCoefficient * (ikjbs_tem.laborCost ?? 0))), 1056 + OvertimeLaborCost = 0,
  1057 + LaborCost = ikjbs_tem.laborCost,
1052 IsEffective = StatusEnum.有效.GetHashCode(), 1058 IsEffective = StatusEnum.有效.GetHashCode(),
1053 } 1059 }
1054 ); 1060 );
@@ -1328,12 +1334,18 @@ namespace NCC.Extend.LqXhHyhk @@ -1328,12 +1334,18 @@ namespace NCC.Extend.LqXhHyhk
1328 Kjblsyj = ikjbs_tem.kjblsyj, 1334 Kjblsyj = ikjbs_tem.kjblsyj,
1329 Yjsj = DateTime.Now, 1335 Yjsj = DateTime.Now,
1330 Hkpxid = lqXhPxmxEntity.Id, 1336 Hkpxid = lqXhPxmxEntity.Id,
  1337 + // OriginalHdpxNumber = ikjbs_tem.hdpxNumber,
  1338 + // OvertimeHdpxNumber = (decimal)(entity.OvertimeCoefficient * (ikjbs_tem.hdpxNumber ?? 0)),
  1339 + // HdpxNumber = (decimal)((ikjbs_tem.hdpxNumber ?? 0) + (entity.OvertimeCoefficient * (ikjbs_tem.hdpxNumber ?? 0))),
  1340 + // OriginalLaborCost = ikjbs_tem.laborCost,
  1341 + // OvertimeLaborCost = (decimal)(entity.OvertimeCoefficient * (ikjbs_tem.laborCost ?? 0)),
  1342 + // LaborCost = (decimal)((ikjbs_tem.laborCost ?? 0) + (entity.OvertimeCoefficient * (ikjbs_tem.laborCost ?? 0))),
1331 OriginalHdpxNumber = ikjbs_tem.hdpxNumber, 1343 OriginalHdpxNumber = ikjbs_tem.hdpxNumber,
1332 - OvertimeHdpxNumber = (decimal)(entity.OvertimeCoefficient * (ikjbs_tem.hdpxNumber ?? 0)),  
1333 - HdpxNumber = (decimal)((ikjbs_tem.hdpxNumber ?? 0) + (entity.OvertimeCoefficient * (ikjbs_tem.hdpxNumber ?? 0))), 1344 + OvertimeHdpxNumber = 0,
  1345 + HdpxNumber = ikjbs_tem.hdpxNumber,
1334 OriginalLaborCost = ikjbs_tem.laborCost, 1346 OriginalLaborCost = ikjbs_tem.laborCost,
1335 - OvertimeLaborCost = (decimal)(entity.OvertimeCoefficient * (ikjbs_tem.laborCost ?? 0)),  
1336 - LaborCost = (decimal)((ikjbs_tem.laborCost ?? 0) + (entity.OvertimeCoefficient * (ikjbs_tem.laborCost ?? 0))), 1347 + OvertimeLaborCost = 0,
  1348 + LaborCost = ikjbs_tem.laborCost,
1337 IsEffective = StatusEnum.有效.GetHashCode(), 1349 IsEffective = StatusEnum.有效.GetHashCode(),
1338 }); 1350 });
1339 } 1351 }
netcore/src/Modularity/Extend/NCC.Extend/Utils/WeChatBotService.cs
@@ -3,6 +3,7 @@ using System.Net.Http; @@ -3,6 +3,7 @@ using System.Net.Http;
3 using System.Text; 3 using System.Text;
4 using System.Threading.Tasks; 4 using System.Threading.Tasks;
5 using Newtonsoft.Json; 5 using Newtonsoft.Json;
  6 +using NCC;
6 7
7 namespace NCC.Extend.Utils 8 namespace NCC.Extend.Utils
8 { 9 {
@@ -12,17 +13,28 @@ namespace NCC.Extend.Utils @@ -12,17 +13,28 @@ namespace NCC.Extend.Utils
12 public class WeChatBotService 13 public class WeChatBotService
13 { 14 {
14 private readonly HttpClient _httpClient; 15 private readonly HttpClient _httpClient;
15 - private const string BOT_API_URL = "http://wx.lvqianmeiye.com/api/Bot/send-text";  
16 - //正式地址  
17 - //https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=581c22a6-cb67-42e5-8c76-b8e90052e188  
18 - //测试地址  
19 - //https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6f8686ec-5011-4c1d-bae9-d82a2a2f4d83  
20 - private const string WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=496f1add-122b-43fc-9e38-0ca79c48b33f";  
21 - private const string WEBHOOK_URL_TEST = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6f8686ec-5011-4c1d-bae9-d82a2a2f4d83"; 16 + private readonly string _botApiUrl;
  17 + private readonly string _webhookUrl;
22 18
  19 + /// <summary>
  20 + /// 初始化企业微信机器人服务
  21 + /// </summary>
  22 + /// <param name="httpClient">HTTP客户端</param>
23 public WeChatBotService(HttpClient httpClient) 23 public WeChatBotService(HttpClient httpClient)
24 { 24 {
25 _httpClient = httpClient; 25 _httpClient = httpClient;
  26 +
  27 + // 从配置文件中读取企业微信机器人配置
  28 + _botApiUrl = App.Configuration["WeChatBot:BotApiUrl"] ?? "http://wx.lvqianmeiye.com/api/Bot/send-text";
  29 +
  30 + // 从配置文件中读取Webhook地址(正式或测试地址,通过配置文件切换)
  31 + _webhookUrl = App.Configuration["WeChatBot:WebhookUrl"];
  32 +
  33 + // 如果配置文件中没有配置,使用默认值
  34 + if (string.IsNullOrEmpty(_webhookUrl))
  35 + {
  36 + _webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=496f1add-122b-43fc-9e38-0ca79c48b33f";
  37 + }
26 } 38 }
27 39
28 /// <summary> 40 /// <summary>
@@ -36,8 +48,7 @@ namespace NCC.Extend.Utils @@ -36,8 +48,7 @@ namespace NCC.Extend.Utils
36 { 48 {
37 var requestData = new 49 var requestData = new
38 { 50 {
39 - webhookUrl = WEBHOOK_URL,  
40 - // webhookUrl = WEBHOOK_URL_TEST, 51 + webhookUrl = _webhookUrl,
41 content = content, 52 content = content,
42 mentionedList = (string)null, 53 mentionedList = (string)null,
43 mentionedMobileList = (string)null, 54 mentionedMobileList = (string)null,
@@ -46,7 +57,7 @@ namespace NCC.Extend.Utils @@ -46,7 +57,7 @@ namespace NCC.Extend.Utils
46 var json = JsonConvert.SerializeObject(requestData); 57 var json = JsonConvert.SerializeObject(requestData);
47 var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); 58 var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
48 59
49 - var response = await _httpClient.PostAsync(BOT_API_URL, httpContent); 60 + var response = await _httpClient.PostAsync(_botApiUrl, httpContent);
50 61
51 if (response.IsSuccessStatusCode) 62 if (response.IsSuccessStatusCode)
52 { 63 {