Commit e745bcc24062caa5748de0d97678e3a00e605f7b

Authored by “wangming”
1 parent 89aeb0ca

修复转卡方法中的空引用异常和First()方法错误

- 修复金三角用户查询中的First()方法错误,使用ToListAsync() + FirstOrDefault()避免异常
- 添加空值检查,当健康师在金三角团队中不存在时提供友好错误信息
- 完善转卡记录字段,添加门店名称、门店编号、会员账号、顾客类型、退卡时间等字段
- 完善开单记录字段,添加开单日期、顾客类型、总业绩等字段
- 修复金三角用户查询条件,使用正确的用户账号字段(jkszh)而不是用户ID(jks)
- 提高转卡操作的健壮性和错误处理能力
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
@@ -96,6 +96,7 @@ namespace NCC.Extend.LqHytkHytk @@ -96,6 +96,7 @@ namespace NCC.Extend.LqHytkHytk
96 .WhereIF(queryTksj != null, p => p.Tksj >= new DateTime(startTksj.ToDate().Year, startTksj.ToDate().Month, startTksj.ToDate().Day, 0, 0, 0)) 96 .WhereIF(queryTksj != null, p => p.Tksj >= new DateTime(startTksj.ToDate().Year, startTksj.ToDate().Month, startTksj.ToDate().Day, 0, 0, 0))
97 .WhereIF(queryTksj != null, p => p.Tksj <= new DateTime(endTksj.ToDate().Year, endTksj.ToDate().Month, endTksj.ToDate().Day, 23, 59, 59)) 97 .WhereIF(queryTksj != null, p => p.Tksj <= new DateTime(endTksj.ToDate().Year, endTksj.ToDate().Month, endTksj.ToDate().Day, 23, 59, 59))
98 .WhereIF(!string.IsNullOrEmpty(input.czry), p => p.Czry.Equals(input.czry)) 98 .WhereIF(!string.IsNullOrEmpty(input.czry), p => p.Czry.Equals(input.czry))
  99 + .WhereIF(input.isEffective != 0, p => p.IsEffective == input.isEffective)
