Commit baff42576ab7393c73a8cd1be3d0dacb8c9df126
Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP
Showing
12 changed files
with
1668 additions
and
211 deletions
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionOutput.cs
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqKdKdjlb | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 开单品项明细记录输出 | |
| 7 | + /// </summary> | |
| 8 | + public class BillingItemDetailListOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 品项明细ID | |
| 12 | + /// </summary> | |
| 13 | + public string id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 开单ID | |
| 17 | + /// </summary> | |
| 18 | + public string billingId { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 开单时间 | |
| 22 | + /// </summary> | |
| 23 | + public DateTime? billingTime { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 营销活动名称 | |
| 27 | + /// </summary> | |
| 28 | + public string activityName { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 会员名称 | |
| 32 | + /// </summary> | |
| 33 | + public string memberName { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 会员手机 | |
| 37 | + /// </summary> | |
| 38 | + public string memberPhone { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 品项名称 | |
| 42 | + /// </summary> | |
| 43 | + public string itemName { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 品项类型(分类④-统计品项用) | |
| 47 | + /// </summary> | |
| 48 | + public string itemType { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 实付金额 | |
| 52 | + /// </summary> | |
| 53 | + public decimal actualPrice { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 项目次数 | |
| 57 | + /// </summary> | |
| 58 | + public decimal projectNumber { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 来源类型(购买/赠送/体验) | |
| 62 | + /// </summary> | |
| 63 | + public string sourceType { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 备注 | |
| 67 | + /// </summary> | |
| 68 | + public string remark { get; set; } | |
| 69 | + } | |
| 70 | +} | |
| 71 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs
0 → 100644
| 1 | +using NCC.Common.Filter; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqKdKdjlb | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 开单品项明细记录查询输入参数 | |
| 7 | + /// </summary> | |
| 8 | + public class BillingItemDetailListQueryInput : PageInputBase | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 品项明细ID | |
| 12 | + /// </summary> | |
| 13 | + public string Id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 开单ID | |
| 17 | + /// </summary> | |
| 18 | + public string BillingId { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 开单时间(开始) | |
| 22 | + /// </summary> | |
| 23 | + public string StartBillingTime { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 开单时间(结束) | |
| 27 | + /// </summary> | |
| 28 | + public string EndBillingTime { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 营销活动ID | |
| 32 | + /// </summary> | |
| 33 | + public string ActivityId { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 会员ID | |
| 37 | + /// </summary> | |
| 38 | + public string MemberId { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 会员名称(模糊查询) | |
| 42 | + /// </summary> | |
| 43 | + public string MemberName { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 会员手机号 | |
| 47 | + /// </summary> | |
| 48 | + public string MemberPhone { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 品项ID | |
| 52 | + /// </summary> | |
| 53 | + public string ItemId { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 品项名称(模糊查询) | |
| 57 | + /// </summary> | |
| 58 | + public string ItemName { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 品项类型(分类④-统计品项用) | |
| 62 | + /// </summary> | |
| 63 | + public string ItemType { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 来源类型(购买/赠送/体验) | |
| 67 | + /// </summary> | |
| 68 | + public string SourceType { get; set; } | |
| 69 | + } | |
| 70 | +} | |
| 71 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.LqKhxx | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 会员品项信息输出 | |
| 8 | + /// </summary> | |
| 9 | + public class MemberItemInfoOutput | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 会员ID | |
| 13 | + /// </summary> | |
| 14 | + public string MemberId { get; set; } | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 会员编号(档案号) | |
| 18 | + /// </summary> | |
| 19 | + public string MemberCode { get; set; } | |
| 20 | + | |
| 21 | + /// <summary> | |
| 22 | + /// 会员名称 | |
| 23 | + /// </summary> | |
| 24 | + public string MemberName { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 会员电话 | |
| 28 | + /// </summary> | |
| 29 | + public string MemberPhone { get; set; } | |
| 30 | + | |
| 31 | + /// <summary> | |
| 32 | + /// 所属门店ID | |
| 33 | + /// </summary> | |
| 34 | + public string BelongStoreId { get; set; } | |
| 35 | + | |
| 36 | + /// <summary> | |
| 37 | + /// 所属门店名称 | |
| 38 | + /// </summary> | |
| 39 | + public string BelongStoreName { get; set; } | |
| 40 | + | |
| 41 | + /// <summary> | |
| 42 | + /// 开单信息列表 | |
| 43 | + /// </summary> | |
| 44 | + public List<BillingItemInfo> BillingItems { get; set; } = new List<BillingItemInfo>(); | |
| 45 | + } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 开单信息 | |
| 49 | + /// </summary> | |
| 50 | + public class BillingItemInfo | |
| 51 | + { | |
| 52 | + /// <summary> | |
| 53 | + /// 开单ID | |
| 54 | + /// </summary> | |
| 55 | + public string BillingId { get; set; } | |
| 56 | + | |
| 57 | + /// <summary> | |
| 58 | + /// 开单时间 | |
| 59 | + /// </summary> | |
| 60 | + public DateTime? BillingTime { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 开单门店ID | |
| 64 | + /// </summary> | |
| 65 | + public string BillingStoreId { get; set; } | |
| 66 | + | |
| 67 | + /// <summary> | |
| 68 | + /// 开单门店名称 | |
| 69 | + /// </summary> | |
| 70 | + public string BillingStoreName { get; set; } | |
| 71 | + | |
| 72 | + /// <summary> | |
| 73 | + /// 开单人ID | |
| 74 | + /// </summary> | |
| 75 | + public string BillingUserId { get; set; } | |
| 76 | + | |
| 77 | + /// <summary> | |
| 78 | + /// 开单人名称 | |
| 79 | + /// </summary> | |
| 80 | + public string BillingUserName { get; set; } | |
| 81 | + | |
| 82 | + /// <summary> | |
| 83 | + /// 购买品项列表 | |
| 84 | + /// </summary> | |
| 85 | + public List<ItemDetail> PurchaseItems { get; set; } = new List<ItemDetail>(); | |
| 86 | + | |
| 87 | + /// <summary> | |
| 88 | + /// 购买品项总金额 | |
| 89 | + /// </summary> | |
| 90 | + public decimal PurchaseItemsTotalAmount { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 赠送品项列表 | |
| 94 | + /// </summary> | |
| 95 | + public List<ItemDetail> GiftItems { get; set; } = new List<ItemDetail>(); | |
| 96 | + | |
| 97 | + /// <summary> | |
| 98 | + /// 体验品项列表 | |
| 99 | + /// </summary> | |
| 100 | + public List<ItemDetail> ExperienceItems { get; set; } = new List<ItemDetail>(); | |
| 101 | + | |
| 102 | + /// <summary> | |
| 103 | + /// 已消耗品项列表 | |
| 104 | + /// </summary> | |
| 105 | + public List<ItemDetail> ConsumedItems { get; set; } = new List<ItemDetail>(); | |
| 106 | + | |
| 107 | + /// <summary> | |
| 108 | + /// 已消耗品项总金额 | |
| 109 | + /// </summary> | |
| 110 | + public decimal ConsumedItemsTotalAmount { get; set; } | |
| 111 | + | |
| 112 | + /// <summary> | |
| 113 | + /// 已退卡品项列表 | |
| 114 | + /// </summary> | |
| 115 | + public List<ItemDetail> RefundedItems { get; set; } = new List<ItemDetail>(); | |
| 116 | + | |
| 117 | + /// <summary> | |
| 118 | + /// 已退卡品项总金额 | |
| 119 | + /// </summary> | |
| 120 | + public decimal RefundedItemsTotalAmount { get; set; } | |
| 121 | + | |
| 122 | + /// <summary> | |
| 123 | + /// 已储扣品项列表 | |
| 124 | + /// </summary> | |
| 125 | + public List<ItemDetail> DeductedItems { get; set; } = new List<ItemDetail>(); | |
| 126 | + | |
| 127 | + /// <summary> | |
| 128 | + /// 已储扣品项总金额 | |
| 129 | + /// </summary> | |
| 130 | + public decimal DeductedItemsTotalAmount { get; set; } | |
| 131 | + } | |
| 132 | + | |
| 133 | + /// <summary> | |
| 134 | + /// 品项明细 | |
| 135 | + /// </summary> | |
| 136 | + public class ItemDetail | |
| 137 | + { | |
| 138 | + /// <summary> | |
| 139 | + /// 品项ID | |
| 140 | + /// </summary> | |
| 141 | + public string ItemId { get; set; } | |
| 142 | + | |
| 143 | + /// <summary> | |
| 144 | + /// 品项名称 | |
| 145 | + /// </summary> | |
| 146 | + public string ItemName { get; set; } | |
| 147 | + | |
| 148 | + /// <summary> | |
| 149 | + /// 品项价格 | |
| 150 | + /// </summary> | |
| 151 | + public decimal ItemPrice { get; set; } | |
| 152 | + | |
| 153 | + /// <summary> | |
| 154 | + /// 项目次数 | |
| 155 | + /// </summary> | |
| 156 | + public decimal ProjectNumber { get; set; } | |
| 157 | + | |
| 158 | + /// <summary> | |
| 159 | + /// 总金额 | |
| 160 | + /// </summary> | |
| 161 | + public decimal TotalAmount { get; set; } | |
| 162 | + } | |
| 163 | +} | |
| 164 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoQueryInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Filter; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.LqKhxx | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 会员品项信息查询输入参数 | |
| 8 | + /// </summary> | |
| 9 | + public class MemberItemInfoQueryInput : PageInputBase | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 会员ID | |
| 13 | + /// </summary> | |
| 14 | + public string MemberId { get; set; } | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 所属门店ID | |
| 18 | + /// </summary> | |
| 19 | + public string BelongStoreId { get; set; } | |
| 20 | + | |
| 21 | + /// <summary> | |
| 22 | + /// 开单门店ID | |
| 23 | + /// </summary> | |
| 24 | + public string BillingStoreId { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 开单人ID | |
| 28 | + /// </summary> | |
| 29 | + public string BillingUserId { get; set; } | |
| 30 | + | |
| 31 | + /// <summary> | |
| 32 | + /// 购买品项ID | |
| 33 | + /// </summary> | |
| 34 | + public string PurchaseItemId { get; set; } | |
| 35 | + | |
| 36 | + /// <summary> | |
| 37 | + /// 赠送品项ID | |
| 38 | + /// </summary> | |
| 39 | + public string GiftItemId { get; set; } | |
| 40 | + | |
| 41 | + /// <summary> | |
| 42 | + /// 体验品项ID | |
| 43 | + /// </summary> | |
| 44 | + public string ExperienceItemId { get; set; } | |
| 45 | + } | |
| 46 | +} | |
| 47 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
| ... | ... | @@ -456,7 +456,7 @@ namespace NCC.Extend |
| 456 | 456 | var actualCompletedPerformance = unit.BillingPerformance - unit.RefundPerformance; // 实际完成业绩 |
| 457 | 457 | unit.CompletedPerformance = actualCompletedPerformance; // 实际完成业绩 |
| 458 | 458 | var completionRate = unit.TargetPerformance > 0 ? (actualCompletedPerformance / unit.TargetPerformance * 100m) : 0m; |
| 459 | - unit.CompletionRate = decimal.Round(completionRate, 2); | |
| 459 | + unit.CompletionRate = decimal.Round(completionRate, 2); | |
| 460 | 460 | } |
| 461 | 461 | |
| 462 | 462 | var outputList = businessUnitDict.Values.OrderByDescending(x => x.CompletedPerformance).ToList(); |
| ... | ... | @@ -470,17 +470,23 @@ namespace NCC.Extend |
| 470 | 470 | /// 获取天王团业绩完成情况 |
| 471 | 471 | /// </summary> |
| 472 | 472 | /// <remarks> |
| 473 | - /// 根据指定日期统计各天王团的业绩完成情况,包括目标业绩、开单业绩、退卡金额、实际完成业绩、完成率 | |
| 473 | + /// 根据指定日期统计各天王团的业绩完成情况,包括目标业绩、开单业绩、储扣金额、退卡金额、实际完成业绩、完成率 | |
| 474 | 474 | /// |
| 475 | 475 | /// 业绩统计规则: |
| 476 | - /// - 教育部(教育一部、教育二部):统计生美品项(lq_xmzl.qt2='生美')相关的开单和退卡业绩 | |
| 477 | - /// - 科技部(科技一部、科技二部):统计科美品项(lq_xmzl.qt2='科美')相关的开单和退卡业绩 | |
| 478 | - /// - 大项目部(大项目一部、大项目二部):统计医美品项(lq_xmzl.qt2='医美')相关的开单和退卡业绩 | |
| 476 | + /// - 教育部(教育一部、教育二部):统计生美品项(lq_xmzl.qt2='生美')相关的开单、储扣和退卡业绩 | |
| 477 | + /// - 科技部(科技一部、科技二部):统计科美品项(lq_xmzl.qt2='科美')相关的开单、储扣和退卡业绩 | |
| 478 | + /// - 大项目部(大项目一部、大项目二部):统计医美品项(lq_xmzl.qt2='医美')相关的开单、储扣和退卡业绩 | |
| 479 | 479 | /// |
| 480 | 480 | /// 业绩分配规则: |
| 481 | - /// - 生美品项的开单和退卡业绩全部归教育部(不按目标比例分配) | |
| 482 | - /// - 科美品项的开单和退卡业绩全部归科技部(不按目标比例分配) | |
| 483 | - /// - 医美品项的开单和退卡业绩全部归大项目部(不按目标比例分配) | |
| 481 | + /// - 生美品项的开单、储扣和退卡业绩全部归教育部(不按目标比例分配) | |
| 482 | + /// - 科美品项的开单、储扣和退卡业绩全部归科技部(不按目标比例分配) | |
| 483 | + /// - 医美品项的开单、储扣和退卡业绩全部归大项目部(不按目标比例分配) | |
| 484 | + /// | |
| 485 | + /// 业绩计算规则: | |
| 486 | + /// - 开单业绩:原始开单业绩(不减去储扣金额) | |
| 487 | + /// - 储扣金额:储值扣减金额总和 | |
| 488 | + /// - 退卡业绩:退卡金额总和 | |
| 489 | + /// - 实际完成业绩 = 开单业绩 - 储扣金额 - 退卡金额 | |
| 484 | 490 | /// |
| 485 | 491 | /// 示例请求: |
| 486 | 492 | /// ```json |
| ... | ... | @@ -500,10 +506,11 @@ namespace NCC.Extend |
| 500 | 506 | /// - DepartmentId: 部门ID |
| 501 | 507 | /// - DepartmentName: 部门名称(教育一部、教育二部、科技一部、科技二部、大项目一部、大项目二部) |
| 502 | 508 | /// - TargetPerformance: 目标业绩(来自门店目标表lq_md_target,根据部门类型使用对应字段:教育部使用F_EducationDepartmentTarget,科技部使用F_TechDepartmentTarget,大项目部使用F_MajorProjectDepartmentTarget,根据开始时间所在月份获取,通过对应归属字段关联,如果未查询到则为0) |
| 503 | - /// - BillingPerformance: 开单业绩(指定时间范围内,按品项分类过滤后的开单业绩总和,按门店目标比例分配) | |
| 504 | - /// - RefundPerformance: 退款业绩(指定时间范围内,按品项分类过滤后的退卡业绩总和,按门店目标比例分配) | |
| 509 | + /// - BillingPerformance: 开单业绩(原始开单业绩,不减去储扣金额) | |
| 510 | + /// - DeductAmount: 储扣金额(储值扣减金额总和) | |
| 511 | + /// - RefundPerformance: 退卡业绩(退卡金额总和) | |
| 505 | 512 | /// - ActualPerformance: 开单业绩(与BillingPerformance相同,用于兼容) |
| 506 | - /// - CompletedPerformance: 实际完成业绩(开单业绩 - 退款业绩) | |
| 513 | + /// - CompletedPerformance: 实际完成业绩(开单业绩 - 储扣金额 - 退卡金额) | |
| 507 | 514 | /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100) |
| 508 | 515 | /// - StoreCount: 门店数量(根据门店目标表中归属该部门的门店数统计) |
| 509 | 516 | /// </remarks> |
| ... | ... | @@ -582,7 +589,8 @@ namespace NCC.Extend |
| 582 | 589 | ActualPerformance = 0, |
| 583 | 590 | CompletedPerformance = 0, |
| 584 | 591 | CompletionRate = 0, |
| 585 | - StoreCount = Convert.ToInt32(dept.StoreCount) | |
| 592 | + StoreCount = Convert.ToInt32(dept.StoreCount), | |
| 593 | + DeductAmount = 0 | |
| 586 | 594 | }; |
| 587 | 595 | // 记录部门对应的字段类型 |
| 588 | 596 | departmentFieldMap[deptId] = deptType.Value.fieldName; |
| ... | ... | @@ -620,6 +628,7 @@ namespace NCC.Extend |
| 620 | 628 | } |
| 621 | 629 | } |
| 622 | 630 | |
| 631 | + // 如果目标表中没有对应月份的数据,直接返回空业绩列表 | |
| 623 | 632 | if (!storeIds.Any()) |
| 624 | 633 | { |
| 625 | 634 | return departmentDict.Values.OrderByDescending(x => x.CompletedPerformance).ToList(); |
| ... | ... | @@ -630,7 +639,7 @@ namespace NCC.Extend |
| 630 | 639 | // 2.2 查询目标表数据(一次性查询,数据量小) |
| 631 | 640 | // 直接查询,不使用 MAX(),因为每个门店在目标表中应该只有一条记录 |
| 632 | 641 | var targetSql = $@" |
| 633 | - SELECT | |
| 642 | + SELECT | |
| 634 | 643 | F_StoreId, |
| 635 | 644 | F_EducationDepartment, |
| 636 | 645 | F_TechDepartment, |
| ... | ... | @@ -650,220 +659,163 @@ namespace NCC.Extend |
| 650 | 659 | } |
| 651 | 660 | } |
| 652 | 661 | |
| 653 | - // 2.3 从品项明细表统计业绩,按门店+品项分类分组 | |
| 662 | + // 如果targetDict为空,说明目标表中没有数据,直接返回 | |
| 663 | + if (!targetDict.Any()) | |
| 664 | + { | |
| 665 | + return departmentDict.Values.OrderByDescending(x => x.CompletedPerformance).ToList(); | |
| 666 | + } | |
| 667 | + | |
| 668 | + // 2.3 分步骤查询:按生美、科美、医美分别查询开单业绩、退卡业绩、储扣金额 | |
| 654 | 669 | // 重要:业绩从品项明细表(lq_kd_pxmx)的 F_ActualPrice 获取,不是从开单记录表的 sfyj 获取 |
| 655 | 670 | // 因为一个开单可能包含多个品项,每个品项属于不同的分类(生美、科美、医美) |
| 656 | 671 | // 例如:开单A实付500元,包含科美100元、医美250元、生美150元 |
| 657 | 672 | // 那么科技部统计100元,大项目部统计250元,教育部统计150元 |
| 658 | - var billingSql = $@" | |
| 659 | - SELECT | |
| 660 | - temp.djmd as StoreId, | |
| 661 | - item.qt2 as ItemType, | |
| 662 | - COALESCE(SUM(temp.F_ActualPrice), 0) as StoreAmount | |
| 663 | - FROM ( | |
| 664 | - SELECT pxmx.px, pxmx.F_ActualPrice, billing.djmd | |
| 665 | - FROM lq_kd_pxmx pxmx | |
| 666 | - INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 667 | - WHERE pxmx.F_IsEffective = 1 | |
| 668 | - AND billing.F_IsEffective = 1 | |
| 669 | - AND billing.djmd IN ('{storeIdsStr}') | |
| 670 | - AND billing.kdrq >= '{startDate:yyyy-MM-dd} 00:00:00' | |
| 671 | - AND billing.kdrq < '{endDate.AddDays(1):yyyy-MM-dd} 00:00:00' | |
| 672 | - ) temp | |
| 673 | - INNER JOIN lq_xmzl item ON temp.px = item.F_Id | |
| 674 | - WHERE item.F_IsEffective = 1 | |
| 675 | - AND item.qt2 IN ('生美', '科美', '医美') | |
| 676 | - GROUP BY temp.djmd, item.qt2"; | |
| 677 | - | |
| 678 | - var billingData = await _db.Ado.SqlQueryAsync<dynamic>(billingSql); | |
| 679 | - | |
| 680 | - // 2.4 从退卡明细表统计退卡业绩,按门店+品项分类分组 | |
| 681 | - // 重要:退卡业绩从退卡明细表(lq_hytk_mx)的 tkje 获取,按品项分类统计 | |
| 682 | - var refundSql = $@" | |
| 683 | - SELECT | |
| 684 | - temp.md as StoreId, | |
| 685 | - item.qt2 as ItemType, | |
| 686 | - COALESCE(SUM(temp.tkje), 0) as StoreRefundAmount | |
| 687 | - FROM ( | |
| 688 | - SELECT refund_mx.px, refund_mx.tkje, refund.md | |
| 689 | - FROM lq_hytk_mx refund_mx | |
| 690 | - INNER JOIN lq_hytk_hytk refund ON refund_mx.F_RefundInfoId = refund.F_Id | |
| 691 | - WHERE refund_mx.F_IsEffective = 1 | |
| 692 | - AND refund.F_IsEffective = 1 | |
| 693 | - AND refund.md IN ('{storeIdsStr}') | |
| 694 | - AND refund.tksj >= '{startDate:yyyy-MM-dd} 00:00:00' | |
| 695 | - AND refund.tksj < '{endDate.AddDays(1):yyyy-MM-dd} 00:00:00' | |
| 696 | - ) temp | |
| 697 | - INNER JOIN lq_xmzl item ON temp.px = item.F_Id | |
| 698 | - WHERE item.F_IsEffective = 1 | |
| 699 | - AND item.qt2 IN ('生美', '科美', '医美') | |
| 700 | - GROUP BY temp.md, item.qt2"; | |
| 701 | - | |
| 702 | - var refundData = await _db.Ado.SqlQueryAsync<dynamic>(refundSql); | |
| 703 | - | |
| 704 | - // 2.5 从储扣明细表统计储扣金额,按门店+品项分类分组 | |
| 705 | - // 重要:储扣金额从储扣明细表(lq_kd_deductinfo)的 F_Amount 获取,按品项分类统计 | |
| 706 | - var deductSql = $@" | |
| 707 | - SELECT | |
| 708 | - billing.djmd as StoreId, | |
| 709 | - item.qt2 as ItemType, | |
| 710 | - COALESCE(SUM(deduct.F_Amount), 0) as StoreDeductAmount | |
| 711 | - FROM lq_kd_deductinfo deduct | |
| 712 | - INNER JOIN lq_kd_kdjlb billing ON deduct.F_BillingId = billing.F_Id | |
| 713 | - INNER JOIN lq_xmzl item ON deduct.F_ItemId = item.F_Id | |
| 714 | - WHERE deduct.F_IsEffective = 1 | |
| 715 | - AND billing.F_IsEffective = 1 | |
| 716 | - AND item.F_IsEffective = 1 | |
| 717 | - AND billing.djmd IN ('{storeIdsStr}') | |
| 718 | - AND billing.kdrq >= '{startDate:yyyy-MM-dd} 00:00:00' | |
| 719 | - AND billing.kdrq < '{endDate.AddDays(1):yyyy-MM-dd} 00:00:00' | |
| 720 | - AND item.qt2 IN ('生美', '科美', '医美') | |
| 721 | - GROUP BY billing.djmd, item.qt2"; | |
| 722 | - | |
| 723 | - var deductData = await _db.Ado.SqlQueryAsync<dynamic>(deductSql); | |
| 724 | - | |
| 725 | - // 2.6 根据品项分类和目标表分配业绩到对应部门 | |
| 726 | - // 分配规则: | |
| 727 | - // - 生美品项 → 分配给教育部门(F_EducationDepartment) | |
| 728 | - // - 科美品项 → 分配给科技部门(F_TechDepartment) | |
| 729 | - // - 医美品项 → 分配给大项目部门(F_MajorProjectDepartment) | |
| 730 | - // 注意:一个门店可能同时属于多个部门,但每个品项的业绩只分配给对应的一个部门 | |
| 731 | - foreach (var billing in billingData ?? Enumerable.Empty<dynamic>()) | |
| 732 | - { | |
| 733 | - var storeId = billing?.StoreId?.ToString(); | |
| 734 | - var itemType = billing?.ItemType?.ToString(); | |
| 735 | - var storeAmount = billing?.StoreAmount != null ? Convert.ToDecimal(billing.StoreAmount) : 0m; | |
| 736 | 673 | |
| 737 | - if (string.IsNullOrEmpty(storeId) || string.IsNullOrEmpty(itemType) || storeAmount <= 0 || !targetDict.ContainsKey(storeId)) | |
| 738 | - continue; | |
| 674 | + // 品项类型列表 | |
| 675 | + var itemTypes = new[] { "生美", "科美", "医美" }; | |
| 739 | 676 | |
| 740 | - var target = targetDict[storeId]; | |
| 741 | - if (target == null) | |
| 742 | - continue; | |
| 743 | - | |
| 744 | - string targetDeptId = null; | |
| 745 | - | |
| 746 | - // 根据品项分类,分配到对应的部门 | |
| 677 | + // 循环处理每个品项类型 | |
| 678 | + foreach (var itemType in itemTypes) | |
| 679 | + { | |
| 680 | + // 确定该品项类型对应的部门字段 | |
| 681 | + string deptField = ""; | |
| 747 | 682 | if (itemType == "生美") |
| 748 | 683 | { |
| 749 | - // 生美品项 → 教育部门 | |
| 750 | - targetDeptId = target.F_EducationDepartment?.ToString(); | |
| 684 | + deptField = "F_EducationDepartment"; | |
| 751 | 685 | } |
| 752 | 686 | else if (itemType == "科美") |
| 753 | 687 | { |
| 754 | - // 科美品项 → 科技部门 | |
| 755 | - targetDeptId = target.F_TechDepartment?.ToString(); | |
| 688 | + deptField = "F_TechDepartment"; | |
| 756 | 689 | } |
| 757 | 690 | else if (itemType == "医美") |
| 758 | 691 | { |
| 759 | - // 医美品项 → 大项目部门 | |
| 760 | - targetDeptId = target.F_MajorProjectDepartment?.ToString(); | |
| 692 | + deptField = "F_MajorProjectDepartment"; | |
| 761 | 693 | } |
| 762 | 694 | |
| 763 | - // 只分配给在查询列表中的部门,避免重复统计 | |
| 764 | - if (!string.IsNullOrEmpty(targetDeptId) && departmentDict.ContainsKey(targetDeptId)) | |
| 765 | - { | |
| 766 | - departmentDict[targetDeptId].BillingPerformance += storeAmount; | |
| 767 | - } | |
| 768 | - } | |
| 695 | + // 2.3.1 查询开单业绩(按品项类型) | |
| 696 | + var billingSql = $@" | |
| 697 | + SELECT | |
| 698 | + target.{deptField} as TargetDeptId, | |
| 699 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as StoreAmount | |
| 700 | + FROM lq_kd_pxmx pxmx | |
| 701 | + INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 702 | + INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 703 | + INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId AND target.F_Month = '{month}' | |
| 704 | + WHERE pxmx.F_IsEffective = 1 | |
| 705 | + AND billing.F_IsEffective = 1 | |
| 706 | + AND item.F_IsEffective = 1 | |
| 707 | + AND item.qt2 = '{itemType}' | |
| 708 | + AND billing.djmd IN ('{storeIdsStr}') | |
| 709 | + AND billing.kdrq >= '{startDate.ToString("yyyy-MM-dd")} 00:00:00' | |
| 710 | + AND billing.kdrq < '{endDate.AddDays(1).ToString("yyyy-MM-dd")} 00:00:00' | |
| 711 | + GROUP BY target.{deptField}"; | |
| 769 | 712 | |
| 770 | - // 2.6.1 减去储扣金额 | |
| 771 | - foreach (var deduct in deductData ?? Enumerable.Empty<dynamic>()) | |
| 772 | - { | |
| 773 | - var storeId = deduct?.StoreId?.ToString(); | |
| 774 | - var itemType = deduct?.ItemType?.ToString(); | |
| 775 | - var storeDeductAmount = deduct?.StoreDeductAmount != null ? Convert.ToDecimal(deduct.StoreDeductAmount) : 0m; | |
| 713 | + var billingData = await _db.Ado.SqlQueryAsync<dynamic>(billingSql); | |
| 776 | 714 | |
| 777 | - if (string.IsNullOrEmpty(storeId) || string.IsNullOrEmpty(itemType) || storeDeductAmount <= 0 || !targetDict.ContainsKey(storeId)) | |
| 778 | - continue; | |
| 779 | - | |
| 780 | - var target = targetDict[storeId]; | |
| 781 | - if (target == null) | |
| 782 | - continue; | |
| 715 | + // 分配开单业绩到对应部门 | |
| 716 | + foreach (var billing in billingData ?? Enumerable.Empty<dynamic>()) | |
| 717 | + { | |
| 718 | + var storeAmount = billing?.StoreAmount != null ? Convert.ToDecimal(billing.StoreAmount) : 0m; | |
| 719 | + var targetDeptId = billing?.TargetDeptId?.ToString(); | |
| 783 | 720 | |
| 784 | - string targetDeptId = null; | |
| 721 | + if (storeAmount <= 0 || string.IsNullOrEmpty(targetDeptId)) | |
| 722 | + continue; | |
| 785 | 723 | |
| 786 | - // 根据品项分类,从对应的部门减去储扣金额 | |
| 787 | - if (itemType == "生美") | |
| 788 | - { | |
| 789 | - // 生美品项储扣 → 从教育部门减去 | |
| 790 | - targetDeptId = target.F_EducationDepartment?.ToString(); | |
| 791 | - } | |
| 792 | - else if (itemType == "科美") | |
| 793 | - { | |
| 794 | - // 科美品项储扣 → 从科技部门减去 | |
| 795 | - targetDeptId = target.F_TechDepartment?.ToString(); | |
| 796 | - } | |
| 797 | - else if (itemType == "医美") | |
| 798 | - { | |
| 799 | - // 医美品项储扣 → 从大项目部门减去 | |
| 800 | - targetDeptId = target.F_MajorProjectDepartment?.ToString(); | |
| 724 | + // 只分配给在查询列表中的部门,避免重复统计 | |
| 725 | + if (departmentDict.ContainsKey(targetDeptId)) | |
| 726 | + { | |
| 727 | + departmentDict[targetDeptId].BillingPerformance += storeAmount; | |
| 728 | + } | |
| 801 | 729 | } |
| 802 | 730 | |
| 803 | - // 只从在查询列表中的部门减去,避免重复统计 | |
| 804 | - if (!string.IsNullOrEmpty(targetDeptId) && departmentDict.ContainsKey(targetDeptId)) | |
| 731 | + // 2.3.2 查询退卡业绩(按品项类型) | |
| 732 | + var refundSql = $@" | |
| 733 | + SELECT | |
| 734 | + target.{deptField} as TargetDeptId, | |
| 735 | + COALESCE(SUM(refund_mx.tkje), 0) as StoreRefundAmount | |
| 736 | + FROM lq_hytk_mx refund_mx | |
| 737 | + INNER JOIN lq_hytk_hytk refund ON refund_mx.F_RefundInfoId = refund.F_Id | |
| 738 | + INNER JOIN lq_xmzl item ON refund_mx.px = item.F_Id | |
| 739 | + INNER JOIN lq_md_target target ON refund.md = target.F_StoreId AND target.F_Month = '{month}' | |
| 740 | + WHERE refund_mx.F_IsEffective = 1 | |
| 741 | + AND refund.F_IsEffective = 1 | |
| 742 | + AND item.F_IsEffective = 1 | |
| 743 | + AND item.qt2 = '{itemType}' | |
| 744 | + AND refund.md IN ('{storeIdsStr}') | |
| 745 | + AND refund.tksj >= '{startDate.ToString("yyyy-MM-dd")} 00:00:00' | |
| 746 | + AND refund.tksj < '{endDate.AddDays(1).ToString("yyyy-MM-dd")} 00:00:00' | |
| 747 | + GROUP BY target.{deptField}"; | |
| 748 | + | |
| 749 | + var refundData = await _db.Ado.SqlQueryAsync<dynamic>(refundSql); | |
| 750 | + | |
| 751 | + // 分配退卡业绩到对应部门 | |
| 752 | + foreach (var refund in refundData ?? Enumerable.Empty<dynamic>()) | |
| 805 | 753 | { |
| 806 | - departmentDict[targetDeptId].BillingPerformance -= storeDeductAmount; | |
| 807 | - } | |
| 808 | - } | |
| 754 | + var storeRefundAmount = refund?.StoreRefundAmount != null ? Convert.ToDecimal(refund.StoreRefundAmount) : 0m; | |
| 755 | + var targetDeptId = refund?.TargetDeptId?.ToString(); | |
| 809 | 756 | |
| 810 | - // 2.7 根据品项分类和目标表分配退卡业绩到对应部门 | |
| 811 | - // 分配规则与开单业绩相同: | |
| 812 | - // - 生美品项退卡 → 分配给教育部门 | |
| 813 | - // - 科美品项退卡 → 分配给科技部门 | |
| 814 | - // - 医美品项退卡 → 分配给大项目部门 | |
| 815 | - foreach (var refund in refundData ?? Enumerable.Empty<dynamic>()) | |
| 816 | - { | |
| 817 | - var storeId = refund?.StoreId?.ToString(); | |
| 818 | - var itemType = refund?.ItemType?.ToString(); | |
| 819 | - var storeRefundAmount = refund?.StoreRefundAmount != null ? Convert.ToDecimal(refund.StoreRefundAmount) : 0m; | |
| 757 | + if (storeRefundAmount <= 0 || string.IsNullOrEmpty(targetDeptId)) | |
| 758 | + continue; | |
| 820 | 759 | |
| 821 | - if (string.IsNullOrEmpty(storeId) || string.IsNullOrEmpty(itemType) || storeRefundAmount <= 0 || !targetDict.ContainsKey(storeId)) | |
| 822 | - continue; | |
| 760 | + // 只分配给在查询列表中的部门,避免重复统计 | |
| 761 | + if (departmentDict.ContainsKey(targetDeptId)) | |
| 762 | + { | |
| 763 | + departmentDict[targetDeptId].RefundPerformance += storeRefundAmount; | |
| 764 | + } | |
| 765 | + } | |
| 823 | 766 | |
| 824 | - var target = targetDict[storeId]; | |
| 825 | - if (target == null) | |
| 826 | - continue; | |
| 767 | + // 2.3.3 查询储扣金额(按品项类型) | |
| 768 | + var deductSql = $@" | |
| 769 | + SELECT | |
| 770 | + target.{deptField} as TargetDeptId, | |
| 771 | + COALESCE(SUM(deduct.F_Amount), 0) as StoreDeductAmount | |
| 772 | + FROM lq_kd_deductinfo deduct | |
| 773 | + INNER JOIN lq_kd_kdjlb billing ON deduct.F_BillingId = billing.F_Id | |
| 774 | + INNER JOIN lq_xmzl item ON deduct.F_ItemId = item.F_Id | |
| 775 | + INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId AND target.F_Month = '{month}' | |
| 776 | + WHERE deduct.F_IsEffective = 1 | |
| 777 | + AND billing.F_IsEffective = 1 | |
| 778 | + AND item.F_IsEffective = 1 | |
| 779 | + AND item.qt2 = '{itemType}' | |
| 780 | + AND billing.djmd IN ('{storeIdsStr}') | |
| 781 | + AND billing.kdrq >= '{startDate.ToString("yyyy-MM-dd")} 00:00:00' | |
| 782 | + AND billing.kdrq < '{endDate.AddDays(1).ToString("yyyy-MM-dd")} 00:00:00' | |
| 783 | + GROUP BY target.{deptField}"; | |
| 827 | 784 | |
| 828 | - string targetDeptId = null; | |
| 785 | + var deductData = await _db.Ado.SqlQueryAsync<dynamic>(deductSql); | |
| 829 | 786 | |
| 830 | - // 根据品项分类,分配到对应的部门 | |
| 831 | - if (itemType == "生美") | |
| 832 | - { | |
| 833 | - // 生美品项退卡 → 教育部门 | |
| 834 | - targetDeptId = target.F_EducationDepartment?.ToString(); | |
| 835 | - } | |
| 836 | - else if (itemType == "科美") | |
| 837 | - { | |
| 838 | - // 科美品项退卡 → 科技部门 | |
| 839 | - targetDeptId = target.F_TechDepartment?.ToString(); | |
| 840 | - } | |
| 841 | - else if (itemType == "医美") | |
| 787 | + // 分配储扣金额到对应部门 | |
| 788 | + foreach (var deduct in deductData ?? Enumerable.Empty<dynamic>()) | |
| 842 | 789 | { |
| 843 | - // 医美品项退卡 → 大项目部门 | |
| 844 | - targetDeptId = target.F_MajorProjectDepartment?.ToString(); | |
| 845 | - } | |
| 790 | + var storeDeductAmount = deduct?.StoreDeductAmount != null ? Convert.ToDecimal(deduct.StoreDeductAmount) : 0m; | |
| 791 | + var targetDeptId = deduct?.TargetDeptId?.ToString(); | |
| 846 | 792 | |
| 847 | - // 只分配给在查询列表中的部门,避免重复统计 | |
| 848 | - if (!string.IsNullOrEmpty(targetDeptId) && departmentDict.ContainsKey(targetDeptId)) | |
| 849 | - { | |
| 850 | - departmentDict[targetDeptId].RefundPerformance += storeRefundAmount; | |
| 793 | + if (storeDeductAmount <= 0 || string.IsNullOrEmpty(targetDeptId)) | |
| 794 | + continue; | |
| 795 | + | |
| 796 | + // 只累加到在查询列表中的部门,避免重复统计 | |
| 797 | + if (departmentDict.ContainsKey(targetDeptId)) | |
| 798 | + { | |
| 799 | + departmentDict[targetDeptId].DeductAmount += storeDeductAmount; | |
| 800 | + } | |
| 851 | 801 | } |
| 852 | 802 | } |
| 853 | 803 | |
| 854 | 804 | // 第三步:计算实际完成业绩和完成率,并保留两位小数 |
| 855 | 805 | foreach (var dept in departmentDict.Values) |
| 856 | 806 | { |
| 857 | - dept.BillingPerformance = decimal.Round(dept.BillingPerformance, 2); | |
| 858 | - dept.RefundPerformance = decimal.Round(dept.RefundPerformance, 2); | |
| 859 | - dept.ActualPerformance = dept.BillingPerformance; // 开单业绩 | |
| 860 | - var actualCompletedPerformance = dept.BillingPerformance - dept.RefundPerformance; // 实际完成业绩 | |
| 807 | + dept.BillingPerformance = decimal.Round(dept.BillingPerformance, 2); // 开单业绩(原始,不减去储扣) | |
| 808 | + dept.RefundPerformance = decimal.Round(dept.RefundPerformance, 2); // 退卡业绩 | |
| 809 | + dept.DeductAmount = decimal.Round(dept.DeductAmount, 2); // 储扣金额,保留两位小数 | |
| 810 | + dept.ActualPerformance = dept.BillingPerformance; // 开单业绩(与BillingPerformance相同,用于兼容) | |
| 811 | + // 实际完成业绩 = 开单业绩 - 储扣金额 - 退卡金额 | |
| 812 | + var actualCompletedPerformance = dept.BillingPerformance - dept.DeductAmount - dept.RefundPerformance; | |
| 861 | 813 | dept.CompletedPerformance = decimal.Round(actualCompletedPerformance, 2); // 实际完成业绩,保留两位小数 |
| 862 | 814 | |
| 863 | - var completionRate = dept.TargetPerformance > 0 | |
| 864 | - ? (actualCompletedPerformance / dept.TargetPerformance * 100m) | |
| 865 | - : 0m; | |
| 866 | - dept.CompletionRate = decimal.Round(completionRate, 2); | |
| 815 | + var completionRate = dept.TargetPerformance > 0 | |
| 816 | + ? (actualCompletedPerformance / dept.TargetPerformance * 100m) | |
| 817 | + : 0m; | |
| 818 | + dept.CompletionRate = decimal.Round(completionRate, 2); | |
| 867 | 819 | } |
| 868 | 820 | |
| 869 | 821 | var outputList = departmentDict.Values |
| ... | ... | @@ -1174,12 +1126,13 @@ namespace NCC.Extend |
| 1174 | 1126 | } |
| 1175 | 1127 | |
| 1176 | 1128 | // SQL查询:统计科技部老师的消耗业绩、见客数、项目数 |
| 1129 | + // 注意:GROUP BY 中移除了 user.F_RealName,避免同一老师ID因姓名不同产生重复记录 | |
| 1177 | 1130 | var consumeSql = $@" |
| 1178 | 1131 | SELECT |
| 1179 | 1132 | techDept.F_Id as TechDepartmentId, |
| 1180 | 1133 | techDept.F_FullName as TechDepartmentName, |
| 1181 | 1134 | consume.kjbls as TeacherId, |
| 1182 | - user.F_RealName as TeacherName, | |
| 1135 | + MAX(user.F_RealName) as TeacherName, | |
| 1183 | 1136 | COUNT(DISTINCT hyhk.hy) as CustomerCount, |
| 1184 | 1137 | SUM(consume.F_hdpxNumber) as ConsumeProjectCount, |
| 1185 | 1138 | SUM(consume.kjblsyj) as ConsumeAchievement |
| ... | ... | @@ -1194,7 +1147,7 @@ namespace NCC.Extend |
| 1194 | 1147 | AND DATE(hyhk.hksj) <= '{endDate:yyyy-MM-dd}' |
| 1195 | 1148 | {techFilter} |
| 1196 | 1149 | {teacherFilter} |
| 1197 | - GROUP BY techDept.F_Id, techDept.F_FullName, consume.kjbls, user.F_RealName"; | |
| 1150 | + GROUP BY techDept.F_Id, techDept.F_FullName, consume.kjbls"; | |
| 1198 | 1151 | |
| 1199 | 1152 | var consumeResult = await _db.Ado.SqlQueryAsync<dynamic>(consumeSql); |
| 1200 | 1153 | |
| ... | ... | @@ -1202,48 +1155,257 @@ namespace NCC.Extend |
| 1202 | 1155 | var orderSql = $@" |
| 1203 | 1156 | SELECT |
| 1204 | 1157 | techDept.F_Id as TechDepartmentId, |
| 1158 | + techDept.F_FullName as TechDepartmentName, | |
| 1205 | 1159 | ord.kjbls as TeacherId, |
| 1160 | + MAX(user.F_RealName) as TeacherName, | |
| 1206 | 1161 | SUM(ord.kjblsyj) as OrderAchievement |
| 1207 | 1162 | FROM lq_kd_kjbsyj ord |
| 1208 | 1163 | INNER JOIN lq_kd_kdjlb kdjlb ON ord.glkdbh = kdjlb.F_Id |
| 1209 | 1164 | INNER JOIN lq_mdxx store ON kdjlb.djmd = store.F_Id |
| 1210 | 1165 | LEFT JOIN base_organize techDept ON store.kjb = techDept.F_Id |
| 1166 | + LEFT JOIN BASE_USER user ON ord.kjbls = user.F_Id | |
| 1211 | 1167 | WHERE ord.F_IsEffective = 1 |
| 1212 | 1168 | AND kdjlb.F_IsEffective = 1 |
| 1213 | 1169 | AND DATE(kdjlb.kdrq) >= '{startDate:yyyy-MM-dd}' |
| 1214 | 1170 | AND DATE(kdjlb.kdrq) <= '{endDate:yyyy-MM-dd}' |
| 1215 | 1171 | {techFilter} |
| 1216 | 1172 | {teacherFilterForOrder} |
| 1217 | - GROUP BY techDept.F_Id, ord.kjbls"; | |
| 1173 | + GROUP BY techDept.F_Id, techDept.F_FullName, ord.kjbls"; | |
| 1218 | 1174 | |
| 1219 | 1175 | var orderResult = await _db.Ado.SqlQueryAsync<dynamic>(orderSql); |
| 1220 | 1176 | |
| 1221 | - // 合并数据 | |
| 1177 | + // 合并数据:按员工ID汇总,避免同一员工在多个科技部重复出现 | |
| 1178 | + // 使用 TeacherId 作为唯一键,汇总所有科技部的数据 | |
| 1222 | 1179 | var teacherDict = new Dictionary<string, TechTeacherDailyStatisticsOutput>(); |
| 1180 | + // 记录每个员工在各个科技部的消耗业绩,用于确定主要科技部 | |
| 1181 | + var teacherDeptConsumePerformance = new Dictionary<string, Dictionary<string, decimal>>(); | |
| 1182 | + // 记录每个员工在各个科技部的开单业绩,用于确定主要科技部(当消耗业绩为0时) | |
| 1183 | + var teacherDeptOrderPerformance = new Dictionary<string, Dictionary<string, decimal>>(); | |
| 1223 | 1184 | |
| 1185 | + // 第一步:处理消耗数据,按员工ID汇总 | |
| 1224 | 1186 | foreach (var item in consumeResult ?? Enumerable.Empty<dynamic>()) |
| 1225 | 1187 | { |
| 1226 | - var key = $"{item.TechDepartmentId}_{item.TeacherId}"; | |
| 1227 | - teacherDict[key] = new TechTeacherDailyStatisticsOutput | |
| 1188 | + var teacherId = item.TeacherId?.ToString(); | |
| 1189 | + var techDeptId = item.TechDepartmentId?.ToString(); | |
| 1190 | + var techDeptName = item.TechDepartmentName?.ToString(); | |
| 1191 | + var consumeAchievement = Convert.ToDecimal(item.ConsumeAchievement); | |
| 1192 | + | |
| 1193 | + if (string.IsNullOrEmpty(teacherId)) | |
| 1194 | + continue; | |
| 1195 | + | |
| 1196 | + // 记录员工在各科技部的消耗业绩(累加,因为同一员工在同一科技部可能有多个门店的数据) | |
| 1197 | + if (!teacherDeptConsumePerformance.ContainsKey(teacherId)) | |
| 1228 | 1198 | { |
| 1229 | - TechDepartmentId = item.TechDepartmentId?.ToString(), | |
| 1230 | - TechDepartmentName = item.TechDepartmentName?.ToString(), | |
| 1231 | - TeacherId = item.TeacherId?.ToString(), | |
| 1232 | - TeacherName = item.TeacherName?.ToString(), | |
| 1233 | - CustomerCount = Convert.ToInt32(item.CustomerCount), | |
| 1234 | - ConsumeProjectCount = Convert.ToInt32(item.ConsumeProjectCount), | |
| 1235 | - ConsumeAchievement = Convert.ToDecimal(item.ConsumeAchievement), | |
| 1236 | - OrderAchievement = 0 | |
| 1237 | - }; | |
| 1199 | + teacherDeptConsumePerformance[teacherId] = new Dictionary<string, decimal>(); | |
| 1200 | + } | |
| 1201 | + if (!string.IsNullOrEmpty(techDeptId)) | |
| 1202 | + { | |
| 1203 | + if (!teacherDeptConsumePerformance[teacherId].ContainsKey(techDeptId)) | |
| 1204 | + { | |
| 1205 | + teacherDeptConsumePerformance[teacherId][techDeptId] = 0; | |
| 1206 | + } | |
| 1207 | + teacherDeptConsumePerformance[teacherId][techDeptId] += consumeAchievement; | |
| 1208 | + } | |
| 1209 | + | |
| 1210 | + // 按员工ID汇总数据 | |
| 1211 | + if (!teacherDict.ContainsKey(teacherId)) | |
| 1212 | + { | |
| 1213 | + teacherDict[teacherId] = new TechTeacherDailyStatisticsOutput | |
| 1214 | + { | |
| 1215 | + TechDepartmentId = techDeptId, | |
| 1216 | + TechDepartmentName = techDeptName, | |
| 1217 | + TeacherId = teacherId, | |
| 1218 | + TeacherName = item.TeacherName?.ToString(), | |
| 1219 | + CustomerCount = 0, | |
| 1220 | + ConsumeProjectCount = 0, | |
| 1221 | + ConsumeAchievement = 0, | |
| 1222 | + OrderAchievement = 0 | |
| 1223 | + }; | |
| 1224 | + } | |
| 1225 | + | |
| 1226 | + // 汇总数据(累加项目数和业绩,见客数后面统一重新计算去重) | |
| 1227 | + var teacher = teacherDict[teacherId]; | |
| 1228 | + teacher.ConsumeProjectCount += Convert.ToInt32(item.ConsumeProjectCount); | |
| 1229 | + teacher.ConsumeAchievement += consumeAchievement; | |
| 1238 | 1230 | } |
| 1239 | 1231 | |
| 1240 | - // 合并开单业绩 | |
| 1232 | + // 第二步:处理开单业绩,按员工ID汇总 | |
| 1241 | 1233 | foreach (var item in orderResult ?? Enumerable.Empty<dynamic>()) |
| 1242 | 1234 | { |
| 1243 | - var key = $"{item.TechDepartmentId}_{item.TeacherId}"; | |
| 1244 | - if (teacherDict.ContainsKey(key)) | |
| 1235 | + var teacherId = item.TeacherId?.ToString(); | |
| 1236 | + var techDeptId = item.TechDepartmentId?.ToString(); | |
| 1237 | + var techDeptName = item.TechDepartmentName?.ToString(); | |
| 1238 | + var orderAchievement = Convert.ToDecimal(item.OrderAchievement); | |
| 1239 | + | |
| 1240 | + if (string.IsNullOrEmpty(teacherId)) | |
| 1241 | + continue; | |
| 1242 | + | |
| 1243 | + // 记录员工在各科技部的开单业绩(累加,因为同一员工在同一科技部可能有多个门店的数据) | |
| 1244 | + if (!teacherDeptOrderPerformance.ContainsKey(teacherId)) | |
| 1245 | 1245 | { |
| 1246 | - teacherDict[key].OrderAchievement = Convert.ToDecimal(item.OrderAchievement); | |
| 1246 | + teacherDeptOrderPerformance[teacherId] = new Dictionary<string, decimal>(); | |
| 1247 | + } | |
| 1248 | + if (!string.IsNullOrEmpty(techDeptId)) | |
| 1249 | + { | |
| 1250 | + if (!teacherDeptOrderPerformance[teacherId].ContainsKey(techDeptId)) | |
| 1251 | + { | |
| 1252 | + teacherDeptOrderPerformance[teacherId][techDeptId] = 0; | |
| 1253 | + } | |
| 1254 | + teacherDeptOrderPerformance[teacherId][techDeptId] += orderAchievement; | |
| 1255 | + } | |
| 1256 | + | |
| 1257 | + if (!teacherDict.ContainsKey(teacherId)) | |
| 1258 | + { | |
| 1259 | + // 如果消耗数据中没有,但开单数据中有,需要创建记录 | |
| 1260 | + teacherDict[teacherId] = new TechTeacherDailyStatisticsOutput | |
| 1261 | + { | |
| 1262 | + TechDepartmentId = techDeptId, | |
| 1263 | + TechDepartmentName = techDeptName, | |
| 1264 | + TeacherId = teacherId, | |
| 1265 | + TeacherName = item.TeacherName?.ToString(), | |
| 1266 | + CustomerCount = 0, | |
| 1267 | + ConsumeProjectCount = 0, | |
| 1268 | + ConsumeAchievement = 0, | |
| 1269 | + OrderAchievement = 0 | |
| 1270 | + }; | |
| 1271 | + } | |
| 1272 | + else | |
| 1273 | + { | |
| 1274 | + // 如果消耗数据中有,但名称为空,尝试从开单数据中获取 | |
| 1275 | + if (string.IsNullOrEmpty(teacherDict[teacherId].TeacherName)) | |
| 1276 | + { | |
| 1277 | + teacherDict[teacherId].TeacherName = item.TeacherName?.ToString(); | |
| 1278 | + } | |
| 1279 | + } | |
| 1280 | + | |
| 1281 | + teacherDict[teacherId].OrderAchievement += orderAchievement; | |
| 1282 | + } | |
| 1283 | + | |
| 1284 | + // 第三步:确定每个员工的主要科技部 | |
| 1285 | + // 优先按消耗业绩最多的科技部,如果消耗业绩为0,则按开单业绩最多的科技部 | |
| 1286 | + foreach (var teacherId in teacherDict.Keys.ToList()) | |
| 1287 | + { | |
| 1288 | + var teacher = teacherDict[teacherId]; | |
| 1289 | + string mainDeptId = null; | |
| 1290 | + string mainDeptName = null; | |
| 1291 | + | |
| 1292 | + // 优先按消耗业绩确定主要科技部 | |
| 1293 | + if (teacherDeptConsumePerformance.ContainsKey(teacherId) && teacherDeptConsumePerformance[teacherId].Any()) | |
| 1294 | + { | |
| 1295 | + var mainDept = teacherDeptConsumePerformance[teacherId] | |
| 1296 | + .OrderByDescending(x => x.Value) | |
| 1297 | + .FirstOrDefault(); | |
| 1298 | + | |
| 1299 | + if (!string.IsNullOrEmpty(mainDept.Key) && mainDept.Value > 0) | |
| 1300 | + { | |
| 1301 | + mainDeptId = mainDept.Key; | |
| 1302 | + // 从消耗数据中获取该科技部的名称 | |
| 1303 | + var mainDeptData = consumeResult?.FirstOrDefault(x => | |
| 1304 | + x.TeacherId?.ToString() == teacherId && | |
| 1305 | + x.TechDepartmentId?.ToString() == mainDeptId); | |
| 1306 | + | |
| 1307 | + if (mainDeptData != null) | |
| 1308 | + { | |
| 1309 | + mainDeptName = mainDeptData.TechDepartmentName?.ToString(); | |
| 1310 | + } | |
| 1311 | + } | |
| 1312 | + } | |
| 1313 | + | |
| 1314 | + // 如果消耗业绩为0或没有,则按开单业绩确定主要科技部 | |
| 1315 | + if (string.IsNullOrEmpty(mainDeptId) && teacherDeptOrderPerformance.ContainsKey(teacherId) && teacherDeptOrderPerformance[teacherId].Any()) | |
| 1316 | + { | |
| 1317 | + var mainDept = teacherDeptOrderPerformance[teacherId] | |
| 1318 | + .OrderByDescending(x => x.Value) | |
| 1319 | + .FirstOrDefault(); | |
| 1320 | + | |
| 1321 | + if (!string.IsNullOrEmpty(mainDept.Key) && mainDept.Value > 0) | |
| 1322 | + { | |
| 1323 | + mainDeptId = mainDept.Key; | |
| 1324 | + // 从开单数据中获取该科技部的名称 | |
| 1325 | + var mainDeptData = orderResult?.FirstOrDefault(x => | |
| 1326 | + x.TeacherId?.ToString() == teacherId && | |
| 1327 | + x.TechDepartmentId?.ToString() == mainDeptId); | |
| 1328 | + | |
| 1329 | + if (mainDeptData != null) | |
| 1330 | + { | |
| 1331 | + mainDeptName = mainDeptData.TechDepartmentName?.ToString(); | |
| 1332 | + } | |
| 1333 | + } | |
| 1334 | + } | |
| 1335 | + | |
| 1336 | + // 如果仍然没有确定主要科技部,但已有科技部ID和名称,保持原样 | |
| 1337 | + if (!string.IsNullOrEmpty(mainDeptId)) | |
| 1338 | + { | |
| 1339 | + teacher.TechDepartmentId = mainDeptId; | |
| 1340 | + if (!string.IsNullOrEmpty(mainDeptName)) | |
| 1341 | + { | |
| 1342 | + teacher.TechDepartmentName = mainDeptName; | |
| 1343 | + } | |
| 1344 | + } | |
| 1345 | + else if (string.IsNullOrEmpty(teacher.TechDepartmentName) && !string.IsNullOrEmpty(teacher.TechDepartmentId)) | |
| 1346 | + { | |
| 1347 | + // 如果仍然没有科技部名称,尝试从开单数据中获取 | |
| 1348 | + var orderDeptData = orderResult?.FirstOrDefault(x => | |
| 1349 | + x.TeacherId?.ToString() == teacherId && | |
| 1350 | + x.TechDepartmentId?.ToString() == teacher.TechDepartmentId); | |
| 1351 | + | |
| 1352 | + if (orderDeptData != null && !string.IsNullOrEmpty(orderDeptData.TechDepartmentName?.ToString())) | |
| 1353 | + { | |
| 1354 | + teacher.TechDepartmentName = orderDeptData.TechDepartmentName?.ToString(); | |
| 1355 | + } | |
| 1356 | + } | |
| 1357 | + } | |
| 1358 | + | |
| 1359 | + // 第四步:重新计算见客数(去重,因为同一个客户可能在多个科技部被统计) | |
| 1360 | + // 由于已经汇总了数据,这里需要重新查询去重后的见客数 | |
| 1361 | + var teacherIds = teacherDict.Keys.ToList(); | |
| 1362 | + if (teacherIds.Any()) | |
| 1363 | + { | |
| 1364 | + var teacherIdsStr = string.Join("','", teacherIds); | |
| 1365 | + var customerCountSql = $@" | |
| 1366 | + SELECT | |
| 1367 | + consume.kjbls as TeacherId, | |
| 1368 | + COUNT(DISTINCT hyhk.hy) as CustomerCount | |
| 1369 | + FROM lq_xh_kjbsyj consume | |
| 1370 | + INNER JOIN lq_xh_hyhk hyhk ON consume.glkdbh = hyhk.F_Id | |
| 1371 | + WHERE consume.F_IsEffective = 1 | |
| 1372 | + AND hyhk.F_IsEffective = 1 | |
| 1373 | + AND DATE(hyhk.hksj) >= '{startDate:yyyy-MM-dd}' | |
| 1374 | + AND DATE(hyhk.hksj) <= '{endDate:yyyy-MM-dd}' | |
| 1375 | + AND consume.kjbls IN ('{teacherIdsStr}') | |
| 1376 | + GROUP BY consume.kjbls"; | |
| 1377 | + | |
| 1378 | + var customerCountResult = await _db.Ado.SqlQueryAsync<dynamic>(customerCountSql); | |
| 1379 | + foreach (var item in customerCountResult ?? Enumerable.Empty<dynamic>()) | |
| 1380 | + { | |
| 1381 | + var teacherId = item.TeacherId?.ToString(); | |
| 1382 | + if (!string.IsNullOrEmpty(teacherId) && teacherDict.ContainsKey(teacherId)) | |
| 1383 | + { | |
| 1384 | + teacherDict[teacherId].CustomerCount = Convert.ToInt32(item.CustomerCount); | |
| 1385 | + } | |
| 1386 | + } | |
| 1387 | + | |
| 1388 | + // 第五步:统一查询所有用户名称,确保所有用户都能获取到名称 | |
| 1389 | + var teacherNamesSql = $@" | |
| 1390 | + SELECT | |
| 1391 | + F_Id as TeacherId, | |
| 1392 | + F_RealName as TeacherName | |
| 1393 | + FROM BASE_USER | |
| 1394 | + WHERE F_Id IN ('{teacherIdsStr}')"; | |
| 1395 | + | |
| 1396 | + var teacherNamesResult = await _db.Ado.SqlQueryAsync<dynamic>(teacherNamesSql); | |
| 1397 | + foreach (var item in teacherNamesResult ?? Enumerable.Empty<dynamic>()) | |
| 1398 | + { | |
| 1399 | + var teacherId = item.TeacherId?.ToString(); | |
| 1400 | + var teacherName = item.TeacherName?.ToString(); | |
| 1401 | + if (!string.IsNullOrEmpty(teacherId) && teacherDict.ContainsKey(teacherId)) | |
| 1402 | + { | |
| 1403 | + // 如果名称为空,则填充;如果已有名称,保持不变 | |
| 1404 | + if (string.IsNullOrEmpty(teacherDict[teacherId].TeacherName) && !string.IsNullOrEmpty(teacherName)) | |
| 1405 | + { | |
| 1406 | + teacherDict[teacherId].TeacherName = teacherName; | |
| 1407 | + } | |
| 1408 | + } | |
| 1247 | 1409 | } |
| 1248 | 1410 | } |
| 1249 | 1411 | |
| ... | ... | @@ -1583,6 +1745,8 @@ namespace NCC.Extend |
| 1583 | 1745 | } |
| 1584 | 1746 | #endregion |
| 1585 | 1747 | |
| 1748 | + | |
| 1749 | + | |
| 1586 | 1750 | #region 获取储值扣减金额统计 |
| 1587 | 1751 | /// <summary> |
| 1588 | 1752 | /// 获取储值扣减金额统计 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
| ... | ... | @@ -3071,7 +3071,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 3071 | 3071 | COALESCE(consume_stats.ConsumeAmount, 0) as ConsumeAmount, |
| 3072 | 3072 | COALESCE(consume_stats.HeadCount, 0) as HeadCount, |
| 3073 | 3073 | COALESCE(consume_stats.PersonCount, 0) as PersonCount, |
| 3074 | - COALESCE(consume_stats.ProjectCount, 0) as ProjectCount | |
| 3074 | + CAST(COALESCE(consume_stats.ProjectCount, 0) AS DECIMAL(18,2)) as ProjectCount | |
| 3075 | 3075 | |
| 3076 | 3076 | FROM BASE_USER u |
| 3077 | 3077 | LEFT JOIN lq_mdxx md ON u.F_MDID = md.F_Id |
| ... | ... | @@ -3135,7 +3135,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 3135 | 3135 | SUM(jksyj.jksyj) as ConsumeAmount, |
| 3136 | 3136 | COUNT(DISTINCT hyhk.hy) as HeadCount, |
| 3137 | 3137 | COUNT(DISTINCT CONCAT(jksyj.jkszh, '_', hyhk.hy, '_', DATE(hyhk.hksj))) as PersonCount, |
| 3138 | - SUM(jksyj.F_kdpxNumber) as ProjectCount | |
| 3138 | + CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount | |
| 3139 | 3139 | FROM lq_xh_jksyj jksyj |
| 3140 | 3140 | INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id |
| 3141 | 3141 | WHERE jksyj.jkszh IS NOT NULL |
| ... | ... | @@ -3211,5 +3211,203 @@ namespace NCC.Extend.LqKdKdjlb |
| 3211 | 3211 | } |
| 3212 | 3212 | } |
| 3213 | 3213 | #endregion |
| 3214 | + | |
| 3215 | + #region 获取开单品项明细记录列表 | |
| 3216 | + /// <summary> | |
| 3217 | + /// 获取开单品项明细记录列表(分页) | |
| 3218 | + /// </summary> | |
| 3219 | + /// <remarks> | |
| 3220 | + /// 查询开单品项明细记录,支持多条件筛选和分页查询。采用先分页后关联的查询方式,确保分页准确性和查询性能。 | |
| 3221 | + /// | |
| 3222 | + /// 示例请求: | |
| 3223 | + /// ```json | |
| 3224 | + /// { | |
| 3225 | + /// "currentPage": 1, | |
| 3226 | + /// "pageSize": 10, | |
| 3227 | + /// "sidx": "yjsj", | |
| 3228 | + /// "sort": "DESC", | |
| 3229 | + /// "Id": "品项明细ID", | |
| 3230 | + /// "BillingId": "开单ID", | |
| 3231 | + /// "StartBillingTime": "2025-01-01", | |
| 3232 | + /// "EndBillingTime": "2025-12-31", | |
| 3233 | + /// "ActivityId": "营销活动ID", | |
| 3234 | + /// "MemberId": "会员ID", | |
| 3235 | + /// "MemberName": "会员名称", | |
| 3236 | + /// "MemberPhone": "会员手机号", | |
| 3237 | + /// "ItemId": "品项ID", | |
| 3238 | + /// "ItemName": "品项名称", | |
| 3239 | + /// "ItemType": "品项类型", | |
| 3240 | + /// "SourceType": "来源类型" | |
| 3241 | + /// } | |
| 3242 | + /// ``` | |
| 3243 | + /// | |
| 3244 | + /// 查询参数说明: | |
| 3245 | + /// - currentPage: 当前页码(必填,从1开始) | |
| 3246 | + /// - pageSize: 每页数量(必填) | |
| 3247 | + /// - sidx: 排序字段(可选,默认:yjsj,支持:id、billingId、billingTime、itemName、actualPrice、projectNumber等) | |
| 3248 | + /// - sort: 排序方式(可选,默认:DESC,支持:ASC、DESC) | |
| 3249 | + /// - Id: 品项明细ID(可选,精确匹配) | |
| 3250 | + /// - BillingId: 开单ID(可选,精确匹配) | |
| 3251 | + /// - StartBillingTime: 开单时间开始(可选,格式:yyyy-MM-dd,与EndBillingTime同时使用) | |
| 3252 | + /// - EndBillingTime: 开单时间结束(可选,格式:yyyy-MM-dd,与StartBillingTime同时使用) | |
| 3253 | + /// - ActivityId: 营销活动ID(可选,精确匹配) | |
| 3254 | + /// - MemberId: 会员ID(可选,精确匹配) | |
| 3255 | + /// - MemberName: 会员名称(可选,模糊查询) | |
| 3256 | + /// - MemberPhone: 会员手机号(可选,精确匹配) | |
| 3257 | + /// - ItemId: 品项ID(可选,精确匹配) | |
| 3258 | + /// - ItemName: 品项名称(可选,模糊查询) | |
| 3259 | + /// - ItemType: 品项类型(可选,精确匹配,对应项目资料表的qt2字段) | |
| 3260 | + /// - SourceType: 来源类型(可选,精确匹配,如:购买、赠送、体验) | |
| 3261 | + /// | |
| 3262 | + /// 返回数据结构: | |
| 3263 | + /// ```json | |
| 3264 | + /// { | |
| 3265 | + /// "pagination": { | |
| 3266 | + /// "pageIndex": 1, | |
| 3267 | + /// "pageSize": 10, | |
| 3268 | + /// "total": 100 | |
| 3269 | + /// }, | |
| 3270 | + /// "list": [ | |
| 3271 | + /// { | |
| 3272 | + /// "id": "品项明细ID", | |
| 3273 | + /// "billingId": "开单ID", | |
| 3274 | + /// "billingTime": "2025-11-15T10:00:00", | |
| 3275 | + /// "activityName": "营销活动名称", | |
| 3276 | + /// "memberName": "会员名称", | |
| 3277 | + /// "memberPhone": "会员手机号", | |
| 3278 | + /// "itemName": "品项名称", | |
| 3279 | + /// "itemType": "品项类型", | |
| 3280 | + /// "actualPrice": 500.00, | |
| 3281 | + /// "projectNumber": 5.0, | |
| 3282 | + /// "sourceType": "购买", | |
| 3283 | + /// "remark": "备注" | |
| 3284 | + /// } | |
| 3285 | + /// ] | |
| 3286 | + /// } | |
| 3287 | + /// ``` | |
| 3288 | + /// | |
| 3289 | + /// 返回字段说明: | |
| 3290 | + /// - id: 品项明细ID(主键) | |
| 3291 | + /// - billingId: 开单ID(关联开单记录表) | |
| 3292 | + /// - billingTime: 开单时间(业绩时间yjsj,DateTime格式) | |
| 3293 | + /// - activityName: 营销活动名称(关联营销活动表) | |
| 3294 | + /// - memberName: 会员名称(关联会员表) | |
| 3295 | + /// - memberPhone: 会员手机号(关联会员表) | |
| 3296 | + /// - itemName: 品项名称(品项明细表字段) | |
| 3297 | + /// - itemType: 品项类型(关联项目资料表的qt2字段) | |
| 3298 | + /// - actualPrice: 实付金额(decimal类型) | |
| 3299 | + /// - projectNumber: 项目次数(decimal类型,支持小数) | |
| 3300 | + /// - sourceType: 来源类型(字符串,如:购买、赠送、体验) | |
| 3301 | + /// - remark: 备注(字符串) | |
| 3302 | + /// </remarks> | |
| 3303 | + /// <param name="input">查询参数</param> | |
| 3304 | + /// <returns>开单品项明细记录列表(分页)</returns> | |
| 3305 | + /// <response code="200">查询成功,返回开单品项明细记录列表</response> | |
| 3306 | + /// <response code="400">参数错误,如页码或页大小无效</response> | |
| 3307 | + /// <response code="500">服务器错误,查询过程中发生异常</response> | |
| 3308 | + [HttpGet("billing-item-detail-list")] | |
| 3309 | + public async Task<dynamic> GetBillingItemDetailList([FromQuery] BillingItemDetailListQueryInput input) | |
| 3310 | + { | |
| 3311 | + try | |
| 3312 | + { | |
| 3313 | + var sidx = input.sidx == null ? "yjsj" : input.sidx; | |
| 3314 | + var sort = string.IsNullOrEmpty(input.sort) ? "DESC" : input.sort; | |
| 3315 | + | |
| 3316 | + // 处理开单时间范围 | |
| 3317 | + List<string> queryBillingTime = null; | |
| 3318 | + DateTime? startBillingTime = null; | |
| 3319 | + DateTime? endBillingTime = null; | |
| 3320 | + if (!string.IsNullOrEmpty(input.StartBillingTime) && !string.IsNullOrEmpty(input.EndBillingTime)) | |
| 3321 | + { | |
| 3322 | + queryBillingTime = new List<string> { input.StartBillingTime, input.EndBillingTime }; | |
| 3323 | + startBillingTime = Ext.GetDateTime(queryBillingTime.First()); | |
| 3324 | + endBillingTime = Ext.GetDateTime(queryBillingTime.Last()); | |
| 3325 | + } | |
| 3326 | + | |
| 3327 | + // 优化查询:先分页查询主表,再批量查询关联数据,避免子查询性能问题 | |
| 3328 | + // 1. 先构建基础查询条件 | |
| 3329 | + var baseQuery = _db.Queryable<LqKdPxmxEntity>() | |
| 3330 | + .Where(pxmx => pxmx.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 3331 | + .WhereIF(!string.IsNullOrEmpty(input.Id), pxmx => pxmx.Id == input.Id) | |
| 3332 | + .WhereIF(!string.IsNullOrEmpty(input.BillingId), pxmx => pxmx.Glkdbh == input.BillingId) | |
| 3333 | + .WhereIF(queryBillingTime != null && startBillingTime.HasValue, pxmx => pxmx.Yjsj >= new DateTime(startBillingTime.Value.Year, startBillingTime.Value.Month, startBillingTime.Value.Day, 0, 0, 0)) | |
| 3334 | + .WhereIF(queryBillingTime != null && endBillingTime.HasValue, pxmx => pxmx.Yjsj <= new DateTime(endBillingTime.Value.Year, endBillingTime.Value.Month, endBillingTime.Value.Day, 23, 59, 59)) | |
| 3335 | + .WhereIF(!string.IsNullOrEmpty(input.ActivityId), pxmx => pxmx.ActivityId == input.ActivityId) | |
| 3336 | + .WhereIF(!string.IsNullOrEmpty(input.MemberId), pxmx => pxmx.MemberId == input.MemberId) | |
| 3337 | + .WhereIF(!string.IsNullOrEmpty(input.ItemId), pxmx => pxmx.Px == input.ItemId) | |
| 3338 | + .WhereIF(!string.IsNullOrEmpty(input.ItemName), pxmx => pxmx.Pxmc != null && pxmx.Pxmc.Contains(input.ItemName)) | |
| 3339 | + .WhereIF(!string.IsNullOrEmpty(input.SourceType), pxmx => pxmx.SourceType == input.SourceType); | |
| 3340 | + | |
| 3341 | + // 2. 通过 EXISTS 子查询筛选关联字段(在分页前筛选,确保分页准确) | |
| 3342 | + baseQuery = baseQuery.WhereIF(!string.IsNullOrEmpty(input.MemberName), pxmx => SqlFunc.Subqueryable<LqKhxxEntity>().Where(x => x.Id == pxmx.MemberId && x.Khmc != null && x.Khmc.Contains(input.MemberName)).Any()) | |
| 3343 | + .WhereIF(!string.IsNullOrEmpty(input.MemberPhone), pxmx => SqlFunc.Subqueryable<LqKhxxEntity>().Where(x => x.Id == pxmx.MemberId && x.Sjh == input.MemberPhone).Any()) | |
| 3344 | + .WhereIF(!string.IsNullOrEmpty(input.ItemType), pxmx => SqlFunc.Subqueryable<LqXmzlEntity>().Where(x => x.Id == pxmx.Px && x.Fl4 == input.ItemType).Any()); | |
| 3345 | + | |
| 3346 | + // 3. 先分页查询主表数据(查询实体类,提高性能) | |
| 3347 | + var pagedData = await baseQuery.OrderBy(sidx + " " + sort).ToPagedListAsync(input.currentPage, input.pageSize); | |
| 3348 | + | |
| 3349 | + // 4. 批量查询关联数据 | |
| 3350 | + var itemIds = pagedData.list.Select(x => x.Id).ToList(); | |
| 3351 | + var memberIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.MemberId)).Select(x => x.MemberId).Distinct().ToList(); | |
| 3352 | + var activityIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.ActivityId)).Select(x => x.ActivityId).Distinct().ToList(); | |
| 3353 | + var projectIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.Px)).Select(x => x.Px).Distinct().ToList(); | |
| 3354 | + | |
| 3355 | + // 批量查询会员信息 | |
| 3356 | + var memberDict = new Dictionary<string, (string Name, string Phone)>(); | |
| 3357 | + if (memberIds.Any()) | |
| 3358 | + { | |
| 3359 | + var members = await _db.Queryable<LqKhxxEntity>().Where(x => memberIds.Contains(x.Id)).Select(x => new { x.Id, x.Khmc, x.Sjh }).ToListAsync(); | |
| 3360 | + memberDict = members.ToDictionary(x => x.Id, x => (x.Khmc ?? "", x.Sjh ?? "")); | |
| 3361 | + } | |
| 3362 | + | |
| 3363 | + // 批量查询活动信息 | |
| 3364 | + var activityDict = new Dictionary<string, string>(); | |
| 3365 | + if (activityIds.Any()) | |
| 3366 | + { | |
| 3367 | + var activities = await _db.Queryable<LqPackageInfoEntity>().Where(x => activityIds.Contains(x.Id)).Select(x => new { x.Id, x.ActivityName }).ToListAsync(); | |
| 3368 | + activityDict = activities.ToDictionary(x => x.Id, x => x.ActivityName ?? ""); | |
| 3369 | + } | |
| 3370 | + | |
| 3371 | + // 批量查询项目资料 | |
| 3372 | + var projectDict = new Dictionary<string, string>(); | |
| 3373 | + if (projectIds.Any()) | |
| 3374 | + { | |
| 3375 | + var projects = await _db.Queryable<LqXmzlEntity>().Where(x => projectIds.Contains(x.Id)).Select(x => new { x.Id, x.Qt2 }).ToListAsync(); | |
| 3376 | + projectDict = projects.ToDictionary(x => x.Id, x => x.Qt2 ?? ""); | |
| 3377 | + } | |
| 3378 | + | |
| 3379 | + // 5. 组装返回数据 | |
| 3380 | + var resultList = pagedData.list.Select(pxmx => new BillingItemDetailListOutput | |
| 3381 | + { | |
| 3382 | + id = pxmx.Id, | |
| 3383 | + billingId = pxmx.Glkdbh, | |
| 3384 | + billingTime = pxmx.Yjsj, | |
| 3385 | + activityName = pxmx.ActivityId != null && activityDict.ContainsKey(pxmx.ActivityId) ? activityDict[pxmx.ActivityId] : "", | |
| 3386 | + memberName = pxmx.MemberId != null && memberDict.ContainsKey(pxmx.MemberId) ? memberDict[pxmx.MemberId].Name : "", | |
| 3387 | + memberPhone = pxmx.MemberId != null && memberDict.ContainsKey(pxmx.MemberId) ? memberDict[pxmx.MemberId].Phone : "", | |
| 3388 | + itemName = pxmx.Pxmc, | |
| 3389 | + itemType = pxmx.Px != null && projectDict.ContainsKey(pxmx.Px) ? projectDict[pxmx.Px] : "", | |
| 3390 | + actualPrice = pxmx.ActualPrice, | |
| 3391 | + projectNumber = pxmx.ProjectNumber, | |
| 3392 | + sourceType = pxmx.SourceType, | |
| 3393 | + remark = pxmx.Remark | |
| 3394 | + }).ToList(); | |
| 3395 | + | |
| 3396 | + // 6. 返回分页结果 | |
| 3397 | + var result = new SqlSugarPagedList<BillingItemDetailListOutput> | |
| 3398 | + { | |
| 3399 | + list = resultList, | |
| 3400 | + pagination = pagedData.pagination | |
| 3401 | + }; | |
| 3402 | + | |
| 3403 | + return PageResult<BillingItemDetailListOutput>.SqlSugarPageResult(result); | |
| 3404 | + } | |
| 3405 | + catch (Exception ex) | |
| 3406 | + { | |
| 3407 | + _logger.LogError(ex, $"获取开单品项明细记录列表失败: {ex.ToString()}"); | |
| 3408 | + throw NCCException.Oh(ErrorCode.COM1005, $"获取开单品项明细记录列表失败: {ex.Message}"); | |
| 3409 | + } | |
| 3410 | + } | |
| 3411 | + #endregion | |
| 3214 | 3412 | } |
| 3215 | 3413 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
| ... | ... | @@ -776,5 +776,676 @@ namespace NCC.Extend.LqKhxx |
| 776 | 776 | } |
| 777 | 777 | #endregion |
| 778 | 778 | |
| 779 | + #region 获取会员品项信息 | |
| 780 | + /// <summary> | |
| 781 | + /// 获取所有会员的品项信息(分页) | |
| 782 | + /// </summary> | |
| 783 | + /// <remarks> | |
| 784 | + /// 查询所有会员的品项信息,包括购买品项、赠送品项、体验品项、已消耗品项、已退卡品项、已储扣品项 | |
| 785 | + /// | |
| 786 | + /// 示例请求: | |
| 787 | + /// ```json | |
| 788 | + /// { | |
| 789 | + /// "currentPage": 1, | |
| 790 | + /// "pageSize": 10, | |
| 791 | + /// "MemberId": "会员ID", | |
| 792 | + /// "BelongStoreId": "所属门店ID", | |
| 793 | + /// "BillingStoreId": "开单门店ID", | |
| 794 | + /// "BillingUserId": "开单人ID", | |
| 795 | + /// "PurchaseItemId": "购买品项ID", | |
| 796 | + /// "GiftItemId": "赠送品项ID", | |
| 797 | + /// "ExperienceItemId": "体验品项ID" | |
| 798 | + /// } | |
| 799 | + /// ``` | |
| 800 | + /// | |
| 801 | + /// 参数说明: | |
| 802 | + /// - currentPage: 当前页码(必填) | |
| 803 | + /// - pageSize: 每页数量(必填) | |
| 804 | + /// - MemberId: 会员ID(可选) | |
| 805 | + /// - BelongStoreId: 所属门店ID(可选) | |
| 806 | + /// - BillingStoreId: 开单门店ID(可选) | |
| 807 | + /// - BillingUserId: 开单人ID(可选) | |
| 808 | + /// - PurchaseItemId: 购买品项ID(可选) | |
| 809 | + /// - GiftItemId: 赠送品项ID(可选) | |
| 810 | + /// - ExperienceItemId: 体验品项ID(可选) | |
| 811 | + /// | |
| 812 | + /// 返回数据结构说明: | |
| 813 | + /// ```json | |
| 814 | + /// { | |
| 815 | + /// "pagination": { | |
| 816 | + /// "pageIndex": 1, | |
| 817 | + /// "pageSize": 10, | |
| 818 | + /// "total": 100 | |
| 819 | + /// }, | |
| 820 | + /// "list": [ | |
| 821 | + /// { | |
| 822 | + /// "memberId": "会员ID", | |
| 823 | + /// "memberCode": "会员编号(档案号)", | |
| 824 | + /// "memberName": "会员名称", | |
| 825 | + /// "memberPhone": "会员电话", | |
| 826 | + /// "belongStoreId": "所属门店ID", | |
| 827 | + /// "belongStoreName": "所属门店名称", | |
| 828 | + /// "billingItems": [ | |
| 829 | + /// { | |
| 830 | + /// "billingId": "开单ID", | |
| 831 | + /// "billingTime": "2025-11-15T10:00:00", | |
| 832 | + /// "billingStoreId": "开单门店ID", | |
| 833 | + /// "billingStoreName": "开单门店名称", | |
| 834 | + /// "billingUserId": "开单人ID", | |
| 835 | + /// "billingUserName": "开单人名称", | |
| 836 | + /// "purchaseItems": [ | |
| 837 | + /// { | |
| 838 | + /// "itemId": "品项ID", | |
| 839 | + /// "itemName": "品项名称", | |
| 840 | + /// "itemPrice": 100.00, | |
| 841 | + /// "projectNumber": 5.0, | |
| 842 | + /// "totalAmount": 500.00 | |
| 843 | + /// } | |
| 844 | + /// ], | |
| 845 | + /// "purchaseItemsTotalAmount": 500.00, | |
| 846 | + /// "giftItems": [ | |
| 847 | + /// { | |
| 848 | + /// "itemId": "品项ID", | |
| 849 | + /// "itemName": "品项名称", | |
| 850 | + /// "itemPrice": 50.00, | |
| 851 | + /// "projectNumber": 2.0, | |
| 852 | + /// "totalAmount": 100.00 | |
| 853 | + /// } | |
| 854 | + /// ], | |
| 855 | + /// "experienceItems": [ | |
| 856 | + /// { | |
| 857 | + /// "itemId": "品项ID", | |
| 858 | + /// "itemName": "品项名称", | |
| 859 | + /// "itemPrice": 30.00, | |
| 860 | + /// "projectNumber": 1.0, | |
| 861 | + /// "totalAmount": 30.00 | |
| 862 | + /// } | |
| 863 | + /// ], | |
| 864 | + /// "consumedItems": [ | |
| 865 | + /// { | |
| 866 | + /// "itemId": "品项ID", | |
| 867 | + /// "itemName": "品项名称", | |
| 868 | + /// "itemPrice": 100.00, | |
| 869 | + /// "projectNumber": 2.0, | |
| 870 | + /// "totalAmount": 200.00 | |
| 871 | + /// } | |
| 872 | + /// ], | |
| 873 | + /// "consumedItemsTotalAmount": 200.00, | |
| 874 | + /// "refundedItems": [ | |
| 875 | + /// { | |
| 876 | + /// "itemId": "品项ID", | |
| 877 | + /// "itemName": "品项名称", | |
| 878 | + /// "itemPrice": 100.00, | |
| 879 | + /// "projectNumber": 1.0, | |
| 880 | + /// "totalAmount": 100.00 | |
| 881 | + /// } | |
| 882 | + /// ], | |
| 883 | + /// "refundedItemsTotalAmount": 100.00, | |
| 884 | + /// "deductedItems": [ | |
| 885 | + /// { | |
| 886 | + /// "itemId": "品项ID", | |
| 887 | + /// "itemName": "品项名称", | |
| 888 | + /// "itemPrice": 100.00, | |
| 889 | + /// "projectNumber": 1.0, | |
| 890 | + /// "totalAmount": 100.00 | |
| 891 | + /// } | |
| 892 | + /// ], | |
| 893 | + /// "deductedItemsTotalAmount": 100.00 | |
| 894 | + /// } | |
| 895 | + /// ] | |
| 896 | + /// } | |
| 897 | + /// ] | |
| 898 | + /// } | |
| 899 | + /// ``` | |
| 900 | + /// | |
| 901 | + /// 返回字段说明: | |
| 902 | + /// | |
| 903 | + /// **分页信息 (pagination)**: | |
| 904 | + /// - pageIndex: 当前页码 | |
| 905 | + /// - pageSize: 每页数量 | |
| 906 | + /// - total: 总记录数 | |
| 907 | + /// | |
| 908 | + /// **会员信息 (list[].memberInfo)**: | |
| 909 | + /// - memberId: 会员ID | |
| 910 | + /// - memberCode: 会员编号(档案号) | |
| 911 | + /// - memberName: 会员名称 | |
| 912 | + /// - memberPhone: 会员电话 | |
| 913 | + /// - belongStoreId: 所属门店ID | |
| 914 | + /// - belongStoreName: 所属门店名称 | |
| 915 | + /// | |
| 916 | + /// **开单信息 (list[].billingItems[])**: | |
| 917 | + /// - billingId: 开单ID | |
| 918 | + /// - billingTime: 开单时间(DateTime格式) | |
| 919 | + /// - billingStoreId: 开单门店ID | |
| 920 | + /// - billingStoreName: 开单门店名称 | |
| 921 | + /// - billingUserId: 开单人ID | |
| 922 | + /// - billingUserName: 开单人名称 | |
| 923 | + /// | |
| 924 | + /// **品项信息 (list[].billingItems[].*Items[])**: | |
| 925 | + /// - purchaseItems: 购买品项列表 | |
| 926 | + /// - purchaseItemsTotalAmount: 购买品项总金额 | |
| 927 | + /// - giftItems: 赠送品项列表 | |
| 928 | + /// - experienceItems: 体验品项列表 | |
| 929 | + /// - consumedItems: 已消耗品项列表 | |
| 930 | + /// - consumedItemsTotalAmount: 已消耗品项总金额 | |
| 931 | + /// - refundedItems: 已退卡品项列表 | |
| 932 | + /// - refundedItemsTotalAmount: 已退卡品项总金额 | |
| 933 | + /// - deductedItems: 已储扣品项列表 | |
| 934 | + /// - deductedItemsTotalAmount: 已储扣品项总金额 | |
| 935 | + /// | |
| 936 | + /// **品项明细 (ItemDetail)**: | |
| 937 | + /// - itemId: 品项ID | |
| 938 | + /// - itemName: 品项名称 | |
| 939 | + /// - itemPrice: 品项单价 | |
| 940 | + /// - projectNumber: 项目次数(支持小数) | |
| 941 | + /// - totalAmount: 总金额(单价 × 次数) | |
| 942 | + /// </remarks> | |
| 943 | + /// <param name="input">查询参数</param> | |
| 944 | + /// <returns>会员品项信息列表(分页)</returns> | |
| 945 | + /// <response code="200">查询成功,返回会员品项信息列表</response> | |
| 946 | + /// <response code="400">参数错误,如页码或页大小无效</response> | |
| 947 | + /// <response code="500">服务器错误,查询过程中发生异常</response> | |
| 948 | + [HttpPost("get-member-item-info")] | |
| 949 | + public async Task<dynamic> GetMemberItemInfo([FromBody] MemberItemInfoQueryInput input) | |
| 950 | + { | |
| 951 | + try | |
| 952 | + { | |
| 953 | + // 1. 构建会员查询条件 | |
| 954 | + var memberQuery = _db.Queryable<LqKhxxEntity>(); | |
| 955 | + | |
| 956 | + // 筛选条件 | |
| 957 | + if (!string.IsNullOrEmpty(input.MemberId)) | |
| 958 | + { | |
| 959 | + memberQuery = memberQuery.Where(x => x.Id == input.MemberId); | |
| 960 | + } | |
| 961 | + | |
| 962 | + if (!string.IsNullOrEmpty(input.BelongStoreId)) | |
| 963 | + { | |
| 964 | + memberQuery = memberQuery.Where(x => x.Gsmd == input.BelongStoreId); | |
| 965 | + } | |
| 966 | + | |
| 967 | + // 如果指定了开单门店、开单人、品项ID等条件,需要通过开单表进行筛选 | |
| 968 | + if (!string.IsNullOrEmpty(input.BillingStoreId) || | |
| 969 | + !string.IsNullOrEmpty(input.BillingUserId) || | |
| 970 | + !string.IsNullOrEmpty(input.PurchaseItemId) || | |
| 971 | + !string.IsNullOrEmpty(input.GiftItemId) || | |
| 972 | + !string.IsNullOrEmpty(input.ExperienceItemId)) | |
| 973 | + { | |
| 974 | + // 先查询符合条件的开单记录,获取会员ID列表 | |
| 975 | + var billingQuery = _db.Queryable<LqKdKdjlbEntity>() | |
| 976 | + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()); | |
| 977 | + | |
| 978 | + if (!string.IsNullOrEmpty(input.BillingStoreId)) | |
| 979 | + { | |
| 980 | + billingQuery = billingQuery.Where(x => x.Djmd == input.BillingStoreId); | |
| 981 | + } | |
| 982 | + | |
| 983 | + if (!string.IsNullOrEmpty(input.BillingUserId)) | |
| 984 | + { | |
| 985 | + billingQuery = billingQuery.Where(x => x.CreateUser == input.BillingUserId); | |
| 986 | + } | |
| 987 | + | |
| 988 | + // 如果指定了品项ID,需要通过品项明细表进行筛选 | |
| 989 | + if (!string.IsNullOrEmpty(input.PurchaseItemId) || | |
| 990 | + !string.IsNullOrEmpty(input.GiftItemId) || | |
| 991 | + !string.IsNullOrEmpty(input.ExperienceItemId)) | |
| 992 | + { | |
| 993 | + var itemQuery = _db.Queryable<LqKdPxmxEntity>() | |
| 994 | + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()); | |
| 995 | + | |
| 996 | + if (!string.IsNullOrEmpty(input.PurchaseItemId)) | |
| 997 | + { | |
| 998 | + itemQuery = itemQuery.Where(x => x.Px == input.PurchaseItemId && x.SourceType == "购买"); | |
| 999 | + } | |
| 1000 | + | |
| 1001 | + if (!string.IsNullOrEmpty(input.GiftItemId)) | |
| 1002 | + { | |
| 1003 | + itemQuery = itemQuery.Where(x => x.Px == input.GiftItemId && x.SourceType == "赠送"); | |
| 1004 | + } | |
| 1005 | + | |
| 1006 | + if (!string.IsNullOrEmpty(input.ExperienceItemId)) | |
| 1007 | + { | |
| 1008 | + itemQuery = itemQuery.Where(x => x.Px == input.ExperienceItemId && x.SourceType == "体验"); | |
| 1009 | + } | |
| 1010 | + | |
| 1011 | + var itemBillingIds = await itemQuery.Select(x => x.Glkdbh).Distinct().ToListAsync(); | |
| 1012 | + if (itemBillingIds != null && itemBillingIds.Any()) | |
| 1013 | + { | |
| 1014 | + billingQuery = billingQuery.Where(x => itemBillingIds.Contains(x.Id)); | |
| 1015 | + } | |
| 1016 | + else | |
| 1017 | + { | |
| 1018 | + // 如果没有匹配的品项记录,返回空结果 | |
| 1019 | + return PageResult<MemberItemInfoOutput>.SqlSugarPageResult(new SqlSugarPagedList<MemberItemInfoOutput> | |
| 1020 | + { | |
| 1021 | + list = new List<MemberItemInfoOutput>(), | |
| 1022 | + pagination = new PagedModel | |
| 1023 | + { | |
| 1024 | + PageIndex = input.currentPage, | |
| 1025 | + PageSize = input.pageSize, | |
| 1026 | + Total = 0 | |
| 1027 | + } | |
| 1028 | + }); | |
| 1029 | + } | |
| 1030 | + } | |
| 1031 | + | |
| 1032 | + var memberIds = await billingQuery.Select(x => x.Kdhy).Distinct().ToListAsync(); | |
| 1033 | + if (memberIds != null && memberIds.Any()) | |
| 1034 | + { | |
| 1035 | + memberQuery = memberQuery.Where(x => memberIds.Contains(x.Id)); | |
| 1036 | + } | |
| 1037 | + else | |
| 1038 | + { | |
| 1039 | + // 如果没有匹配的开单记录,返回空结果 | |
| 1040 | + return PageResult<MemberItemInfoOutput>.SqlSugarPageResult(new SqlSugarPagedList<MemberItemInfoOutput> | |
| 1041 | + { | |
| 1042 | + list = new List<MemberItemInfoOutput>(), | |
| 1043 | + pagination = new PagedModel | |
| 1044 | + { | |
| 1045 | + PageIndex = input.currentPage, | |
| 1046 | + PageSize = input.pageSize, | |
| 1047 | + Total = 0 | |
| 1048 | + } | |
| 1049 | + }); | |
| 1050 | + } | |
| 1051 | + } | |
| 1052 | + | |
| 1053 | + // 2. 分页查询会员列表 | |
| 1054 | + var totalCount = await memberQuery.CountAsync(); | |
| 1055 | + var skipCount = (input.currentPage - 1) * input.pageSize; | |
| 1056 | + var memberList = await memberQuery | |
| 1057 | + .OrderBy(x => x.Id) | |
| 1058 | + .Skip(skipCount) | |
| 1059 | + .Take(input.pageSize) | |
| 1060 | + .Select(x => new | |
| 1061 | + { | |
| 1062 | + x.Id, | |
| 1063 | + x.Dah, | |
| 1064 | + x.Khmc, | |
| 1065 | + x.Sjh, | |
| 1066 | + x.Gsmd | |
| 1067 | + }) | |
| 1068 | + .ToListAsync(); | |
| 1069 | + | |
| 1070 | + var memberData = new SqlSugarPagedList<dynamic> | |
| 1071 | + { | |
| 1072 | + list = memberList.Cast<dynamic>().ToList(), | |
| 1073 | + pagination = new PagedModel | |
| 1074 | + { | |
| 1075 | + PageIndex = input.currentPage, | |
| 1076 | + PageSize = input.pageSize, | |
| 1077 | + Total = totalCount | |
| 1078 | + } | |
| 1079 | + }; | |
| 1080 | + | |
| 1081 | + if (memberList == null || !memberList.Any()) | |
| 1082 | + { | |
| 1083 | + return PageResult<MemberItemInfoOutput>.SqlSugarPageResult(new SqlSugarPagedList<MemberItemInfoOutput> | |
| 1084 | + { | |
| 1085 | + list = new List<MemberItemInfoOutput>(), | |
| 1086 | + pagination = new PagedModel | |
| 1087 | + { | |
| 1088 | + PageIndex = input.currentPage, | |
| 1089 | + PageSize = input.pageSize, | |
| 1090 | + Total = totalCount | |
| 1091 | + } | |
| 1092 | + }); | |
| 1093 | + } | |
| 1094 | + | |
| 1095 | + var memberIdsList = memberList.Select(x => x.Id).ToList(); | |
| 1096 | + | |
| 1097 | + // 3. 批量查询会员的开单记录 | |
| 1098 | + var billingRecords = await _db.Queryable<LqKdKdjlbEntity>() | |
| 1099 | + .Where(x => memberIdsList.Contains(x.Kdhy) && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1100 | + .Select(x => new | |
| 1101 | + { | |
| 1102 | + x.Id, | |
| 1103 | + x.Kdhy, | |
| 1104 | + x.Kdrq, | |
| 1105 | + x.Djmd, | |
| 1106 | + x.CreateUser | |
| 1107 | + }) | |
| 1108 | + .ToListAsync(); | |
| 1109 | + | |
| 1110 | + var billingIds = billingRecords.Select(x => x.Id).ToList(); | |
| 1111 | + | |
| 1112 | + // 4. 批量查询开单的品项明细 | |
| 1113 | + var itemDetails = new List<dynamic>(); | |
| 1114 | + if (billingIds.Any()) | |
| 1115 | + { | |
| 1116 | + var itemDetailsData = await _db.Queryable<LqKdPxmxEntity>() | |
| 1117 | + .Where(x => billingIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1118 | + .Select(x => new | |
| 1119 | + { | |
| 1120 | + x.Id, | |
| 1121 | + x.Glkdbh, | |
| 1122 | + x.Px, | |
| 1123 | + x.Pxmc, | |
| 1124 | + x.Pxjg, | |
| 1125 | + x.SourceType, | |
| 1126 | + x.ProjectNumber, | |
| 1127 | + x.TotalPrice | |
| 1128 | + }) | |
| 1129 | + .ToListAsync(); | |
| 1130 | + itemDetails = itemDetailsData.Cast<dynamic>().ToList(); | |
| 1131 | + } | |
| 1132 | + | |
| 1133 | + var itemDetailIds = itemDetails.Select(x => (string)x.Id).ToList(); | |
| 1134 | + | |
| 1135 | + // 5. 批量查询消耗品项 | |
| 1136 | + var consumedItems = new List<dynamic>(); | |
| 1137 | + if (itemDetailIds.Any()) | |
| 1138 | + { | |
| 1139 | + var consumedItemsData = await _db.Queryable<LqXhPxmxEntity>() | |
| 1140 | + .Where(x => itemDetailIds.Contains(x.BillingItemId) && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1141 | + .Select(x => new | |
| 1142 | + { | |
| 1143 | + x.BillingItemId, | |
| 1144 | + x.Px, | |
| 1145 | + x.Pxmc, | |
| 1146 | + x.Pxjg, | |
| 1147 | + x.ProjectNumber, | |
| 1148 | + x.TotalPrice | |
| 1149 | + }) | |
| 1150 | + .ToListAsync(); | |
| 1151 | + consumedItems = consumedItemsData.Cast<dynamic>().ToList(); | |
| 1152 | + } | |
| 1153 | + | |
| 1154 | + // 6. 批量查询退卡品项 | |
| 1155 | + var refundedItems = new List<dynamic>(); | |
| 1156 | + if (itemDetailIds.Any()) | |
| 1157 | + { | |
| 1158 | + var refundedItemsData = await _db.Queryable<LqHytkMxEntity>() | |
| 1159 | + .Where(x => itemDetailIds.Contains(x.BillingItemId) && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1160 | + .Select(x => new | |
| 1161 | + { | |
| 1162 | + x.BillingItemId, | |
| 1163 | + x.Px, | |
| 1164 | + x.Pxmc, | |
| 1165 | + x.Pxjg, | |
| 1166 | + x.ProjectNumber, | |
| 1167 | + x.Tkje | |
| 1168 | + }) | |
| 1169 | + .ToListAsync(); | |
| 1170 | + refundedItems = refundedItemsData.Cast<dynamic>().ToList(); | |
| 1171 | + } | |
| 1172 | + | |
| 1173 | + // 7. 批量查询储扣品项 | |
| 1174 | + var deductedItems = new List<dynamic>(); | |
| 1175 | + if (billingIds.Any()) | |
| 1176 | + { | |
| 1177 | + var deductedItemsData = await _db.Queryable<LqKdDeductinfoEntity>() | |
| 1178 | + .Where(x => billingIds.Contains(x.BillingId) && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1179 | + .Select(x => new | |
| 1180 | + { | |
| 1181 | + x.BillingId, | |
| 1182 | + x.ItemId, | |
| 1183 | + x.ItemName, | |
| 1184 | + x.UnitPrice, | |
| 1185 | + x.ProjectNumber, | |
| 1186 | + x.Amount | |
| 1187 | + }) | |
| 1188 | + .ToListAsync(); | |
| 1189 | + deductedItems = deductedItemsData.Cast<dynamic>().ToList(); | |
| 1190 | + } | |
| 1191 | + | |
| 1192 | + // 8. 批量查询门店信息 | |
| 1193 | + var storeIds = new List<string>(); | |
| 1194 | + if (memberList != null && memberList.Any()) | |
| 1195 | + { | |
| 1196 | + storeIds.AddRange(memberList.Select(x => x.Gsmd).Where(x => !string.IsNullOrEmpty(x))); | |
| 1197 | + } | |
| 1198 | + if (billingRecords != null && billingRecords.Any()) | |
| 1199 | + { | |
| 1200 | + storeIds.AddRange(billingRecords.Select(x => x.Djmd).Where(x => !string.IsNullOrEmpty(x))); | |
| 1201 | + } | |
| 1202 | + storeIds = storeIds.Distinct().ToList(); | |
| 1203 | + | |
| 1204 | + var stores = new Dictionary<string, string>(); | |
| 1205 | + if (storeIds.Any()) | |
| 1206 | + { | |
| 1207 | + var storeList = await _db.Queryable<LqMdxxEntity>() | |
| 1208 | + .Where(x => storeIds.Contains(x.Id)) | |
| 1209 | + .Select(x => new { x.Id, x.Dm }) | |
| 1210 | + .ToListAsync(); | |
| 1211 | + | |
| 1212 | + stores = storeList.ToDictionary(x => x.Id, x => x.Dm ?? ""); | |
| 1213 | + } | |
| 1214 | + | |
| 1215 | + // 9. 批量查询用户信息 | |
| 1216 | + var userIds = new List<string>(); | |
| 1217 | + if (billingRecords != null && billingRecords.Any()) | |
| 1218 | + { | |
| 1219 | + userIds = billingRecords.Select(x => x.CreateUser) | |
| 1220 | + .Where(x => !string.IsNullOrEmpty(x)) | |
| 1221 | + .Distinct() | |
| 1222 | + .ToList(); | |
| 1223 | + } | |
| 1224 | + | |
| 1225 | + var users = new Dictionary<string, string>(); | |
| 1226 | + if (userIds.Any()) | |
| 1227 | + { | |
| 1228 | + var userList = await _db.Queryable<UserEntity>() | |
| 1229 | + .Where(x => userIds.Contains(x.Id)) | |
| 1230 | + .Select(x => new { x.Id, x.RealName }) | |
| 1231 | + .ToListAsync(); | |
| 1232 | + | |
| 1233 | + users = userList.ToDictionary(x => x.Id, x => x.RealName ?? ""); | |
| 1234 | + } | |
| 1235 | + | |
| 1236 | + // 10. 组装数据 | |
| 1237 | + var result = new List<MemberItemInfoOutput>(); | |
| 1238 | + | |
| 1239 | + foreach (var member in memberList) | |
| 1240 | + { | |
| 1241 | + var memberOutput = new MemberItemInfoOutput | |
| 1242 | + { | |
| 1243 | + MemberId = member.Id, | |
| 1244 | + MemberCode = member.Dah ?? "", | |
| 1245 | + MemberName = member.Khmc ?? "", | |
| 1246 | + MemberPhone = member.Sjh ?? "", | |
| 1247 | + BelongStoreId = member.Gsmd ?? "", | |
| 1248 | + BelongStoreName = stores.ContainsKey(member.Gsmd ?? "") ? stores[member.Gsmd] : "" | |
| 1249 | + }; | |
| 1250 | + | |
| 1251 | + // 获取该会员的所有开单 | |
| 1252 | + var memberBillings = billingRecords.Where(x => x.Kdhy == member.Id).ToList(); | |
| 1253 | + | |
| 1254 | + foreach (var billing in memberBillings) | |
| 1255 | + { | |
| 1256 | + var billingItem = new BillingItemInfo | |
| 1257 | + { | |
| 1258 | + BillingId = billing.Id, | |
| 1259 | + BillingTime = billing.Kdrq, | |
| 1260 | + BillingStoreId = billing.Djmd ?? "", | |
| 1261 | + BillingStoreName = stores.ContainsKey(billing.Djmd ?? "") ? stores[billing.Djmd] : "", | |
| 1262 | + BillingUserId = billing.CreateUser ?? "", | |
| 1263 | + BillingUserName = users.ContainsKey(billing.CreateUser ?? "") ? users[billing.CreateUser] : "" | |
| 1264 | + }; | |
| 1265 | + | |
| 1266 | + // 获取该开单的所有品项明细 | |
| 1267 | + var billingItemDetails = itemDetails.Where(x => | |
| 1268 | + { | |
| 1269 | + try | |
| 1270 | + { | |
| 1271 | + return x.Glkdbh?.ToString() == billing.Id; | |
| 1272 | + } | |
| 1273 | + catch | |
| 1274 | + { | |
| 1275 | + return false; | |
| 1276 | + } | |
| 1277 | + }).ToList(); | |
| 1278 | + | |
| 1279 | + // 分类品项 | |
| 1280 | + foreach (var item in billingItemDetails) | |
| 1281 | + { | |
| 1282 | + try | |
| 1283 | + { | |
| 1284 | + var itemDetail = new ItemDetail | |
| 1285 | + { | |
| 1286 | + ItemId = item.Px?.ToString() ?? "", | |
| 1287 | + ItemName = item.Pxmc?.ToString() ?? "", | |
| 1288 | + ItemPrice = Convert.ToDecimal(item.Pxjg ?? 0), | |
| 1289 | + ProjectNumber = Convert.ToDecimal(item.ProjectNumber ?? 0), | |
| 1290 | + TotalAmount = Convert.ToDecimal(item.TotalPrice ?? 0) | |
| 1291 | + }; | |
| 1292 | + | |
| 1293 | + var sourceType = item.SourceType?.ToString() ?? ""; | |
| 1294 | + if (sourceType == "购买") | |
| 1295 | + { | |
| 1296 | + billingItem.PurchaseItems.Add(itemDetail); | |
| 1297 | + billingItem.PurchaseItemsTotalAmount += itemDetail.TotalAmount; | |
| 1298 | + } | |
| 1299 | + else if (sourceType == "赠送") | |
| 1300 | + { | |
| 1301 | + billingItem.GiftItems.Add(itemDetail); | |
| 1302 | + } | |
| 1303 | + else if (sourceType == "体验") | |
| 1304 | + { | |
| 1305 | + billingItem.ExperienceItems.Add(itemDetail); | |
| 1306 | + } | |
| 1307 | + } | |
| 1308 | + catch (Exception ex) | |
| 1309 | + { | |
| 1310 | + _logger.LogWarning(ex, "处理品项明细时出错,开单ID:{BillingId}", billing.Id); | |
| 1311 | + } | |
| 1312 | + } | |
| 1313 | + | |
| 1314 | + // 获取消耗品项 | |
| 1315 | + var billingItemIds = billingItemDetails.Select(x => | |
| 1316 | + { | |
| 1317 | + try | |
| 1318 | + { | |
| 1319 | + return x.Id?.ToString() ?? ""; | |
| 1320 | + } | |
| 1321 | + catch | |
| 1322 | + { | |
| 1323 | + return ""; | |
| 1324 | + } | |
| 1325 | + }).Where(x => !string.IsNullOrEmpty(x)).ToList(); | |
| 1326 | + | |
| 1327 | + var consumed = consumedItems.Where(x => | |
| 1328 | + { | |
| 1329 | + try | |
| 1330 | + { | |
| 1331 | + return billingItemIds.Contains(x.BillingItemId?.ToString() ?? ""); | |
| 1332 | + } | |
| 1333 | + catch | |
| 1334 | + { | |
| 1335 | + return false; | |
| 1336 | + } | |
| 1337 | + }).ToList(); | |
| 1338 | + | |
| 1339 | + foreach (var consumedItem in consumed) | |
| 1340 | + { | |
| 1341 | + try | |
| 1342 | + { | |
| 1343 | + billingItem.ConsumedItems.Add(new ItemDetail | |
| 1344 | + { | |
| 1345 | + ItemId = consumedItem.Px?.ToString() ?? "", | |
| 1346 | + ItemName = consumedItem.Pxmc?.ToString() ?? "", | |
| 1347 | + ItemPrice = Convert.ToDecimal(consumedItem.Pxjg ?? 0), | |
| 1348 | + ProjectNumber = Convert.ToDecimal(consumedItem.ProjectNumber ?? 0), | |
| 1349 | + TotalAmount = Convert.ToDecimal(consumedItem.TotalPrice ?? 0) | |
| 1350 | + }); | |
| 1351 | + billingItem.ConsumedItemsTotalAmount += Convert.ToDecimal(consumedItem.TotalPrice ?? 0); | |
| 1352 | + } | |
| 1353 | + catch (Exception ex) | |
| 1354 | + { | |
| 1355 | + _logger.LogWarning(ex, "处理消耗品项时出错"); | |
| 1356 | + } | |
| 1357 | + } | |
| 1358 | + | |
| 1359 | + // 获取退卡品项 | |
| 1360 | + var refunded = refundedItems.Where(x => | |
| 1361 | + { | |
| 1362 | + try | |
| 1363 | + { | |
| 1364 | + return billingItemIds.Contains(x.BillingItemId?.ToString() ?? ""); | |
| 1365 | + } | |
| 1366 | + catch | |
| 1367 | + { | |
| 1368 | + return false; | |
| 1369 | + } | |
| 1370 | + }).ToList(); | |
| 1371 | + | |
| 1372 | + foreach (var refundedItem in refunded) | |
| 1373 | + { | |
| 1374 | + try | |
| 1375 | + { | |
| 1376 | + billingItem.RefundedItems.Add(new ItemDetail | |
| 1377 | + { | |
| 1378 | + ItemId = refundedItem.Px?.ToString() ?? "", | |
| 1379 | + ItemName = refundedItem.Pxmc?.ToString() ?? "", | |
| 1380 | + ItemPrice = Convert.ToDecimal(refundedItem.Pxjg ?? 0), | |
| 1381 | + ProjectNumber = Convert.ToDecimal(refundedItem.ProjectNumber ?? 0), | |
| 1382 | + TotalAmount = Convert.ToDecimal(refundedItem.Tkje ?? 0) | |
| 1383 | + }); | |
| 1384 | + billingItem.RefundedItemsTotalAmount += Convert.ToDecimal(refundedItem.Tkje ?? 0); | |
| 1385 | + } | |
| 1386 | + catch (Exception ex) | |
| 1387 | + { | |
| 1388 | + _logger.LogWarning(ex, "处理退卡品项时出错"); | |
| 1389 | + } | |
| 1390 | + } | |
| 1391 | + | |
| 1392 | + // 获取储扣品项 | |
| 1393 | + var deducted = deductedItems.Where(x => | |
| 1394 | + { | |
| 1395 | + try | |
| 1396 | + { | |
| 1397 | + return x.BillingId?.ToString() == billing.Id; | |
| 1398 | + } | |
| 1399 | + catch | |
| 1400 | + { | |
| 1401 | + return false; | |
| 1402 | + } | |
| 1403 | + }).ToList(); | |
| 1404 | + | |
| 1405 | + foreach (var deductedItem in deducted) | |
| 1406 | + { | |
| 1407 | + try | |
| 1408 | + { | |
| 1409 | + billingItem.DeductedItems.Add(new ItemDetail | |
| 1410 | + { | |
| 1411 | + ItemId = deductedItem.ItemId?.ToString() ?? "", | |
| 1412 | + ItemName = deductedItem.ItemName?.ToString() ?? "", | |
| 1413 | + ItemPrice = Convert.ToDecimal(deductedItem.UnitPrice ?? 0), | |
| 1414 | + ProjectNumber = Convert.ToDecimal(deductedItem.ProjectNumber ?? 0), | |
| 1415 | + TotalAmount = Convert.ToDecimal(deductedItem.Amount ?? 0) | |
| 1416 | + }); | |
| 1417 | + billingItem.DeductedItemsTotalAmount += Convert.ToDecimal(deductedItem.Amount ?? 0); | |
| 1418 | + } | |
| 1419 | + catch (Exception ex) | |
| 1420 | + { | |
| 1421 | + _logger.LogWarning(ex, "处理储扣品项时出错"); | |
| 1422 | + } | |
| 1423 | + } | |
| 1424 | + | |
| 1425 | + memberOutput.BillingItems.Add(billingItem); | |
| 1426 | + } | |
| 1427 | + | |
| 1428 | + result.Add(memberOutput); | |
| 1429 | + } | |
| 1430 | + | |
| 1431 | + return PageResult<MemberItemInfoOutput>.SqlSugarPageResult(new SqlSugarPagedList<MemberItemInfoOutput> | |
| 1432 | + { | |
| 1433 | + list = result, | |
| 1434 | + pagination = new PagedModel | |
| 1435 | + { | |
| 1436 | + PageIndex = input.currentPage, | |
| 1437 | + PageSize = input.pageSize, | |
| 1438 | + Total = totalCount | |
| 1439 | + } | |
| 1440 | + }); | |
| 1441 | + } | |
| 1442 | + catch (Exception ex) | |
| 1443 | + { | |
| 1444 | + _logger.LogError(ex, "查询会员品项信息失败,异常详情:{ExceptionDetail}", ex.ToString()); | |
| 1445 | + throw NCCException.Oh(ErrorCode.COM1005, $"查询会员品项信息失败: {ex.Message}"); | |
| 1446 | + } | |
| 1447 | + } | |
| 1448 | + #endregion | |
| 1449 | + | |
| 779 | 1450 | } |
| 780 | 1451 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
| ... | ... | @@ -971,6 +971,8 @@ namespace NCC.Extend.LqXhHyhk |
| 971 | 971 | { |
| 972 | 972 | memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString(); |
| 973 | 973 | } |
| 974 | + //保存会员信息 | |
| 975 | + await _db.Updateable(memberInfo).ExecuteCommandAsync(); | |
| 974 | 976 | // 新增会员耗卡记录 |
| 975 | 977 | var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); |
| 976 | 978 | // 收集所有需要插入的实体,然后批量插入 |
| ... | ... | @@ -1265,6 +1267,15 @@ namespace NCC.Extend.LqXhHyhk |
| 1265 | 1267 | //更新会员耗卡记录 |
| 1266 | 1268 | await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); |
| 1267 | 1269 | |
| 1270 | + var memberInfo = await _db.Queryable<LqKhxxEntity>().Where(w => w.Id == entity.Hy).FirstAsync(); | |
| 1271 | + //如果会员类型是线索,那么就更新为新客 | |
| 1272 | + if (memberInfo.Khlx == MemberTypeEnum.线索.GetHashCode().ToString()) | |
| 1273 | + { | |
| 1274 | + memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString(); | |
| 1275 | + } | |
| 1276 | + //保存会员信息 | |
| 1277 | + await _db.Updateable(memberInfo).ExecuteCommandAsync(); | |
| 1278 | + | |
| 1268 | 1279 | //清空原有数据 |
| 1269 | 1280 | await _db.Deleteable<LqXhJksyjEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); |
| 1270 | 1281 | await _db.Deleteable<LqXhKjbsyjEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqXmzlService.cs
sql/更新潜客为新客.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 更新会员类型:将存在耗卡记录的潜客(0)更新为新客(1) | |
| 3 | +-- ============================================ | |
| 4 | +-- | |
| 5 | +-- 功能说明: | |
| 6 | +-- 1. 查找会员类型为 0(潜客/线索)的会员 | |
| 7 | +-- 2. 如果该会员存在有效的耗卡记录(lq_xh_hyhk 表中有记录且 F_IsEffective = 1) | |
| 8 | +-- 3. 将该会员的类型更新为 1(新客) | |
| 9 | +-- | |
| 10 | +-- 执行前建议: | |
| 11 | +-- 1. 先执行查询语句查看会更新多少条记录 | |
| 12 | +-- 2. 确认无误后再执行更新语句 | |
| 13 | +-- ============================================ | |
| 14 | + | |
| 15 | +-- 1. 查询语句:查看将要更新的会员数量和信息 | |
| 16 | +SELECT | |
| 17 | + kh.F_Id AS 会员ID, | |
| 18 | + kh.khmc AS 会员名称, | |
| 19 | + kh.sjh AS 手机号, | |
| 20 | + kh.khlx AS 当前类型, | |
| 21 | + COUNT(hyhk.F_Id) AS 耗卡记录数 | |
| 22 | +FROM lq_khxx kh | |
| 23 | +INNER JOIN lq_xh_hyhk hyhk ON kh.F_Id = hyhk.hy | |
| 24 | +WHERE kh.khlx = '0' -- 会员类型为 0(潜客/线索) | |
| 25 | + AND hyhk.F_IsEffective = 1 -- 耗卡记录有效 | |
| 26 | +GROUP BY kh.F_Id, kh.khmc, kh.sjh, kh.khlx; | |
| 27 | + | |
| 28 | +-- 2. 更新语句:将存在耗卡记录的潜客更新为新客 | |
| 29 | +UPDATE lq_khxx kh | |
| 30 | +SET kh.khlx = '1' -- 更新为新客(1) | |
| 31 | +WHERE kh.khlx = '0' -- 会员类型为 0(潜客/线索) | |
| 32 | + AND EXISTS ( | |
| 33 | + -- 确保至少有一条有效的耗卡记录 | |
| 34 | + SELECT 1 | |
| 35 | + FROM lq_xh_hyhk hyhk | |
| 36 | + WHERE hyhk.hy = kh.F_Id | |
| 37 | + AND hyhk.F_IsEffective = 1 | |
| 38 | + LIMIT 1 | |
| 39 | + ); | |
| 40 | + | |
| 41 | +-- 3. 验证语句:查看更新后的结果 | |
| 42 | +SELECT | |
| 43 | + kh.F_Id AS 会员ID, | |
| 44 | + kh.khmc AS 会员名称, | |
| 45 | + kh.sjh AS 手机号, | |
| 46 | + kh.khlx AS 更新后类型, | |
| 47 | + COUNT(hyhk.F_Id) AS 耗卡记录数 | |
| 48 | +FROM lq_khxx kh | |
| 49 | +INNER JOIN lq_xh_hyhk hyhk ON kh.F_Id = hyhk.hy | |
| 50 | +WHERE kh.khlx = '1' -- 会员类型为 1(新客) | |
| 51 | + AND hyhk.F_IsEffective = 1 -- 耗卡记录有效 | |
| 52 | +GROUP BY kh.F_Id, kh.khmc, kh.sjh, kh.khlx | |
| 53 | +HAVING COUNT(hyhk.F_Id) > 0; | |
| 54 | + | ... | ... |