From 27de7fa29b3e7b54a2c760bf5ba8771e8fdfa881 Mon Sep 17 00:00:00 2001 From: “wangming” <“wangming@antissoft.com”> Date: Wed, 18 Mar 2026 16:04:46 +0800 Subject: [PATCH] Enhance member data processing in LqKhxxService and MemberPortraitService to support both member ID and archive number for improved data retrieval. Implement billing, consumption, and refund statistics aggregation to avoid N+1 issues, ensuring accurate financial summaries for members. --- netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend/MemberPortraitService.cs | 60 +++++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 88 insertions(+), 19 deletions(-) diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs index bcf77cd..3c96cad 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs @@ -249,6 +249,53 @@ namespace NCC.Extend.LqKhxx .MergeTable() .OrderBy(sidx + " " + input.sort) .ToPagedListAsync(input.currentPage, input.pageSize); + + // 兜底:若累计字段未维护,列表会显示 0,这里按本页会员批量汇总开单/耗卡/退卡金额,避免 N+1 + // 关联键:开单 lq_kd_kdjlb.kdhy / 耗卡 lq_xh_hyhk.hy / 退卡 lq_hytk_hytk.hy 均存会员 F_Id(即 LqKhxxEntity.Id) + if (data?.list != null && data.list.Any()) + { + var memberIds = data.list.Select(x => x.id).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToArray(); + if (memberIds.Length > 0) + { + var billingStats = await _db.Queryable() + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) + .In(x => x.Kdhy, memberIds) + .GroupBy(x => x.Kdhy) + .Select(x => new { MemberId = x.Kdhy, Amount = SqlFunc.AggregateSum(x.Sfyj) }) + .ToListAsync(); + + var consumeStats = await _db.Queryable() + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) + .In(x => x.Hy, memberIds) + .GroupBy(x => x.Hy) + .Select(x => new { MemberId = x.Hy, Amount = SqlFunc.AggregateSum(x.Xfje) }) + .ToListAsync(); + + var refundStats = await _db.Queryable() + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) + .In(x => x.Hy, memberIds) + .GroupBy(x => x.Hy) + .Select(x => new { MemberId = x.Hy, Amount = SqlFunc.AggregateSum(x.ActualRefundAmount) }) + .ToListAsync(); + + var billingMap = billingStats.ToDictionary(x => x.MemberId, x => Convert.ToDecimal(x.Amount)); + var consumeMap = consumeStats.ToDictionary(x => x.MemberId, x => Convert.ToDecimal(x.Amount)); + var refundMap = refundStats.ToDictionary(x => x.MemberId, x => Convert.ToDecimal(x.Amount)); + + foreach (var row in data.list) + { + var billingAmount = billingMap.TryGetValue(row.id, out var b) ? b : 0m; + var consumeAmount = consumeMap.TryGetValue(row.id, out var c) ? c : 0m; + var refundAmount = refundMap.TryGetValue(row.id, out var r) ? r : 0m; + + row.totalBillingAmount = billingAmount; + row.totalConsumeAmount = consumeAmount; + + var remaining = billingAmount - consumeAmount - refundAmount; + row.remainingRightsAmount = remaining < 0m ? 0m : remaining; + } + } + } return PageResult.SqlSugarPageResult(data); } #endregion diff --git a/netcore/src/Modularity/Extend/NCC.Extend/MemberPortraitService.cs b/netcore/src/Modularity/Extend/NCC.Extend/MemberPortraitService.cs index b5a35af..408af14 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/MemberPortraitService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/MemberPortraitService.cs @@ -65,7 +65,7 @@ namespace NCC.Extend /// ``` /// /// 参数说明: - /// - memberId: 会员主键ID(lq_khxx.F_Id) + /// - memberId: 会员标识(支持会员主键ID:lq_khxx.F_Id,或会员编号/档案号:lq_khxx.Dah) /// /// 会员ID /// 会员画像概览数据 @@ -84,13 +84,19 @@ namespace NCC.Extend { // 基础信息 var member = await _db.Queryable() - .FirstAsync(x => x.Id == memberId && x.IsEffective == StatusEnum.有效.GetHashCode()); + .FirstAsync(x => (x.Id == memberId || x.Dah == memberId) && x.IsEffective == StatusEnum.有效.GetHashCode()); if (member == null) { throw NCCException.Oh($"会员不存在或已失效, memberId={memberId}"); } + // 兼容:部分业务表可能存会员主键Id,部分可能存会员档案号Dah + var memberKeys = new List { member.Id, member.Dah } + .Where(x => !string.IsNullOrEmpty(x)) + .Distinct() + .ToList(); + // 查询门店名称 string storeName = null; if (!string.IsNullOrEmpty(member.Gsmd)) @@ -233,24 +239,24 @@ namespace NCC.Extend // 退卡总金额(如实体中未维护,则从退卡表统计) var refundTotal = await _db.Queryable() .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) - .Where(x => x.Hy == member.Id) + .Where(x => memberKeys.Contains(x.Hy)) .SumAsync(x => (decimal?)x.Tkje) ?? 0m; behavior.TotalRefundAmount = refundTotal; // 开单统计 behavior.BillingCount = await _db.Queryable() - .Where(x => x.IsEffective == 1 && x.Kdhy == member.Id) + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Kdhy)) .CountAsync(); if (behavior.BillingCount > 0) { behavior.FirstBillingTime = await _db.Queryable() - .Where(x => x.IsEffective == 1 && x.Kdhy == member.Id) + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Kdhy)) .MinAsync(x => x.Kdrq); behavior.LastBillingTime = await _db.Queryable() - .Where(x => x.IsEffective == 1 && x.Kdhy == member.Id) + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Kdhy)) .MaxAsync(x => x.Kdrq); behavior.AvgBillingAmount = behavior.TotalBillingAmount > 0 && behavior.BillingCount > 0 @@ -266,17 +272,17 @@ namespace NCC.Extend // 消耗统计 behavior.ConsumeCount = await _db.Queryable() - .Where(x => x.IsEffective == 1 && x.Hy == member.Id) + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Hy)) .CountAsync(); if (behavior.ConsumeCount > 0) { behavior.FirstConsumeTime = await _db.Queryable() - .Where(x => x.IsEffective == 1 && x.Hy == member.Id) + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Hy)) .MinAsync(x => x.Hksj); behavior.LastConsumeTime = await _db.Queryable() - .Where(x => x.IsEffective == 1 && x.Hy == member.Id) + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Hy)) .MaxAsync(x => x.Hksj); behavior.AvgConsumeAmount = behavior.TotalConsumeAmount > 0 && behavior.ConsumeCount > 0 @@ -293,7 +299,7 @@ namespace NCC.Extend // 退卡次数 behavior.RefundCount = await _db.Queryable() .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) - .Where(x => x.Hy == member.Id) + .Where(x => memberKeys.Contains(x.Hy)) .CountAsync(); // 近12个月趋势(以当前月份往前推12个月) @@ -310,11 +316,11 @@ namespace NCC.Extend WHERE kd.F_IsEffective = 1 AND kd.kdrq >= @trendStart AND kd.kdrq <= @trendEnd - AND kd.kdhy = @memberId + AND kd.kdhy IN (@memberId1, @memberId2) GROUP BY DATE_FORMAT(kd.kdrq, '%Y-%m')"; var billingTrend = await _db.Ado.SqlQueryAsync(billingTrendSql, - new { trendStart, trendEnd, memberId }); + new { trendStart, trendEnd, memberId1 = member.Id, memberId2 = member.Dah }); // 耗卡趋势 var consumeTrendSql = $@" @@ -325,11 +331,11 @@ namespace NCC.Extend WHERE xh.F_IsEffective = 1 AND xh.hksj >= @trendStart AND xh.hksj <= @trendEnd - AND xh.hy = @memberId + AND xh.hy IN (@memberId1, @memberId2) GROUP BY DATE_FORMAT(xh.hksj, '%Y-%m')"; var consumeTrend = await _db.Ado.SqlQueryAsync(consumeTrendSql, - new { trendStart, trendEnd, memberId }); + new { trendStart, trendEnd, memberId1 = member.Id, memberId2 = member.Dah }); // 退卡趋势 var refundTrendSql = $@" @@ -340,11 +346,11 @@ namespace NCC.Extend WHERE hytk.F_IsEffective = 1 AND hytk.tksj >= @trendStart AND hytk.tksj <= @trendEnd - AND hytk.hy = @memberId + AND hytk.hy IN (@memberId1, @memberId2) GROUP BY DATE_FORMAT(hytk.tksj, '%Y-%m')"; var refundTrend = await _db.Ado.SqlQueryAsync(refundTrendSql, - new { trendStart, trendEnd, memberId }); + new { trendStart, trendEnd, memberId1 = member.Id, memberId2 = member.Dah }); // 组合趋势数据,补全没有数据的月份 var trendDict = new Dictionary(); @@ -389,7 +395,7 @@ namespace NCC.Extend // 查询权益明细 var baseItems = await _db.Queryable() - .Where(x => x.MemberId == memberId) + .Where(x => memberKeys.Contains(x.MemberId)) .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) .Select(x => new { @@ -484,7 +490,7 @@ namespace NCC.Extend // 品项类型偏好(按品项类型统计消费金额和次数) var itemTypeStats = await _db.Queryable() - .Where(x => x.MemberId == memberId) + .Where(x => memberKeys.Contains(x.MemberId)) .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) .GroupBy(x => x.SourceType) .Select(x => new @@ -506,7 +512,7 @@ namespace NCC.Extend // 门店偏好(按门店统计消费金额和次数) var storeStats = await _db.Queryable() - .Where(x => x.Hy == memberId) + .Where(x => memberKeys.Contains(x.Hy)) .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) .GroupBy(x => x.Md) .Select(x => new @@ -517,6 +523,22 @@ namespace NCC.Extend }) .ToListAsync(); + // 兜底:累计字段为0时,按明细实时汇总(口径与次数统计一致) + if (behavior.TotalBillingAmount <= 0m) + { + behavior.TotalBillingAmount = await _db.Queryable() + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Kdhy)) + .SumAsync(x => (decimal?)x.Sfyj) ?? 0m; + } + if (behavior.TotalConsumeAmount <= 0m) + { + behavior.TotalConsumeAmount = await _db.Queryable() + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Hy)) + .SumAsync(x => (decimal?)x.Xfje) ?? 0m; + } + // 剩余权益金额优先按权益明细计算(更可靠) + behavior.RemainingRightsAmount = remainingItems.Sum(x => x.RemainingValue); + // 查询门店名称 var storeIds = storeStats.Select(x => x.StoreId).ToList(); var storeNames = await _db.Queryable() -- libgit2 0.21.4