99 .Select(it => new LqHytkHytkListOutput 100 .Select(it => new LqHytkHytkListOutput
100 { 101 {
101 id = it.Id, 102 id = it.Id,
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
@@ -43,6 +43,7 @@ using NCC.System.Entitys.Permission; @@ -43,6 +43,7 @@ using NCC.System.Entitys.Permission;
43 using SqlSugar; 43 using SqlSugar;
44 using Yitter.IdGenerator; 44 using Yitter.IdGenerator;
45 using NCC.Extend.Entitys.lq_package_info; 45 using NCC.Extend.Entitys.lq_package_info;
  46 +using NCC.Extend.Entitys.lq_mdxx;
46 47
47 namespace NCC.Extend.LqKdKdjlb 48 namespace NCC.Extend.LqKdKdjlb
48 { 49 {
@@ -216,9 +217,9 @@ namespace NCC.Extend.LqKdKdjlb @@ -216,9 +217,9 @@ namespace NCC.Extend.LqKdKdjlb
216 .WhereIF(queryKdrq != null, p => p.Kdrq <= new DateTime(endKdrq.ToDate().Year, endKdrq.ToDate().Month, endKdrq.ToDate().Day, 23, 59, 59)) 217 .WhereIF(queryKdrq != null, p => p.Kdrq <= new DateTime(endKdrq.ToDate().Year, endKdrq.ToDate().Month, endKdrq.ToDate().Day, 23, 59, 59))
217 .WhereIF(!string.IsNullOrEmpty(input.gjlx), p => p.Gjlx.Equals(input.gjlx)) 218 .WhereIF(!string.IsNullOrEmpty(input.gjlx), p => p.Gjlx.Equals(input.gjlx))
218 .WhereIF(!string.IsNullOrEmpty(input.hgjg), p => p.Hgjg.Equals(input.hgjg)) 219 .WhereIF(!string.IsNullOrEmpty(input.hgjg), p => p.Hgjg.Equals(input.hgjg))
219 - .WhereIF(!string.IsNullOrEmpty(input.zdyj), p => p.Zdyj.Equals(input.zdyj))  
220 - .WhereIF(!string.IsNullOrEmpty(input.sfyj), p => p.Sfyj.Equals(input.sfyj))  
221 - .WhereIF(!string.IsNullOrEmpty(input.qk), p => p.Qk.Equals(input.qk)) 220 + .WhereIF(input.zdyj != null, p => p.Zdyj > input.zdyj)
  221 + .WhereIF(input.sfyj != null, p => p.Sfyj > input.sfyj)
  222 + .WhereIF(input.qk != null, p => p.Qk > input.qk)
222 .WhereIF(!string.IsNullOrEmpty(input.ckfs), p => p.Ckfs.Equals(input.ckfs)) 223 .WhereIF(!string.IsNullOrEmpty(input.ckfs), p => p.Ckfs.Equals(input.ckfs))
223 .WhereIF(!string.IsNullOrEmpty(input.fkfs), p => p.Fkfs.Equals(input.fkfs)) 224 .WhereIF(!string.IsNullOrEmpty(input.fkfs), p => p.Fkfs.Equals(input.fkfs))
224 .WhereIF(!string.IsNullOrEmpty(input.fkyy), p => p.Fkyy.Equals(input.fkyy)) 225 .WhereIF(!string.IsNullOrEmpty(input.fkyy), p => p.Fkyy.Equals(input.fkyy))
@@ -233,6 +234,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -233,6 +234,7 @@ namespace NCC.Extend.LqKdKdjlb
233 .WhereIF(!string.IsNullOrEmpty(input.kdhysjh), p => p.Kdhysjh.Contains(input.kdhysjh)) 234 .WhereIF(!string.IsNullOrEmpty(input.kdhysjh), p => p.Kdhysjh.Contains(input.kdhysjh))
234 .WhereIF(!string.IsNullOrEmpty(input.F_FIleUrl), p => p.F_FIleUrl.Contains(input.F_FIleUrl)) 235 .WhereIF(!string.IsNullOrEmpty(input.F_FIleUrl), p => p.F_FIleUrl.Contains(input.F_FIleUrl))
235 .WhereIF(!string.IsNullOrEmpty(input.CreateUser), p => p.CreateUser.Equals(input.CreateUser)) 236 .WhereIF(!string.IsNullOrEmpty(input.CreateUser), p => p.CreateUser.Equals(input.CreateUser))
  237 + .WhereIF(input.isEffective != 0, p => p.IsEffective == input.isEffective)
236 .Select(it => new LqKdKdjlbListOutput 238 .Select(it => new LqKdKdjlbListOutput
237 { 239 {
238 id = it.Id, 240 id = it.Id,
@@ -781,9 +783,9 @@ namespace NCC.Extend.LqKdKdjlb @@ -781,9 +783,9 @@ namespace NCC.Extend.LqKdKdjlb
781 .WhereIF(queryKdrq != null, p => p.Kdrq <= new DateTime(endKdrq.ToDate().Year, endKdrq.ToDate().Month, endKdrq.ToDate().Day, 23, 59, 59)) 783 .WhereIF(queryKdrq != null, p => p.Kdrq <= new DateTime(endKdrq.ToDate().Year, endKdrq.ToDate().Month, endKdrq.ToDate().Day, 23, 59, 59))
782 .WhereIF(!string.IsNullOrEmpty(input.gjlx), p => p.Gjlx.Equals(input.gjlx)) 784 .WhereIF(!string.IsNullOrEmpty(input.gjlx), p => p.Gjlx.Equals(input.gjlx))
783 .WhereIF(!string.IsNullOrEmpty(input.hgjg), p => p.Hgjg.Equals(input.hgjg)) 785 .WhereIF(!string.IsNullOrEmpty(input.hgjg), p => p.Hgjg.Equals(input.hgjg))
784 - .WhereIF(!string.IsNullOrEmpty(input.zdyj), p => p.Zdyj.Equals(input.zdyj))  
785 - .WhereIF(!string.IsNullOrEmpty(input.sfyj), p => p.Sfyj.Equals(input.sfyj))  
786 - .WhereIF(!string.IsNullOrEmpty(input.qk), p => p.Qk.Equals(input.qk)) 786 + .WhereIF(input.zdyj != null, p => p.Zdyj > input.zdyj)
  787 + .WhereIF(input.sfyj != null, p => p.Sfyj > input.sfyj)
  788 + .WhereIF(input.qk != null, p => p.Qk > input.qk)
787 .WhereIF(!string.IsNullOrEmpty(input.ckfs), p => p.Ckfs.Equals(input.ckfs)) 789 .WhereIF(!string.IsNullOrEmpty(input.ckfs), p => p.Ckfs.Equals(input.ckfs))
788 .WhereIF(!string.IsNullOrEmpty(input.fkfs), p => p.Fkfs.Equals(input.fkfs)) 790 .WhereIF(!string.IsNullOrEmpty(input.fkfs), p => p.Fkfs.Equals(input.fkfs))
789 .WhereIF(!string.IsNullOrEmpty(input.fkyy), p => p.Fkyy.Equals(input.fkyy)) 791 .WhereIF(!string.IsNullOrEmpty(input.fkyy), p => p.Fkyy.Equals(input.fkyy))
@@ -797,6 +799,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -797,6 +799,7 @@ namespace NCC.Extend.LqKdKdjlb
797 .WhereIF(!string.IsNullOrEmpty(input.kdhyc), p => p.Kdhyc.Contains(input.kdhyc)) 799 .WhereIF(!string.IsNullOrEmpty(input.kdhyc), p => p.Kdhyc.Contains(input.kdhyc))
798 .WhereIF(!string.IsNullOrEmpty(input.kdhysjh), p => p.Kdhysjh.Contains(input.kdhysjh)) 800 .WhereIF(!string.IsNullOrEmpty(input.kdhysjh), p => p.Kdhysjh.Contains(input.kdhysjh))
799 .WhereIF(!string.IsNullOrEmpty(input.F_FIleUrl), p => p.F_FIleUrl.Contains(input.F_FIleUrl)) 801 .WhereIF(!string.IsNullOrEmpty(input.F_FIleUrl), p => p.F_FIleUrl.Contains(input.F_FIleUrl))
  802 + .WhereIF(input.isEffective != 0, p => p.IsEffective == input.isEffective)
800 .Select(it => new LqKdKdjlbListOutput 803 .Select(it => new LqKdKdjlbListOutput
801 { 804 {
802 id = it.Id, 805 id = it.Id,
@@ -2324,6 +2327,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -2324,6 +2327,7 @@ namespace NCC.Extend.LqKdKdjlb
2324 /// 转卡是指一个会员把自己剩余的某些品项,转给另外一个会员 2327 /// 转卡是指一个会员把自己剩余的某些品项,转给另外一个会员
2325 /// 转卡会员转出的品项,就做退卡操作 2328 /// 转卡会员转出的品项,就做退卡操作
2326 /// 被转卡会员收到的品项,做开卡操作 2329 /// 被转卡会员收到的品项,做开卡操作
  2330 + /// 系统会自动从原开单记录中获取健康师和科技部老师的业绩数据
2327 /// 2331 ///
2328 /// 示例请求: 2332 /// 示例请求:
2329 /// ```json 2333 /// ```json
@@ -2331,68 +2335,37 @@ namespace NCC.Extend.LqKdKdjlb @@ -2331,68 +2335,37 @@ namespace NCC.Extend.LqKdKdjlb
2331 /// "fromMemberId": "member001", 2335 /// "fromMemberId": "member001",
2332 /// "toMemberId": "member002", 2336 /// "toMemberId": "member002",
2333 /// "storeId": "store001", 2337 /// "storeId": "store001",
2334 - /// "signatureFile": "签名文件路径",  
2335 - /// "remarks": "转卡备注", 2338 + /// "signatureFile": "base64签名数据",
  2339 + /// "remarks": "转卡备注信息",
2336 /// "transferItems": [ 2340 /// "transferItems": [
2337 /// { 2341 /// {
2338 - /// "billingItemId": "item001", 2342 + /// "billingItemId": "pxmx001",
2339 /// "transferQuantity": 5, 2343 /// "transferQuantity": 5,
2340 - /// "itemId": "px001",  
2341 - /// "itemName": "美容项目",  
2342 - /// "itemPrice": 100.00,  
2343 - /// "sourceType": "转卡",  
2344 - /// "healthTeacherPerformances": [  
2345 - /// {  
2346 - /// "healthTeacherId": "jks001",  
2347 - /// "healthTeacherName": "张医生",  
2348 - /// "healthTeacherAccount": "zhang001",  
2349 - /// "performanceAmount": 50.00,  
2350 - /// "laborCost": 20.00,  
2351 - /// "itemQuantity": 5  
2352 - /// }  
2353 - /// ],  
2354 - /// "techTeacherPerformances": [  
2355 - /// {  
2356 - /// "techTeacherId": "kjbs001",  
2357 - /// "techTeacherName": "李老师",  
2358 - /// "techTeacherAccount": "li001",  
2359 - /// "performanceAmount": 30.00,  
2360 - /// "laborCost": 10.00,  
2361 - /// "itemQuantity": 5  
2362 - /// }  
2363 - /// ] 2344 + /// "itemName": "美容项目A",
  2345 + /// "itemPrice": 100.00
2364 /// } 2346 /// }
2365 /// ] 2347 /// ]
2366 /// } 2348 /// }
2367 /// ``` 2349 /// ```
2368 /// 2350 ///
2369 /// 参数说明: 2351 /// 参数说明:
2370 - /// - fromMemberId: 转出会员ID  
2371 - /// - toMemberId: 转入会员ID  
2372 - /// - storeId: 门店ID  
2373 - /// - signatureFile: 转出会员签名  
2374 - /// - remarks: 转卡备注  
2375 - /// - transferItems: 转出品项列表  
2376 - /// - billingItemId: 开单品项明细ID  
2377 - /// - transferQuantity: 转出数量  
2378 - /// - itemId: 品项ID  
2379 - /// - itemName: 品项名称  
2380 - /// - itemPrice: 品项价格  
2381 - /// - sourceType: 来源类型  
2382 - /// - healthTeacherPerformances: 健康师业绩列表  
2383 - /// - healthTeacherId: 健康师ID  
2384 - /// - healthTeacherName: 健康师姓名  
2385 - /// - healthTeacherAccount: 健康师账号  
2386 - /// - performanceAmount: 业绩金额  
2387 - /// - laborCost: 人工成本  
2388 - /// - itemQuantity: 品项数量  
2389 - /// - techTeacherPerformances: 科技部老师业绩列表  
2390 - /// - techTeacherId: 科技部老师ID  
2391 - /// - techTeacherName: 科技部老师姓名  
2392 - /// - techTeacherAccount: 科技部老师账号  
2393 - /// - performanceAmount: 业绩金额  
2394 - /// - laborCost: 人工成本  
2395 - /// - itemQuantity: 品项数量 2352 + /// - fromMemberId: 转出会员ID(必填)
  2353 + /// - toMemberId: 转入会员ID(必填)
  2354 + /// - storeId: 门店ID(必填)
  2355 + /// - signatureFile: 转出会员签名文件(必填)
  2356 + /// - remarks: 转卡备注信息(可选)
  2357 + /// - transferItems: 转出品项列表(必填)
  2358 + /// - billingItemId: 开单品项明细ID(必填)
  2359 + /// - transferQuantity: 转出数量(必填,大于0)
  2360 + /// - itemName: 品项名称(必填)
  2361 + /// - itemPrice: 品项价格(必填,大于等于0)
  2362 + ///
  2363 + /// 业务规则:
  2364 + /// - 转出和转入会员必须存在且有效
  2365 + /// - 转出品项的剩余数量必须足够
  2366 + /// - 系统会自动从原开单记录中获取健康师和科技部老师业绩数据
  2367 + /// - 转卡操作会同时创建退卡记录和开卡记录
  2368 + /// - 所有操作在事务中执行,确保数据一致性
2396 /// </remarks> 2369 /// </remarks>
2397 /// <param name="input">转卡输入参数</param> 2370 /// <param name="input">转卡输入参数</param>
2398 /// <returns>转卡结果</returns> 2371 /// <returns>转卡结果</returns>
@@ -2406,10 +2379,8 @@ namespace NCC.Extend.LqKdKdjlb @@ -2406,10 +2379,8 @@ namespace NCC.Extend.LqKdKdjlb
2406 { 2379 {
2407 throw NCCException.Oh("转卡参数不能为空"); 2380 throw NCCException.Oh("转卡参数不能为空");
2408 } 2381 }
2409 -  
2410 var userInfo = await _userManager.GetUserInfo(); 2382 var userInfo = await _userManager.GetUserInfo();
2411 var transferTime = DateTime.Now; 2383 var transferTime = DateTime.Now;
2412 -  
2413 try 2384 try
2414 { 2385 {
2415 // 开启事务 2386 // 开启事务
@@ -2425,7 +2396,6 @@ namespace NCC.Extend.LqKdKdjlb @@ -2425,7 +2396,6 @@ namespace NCC.Extend.LqKdKdjlb
2425 { 2396 {
2426 throw NCCException.Oh("转入会员不存在或已失效"); 2397 throw NCCException.Oh("转入会员不存在或已失效");
2427 } 2398 }
2428 -  
2429 // 2. 验证转出品项的余额是否足够 2399 // 2. 验证转出品项的余额是否足够
2430 foreach (var item in input.TransferItems) 2400 foreach (var item in input.TransferItems)
2431 { 2401 {
@@ -2434,7 +2404,6 @@ namespace NCC.Extend.LqKdKdjlb @@ -2434,7 +2404,6 @@ namespace NCC.Extend.LqKdKdjlb
2434 { 2404 {
2435 throw NCCException.Oh($"品项明细不存在:{item.ItemName}"); 2405 throw NCCException.Oh($"品项明细不存在:{item.ItemName}");
2436 } 2406 }
2437 -  
2438 // 查询剩余数量 2407 // 查询剩余数量
2439 var remainingCount = await GetItemRemainingCount(item.BillingItemId); 2408 var remainingCount = await GetItemRemainingCount(item.BillingItemId);
2440 if (remainingCount < item.TransferQuantity) 2409 if (remainingCount < item.TransferQuantity)
@@ -2446,6 +2415,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -2446,6 +2415,7 @@ namespace NCC.Extend.LqKdKdjlb
2446 // 3. 创建退卡记录(转出方) 2415 // 3. 创建退卡记录(转出方)
2447 var refundId = YitIdHelper.NextId().ToString(); 2416 var refundId = YitIdHelper.NextId().ToString();
2448 var totalRefundAmount = input.TransferItems.Sum(x => x.ItemPrice * x.TransferQuantity); 2417 var totalRefundAmount = input.TransferItems.Sum(x => x.ItemPrice * x.TransferQuantity);
  2418 + var StoreEntity = await _db.Queryable<LqMdxxEntity>().Where(p => p.Id == input.StoreId).FirstAsync();
2449 var refundEntity = new LqHytkHytkEntity 2419 var refundEntity = new LqHytkHytkEntity
2450 { 2420 {
2451 Id = refundId, 2421 Id = refundId,
@@ -2456,7 +2426,12 @@ namespace NCC.Extend.LqKdKdjlb @@ -2456,7 +2426,12 @@ namespace NCC.Extend.LqKdKdjlb
2456 Hy = input.FromMemberId, 2426 Hy = input.FromMemberId,
2457 Hymc = fromMember.Khmc, 2427 Hymc = fromMember.Khmc,
2458 Md = input.StoreId, 2428 Md = input.StoreId,
  2429 + Mdmc = StoreEntity.Dm,
  2430 + Mdbh = StoreEntity.Mdbm,
2459 SignatureFile = input.SignatureFile, 2431 SignatureFile = input.SignatureFile,
  2432 + Hyzh = input.FromMemberId,
  2433 + Gklx = fromMember.Khlx,
  2434 + Tksj = DateTime.Now,
2460 Tkje = totalRefundAmount, 2435 Tkje = totalRefundAmount,
2461 ActualRefundAmount = totalRefundAmount, // 转卡时实退金额等于退卡总金额 2436 ActualRefundAmount = totalRefundAmount, // 转卡时实退金额等于退卡总金额
2462 Tkyy = "转卡", 2437 Tkyy = "转卡",
@@ -2472,9 +2447,9 @@ namespace NCC.Extend.LqKdKdjlb @@ -2472,9 +2447,9 @@ namespace NCC.Extend.LqKdKdjlb
2472 2447
2473 foreach (var item in input.TransferItems) 2448 foreach (var item in input.TransferItems)
2474 { 2449 {
2475 - var refundPxmxEntity = _db.Queryable<LqKdPxmxEntity>().First(p => p.Id == item.BillingItemId); 2450 + var refundPxmxEntity = await _db.Queryable<LqKdPxmxEntity>().Where(p => p.Id == item.BillingItemId).FirstAsync();
2476 //计算品项扣除总金额 2451 //计算品项扣除总金额
2477 - var 品项扣除总金额 = refundPxmxEntity.Pxjg * item.TransferQuantity; 2452 + var totalItemDeduction = item.ItemPrice * item.TransferQuantity;
2478 //保存退卡明细表 2453 //保存退卡明细表
2479 var refundMxEntity = new LqHytkMxEntity 2454 var refundMxEntity = new LqHytkMxEntity
2480 { 2455 {
@@ -2483,13 +2458,13 @@ namespace NCC.Extend.LqKdKdjlb @@ -2483,13 +2458,13 @@ namespace NCC.Extend.LqKdKdjlb
2483 BillingItemId = item.BillingItemId, 2458 BillingItemId = item.BillingItemId,
2484 CreateTime = transferTime, 2459 CreateTime = transferTime,
2485 CreateUser = userInfo.userId, 2460 CreateUser = userInfo.userId,
2486 - Px = item.ItemId, 2461 + Px = refundPxmxEntity.Px,
2487 Pxmc = item.ItemName, 2462 Pxmc = item.ItemName,
2488 Pxjg = item.ItemPrice, 2463 Pxjg = item.ItemPrice,
2489 - Tkje = 品项扣除总金额, 2464 + Tkje = totalItemDeduction,
2490 ProjectNumber = item.TransferQuantity, 2465 ProjectNumber = item.TransferQuantity,
2491 - SourceType = item.SourceType,  
2492 - TotalPrice = 品项扣除总金额, 2466 + SourceType = refundPxmxEntity.SourceType,
  2467 + TotalPrice = totalItemDeduction,
2493 IsEffective = StatusEnum.有效.GetHashCode() 2468 IsEffective = StatusEnum.有效.GetHashCode()
2494 }; 2469 };
2495 refundMxEntities.Add(refundMxEntity); 2470 refundMxEntities.Add(refundMxEntity);
@@ -2499,7 +2474,12 @@ namespace NCC.Extend.LqKdKdjlb @@ -2499,7 +2474,12 @@ namespace NCC.Extend.LqKdKdjlb
2499 { 2474 {
2500 //获取健康师当月金三角id 2475 //获取健康师当月金三角id
2501 var monthString = DateTime.Now.ToString("yyyyMM"); 2476 var monthString = DateTime.Now.ToString("yyyyMM");
2502 - var GetMonTeam = _db.Queryable<LqJinsanjiaoUserEntity>().Where(p => p.UserId == jks.Jks && p.Month == monthString).First(); 2477 + var GetMonTeamResult = await _db.Queryable<LqJinsanjiaoUserEntity>().Where(p => p.UserId == jks.Jkszh && p.Month == monthString).ToListAsync();
  2478 + var GetMonTeam = GetMonTeamResult.FirstOrDefault();
  2479 + if (GetMonTeam == null)
  2480 + {
  2481 + throw NCCException.Oh($"健康师 {jks.Jksxm} 在 {monthString} 月份的金三角团队中不存在,请先配置金三角团队信息");
  2482 + }
2503 //获取本月 2483 //获取本月
2504 refundJksyjEntities.Add(new LqHytkJksyjEntity 2484 refundJksyjEntities.Add(new LqHytkJksyjEntity
2505 { 2485 {
@@ -2508,7 +2488,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -2508,7 +2488,7 @@ namespace NCC.Extend.LqKdKdjlb
2508 Jks = jks.Jks, 2488 Jks = jks.Jks,
2509 Jksxm = jks.Jksxm, 2489 Jksxm = jks.Jksxm,
2510 Jkszh = jks.Jkszh, 2490 Jkszh = jks.Jkszh,
2511 - Jksyj = (品项扣除总金额 / refundKdyjEntities.Count()), 2491 + Jksyj = (totalItemDeduction / refundKdyjEntities.Count()),
2512 Tksj = DateTime.Now, 2492 Tksj = DateTime.Now,
2513 F_jsjid = GetMonTeam.JsjId, 2493 F_jsjid = GetMonTeam.JsjId,
2514 F_tkpxid = refundMxEntity.Id, 2494 F_tkpxid = refundMxEntity.Id,
@@ -2531,7 +2511,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -2531,7 +2511,7 @@ namespace NCC.Extend.LqKdKdjlb
2531 Kjbls = kjbs.Kjbls, 2511 Kjbls = kjbs.Kjbls,
2532 Kjblsxm = kjbs.Kjblsxm, 2512 Kjblsxm = kjbs.Kjblsxm,
2533 Kjblszh = kjbs.Kjblszh, 2513 Kjblszh = kjbs.Kjblszh,
2534 - Kjblsyj = (品项扣除总金额 / TechTeacherEntities.Count()), 2514 + Kjblsyj = (totalItemDeduction / TechTeacherEntities.Count()),
2535 Tksj = transferTime, 2515 Tksj = transferTime,
2536 F_tkpxid = refundMxEntity.Id, 2516 F_tkpxid = refundMxEntity.Id,
2537 F_LaborCost = kjbs.LaborCost, 2517 F_LaborCost = kjbs.LaborCost,
@@ -2565,8 +2545,11 @@ namespace NCC.Extend.LqKdKdjlb @@ -2565,8 +2545,11 @@ namespace NCC.Extend.LqKdKdjlb
2565 CreateUser = userInfo.userId, 2545 CreateUser = userInfo.userId,
2566 Kdhy = input.ToMemberId, 2546 Kdhy = input.ToMemberId,
2567 Djmd = input.StoreId, 2547 Djmd = input.StoreId,
  2548 + Kdrq = DateTime.Now,
  2549 + Gjlx = toMember.Khlx,
2568 Fkfs = "转卡", 2550 Fkfs = "转卡",
2569 Sfskdd = "否", 2551 Sfskdd = "否",
  2552 + Zdyj = input.TransferItems.Sum(x => x.ItemPrice * x.TransferQuantity),
2570 Sfyj = input.TransferItems.Sum(x => x.ItemPrice * x.TransferQuantity), 2553 Sfyj = input.TransferItems.Sum(x => x.ItemPrice * x.TransferQuantity),
2571 Bz = $"从会员 {fromMember.Khmc} 转入,{input.Remarks}" 2554 Bz = $"从会员 {fromMember.Khmc} 转入,{input.Remarks}"
2572 }; 2555 };
@@ -2578,18 +2561,19 @@ namespace NCC.Extend.LqKdKdjlb @@ -2578,18 +2561,19 @@ namespace NCC.Extend.LqKdKdjlb
2578 2561
2579 foreach (var item in input.TransferItems) 2562 foreach (var item in input.TransferItems)
2580 { 2563 {
  2564 + var refundPxmxEntity = await _db.Queryable<LqKdPxmxEntity>().Where(p => p.Id == item.BillingItemId).FirstAsync();
2581 var billingPxmxEntity = new LqKdPxmxEntity 2565 var billingPxmxEntity = new LqKdPxmxEntity
2582 { 2566 {
2583 Id = YitIdHelper.NextId().ToString(), 2567 Id = YitIdHelper.NextId().ToString(),
2584 Glkdbh = billingId, 2568 Glkdbh = billingId,
2585 - Px = item.ItemId, 2569 + Px = refundPxmxEntity.Px,
2586 Pxmc = item.ItemName, 2570 Pxmc = item.ItemName,
2587 Pxjg = item.ItemPrice, 2571 Pxjg = item.ItemPrice,
2588 MemberId = input.ToMemberId, 2572 MemberId = input.ToMemberId,
2589 CreateTIme = transferTime, 2573 CreateTIme = transferTime,
2590 ProjectNumber = item.TransferQuantity, 2574 ProjectNumber = item.TransferQuantity,
2591 IsEnabled = StatusEnum.有效.GetHashCode(), 2575 IsEnabled = StatusEnum.有效.GetHashCode(),
2592 - SourceType = item.SourceType, 2576 + SourceType = refundPxmxEntity.SourceType,
2593 TotalPrice = item.ItemPrice * item.TransferQuantity, 2577 TotalPrice = item.ItemPrice * item.TransferQuantity,
2594 ActualPrice = item.ItemPrice * item.TransferQuantity, 2578 ActualPrice = item.ItemPrice * item.TransferQuantity,
2595 IsEffective = StatusEnum.有效.GetHashCode(), 2579 IsEffective = StatusEnum.有效.GetHashCode(),
@@ -2666,361 +2650,6 @@ namespace NCC.Extend.LqKdKdjlb @@ -2666,361 +2650,6 @@ namespace NCC.Extend.LqKdKdjlb
2666 } 2650 }
2667 #endregion 2651 #endregion
2668 2652
2669 - // #region 会员转卡操作  
2670 - // /// <summary>  
2671 - // /// 会员转卡操作  
2672 - // /// </summary>  
2673 - // /// <remarks>  
2674 - // /// 转卡是指一个会员把自己剩余的某些品项,转给另外一个会员  
2675 - // /// 转卡会员转出的品项,就做退卡操作  
2676 - // /// 被转卡会员收到的品项,做开卡操作  
2677 - // ///  
2678 - // /// 示例请求:  
2679 - // /// ```json  
2680 - // /// {  
2681 - // /// "fromMemberId": "member001",  
2682 - // /// "toMemberId": "member002",  
2683 - // /// "storeId": "store001",  
2684 - // /// "signatureFile": "签名文件路径",  
2685 - // /// "remarks": "转卡备注",  
2686 - // /// "transferItems": [  
2687 - // /// {  
2688 - // /// "billingItemId": "item001",  
2689 - // /// "transferQuantity": 5,  
2690 - // /// "itemId": "px001",  
2691 - // /// "itemName": "美容项目",  
2692 - // /// "itemPrice": 100.00,  
2693 - // /// "sourceType": "转卡",  
2694 - // /// "healthTeacherPerformances": [  
2695 - // /// {  
2696 - // /// "healthTeacherId": "jks001",  
2697 - // /// "healthTeacherName": "张医生",  
2698 - // /// "healthTeacherAccount": "zhang001",  
2699 - // /// "performanceAmount": 50.00,  
2700 - // /// "laborCost": 20.00,  
2701 - // /// "itemQuantity": 5  
2702 - // /// }  
2703 - // /// ],  
2704 - // /// "techTeacherPerformances": [  
2705 - // /// {  
2706 - // /// "techTeacherId": "kjbs001",  
2707 - // /// "techTeacherName": "李老师",  
2708 - // /// "techTeacherAccount": "li001",  
2709 - // /// "performanceAmount": 30.00,  
2710 - // /// "laborCost": 10.00,  
2711 - // /// "itemQuantity": 5  
2712 - // /// }  
2713 - // /// ]  
2714 - // /// }  
2715 - // /// ]  
2716 - // /// }  
2717 - // /// ```  
2718 - // ///  
2719 - // /// 参数说明:  
2720 - // /// - fromMemberId: 转出会员ID  
2721 - // /// - toMemberId: 转入会员ID  
2722 - // /// - storeId: 门店ID  
2723 - // /// - signatureFile: 转出会员签名  
2724 - // /// - remarks: 转卡备注  
2725 - // /// - transferItems: 转出品项列表  
2726 - // /// - billingItemId: 开单品项明细ID  
2727 - // /// - transferQuantity: 转出数量  
2728 - // /// - itemId: 品项ID  
2729 - // /// - itemName: 品项名称  
2730 - // /// - itemPrice: 品项价格  
2731 - // /// - sourceType: 来源类型  
2732 - // /// - healthTeacherPerformances: 健康师业绩列表  
2733 - // /// - healthTeacherId: 健康师ID  
2734 - // /// - healthTeacherName: 健康师姓名  
2735 - // /// - healthTeacherAccount: 健康师账号  
2736 - // /// - performanceAmount: 业绩金额  
2737 - // /// - laborCost: 人工成本  
2738 - // /// - itemQuantity: 品项数量  
2739 - // /// - techTeacherPerformances: 科技部老师业绩列表  
2740 - // /// - techTeacherId: 科技部老师ID  
2741 - // /// - techTeacherName: 科技部老师姓名  
2742 - // /// - techTeacherAccount: 科技部老师账号  
2743 - // /// - performanceAmount: 业绩金额  
2744 - // /// - laborCost: 人工成本  
2745 - // /// - itemQuantity: 品项数量  
2746 - // /// </remarks>  
2747 - // /// <param name="input">转卡输入参数</param>  
2748 - // /// <returns>转卡结果</returns>  
2749 - // /// <response code="200">转卡成功</response>  
2750 - // /// <response code="400">参数错误或业务规则验证失败</response>  
2751 - // /// <response code="500">服务器错误</response>  
2752 - // [HttpPost("TransferCard")]  
2753 - // public async Task<TransferCardOutput> TransferCard([FromBody] TransferCardInput input)  
2754 - // {  
2755 - // if (input == null)  
2756 - // {  
2757 - // throw NCCException.Oh("转卡参数不能为空");  
2758 - // }  
2759 -  
2760 - // var userInfo = await _userManager.GetUserInfo();  
2761 - // var transferTime = DateTime.Now;  
2762 -  
2763 - // try  
2764 - // {  
2765 - // // 开启事务  
2766 - // _db.BeginTran();  
2767 -  
2768 - // // 1. 验证转出和转入会员是否存在  
2769 - // var fromMember = await _db.Queryable<LqKhxxEntity>().Where(x => x.Id == input.FromMemberId && x.IsEffective == StatusEnum.有效.GetHashCode()).FirstAsync();  
2770 - // if (fromMember == null)  
2771 - // {  
2772 - // throw NCCException.Oh("转出会员不存在或已失效");  
2773 - // }  
2774 - // var toMember = await _db.Queryable<LqKhxxEntity>().Where(x => x.Id == input.ToMemberId && x.IsEffective == StatusEnum.有效.GetHashCode()).FirstAsync();  
2775 - // if (toMember == null)  
2776 - // {  
2777 - // throw NCCException.Oh("转入会员不存在或已失效");  
2778 - // }  
2779 -  
2780 - // // 2. 验证转出品项的余额是否足够  
2781 - // foreach (var item in input.TransferItems)  
2782 - // {  
2783 - // var billingItem = await _db.Queryable<LqKdPxmxEntity>().Where(x => x.Id == item.BillingItemId && x.IsEffective == StatusEnum.有效.GetHashCode()).FirstAsync();  
2784 - // if (billingItem == null)  
2785 - // {  
2786 - // throw NCCException.Oh($"品项明细不存在:{item.ItemName}");  
2787 - // }  
2788 -  
2789 - // // 查询剩余数量  
2790 - // var remainingCount = await GetItemRemainingCount(item.BillingItemId);  
2791 - // if (remainingCount < item.TransferQuantity)  
2792 - // {  
2793 - // throw NCCException.Oh($"品项 {item.ItemName} 剩余数量不足,当前剩余:{remainingCount},需要转出:{item.TransferQuantity}");  
2794 - // }  
2795 - // }  
2796 -  
2797 - // // 3. 创建退卡记录(转出方)  
2798 - // var refundId = YitIdHelper.NextId().ToString();  
2799 - // var totalRefundAmount = input.TransferItems.Sum(x => x.ItemPrice * x.TransferQuantity);  
2800 - // var refundEntity = new LqHytkHytkEntity  
2801 - // {  
2802 - // Id = refundId,  
2803 - // F_CreateTime = transferTime,  
2804 - // F_CreateUser = userInfo.userId,  
2805 - // IsEffective = StatusEnum.有效.GetHashCode(),  
2806 - // Czry = userInfo.userId,  
2807 - // Hy = input.FromMemberId,  
2808 - // Hymc = fromMember.Khmc,  
2809 - // Md = input.StoreId,  
2810 - // SignatureFile = input.SignatureFile,  
2811 - // Tkje = totalRefundAmount,  
2812 - // ActualRefundAmount = totalRefundAmount, // 转卡时实退金额等于退卡总金额  
2813 - // Tkyy = "转卡",  
2814 - // Bz = $"转卡给会员:{toMember.Khmc},{input.Remarks}"  
2815 - // };  
2816 - // await _db.Insertable(refundEntity).ExecuteCommandAsync();  
2817 -  
2818 - // // 4. 创建退卡品项明细和业绩记录  
2819 - // var refundMxEntities = new List<LqHytkMxEntity>();  
2820 - // var refundJksyjEntities = new List<LqHytkJksyjEntity>();  
2821 - // var refundKjbsyjEntities = new List<LqHytkKjbsyjEntity>();  
2822 -  
2823 - // foreach (var item in input.TransferItems)  
2824 - // {  
2825 - // var refundMxEntity = new LqHytkMxEntity  
2826 - // {  
2827 - // Id = YitIdHelper.NextId().ToString(),  
2828 - // RefundInfoId = refundId,  
2829 - // BillingItemId = item.BillingItemId,  
2830 - // CreateTime = transferTime,  
2831 - // CreateUser = userInfo.userId,  
2832 - // Px = item.ItemId,  
2833 - // Pxmc = item.ItemName,  
2834 - // Pxjg = item.ItemPrice,  
2835 - // Tkje = item.ItemPrice * item.TransferQuantity,  
2836 - // ProjectNumber = item.TransferQuantity,  
2837 - // SourceType = item.SourceType,  
2838 - // TotalPrice = item.ItemPrice * item.TransferQuantity,  
2839 - // IsEffective = StatusEnum.有效.GetHashCode()  
2840 - // };  
2841 - // refundMxEntities.Add(refundMxEntity);  
2842 -  
2843 - // // 创建退卡健康师业绩记录  
2844 - // if (item.HealthTeacherPerformances != null && item.HealthTeacherPerformances.Any())  
2845 - // {  
2846 - // foreach (var jks in item.HealthTeacherPerformances)  
2847 - // {  
2848 - // refundJksyjEntities.Add(new LqHytkJksyjEntity  
2849 - // {  
2850 - // Id = YitIdHelper.NextId().ToString(),  
2851 - // Gltkbh = refundId,  
2852 - // Jks = jks.HealthTeacherId,  
2853 - // Jksxm = jks.HealthTeacherName,  
2854 - // Jkszh = jks.HealthTeacherAccount,  
2855 - // Jksyj = jks.PerformanceAmount,  
2856 - // Tksj = transferTime,  
2857 - // F_jsjid = jks.HealthTeacherId,  
2858 - // F_tkpxid = refundMxEntity.Id,  
2859 - // F_LaborCost = jks.LaborCost,  
2860 - // F_tkpxNumber = jks.ItemQuantity,  
2861 - // F_CreateTime = transferTime,  
2862 - // F_CreateUser = userInfo.userId,  
2863 - // CardReturn = refundMxEntity.Id,  
2864 - // IsEffective = StatusEnum.有效.GetHashCode()  
2865 - // });  
2866 - // }  
2867 - // }  
2868 -  
2869 - // // 创建退卡科技部老师业绩记录  
2870 - // if (item.TechTeacherPerformances != null && item.TechTeacherPerformances.Any())  
2871 - // {  
2872 - // foreach (var kjbs in item.TechTeacherPerformances)  
2873 - // {  
2874 - // refundKjbsyjEntities.Add(new LqHytkKjbsyjEntity  
2875 - // {  
2876 - // Id = YitIdHelper.NextId().ToString(),  
2877 - // Gltkbh = refundId,  
2878 - // Kjbls = kjbs.TechTeacherId,  
2879 - // Kjblsxm = kjbs.TechTeacherName,  
2880 - // Kjblszh = kjbs.TechTeacherAccount,  
2881 - // Kjblsyj = kjbs.PerformanceAmount,  
2882 - // Tksj = transferTime,  
2883 - // F_tkpxid = refundMxEntity.Id,  
2884 - // F_LaborCost = kjbs.LaborCost,  
2885 - // F_tkpxNumber = kjbs.ItemQuantity,  
2886 - // F_CreateTime = transferTime,  
2887 - // F_CreateUser = userInfo.userId,  
2888 - // CardReturn = refundMxEntity.Id,  
2889 - // IsEffective = StatusEnum.有效.GetHashCode()  
2890 - // });  
2891 - // }  
2892 - // }  
2893 - // }  
2894 -  
2895 - // await _db.Insertable(refundMxEntities).ExecuteCommandAsync();  
2896 - // if (refundJksyjEntities.Any())  
2897 - // {  
2898 - // await _db.Insertable(refundJksyjEntities).ExecuteCommandAsync();  
2899 - // }  
2900 - // if (refundKjbsyjEntities.Any())  
2901 - // {  
2902 - // await _db.Insertable(refundKjbsyjEntities).ExecuteCommandAsync();  
2903 - // }  
2904 -  
2905 - // // 5. 创建开卡记录(转入方)  
2906 - // var billingId = YitIdHelper.NextId().ToString();  
2907 - // var billingEntity = new LqKdKdjlbEntity  
2908 - // {  
2909 - // Id = billingId,  
2910 - // CreateTime = transferTime,  
2911 - // UpdateTime = transferTime,  
2912 - // IsEffective = StatusEnum.有效.GetHashCode(),  
2913 - // CreateUser = userInfo.userId,  
2914 - // Kdhy = input.ToMemberId,  
2915 - // Djmd = input.StoreId,  
2916 - // Sfyj = input.TransferItems.Sum(x => x.ItemPrice * x.TransferQuantity),  
2917 - // Bz = $"从会员 {fromMember.Khmc} 转入,{input.Remarks}"  
2918 - // };  
2919 - // await _db.Insertable(billingEntity).ExecuteCommandAsync();  
2920 - // // 6. 创建开单品项明细和业绩记录  
2921 - // var billingPxmxEntities = new List<LqKdPxmxEntity>();  
2922 - // var billingJksyjEntities = new List<LqKdJksyjEntity>();  
2923 - // var billingKjbsyjEntities = new List<LqKdKjbsyjEntity>();  
2924 -  
2925 - // foreach (var item in input.TransferItems)  
2926 - // {  
2927 - // var billingPxmxEntity = new LqKdPxmxEntity  
2928 - // {  
2929 - // Id = YitIdHelper.NextId().ToString(),  
2930 - // Glkdbh = billingId,  
2931 - // Px = item.ItemId,  
2932 - // Pxmc = item.ItemName,  
2933 - // Pxjg = item.ItemPrice,  
2934 - // MemberId = input.ToMemberId,  
2935 - // CreateTIme = transferTime,  
2936 - // ProjectNumber = item.TransferQuantity,  
2937 - // IsEnabled = StatusEnum.有效.GetHashCode(),  
2938 - // SourceType = item.SourceType,  
2939 - // TotalPrice = item.ItemPrice * item.TransferQuantity,  
2940 - // ActualPrice = item.ItemPrice * item.TransferQuantity,  
2941 - // IsEffective = StatusEnum.有效.GetHashCode(),  
2942 - // Remark = $"从会员 {fromMember.Khmc} 转入"  
2943 - // };  
2944 - // billingPxmxEntities.Add(billingPxmxEntity);  
2945 -  
2946 - // // 创建开卡健康师业绩记录  
2947 - // if (item.HealthTeacherPerformances != null && item.HealthTeacherPerformances.Any())  
2948 - // {  
2949 - // foreach (var jks in item.HealthTeacherPerformances)  
2950 - // {  
2951 - // billingJksyjEntities.Add(new LqKdJksyjEntity  
2952 - // {  
2953 - // Id = YitIdHelper.NextId().ToString(),  
2954 - // Glkdbh = billingId,  
2955 - // Jks = jks.HealthTeacherId,  
2956 - // Jksxm = jks.HealthTeacherName,  
2957 - // Jkszh = jks.HealthTeacherAccount,  
2958 - // Jksyj = jks.PerformanceAmount.ToString(),  
2959 - // Yjsj = transferTime,  
2960 - // Jsj_id = jks.HealthTeacherId,  
2961 - // Kdpxid = billingPxmxEntity.Id,  
2962 - // IsEffective = StatusEnum.有效.GetHashCode()  
2963 - // });  
2964 - // }  
2965 - // }  
2966 -  
2967 - // // 创建开卡科技部老师业绩记录  
2968 - // if (item.TechTeacherPerformances != null && item.TechTeacherPerformances.Any())  
2969 - // {  
2970 - // foreach (var kjbs in item.TechTeacherPerformances)  
2971 - // {  
2972 - // billingKjbsyjEntities.Add(new LqKdKjbsyjEntity  
2973 - // {  
2974 - // Id = YitIdHelper.NextId().ToString(),  
2975 - // Glkdbh = billingId,  
2976 - // Kjbls = kjbs.TechTeacherId,  
2977 - // Kjblsxm = kjbs.TechTeacherName,  
2978 - // Kjblszh = kjbs.TechTeacherAccount,  
2979 - // Kjblsyj = kjbs.PerformanceAmount.ToString(),  
2980 - // Yjsj = transferTime,  
2981 - // Kdpxid = billingPxmxEntity.Id,  
2982 - // LaborCost = kjbs.LaborCost,  
2983 - // IsEffective = StatusEnum.有效.GetHashCode()  
2984 - // });  
2985 - // }  
2986 - // }  
2987 - // }  
2988 -  
2989 - // await _db.Insertable(billingPxmxEntities).ExecuteCommandAsync();  
2990 - // if (billingJksyjEntities.Any())  
2991 - // {  
2992 - // await _db.Insertable(billingJksyjEntities).ExecuteCommandAsync();  
2993 - // }  
2994 - // if (billingKjbsyjEntities.Any())  
2995 - // {  
2996 - // await _db.Insertable(billingKjbsyjEntities).ExecuteCommandAsync();  
2997 - // }  
2998 - // // 提交事务  
2999 - // _db.CommitTran();  
3000 -  
3001 - // return new TransferCardOutput  
3002 - // {  
3003 - // Success = true,  
3004 - // TransferId = refundId,  
3005 - // RefundId = refundId,  
3006 - // BillingId = billingId,  
3007 - // TotalAmount = input.TransferItems.Sum(x => x.ItemPrice * x.TransferQuantity),  
3008 - // TotalQuantity = input.TransferItems.Sum(x => x.TransferQuantity),  
3009 - // FromMemberName = fromMember.Khmc,  
3010 - // ToMemberName = toMember.Khmc,  
3011 - // TransferTime = transferTime,  
3012 - // Message = "转卡操作成功"  
3013 - // };  
3014 - // }  
3015 - // catch (Exception ex)  
3016 - // {  
3017 - // _db.RollbackTran();  
3018 - // _logger.LogError(ex, "转卡操作失败:{Message}", ex.Message);  
3019 - // throw NCCException.Oh($"转卡操作失败:{ex.Message}");  
3020 - // }  
3021 - // }  
3022 - // #endregion  
3023 -  
3024 #region 获取品项剩余数量 2653 #region 获取品项剩余数量
3025 /// <summary> 2654 /// <summary>
3026 /// 获取品项剩余数量 2655 /// 获取品项剩余数量
@@ -3032,13 +2661,25 @@ namespace NCC.Extend.LqKdKdjlb @@ -3032,13 +2661,25 @@ namespace NCC.Extend.LqKdKdjlb
3032 try 2661 try
3033 { 2662 {
3034 // 查询购买数量 2663 // 查询购买数量
3035 - var purchasedCount = await _db.Queryable<LqKdPxmxEntity>().Where(x => x.Id == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode()).Select(x => x.ProjectNumber).FirstAsync(); 2664 + var purchasedCount = await _db.Queryable<LqKdPxmxEntity>()
  2665 + .Where(x => x.Id == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode())
  2666 + .SumAsync(x => x.ProjectNumber);
  2667 +
3036 // 查询消费数量 2668 // 查询消费数量
3037 - var consumedCount = await _db.Queryable<LqXhPxmxEntity>().Where(x => x.BillingItemId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode()).SumAsync(x => x.ProjectNumber); 2669 + var consumedCount = await _db.Queryable<LqXhPxmxEntity>()
  2670 + .Where(x => x.BillingItemId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode())
  2671 + .SumAsync(x => x.ProjectNumber);
  2672 +
3038 // 查询退卡数量 2673 // 查询退卡数量
3039 - var refundedCount = await _db.Queryable<LqHytkMxEntity>().Where(x => x.BillingItemId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode()).SumAsync(x => x.ProjectNumber); 2674 + var refundedCount = await _db.Queryable<LqHytkMxEntity>()
  2675 + .Where(x => x.BillingItemId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode())
  2676 + .SumAsync(x => x.ProjectNumber);
  2677 +
3040 // 查询储扣数量 2678 // 查询储扣数量
3041 - var deductCount = await _db.Queryable<LqKdDeductinfoEntity>().Where(x => x.DeductId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode()).SumAsync(x => x.ProjectNumber); 2679 + var deductCount = await _db.Queryable<LqKdDeductinfoEntity>()
  2680 + .Where(x => x.DeductId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode())
  2681 + .SumAsync(x => x.ProjectNumber) ?? 0;
  2682 +
3042 // 计算剩余数量 2683 // 计算剩余数量
3043 var remainingCount = (int)(purchasedCount - consumedCount - refundedCount - deductCount); 2684 var remainingCount = (int)(purchasedCount - consumedCount - refundedCount - deductCount);
3044 return Math.Max(0, remainingCount); // 确保不为负数 2685 return Math.Max(0, remainingCount); // 确保不为负数
netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs
@@ -684,55 +684,55 @@ namespace NCC.Extend.LqTkjlb @@ -684,55 +684,55 @@ namespace NCC.Extend.LqTkjlb
684 var tkData = await _db.Ado.SqlQueryAsync<dynamic>(tkSql, new { eventId }); 684 var tkData = await _db.Ado.SqlQueryAsync<dynamic>(tkSql, new { eventId });
685 var tkDict = tkData.ToDictionary(x => (string)x.F_StoreId, x => x); 685 var tkDict = tkData.ToDictionary(x => (string)x.F_StoreId, x => x);
686 686
687 - // 第二步:获取邀约数据 687 + // 第二步:获取邀约数据(按会员ID去重)
688 var yaoySql = @" 688 var yaoySql = @"
689 SELECT 689 SELECT
690 tk.F_StoreId, 690 tk.F_StoreId,
691 - COUNT(DISTINCT yy.F_Id) as yaoy_count 691 + COUNT(DISTINCT tk.F_MemberId) as yaoy_count
692 FROM lq_tkjlb tk 692 FROM lq_tkjlb tk
693 LEFT JOIN lq_yaoyjl yy ON tk.F_MemberId = yy.yykh AND yy.F_StoreId = tk.F_StoreId 693 LEFT JOIN lq_yaoyjl yy ON tk.F_MemberId = yy.yykh AND yy.F_StoreId = tk.F_StoreId
694 - WHERE tk.F_EventId = @eventId 694 + WHERE tk.F_EventId = @eventId AND yy.F_Id IS NOT NULL
695 GROUP BY tk.F_StoreId"; 695 GROUP BY tk.F_StoreId";
696 696
697 var yaoyData = await _db.Ado.SqlQueryAsync<dynamic>(yaoySql, new { eventId }); 697 var yaoyData = await _db.Ado.SqlQueryAsync<dynamic>(yaoySql, new { eventId });
698 var yaoyDict = yaoyData.ToDictionary(x => (string)x.F_StoreId, x => (int)x.yaoy_count); 698 var yaoyDict = yaoyData.ToDictionary(x => (string)x.F_StoreId, x => (int)x.yaoy_count);
699 699
700 - // 第三步:获取预约数据 700 + // 第三步:获取预约数据(按会员ID去重)
701 var yySql = @" 701 var yySql = @"
702 SELECT 702 SELECT
703 tk.F_StoreId, 703 tk.F_StoreId,
704 - COUNT(DISTINCT yyjl.F_Id) as yy_count 704 + COUNT(DISTINCT tk.F_MemberId) as yy_count
705 FROM lq_tkjlb tk 705 FROM lq_tkjlb tk
706 LEFT JOIN lq_yyjl yyjl ON tk.F_MemberId = yyjl.gk AND yyjl.F_Status = '已确认' 706 LEFT JOIN lq_yyjl yyjl ON tk.F_MemberId = yyjl.gk AND yyjl.F_Status = '已确认'
707 - WHERE tk.F_EventId = @eventId 707 + WHERE tk.F_EventId = @eventId AND yyjl.F_Id IS NOT NULL
708 GROUP BY tk.F_StoreId"; 708 GROUP BY tk.F_StoreId";
709 709
710 var yyData = await _db.Ado.SqlQueryAsync<dynamic>(yySql, new { eventId }); 710 var yyData = await _db.Ado.SqlQueryAsync<dynamic>(yySql, new { eventId });
711 var yyDict = yyData.ToDictionary(x => (string)x.F_StoreId, x => (int)x.yy_count); 711 var yyDict = yyData.ToDictionary(x => (string)x.F_StoreId, x => (int)x.yy_count);
712 712
713 - // 第四步:获取耗卡数据 713 + // 第四步:获取耗卡数据(按会员ID去重,但金额不去重)
714 var hkSql = @" 714 var hkSql = @"
715 SELECT 715 SELECT
716 tk.F_StoreId, 716 tk.F_StoreId,
717 - COUNT(DISTINCT xh.F_Id) as hk_count, 717 + COUNT(DISTINCT tk.F_MemberId) as hk_count,
718 COALESCE(SUM(xh.xfje), 0) as hk_amount 718 COALESCE(SUM(xh.xfje), 0) as hk_amount
719 FROM lq_tkjlb tk 719 FROM lq_tkjlb tk
720 LEFT JOIN lq_xh_hyhk xh ON tk.F_MemberId = xh.hy AND xh.F_IsEffective = 1 720 LEFT JOIN lq_xh_hyhk xh ON tk.F_MemberId = xh.hy AND xh.F_IsEffective = 1
721 - WHERE tk.F_EventId = @eventId 721 + WHERE tk.F_EventId = @eventId AND xh.F_Id IS NOT NULL
722 GROUP BY tk.F_StoreId"; 722 GROUP BY tk.F_StoreId";
723 723
724 var hkData = await _db.Ado.SqlQueryAsync<dynamic>(hkSql, new { eventId }); 724 var hkData = await _db.Ado.SqlQueryAsync<dynamic>(hkSql, new { eventId });
725 var hkDict = hkData.ToDictionary(x => (string)x.F_StoreId, x => new { count = (int)x.hk_count, amount = (decimal)x.hk_amount }); 725 var hkDict = hkData.ToDictionary(x => (string)x.F_StoreId, x => new { count = (int)x.hk_count, amount = (decimal)x.hk_amount });
726 726
727 - // 第五步:获取开单数据 727 + // 第五步:获取开单数据(按会员ID去重,但金额不去重)
728 var kdSql = @" 728 var kdSql = @"
729 SELECT 729 SELECT
730 tk.F_StoreId, 730 tk.F_StoreId,
731 - COUNT(DISTINCT kd.F_Id) as kd_count, 731 + COUNT(DISTINCT tk.F_MemberId) as kd_count,
732 COALESCE(SUM(kd.sfyj), 0) as kd_amount 732 COALESCE(SUM(kd.sfyj), 0) as kd_amount
733 FROM lq_tkjlb tk 733 FROM lq_tkjlb tk
734 LEFT JOIN lq_kd_kdjlb kd ON tk.F_MemberId = kd.kdhy AND kd.F_IsEffective = 1 734 LEFT JOIN lq_kd_kdjlb kd ON tk.F_MemberId = kd.kdhy AND kd.F_IsEffective = 1
735 - WHERE tk.F_EventId = @eventId 735 + WHERE tk.F_EventId = @eventId AND kd.F_Id IS NOT NULL
736 GROUP BY tk.F_StoreId"; 736 GROUP BY tk.F_StoreId";
737 737
738 var kdData = await _db.Ado.SqlQueryAsync<dynamic>(kdSql, new { eventId }); 738 var kdData = await _db.Ado.SqlQueryAsync<dynamic>(kdSql, new { eventId });
@@ -770,6 +770,91 @@ namespace NCC.Extend.LqTkjlb @@ -770,6 +770,91 @@ namespace NCC.Extend.LqTkjlb
770 770
771 #region 门店顾客详情 771 #region 门店顾客详情
772 /// <summary> 772 /// <summary>
  773 + /// 生成门店顾客详情查询SQL
  774 + /// </summary>
  775 + /// <param name="isPaged">是否分页</param>
  776 + /// <returns>SQL语句</returns>
  777 + private string GetStoreCustomerDetailsSql(bool isPaged = false)
  778 + {
  779 + var baseSql = @"
  780 + SELECT
  781 + tk.F_Id as tk_id, -- 拓客记录ID
  782 + tk.F_CustomerPhone as customer_phone, -- 顾客手机号
  783 + tk.F_MemberId as member_id, -- 会员ID
  784 + tk.F_CustomerName as customer_name, -- 顾客姓名
  785 + tk.F_CreateTime as tk_time, -- 拓客时间
  786 + -- 邀约信息
  787 + yaoy.F_Id as yaoy_id, -- 邀约ID
  788 + yaoy.F_CreateTime as yaoy_time, -- 邀约时间
  789 + yaoy.F_Status as yaoy_status, -- 邀约状态
  790 + -- 预约信息
  791 + yy.F_Id as yy_id, -- 预约ID
  792 + yy.F_Status as yy_status, -- 预约状态
  793 + yy.F_CreateTime as yy_time, -- 预约时间
  794 + yy.yysj as appointment_time, -- 预约到店时间
  795 + -- 耗卡信息(聚合)
  796 + xh_summary.total_consume_amount, -- 耗卡总金额
  797 + xh_summary.consume_count, -- 耗卡次数
  798 + xh_summary.first_consume_time, -- 首次耗卡时间
  799 + xh_summary.last_consume_time, -- 最后耗卡时间
  800 + -- 开卡信息(聚合)
  801 + kd_summary.total_billing_amount, -- 开卡总金额
  802 + kd_summary.total_debt_amount, -- 总欠款金额
  803 + kd_summary.billing_count, -- 开卡次数
  804 + kd_summary.first_billing_time, -- 首次开卡时间
  805 + kd_summary.last_billing_time, -- 最后开卡时间
  806 + -- 状态描述
  807 + CASE
  808 + WHEN yaoy.F_Id IS NOT NULL THEN '已邀约'
  809 + ELSE '未邀约'
  810 + END as invitation_status, -- 邀约状态描述
  811 + CASE
  812 + WHEN yy.F_Id IS NOT NULL THEN '已预约'
  813 + ELSE '未预约'
  814 + END as appointment_status, -- 预约状态描述
  815 + CASE
  816 + WHEN xh_summary.total_consume_amount > 0 THEN '已耗卡'
  817 + ELSE '未耗卡'
  818 + END as consume_status, -- 耗卡状态描述
  819 + CASE
  820 + WHEN kd_summary.total_billing_amount > 0 THEN '已开卡'
  821 + ELSE '未开卡'
  822 + END as billing_status -- 开卡状态描述
  823 + FROM lq_tkjlb tk
  824 + LEFT JOIN lq_yaoyjl yaoy ON tk.F_MemberId = yaoy.yykh
  825 + AND yaoy.F_StoreId = tk.F_StoreId
  826 + LEFT JOIN lq_yyjl yy ON tk.F_MemberId = yy.gk
  827 + AND yy.F_Status = '已确认'
  828 + LEFT JOIN (
  829 + SELECT
  830 + hy as member_id,
  831 + SUM(xfje) as total_consume_amount,
  832 + COUNT(*) as consume_count,
  833 + MIN(F_CreateTime) as first_consume_time,
  834 + MAX(F_CreateTime) as last_consume_time
  835 + FROM lq_xh_hyhk
  836 + WHERE F_IsEffective = 1
  837 + GROUP BY hy
  838 + ) xh_summary ON tk.F_MemberId = xh_summary.member_id
  839 + LEFT JOIN (
  840 + SELECT
  841 + kdhy as member_id,
  842 + SUM(sfyj) as total_billing_amount,
  843 + SUM(qk) as total_debt_amount,
  844 + COUNT(*) as billing_count,
  845 + MIN(F_CreateTime) as first_billing_time,
  846 + MAX(F_CreateTime) as last_billing_time
  847 + FROM lq_kd_kdjlb
  848 + WHERE F_IsEffective = 1
  849 + GROUP BY kdhy
  850 + ) kd_summary ON tk.F_MemberId = kd_summary.member_id
  851 + WHERE tk.F_EventId = @eventId
  852 + AND tk.F_StoreId = @storeId
  853 + ORDER BY tk.F_CreateTime DESC";
  854 + return isPaged ? baseSql + " LIMIT @offset, @pageSize" : baseSql;
  855 + }
  856 +
  857 + /// <summary>
773 /// 获取门店拓客活动顾客详情 858 /// 获取门店拓客活动顾客详情
774 /// </summary> 859 /// </summary>
775 /// <param name="eventId">活动ID</param> 860 /// <param name="eventId">活动ID</param>
@@ -780,37 +865,7 @@ namespace NCC.Extend.LqTkjlb @@ -780,37 +865,7 @@ namespace NCC.Extend.LqTkjlb
780 { 865 {
781 try 866 try
782 { 867 {
783 - var sql = @"  
784 - SELECT  
785 - tk.F_Id as tk_id, -- 拓客记录ID  
786 - tk.F_CustomerPhone as customer_phone, -- 顾客手机号  
787 - tk.F_MemberId as member_id, -- 会员ID  
788 - tk.F_CustomerName as customer_name, -- 顾客姓名  
789 - tk.F_CreateTime as tk_time, -- 拓客时间  
790 - yy.F_Id as yy_id, -- 预约ID  
791 - yy.F_Status as yy_status, -- 预约状态  
792 - yy.F_CreateTime as yy_time, -- 预约时间  
793 - yy.yysj as appointment_time, -- 预约到店时间  
794 - xh.F_Id as xh_id, -- 耗卡ID  
795 - xh.F_CreateTime as xh_time, -- 耗卡时间  
796 - xh.xfje as consume_amount, -- 耗卡金额  
797 - CASE  
798 - WHEN yy.F_Id IS NOT NULL THEN '已预约'  
799 - ELSE '未预约'  
800 - END as appointment_status, -- 预约状态描述  
801 - CASE  
802 - WHEN xh.F_Id IS NOT NULL THEN '已耗卡'  
803 - ELSE '未耗卡'  
804 - END as consume_status -- 耗卡状态描述  
805 - FROM lq_tkjlb tk  
806 - LEFT JOIN lq_yyjl yy ON tk.F_MemberId = yy.gk  
807 - AND yy.F_Status = '已确认'  
808 - LEFT JOIN lq_xh_hyhk xh ON tk.F_MemberId = xh.hy  
809 - AND xh.F_IsEffective = 1  
810 - WHERE tk.F_EventId = @eventId  
811 - AND tk.F_StoreId = @storeId  
812 - ORDER BY tk.F_CreateTime DESC";  
813 - 868 + var sql = GetStoreCustomerDetailsSql(false);
814 var result = await _db.Ado.SqlQueryAsync<dynamic>(sql, new { eventId, storeId }); 869 var result = await _db.Ado.SqlQueryAsync<dynamic>(sql, new { eventId, storeId });
815 870
816 return new 871 return new
@@ -839,44 +894,12 @@ namespace NCC.Extend.LqTkjlb @@ -839,44 +894,12 @@ namespace NCC.Extend.LqTkjlb
839 { 894 {
840 try 895 try
841 { 896 {
842 - var sql = @"  
843 - SELECT  
844 - tk.F_Id as tk_id, -- 拓客记录ID  
845 - tk.F_CustomerPhone as customer_phone, -- 顾客手机号  
846 - tk.F_MemberId as member_id, -- 会员ID  
847 - tk.F_CustomerName as customer_name, -- 顾客姓名  
848 - tk.F_CreateTime as tk_time, -- 拓客时间  
849 - yy.F_Id as yy_id, -- 预约ID  
850 - yy.F_Status as yy_status, -- 预约状态  
851 - yy.F_CreateTime as yy_time, -- 预约时间  
852 - yy.yysj as appointment_time, -- 预约到店时间  
853 - xh.F_Id as xh_id, -- 耗卡ID  
854 - xh.F_CreateTime as xh_time, -- 耗卡时间  
855 - xh.xfje as consume_amount, -- 耗卡金额  
856 - CASE  
857 - WHEN yy.F_Id IS NOT NULL THEN '已预约'  
858 - ELSE '未预约'  
859 - END as appointment_status, -- 预约状态描述  
860 - CASE  
861 - WHEN xh.F_Id IS NOT NULL THEN '已耗卡'  
862 - ELSE '未耗卡'  
863 - END as consume_status -- 耗卡状态描述  
864 - FROM lq_tkjlb tk  
865 - LEFT JOIN lq_yyjl yy ON tk.F_MemberId = yy.gk  
866 - AND yy.F_Status = '已确认'  
867 - LEFT JOIN lq_xh_hyhk xh ON tk.F_MemberId = xh.hy  
868 - AND xh.F_IsEffective = 1  
869 - WHERE tk.F_EventId = @eventId  
870 - AND tk.F_StoreId = @storeId  
871 - ORDER BY tk.F_CreateTime DESC  
872 - LIMIT @offset, @pageSize";  
873 - 897 + var sql = GetStoreCustomerDetailsSql(true);
874 var countSql = @" 898 var countSql = @"
875 SELECT COUNT(*) as total 899 SELECT COUNT(*) as total
876 FROM lq_tkjlb tk 900 FROM lq_tkjlb tk
877 WHERE tk.F_EventId = @eventId 901 WHERE tk.F_EventId = @eventId
878 AND tk.F_StoreId = @storeId"; 902 AND tk.F_StoreId = @storeId";
879 -  
880 var offset = (pageIndex - 1) * pageSize; 903 var offset = (pageIndex - 1) * pageSize;
881 var result = await _db.Ado.SqlQueryAsync<dynamic>(sql, new { eventId, storeId, offset, pageSize }); 904 var result = await _db.Ado.SqlQueryAsync<dynamic>(sql, new { eventId, storeId, offset, pageSize });
882 var totalResult = await _db.Ado.SqlQueryAsync<dynamic>(countSql, new { eventId, storeId }); 905 var totalResult = await _db.Ado.SqlQueryAsync<dynamic>(countSql, new { eventId, storeId });
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
@@ -193,6 +193,7 @@ namespace NCC.Extend.LqXhHyhk @@ -193,6 +193,7 @@ namespace NCC.Extend.LqXhHyhk
193 .WhereIF(queryHksj != null, p => p.Hksj >= new DateTime(startHksj.ToDate().Year, startHksj.ToDate().Month, startHksj.ToDate().Day, 0, 0, 0)) 193 .WhereIF(queryHksj != null, p => p.Hksj >= new DateTime(startHksj.ToDate().Year, startHksj.ToDate().Month, startHksj.ToDate().Day, 0, 0, 0))
194 .WhereIF(queryHksj != null, p => p.Hksj <= new DateTime(endHksj.ToDate().Year, endHksj.ToDate().Month, endHksj.ToDate().Day, 23, 59, 59)) 194 .WhereIF(queryHksj != null, p => p.Hksj <= new DateTime(endHksj.ToDate().Year, endHksj.ToDate().Month, endHksj.ToDate().Day, 23, 59, 59))
195 .WhereIF(!string.IsNullOrEmpty(input.czry), p => p.Czry.Equals(input.czry)) 195 .WhereIF(!string.IsNullOrEmpty(input.czry), p => p.Czry.Equals(input.czry))
  196 + .WhereIF(input.isEffective != 0, p => p.IsEffective == input.isEffective)
196 .Select(it => new LqXhHyhkListOutput 197 .Select(it => new LqXhHyhkListOutput
197 { 198 {
198 id = it.Id, 199 id = it.Id,
netcore/src/Modularity/Extend/NCC.Extend/LqXmzlService.cs
@@ -15,6 +15,12 @@ using System.Linq; @@ -15,6 +15,12 @@ using System.Linq;
15 using System.Threading.Tasks; 15 using System.Threading.Tasks;
16 using NCC.Extend.Entitys.lq_xmzl; 16 using NCC.Extend.Entitys.lq_xmzl;
17 using NCC.Extend.Entitys.Dto.LqXmzl; 17 using NCC.Extend.Entitys.Dto.LqXmzl;
  18 +using NCC.Extend.Entitys.lq_kd_pxmx;
  19 +using NCC.Extend.Entitys.lq_xh_pxmx;
  20 +using NCC.Extend.Entitys.lq_hytk_mx;
  21 +using NCC.Extend.Entitys.lq_kd_kdjlb;
  22 +using NCC.Extend.Entitys.lq_xh_hyhk;
  23 +using NCC.Extend.Entitys.lq_hytk_hytk;
18 using Yitter.IdGenerator; 24 using Yitter.IdGenerator;
19 using NCC.Common.Helper; 25 using NCC.Common.Helper;
20 using NCC.JsonSerialization; 26 using NCC.JsonSerialization;
@@ -28,7 +34,7 @@ namespace NCC.Extend.LqXmzl @@ -28,7 +34,7 @@ namespace NCC.Extend.LqXmzl
28 /// <summary> 34 /// <summary>
29 /// 项目资料服务 35 /// 项目资料服务
30 /// </summary> 36 /// </summary>
31 - [ApiDescriptionSettings(Tag = "绿纤项目资料服务", Name = "LqXmzl", Order = 200)] 37 + [ApiDescriptionSettings(Tag = "绿纤品项资料服务", Name = "LqXmzl", Order = 200)]
32 [Route("api/Extend/[controller]")] 38 [Route("api/Extend/[controller]")]
33 public class LqXmzlService : ILqXmzlService, IDynamicApiController, ITransient 39 public class LqXmzlService : ILqXmzlService, IDynamicApiController, ITransient
34 { 40 {
@@ -46,7 +52,7 @@ namespace NCC.Extend.LqXmzl @@ -46,7 +52,7 @@ namespace NCC.Extend.LqXmzl
46 _userManager = userManager; 52 _userManager = userManager;
47 } 53 }
48 54
49 - #region 获取项目资料 55 + #region 获取品项资料
50 56
51 /// <summary> 57 /// <summary>
52 /// 获取项目资料 58 /// 获取项目资料
@@ -62,9 +68,9 @@ namespace NCC.Extend.LqXmzl @@ -62,9 +68,9 @@ namespace NCC.Extend.LqXmzl
62 } 68 }
63 #endregion 69 #endregion
64 70
65 - #region 获取项目资料列表 71 + #region 获取品项资料列表
66 /// <summary> 72 /// <summary>
67 - /// 获取项目资料列表 73 + /// 获取品项资料列表
68 /// </summary> 74 /// </summary>
69 /// <param name="input">请求参数</param> 75 /// <param name="input">请求参数</param>
70 /// <returns></returns> 76 /// <returns></returns>
@@ -114,9 +120,9 @@ namespace NCC.Extend.LqXmzl @@ -114,9 +120,9 @@ namespace NCC.Extend.LqXmzl
114 } 120 }
115 #endregion 121 #endregion
116 122
117 - #region 新建项目资料 123 + #region 新建品项资料
118 /// <summary> 124 /// <summary>
119 - /// 新建项目资料 125 + /// 新建品项资料
120 /// </summary> 126 /// </summary>
121 /// <param name="input">参数</param> 127 /// <param name="input">参数</param>
122 /// <returns></returns> 128 /// <returns></returns>
@@ -131,9 +137,9 @@ namespace NCC.Extend.LqXmzl @@ -131,9 +137,9 @@ namespace NCC.Extend.LqXmzl
131 } 137 }
132 #endregion 138 #endregion
133 139
134 - #region 获取项目资料无分页列表 140 + #region 获取品项资料无分页列表
135 /// <summary> 141 /// <summary>
136 - /// 获取项目资料无分页列表 142 + /// 获取品项资料无分页列表
137 /// </summary> 143 /// </summary>
138 /// <param name="input">请求参数</param> 144 /// <param name="input">请求参数</param>
139 /// <returns></returns> 145 /// <returns></returns>
@@ -177,9 +183,9 @@ namespace NCC.Extend.LqXmzl @@ -177,9 +183,9 @@ namespace NCC.Extend.LqXmzl
177 } 183 }
178 #endregion 184 #endregion
179 185
180 - #region 导出项目资料 186 + #region 导出品项资料
181 /// <summary> 187 /// <summary>
182 - /// 导出项目资料 188 + /// 导出品项资料
183 /// </summary> 189 /// </summary>
184 /// <param name="input">请求参数</param> 190 /// <param name="input">请求参数</param>
185 /// <returns></returns> 191 /// <returns></returns>
@@ -225,9 +231,9 @@ namespace NCC.Extend.LqXmzl @@ -225,9 +231,9 @@ namespace NCC.Extend.LqXmzl
225 } 231 }
226 #endregion 232 #endregion
227 233
228 - #region 批量删除项目资料 234 + #region 批量删除品项资料
229 /// <summary> 235 /// <summary>
230 - /// 批量删除项目资料 236 + /// 批量删除品项资料
231 /// </summary> 237 /// </summary>
232 /// <param name="ids">主键数组</param> 238 /// <param name="ids">主键数组</param>
233 /// <returns></returns> 239 /// <returns></returns>
@@ -257,9 +263,9 @@ namespace NCC.Extend.LqXmzl @@ -257,9 +263,9 @@ namespace NCC.Extend.LqXmzl
257 } 263 }
258 #endregion 264 #endregion
259 265
260 - #region 更新项目资料 266 + #region 更新品项资料
261 /// <summary> 267 /// <summary>
262 - /// 更新项目资料 268 + /// 更新品项资料
263 /// </summary> 269 /// </summary>
264 /// <param name="id">主键</param> 270 /// <param name="id">主键</param>
265 /// <param name="input">参数</param> 271 /// <param name="input">参数</param>
@@ -273,9 +279,9 @@ namespace NCC.Extend.LqXmzl @@ -273,9 +279,9 @@ namespace NCC.Extend.LqXmzl
273 } 279 }
274 #endregion 280 #endregion
275 281
276 - #region 删除项目资料 282 + #region 删除品项资料
277 /// <summary> 283 /// <summary>
278 - /// 删除项目资料 284 + /// 删除品项资料
279 /// </summary> 285 /// </summary>
280 /// <param name="id">主键</param> 286 /// <param name="id">主键</param>
281 /// <returns></returns> 287 /// <returns></returns>
@@ -386,6 +392,309 @@ namespace NCC.Extend.LqXmzl @@ -386,6 +392,309 @@ namespace NCC.Extend.LqXmzl
386 } 392 }
387 #endregion 393 #endregion
388 394
  395 + #region 品项维度统计
  396 + /// <summary>
  397 + /// 品项维度统计
  398 + /// </summary>
  399 + /// <remarks>
  400 + /// 按品项维度统计开卡、消耗、退卡等数据
  401 + /// 包括业绩、人数、占比、复购率等指标
  402 + ///
  403 + /// 示例请求:
  404 + /// ```json
  405 + /// {
  406 + /// "startTime": "2024-01-01",
  407 + /// "endTime": "2024-12-31",
  408 + /// "storeId": "store001",
  409 + /// "category": "美容",
  410 + /// "itemId": "item001"
  411 + /// }
  412 + /// ```
  413 + ///
  414 + /// 参数说明:
  415 + /// - startTime: 开始时间(可选)
  416 + /// - endTime: 结束时间(可选)
  417 + /// - storeId: 门店ID(可选)
  418 + /// - category: 品项分类(可选)
  419 + /// - itemId: 品项ID(可选,单个品项统计)
  420 + /// </remarks>
  421 + /// <param name="input">统计输入参数</param>
  422 + /// <returns>品项维度统计数据</returns>
  423 + /// <response code="200">统计成功</response>
  424 + /// <response code="400">参数错误</response>
  425 + /// <response code="500">服务器错误</response>
  426 + [HttpPost("GetItemStatistics")]
  427 + public async Task<dynamic> GetItemStatistics([FromBody] LqXmzlStatisticsInput input)
  428 + {
  429 + try
  430 + {
  431 + // 第一步:获取品项基础信息
  432 + var itemsQuery = _db.Queryable<LqXmzlEntity>()
  433 + .Where(x => x.IsEffective == 1);
  434 +
  435 + if (!string.IsNullOrEmpty(input.ItemId))
  436 + {
  437 + itemsQuery = itemsQuery.Where(x => x.Id == input.ItemId);
  438 + }
  439 +
  440 + if (!string.IsNullOrEmpty(input.Category))
  441 + {
  442 + itemsQuery = itemsQuery.Where(x => x.Fl1 == input.Category || x.Fl2 == input.Category || x.Fl == input.Category);
  443 + }
  444 +
  445 + var items = await itemsQuery.ToListAsync();
  446 +
  447 + if (!items.Any())
  448 + {
  449 + return new
  450 + {
  451 + success = true,
  452 + data = new List<LqXmzlStatisticsOutput>(),
  453 + message = "未找到符合条件的品项"
  454 + };
  455 + }
  456 +
  457 + var itemIds = items.Select(x => x.Id).ToList();
  458 +
  459 + // 第二步:开卡数据统计
  460 + var billingStats = await GetBillingStatistics(itemIds, input);
  461 +
  462 + // 第三步:消耗数据统计
  463 + var consumeStats = await GetConsumeStatistics(itemIds, input);
  464 +
  465 + // 第四步:退卡数据统计
  466 + var refundStats = await GetRefundStatistics(itemIds, input);
  467 +
  468 + // 第五步:计算总数据用于占比计算
  469 + var totalBillingAmount = billingStats.Sum(x => x.BillingAmount);
  470 + var totalConsumeAmount = consumeStats.Sum(x => x.ConsumeAmount);
  471 + var totalBuyers = billingStats.Sum(x => x.TotalBuyers);
  472 +
  473 + // 第六步:合并数据并计算占比
  474 + var result = new List<LqXmzlStatisticsOutput>();
  475 +
  476 + foreach (var item in items)
  477 + {
  478 + var billingData = billingStats.FirstOrDefault(x => x.ItemId == item.Id);
  479 + var consumeData = consumeStats.FirstOrDefault(x => x.ItemId == item.Id);
  480 + var refundData = refundStats.FirstOrDefault(x => x.ItemId == item.Id);
  481 +
  482 + var output = new LqXmzlStatisticsOutput
  483 + {
  484 + ItemId = item.Id,
  485 + ItemName = item.Xmmc,
  486 + ItemNumber = item.Xmbh,
  487 + BillingAmount = billingData?.BillingAmount ?? 0,
  488 + BillingAmountRatio = totalBillingAmount > 0 ? (billingData?.BillingAmount ?? 0) / totalBillingAmount : 0,
  489 + TotalBuyers = billingData?.TotalBuyers ?? 0,
  490 + ItemRatio = totalBuyers > 0 ? (billingData?.TotalBuyers ?? 0) / (decimal)totalBuyers : 0,
  491 + RepeatBuyers = billingData?.RepeatBuyers ?? 0,
  492 + RepeatBuyRate = (billingData?.TotalBuyers ?? 0) > 0 ? (billingData?.RepeatBuyers ?? 0) / (decimal)(billingData?.TotalBuyers ?? 1) : 0,
  493 + ConsumeAmount = consumeData?.ConsumeAmount ?? 0,
  494 + ConsumeAmountRatio = totalConsumeAmount > 0 ? (consumeData?.ConsumeAmount ?? 0) / totalConsumeAmount : 0,
  495 + ConsumePurchaseCount = consumeData?.ConsumePurchaseCount ?? 0,
  496 + ConsumeGiftCount = consumeData?.ConsumeGiftCount ?? 0,
  497 + ConsumeExperienceCount = consumeData?.ConsumeExperienceCount ?? 0,
  498 + RefundAmount = refundData?.RefundAmount ?? 0,
  499 + RefundCount = refundData?.RefundCount ?? 0
  500 + };
  501 +
  502 + result.Add(output);
  503 + }
  504 +
  505 + return new
  506 + {
  507 + success = true,
  508 + data = result,
  509 + message = "品项维度统计成功"
  510 + };
  511 + }
  512 + catch (Exception ex)
  513 + {
  514 + throw NCCException.Oh($"品项维度统计失败:{ex.Message}");
  515 + }
  516 + }
  517 +
  518 + /// <summary>
  519 + /// 获取开卡统计数据
  520 + /// </summary>
  521 + private async Task<List<ItemStatisticsData>> GetBillingStatistics(List<string> itemIds, LqXmzlStatisticsInput input)
  522 + {
  523 + var query = _db.Queryable<LqKdPxmxEntity>()
  524 + .Where(x => itemIds.Contains(x.Px) && x.IsEffective == 1);
  525 +
  526 + // 时间过滤
  527 + if (input.StartTime.HasValue)
  528 + {
  529 + query = query.Where(x => x.CreateTIme >= input.StartTime.Value);
  530 + }
  531 + if (input.EndTime.HasValue)
  532 + {
  533 + query = query.Where(x => x.CreateTIme <= input.EndTime.Value);
  534 + }
  535 +
  536 + // 门店过滤(通过开单记录关联)
  537 + if (!string.IsNullOrEmpty(input.StoreId))
  538 + {
  539 + query = query.InnerJoin<LqKdKdjlbEntity>((px, kd) => px.Glkdbh == kd.Id)
  540 + .Where((px, kd) => kd.Djmd == input.StoreId && kd.IsEffective == 1);
  541 + }
  542 +
  543 + var result = await query
  544 + .GroupBy(x => x.Px)
  545 + .Select(x => new ItemStatisticsData
  546 + {
  547 + ItemId = x.Px,
  548 + BillingAmount = SqlFunc.AggregateSum(x.Pxjg * x.ProjectNumber),
  549 + TotalBuyers = SqlFunc.AggregateCount(x.MemberId),
  550 + RepeatBuyers = 0 // 复购人数需要单独计算
  551 + })
  552 + .ToListAsync();
  553 +
  554 + // 单独计算复购人数
  555 + foreach (var item in result)
  556 + {
  557 + var memberCountQuery = _db.Queryable<LqKdPxmxEntity>()
  558 + .Where(x => x.Px == item.ItemId && x.IsEffective == 1);
  559 +
  560 + if (input.StartTime.HasValue)
  561 + {
  562 + memberCountQuery = memberCountQuery.Where(x => x.CreateTIme >= input.StartTime.Value);
  563 + }
  564 + if (input.EndTime.HasValue)
  565 + {
  566 + memberCountQuery = memberCountQuery.Where(x => x.CreateTIme <= input.EndTime.Value);
  567 + }
389 568
  569 + if (!string.IsNullOrEmpty(input.StoreId))
  570 + {
  571 + memberCountQuery = memberCountQuery.InnerJoin<LqKdKdjlbEntity>((px, kd) => px.Glkdbh == kd.Id)
  572 + .Where((px, kd) => kd.Djmd == input.StoreId && kd.IsEffective == 1);
  573 + }
  574 +
  575 + var memberStats = await memberCountQuery
  576 + .GroupBy(x => x.MemberId)
  577 + .Having(x => SqlFunc.AggregateCount(x.MemberId) > 1)
  578 + .Select(x => SqlFunc.AggregateCount(x.MemberId))
  579 + .ToListAsync();
  580 +
  581 + item.RepeatBuyers = memberStats.Count;
  582 + }
  583 +
  584 + return result;
  585 + }
  586 +
  587 + /// <summary>
  588 + /// 获取消耗统计数据
  589 + /// </summary>
  590 + private async Task<List<ItemConsumeStatisticsData>> GetConsumeStatistics(List<string> itemIds, LqXmzlStatisticsInput input)
  591 + {
  592 + var query = _db.Queryable<LqXhPxmxEntity>()
  593 + .Where(x => itemIds.Contains(x.Px));
  594 +
  595 + // 时间过滤
  596 + if (input.StartTime.HasValue)
  597 + {
  598 + query = query.Where(x => x.CreateTIme >= input.StartTime.Value);
  599 + }
  600 + if (input.EndTime.HasValue)
  601 + {
  602 + query = query.Where(x => x.CreateTIme <= input.EndTime.Value);
  603 + }
  604 +
  605 + // 门店过滤(通过耗卡记录关联)
  606 + if (!string.IsNullOrEmpty(input.StoreId))
  607 + {
  608 + query = query.InnerJoin<LqXhHyhkEntity>((px, xh) => px.ConsumeInfoId == xh.Id)
  609 + .Where((px, xh) => xh.Md == input.StoreId && xh.IsEffective == 1);
  610 + }
  611 +
  612 + var result = await query
  613 + .GroupBy(x => x.Px)
  614 + .Select(x => new ItemConsumeStatisticsData
  615 + {
  616 + ItemId = x.Px,
  617 + ConsumeAmount = SqlFunc.AggregateSum(x.Pxjg * x.ProjectNumber),
  618 + ConsumePurchaseCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.SourceType == "购买", x.ProjectNumber, 0)),
  619 + ConsumeGiftCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.SourceType == "赠送", x.ProjectNumber, 0)),
  620 + ConsumeExperienceCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.SourceType == "体验", x.ProjectNumber, 0))
  621 + })
  622 + .ToListAsync();
  623 +
  624 + return result;
  625 + }
  626 +
  627 + /// <summary>
  628 + /// 获取退卡统计数据
  629 + /// </summary>
  630 + private async Task<List<ItemRefundStatisticsData>> GetRefundStatistics(List<string> itemIds, LqXmzlStatisticsInput input)
  631 + {
  632 + var query = _db.Queryable<LqHytkMxEntity>()
  633 + .Where(x => itemIds.Contains(x.Px));
  634 +
  635 + // 时间过滤
  636 + if (input.StartTime.HasValue)
  637 + {
  638 + query = query.Where(x => x.Tksj >= input.StartTime.Value);
  639 + }
  640 + if (input.EndTime.HasValue)
  641 + {
  642 + query = query.Where(x => x.Tksj <= input.EndTime.Value);
  643 + }
  644 +
  645 + // 门店过滤(通过退卡记录关联)
  646 + if (!string.IsNullOrEmpty(input.StoreId))
  647 + {
  648 + query = query.InnerJoin<LqHytkHytkEntity>((mx, hytk) => mx.RefundInfoId == hytk.Id)
  649 + .Where((mx, hytk) => hytk.Md == input.StoreId && hytk.IsEffective == 1);
  650 + }
  651 +
  652 + var result = await query
  653 + .GroupBy(x => x.Px)
  654 + .Select(x => new ItemRefundStatisticsData
  655 + {
  656 + ItemId = x.Px,
  657 + RefundAmount = SqlFunc.AggregateSum(x.Tkje ?? 0),
  658 + RefundCount = SqlFunc.AggregateCount(x.Id)
  659 + })
  660 + .ToListAsync();
  661 +
  662 + return result;
  663 + }
  664 + #endregion
  665 +
  666 + }
  667 +
  668 + /// <summary>
  669 + /// 品项统计数据(内部类)
  670 + /// </summary>
  671 + public class ItemStatisticsData
  672 + {
  673 + public string ItemId { get; set; }
  674 + public decimal BillingAmount { get; set; }
  675 + public int TotalBuyers { get; set; }
  676 + public int RepeatBuyers { get; set; }
  677 + }
  678 +
  679 + /// <summary>
  680 + /// 品项消耗统计数据(内部类)
  681 + /// </summary>
  682 + public class ItemConsumeStatisticsData
  683 + {
  684 + public string ItemId { get; set; }
  685 + public decimal ConsumeAmount { get; set; }
  686 + public int ConsumePurchaseCount { get; set; }
  687 + public int ConsumeGiftCount { get; set; }
  688 + public int ConsumeExperienceCount { get; set; }
  689 + }
  690 +
  691 + /// <summary>
  692 + /// 品项退卡统计数据(内部类)
  693 + /// </summary>
  694 + public class ItemRefundStatisticsData
  695 + {
  696 + public string ItemId { get; set; }
  697 + public decimal RefundAmount { get; set; }
  698 + public int RefundCount { get; set; }
390 } 699 }
391 } 700 }