Commit a78c0ae8c55c3de6aee30ea290908ab4154f4409
1 parent
5e629136
feat: 退卡服务添加健康师和科技部老师筛选功能,返回品项明细和业绩数据;开单服务添加健康师和科技部老师筛选及业绩返回;科技部老师统计添加手工费统计;修复Ge…
…tTechTeacherStatistics方法错误;WeChatBot配置化
Showing
11 changed files
with
316 additions
and
41 deletions
netcore/src/Application/NCC.API/appsettings.json
| ... | ... | @@ -181,6 +181,11 @@ |
| 181 | 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 | 189 | "NCC_App": { |
| 185 | 190 | "CodeAreasName": "SubDev,Food,Extend,test", |
| 186 | 191 | //系统文件路径(末尾必须带斜杆) | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/LqHytkHytkListOutput.cs
| 1 | 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 | 7 | namespace NCC.Extend.Entitys.Dto.LqHytkHytk |
| 4 | 8 | { |
| ... | ... | @@ -102,5 +106,19 @@ namespace NCC.Extend.Entitys.Dto.LqHytkHytk |
| 102 | 106 | /// </summary> |
| 103 | 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 | 93 | /// </summary> |
| 94 | 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 | 107 | \ No newline at end of file | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqXhHyhk/MemberRemainingItemsOutput.cs
| ... | ... | @@ -25,6 +25,7 @@ namespace NCC.Extend.Entitys.Dto.LqXhHyhk |
| 25 | 25 | public string MemberName { get; set; } |
| 26 | 26 | |
| 27 | 27 | /// <summary> |
| 28 | + /// <summary> | |
| 28 | 29 | /// 剩余品项列表 |
| 29 | 30 | /// </summary> |
| 30 | 31 | /// <remarks>按剩余数量降序排列的品项列表</remarks> |
| ... | ... | @@ -71,6 +72,12 @@ namespace NCC.Extend.Entitys.Dto.LqXhHyhk |
| 71 | 72 | public string SourceType { get; set; } |
| 72 | 73 | |
| 73 | 74 | /// <summary> |
| 75 | + /// 备注 | |
| 76 | + /// </summary> | |
| 77 | + /// <example>备注</example> | |
| 78 | + public string Remark { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 74 | 81 | /// 总购买数量 |
| 75 | 82 | /// </summary> |
| 76 | 83 | /// <remarks>该品项的总购买次数</remarks> | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
| ... | ... | @@ -70,8 +70,35 @@ namespace NCC.Extend.LqHytkHytk |
| 70 | 70 | /// <summary> |
| 71 | 71 | /// 获取退卡信息列表 |
| 72 | 72 | /// </summary> |
| 73 | + /// <remarks> | |
| 74 | + /// 获取退卡记录列表,支持根据健康师ID或科技部老师ID筛选,返回品项明细、健康师业绩和科技部老师业绩 | |
| 75 | + /// | |
| 76 | + /// 示例请求: | |
| 77 | + /// ```json | |
| 78 | + /// GET /api/Extend/LqHytkHytk?jksId=健康师ID&kjblsId=科技部老师ID&currentPage=1&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 | 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 | 102 | [HttpGet("")] |
| 76 | 103 | public async Task<dynamic> GetList([FromQuery] LqHytkHytkListQueryInput input) |
| 77 | 104 | { |
| ... | ... | @@ -79,7 +106,48 @@ namespace NCC.Extend.LqHytkHytk |
| 79 | 106 | List<string> queryTksj = input.tksj != null ? input.tksj.Split(',').ToObeject<List<string>>() : null; |
| 80 | 107 | DateTime? startTksj = queryTksj != null ? Ext.GetDateTime(queryTksj.First()) : null; |
| 81 | 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 | 151 | .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id)) |
| 84 | 152 | .WhereIF(!string.IsNullOrEmpty(input.md), p => p.Md.Equals(input.md)) |
| 85 | 153 | .WhereIF(!string.IsNullOrEmpty(input.mdbh), p => p.Mdbh.Contains(input.mdbh)) |
| ... | ... | @@ -123,6 +191,104 @@ namespace NCC.Extend.LqHytkHytk |
| 123 | 191 | .MergeTable() |
| 124 | 192 | .OrderBy(sidx + " " + input.sort) |
| 125 | 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 | 292 | return PageResult<LqHytkHytkListOutput>.SqlSugarPageResult(data); |
| 127 | 293 | } |
| 128 | 294 | #endregion | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
| ... | ... | @@ -454,10 +454,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 454 | 454 | { |
| 455 | 455 | throw NCCException.Oh("健康师ID不能为空"); |
| 456 | 456 | } |
| 457 | - | |
| 458 | 457 | var sidx = input.sidx == null ? "kdrq" : input.sidx; |
| 459 | 458 | var sort = string.IsNullOrEmpty(input.sort) ? "DESC" : input.sort; |
| 460 | - | |
| 461 | 459 | // 解析时间范围 |
| 462 | 460 | DateTime? startDate = null; |
| 463 | 461 | DateTime? endDate = null; |
| ... | ... | @@ -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 | 478 | .Where((jksyj, kdjlb) => jksyj.Jkszh == input.jksId) |
| 482 | 479 | .WhereIF(input.isEffective != 0, (jksyj, kdjlb) => jksyj.IsEffective == input.isEffective && kdjlb.IsEffective == input.isEffective) |
| 483 | 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 | 551 | x.Pxmc, |
| 552 | 552 | x.Pxjg, |
| 553 | 553 | x.SourceType, |
| 554 | - x.ProjectNumber | |
| 554 | + x.ProjectNumber, | |
| 555 | + x.Remark | |
| 555 | 556 | }) |
| 556 | 557 | .ToListAsync(); |
| 557 | 558 | |
| ... | ... | @@ -600,6 +601,7 @@ namespace NCC.Extend.LqKhxx |
| 600 | 601 | ItemPrice = item.Pxjg, |
| 601 | 602 | SourceType = item.SourceType, |
| 602 | 603 | TotalPurchased = item.ProjectNumber, |
| 604 | + Remark = item.Remark, | |
| 603 | 605 | ConsumedCount = consumedData.FirstOrDefault(c => c.BillingItemId == item.Id)?.TotalConsumed ?? 0, |
| 604 | 606 | RefundedCount = refundedData.FirstOrDefault(r => r.BillingItemId == item.Id)?.TotalRefunded ?? 0, |
| 605 | 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 | 831 | /// <summary> |
| 832 | 832 | /// 获取科技部老师业绩统计 |
| 833 | 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 | 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 | 868 | [HttpPost("GetTechTeacherStatistics")] |
| 837 | 869 | [AllowAnonymous] |
| 838 | 870 | public async Task<List<TechTeacherSimpleStatisticsOutput>> GetTechTeacherStatistics(TechTeacherStatisticsInput input) |
| ... | ... | @@ -852,10 +884,21 @@ namespace NCC.Extend.LqStatistics |
| 852 | 884 | // 2. 获取业绩流水数据 |
| 853 | 885 | var flowQuery = _db.Queryable<VTechTeacherFlowEntity>(); |
| 854 | 886 | |
| 855 | - // 老师过滤 | |
| 887 | + // 老师过滤(支持通过ID或账号匹配) | |
| 856 | 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 | 904 | if (!string.IsNullOrEmpty(input.TeacherName)) |
| ... | ... | @@ -876,23 +919,25 @@ namespace NCC.Extend.LqStatistics |
| 876 | 919 | |
| 877 | 920 | var flowRecords = await flowQuery.ToListAsync(); |
| 878 | 921 | |
| 879 | - // 3. 按老师分组统计业绩数据 | |
| 922 | + // 3. 按老师账号分组统计业绩数据(包括耗卡手工费) | |
| 923 | + // 注意:视图中的teacher_id是kjbls,teacher_account是kjblszh,使用账号来匹配更准确 | |
| 880 | 924 | var teacherStatsDict = flowRecords |
| 881 | 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 | 931 | .ToDictionary( |
| 888 | - g => g.Key, | |
| 932 | + g => g.Key.TeacherAccount ?? string.Empty, | |
| 889 | 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 | 936 | ConsumeAchievement = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.Achievement), |
| 893 | 937 | OrderAchievement = g.Where(x => x.BusinessType == "开卡").Sum(x => x.Achievement), |
| 894 | 938 | OrderItemCount = g.Where(x => x.BusinessType == "开卡").Sum(x => x.ItemCount), |
| 895 | 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 | 953 | if (!string.IsNullOrEmpty(input.TeacherName) && !teacher.TeacherName.Contains(input.TeacherName)) |
| 909 | 954 | continue; |
| 910 | 955 | |
| 956 | + // 使用账号来匹配业绩数据(因为视图中的teacher_account是kjblszh) | |
| 957 | + var teacherAccount = teacher.TeacherAccount ?? string.Empty; | |
| 911 | 958 | var stats = teacherStatsDict.GetValueOrDefault( |
| 912 | - new | |
| 913 | - { | |
| 914 | - TeacherId = teacher.TeacherId, | |
| 915 | - TeacherName = teacher.TeacherName, | |
| 916 | - TeacherAccount = teacher.TeacherAccount, | |
| 917 | - }, | |
| 959 | + teacherAccount, | |
| 918 | 960 | new |
| 919 | 961 | { |
| 920 | 962 | ConsumeProjectCount = 0, |
| ... | ... | @@ -922,6 +964,7 @@ namespace NCC.Extend.LqStatistics |
| 922 | 964 | OrderAchievement = 0m, |
| 923 | 965 | OrderItemCount = 0, |
| 924 | 966 | ConsumeItemCount = 0, |
| 967 | + ConsumeLaborCost = 0m, | |
| 925 | 968 | } |
| 926 | 969 | ); |
| 927 | 970 | |
| ... | ... | @@ -934,6 +977,7 @@ namespace NCC.Extend.LqStatistics |
| 934 | 977 | OrderAchievement = stats.OrderAchievement, |
| 935 | 978 | OrderItemCount = stats.OrderItemCount, |
| 936 | 979 | ConsumeItemCount = stats.ConsumeItemCount, |
| 980 | + ConsumeLaborCost = stats.ConsumeLaborCost, | |
| 937 | 981 | }; |
| 938 | 982 | |
| 939 | 983 | result.Add(teacherStats); |
| ... | ... | @@ -3672,6 +3716,5 @@ namespace NCC.Extend.LqStatistics |
| 3672 | 3716 | } |
| 3673 | 3717 | |
| 3674 | 3718 | #endregion |
| 3675 | - | |
| 3676 | 3719 | } |
| 3677 | 3720 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
| ... | ... | @@ -1043,12 +1043,18 @@ namespace NCC.Extend.LqXhHyhk |
| 1043 | 1043 | Kjblsyj = ikjbs_tem.kjblsyj, |
| 1044 | 1044 | Yjsj = DateTime.Now, |
| 1045 | 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 | 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 | 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 | 1058 | IsEffective = StatusEnum.有效.GetHashCode(), |
| 1053 | 1059 | } |
| 1054 | 1060 | ); |
| ... | ... | @@ -1328,12 +1334,18 @@ namespace NCC.Extend.LqXhHyhk |
| 1328 | 1334 | Kjblsyj = ikjbs_tem.kjblsyj, |
| 1329 | 1335 | Yjsj = DateTime.Now, |
| 1330 | 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 | 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 | 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 | 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 | 3 | using System.Text; |
| 4 | 4 | using System.Threading.Tasks; |
| 5 | 5 | using Newtonsoft.Json; |
| 6 | +using NCC; | |
| 6 | 7 | |
| 7 | 8 | namespace NCC.Extend.Utils |
| 8 | 9 | { |
| ... | ... | @@ -12,17 +13,28 @@ namespace NCC.Extend.Utils |
| 12 | 13 | public class WeChatBotService |
| 13 | 14 | { |
| 14 | 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 | 23 | public WeChatBotService(HttpClient httpClient) |
| 24 | 24 | { |
| 25 | 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 | 40 | /// <summary> |
| ... | ... | @@ -36,8 +48,7 @@ namespace NCC.Extend.Utils |
| 36 | 48 | { |
| 37 | 49 | var requestData = new |
| 38 | 50 | { |
| 39 | - webhookUrl = WEBHOOK_URL, | |
| 40 | - // webhookUrl = WEBHOOK_URL_TEST, | |
| 51 | + webhookUrl = _webhookUrl, | |
| 41 | 52 | content = content, |
| 42 | 53 | mentionedList = (string)null, |
| 43 | 54 | mentionedMobileList = (string)null, |
| ... | ... | @@ -46,7 +57,7 @@ namespace NCC.Extend.Utils |
| 46 | 57 | var json = JsonConvert.SerializeObject(requestData); |
| 47 | 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 | 62 | if (response.IsSuccessStatusCode) |
| 52 | 63 | { | ... | ... |