Commit 27de7fa29b3e7b54a2c760bf5ba8771e8fdfa881

Authored by “wangming”
1 parent f4739ed5

Enhance member data processing in LqKhxxService and MemberPortraitService to sup…

…port 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
... ... @@ -249,6 +249,53 @@ namespace NCC.Extend.LqKhxx
249 249 .MergeTable()
250 250 .OrderBy(sidx + " " + input.sort)
251 251 .ToPagedListAsync(input.currentPage, input.pageSize);
  252 +
  253 + // 兜底:若累计字段未维护,列表会显示 0,这里按本页会员批量汇总开单/耗卡/退卡金额,避免 N+1
  254 + // 关联键:开单 lq_kd_kdjlb.kdhy / 耗卡 lq_xh_hyhk.hy / 退卡 lq_hytk_hytk.hy 均存会员 F_Id(即 LqKhxxEntity.Id)
  255 + if (data?.list != null && data.list.Any())
  256 + {
  257 + var memberIds = data.list.Select(x => x.id).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToArray();
  258 + if (memberIds.Length > 0)
  259 + {
  260 + var billingStats = await _db.Queryable<LqKdKdjlbEntity>()
  261 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
  262 + .In(x => x.Kdhy, memberIds)
  263 + .GroupBy(x => x.Kdhy)
  264 + .Select(x => new { MemberId = x.Kdhy, Amount = SqlFunc.AggregateSum(x.Sfyj) })
  265 + .ToListAsync();
  266 +
  267 + var consumeStats = await _db.Queryable<LqXhHyhkEntity>()
  268 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
  269 + .In(x => x.Hy, memberIds)
  270 + .GroupBy(x => x.Hy)
  271 + .Select(x => new { MemberId = x.Hy, Amount = SqlFunc.AggregateSum(x.Xfje) })
  272 + .ToListAsync();
  273 +
  274 + var refundStats = await _db.Queryable<LqHytkHytkEntity>()
  275 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
  276 + .In(x => x.Hy, memberIds)
  277 + .GroupBy(x => x.Hy)
  278 + .Select(x => new { MemberId = x.Hy, Amount = SqlFunc.AggregateSum(x.ActualRefundAmount) })
  279 + .ToListAsync();
  280 +
  281 + var billingMap = billingStats.ToDictionary(x => x.MemberId, x => Convert.ToDecimal(x.Amount));
  282 + var consumeMap = consumeStats.ToDictionary(x => x.MemberId, x => Convert.ToDecimal(x.Amount));
  283 + var refundMap = refundStats.ToDictionary(x => x.MemberId, x => Convert.ToDecimal(x.Amount));
  284 +
  285 + foreach (var row in data.list)
  286 + {
  287 + var billingAmount = billingMap.TryGetValue(row.id, out var b) ? b : 0m;
  288 + var consumeAmount = consumeMap.TryGetValue(row.id, out var c) ? c : 0m;
  289 + var refundAmount = refundMap.TryGetValue(row.id, out var r) ? r : 0m;
  290 +
  291 + row.totalBillingAmount = billingAmount;
  292 + row.totalConsumeAmount = consumeAmount;
  293 +
  294 + var remaining = billingAmount - consumeAmount - refundAmount;
  295 + row.remainingRightsAmount = remaining < 0m ? 0m : remaining;
  296 + }
  297 + }
  298 + }
252 299 return PageResult<LqKhxxListOutput>.SqlSugarPageResult(data);
253 300 }
254 301 #endregion
... ...
netcore/src/Modularity/Extend/NCC.Extend/MemberPortraitService.cs
... ... @@ -65,7 +65,7 @@ namespace NCC.Extend
65 65 /// ```
66 66 ///
67 67 /// 参数说明:
68   - /// - memberId: 会员主键ID(lq_khxx.F_Id
  68 + /// - memberId: 会员标识(支持会员主键ID:lq_khxx.F_Id,或会员编号/档案号:lq_khxx.Dah
69 69 /// </remarks>
70 70 /// <param name="memberId">会员ID</param>
71 71 /// <returns>会员画像概览数据</returns>
... ... @@ -84,13 +84,19 @@ namespace NCC.Extend
84 84 {
85 85 // 基础信息
86 86 var member = await _db.Queryable<LqKhxxEntity>()
87   - .FirstAsync(x => x.Id == memberId && x.IsEffective == StatusEnum.有效.GetHashCode());
  87 + .FirstAsync(x => (x.Id == memberId || x.Dah == memberId) && x.IsEffective == StatusEnum.有效.GetHashCode());
88 88  
89 89 if (member == null)
90 90 {
91 91 throw NCCException.Oh($"会员不存在或已失效, memberId={memberId}");
92 92 }
93 93  
  94 + // 兼容:部分业务表可能存会员主键Id,部分可能存会员档案号Dah
  95 + var memberKeys = new List<string> { member.Id, member.Dah }
  96 + .Where(x => !string.IsNullOrEmpty(x))
  97 + .Distinct()
  98 + .ToList();
  99 +
94 100 // 查询门店名称
95 101 string storeName = null;
96 102 if (!string.IsNullOrEmpty(member.Gsmd))
... ... @@ -233,24 +239,24 @@ namespace NCC.Extend
233 239 // 退卡总金额(如实体中未维护,则从退卡表统计)
234 240 var refundTotal = await _db.Queryable<LqHytkHytkEntity>()
235 241 .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
236   - .Where(x => x.Hy == member.Id)
  242 + .Where(x => memberKeys.Contains(x.Hy))
237 243 .SumAsync(x => (decimal?)x.Tkje) ?? 0m;
238 244  
239 245 behavior.TotalRefundAmount = refundTotal;
240 246  
241 247 // 开单统计
242 248 behavior.BillingCount = await _db.Queryable<LqKdKdjlbEntity>()
243   - .Where(x => x.IsEffective == 1 && x.Kdhy == member.Id)
  249 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Kdhy))
244 250 .CountAsync();
245 251  
246 252 if (behavior.BillingCount > 0)
247 253 {
248 254 behavior.FirstBillingTime = await _db.Queryable<LqKdKdjlbEntity>()
249   - .Where(x => x.IsEffective == 1 && x.Kdhy == member.Id)
  255 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Kdhy))
250 256 .MinAsync(x => x.Kdrq);
251 257  
252 258 behavior.LastBillingTime = await _db.Queryable<LqKdKdjlbEntity>()
253   - .Where(x => x.IsEffective == 1 && x.Kdhy == member.Id)
  259 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Kdhy))
254 260 .MaxAsync(x => x.Kdrq);
255 261  
256 262 behavior.AvgBillingAmount = behavior.TotalBillingAmount > 0 && behavior.BillingCount > 0
... ... @@ -266,17 +272,17 @@ namespace NCC.Extend
266 272  
267 273 // 消耗统计
268 274 behavior.ConsumeCount = await _db.Queryable<LqXhHyhkEntity>()
269   - .Where(x => x.IsEffective == 1 && x.Hy == member.Id)
  275 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Hy))
270 276 .CountAsync();
271 277  
272 278 if (behavior.ConsumeCount > 0)
273 279 {
274 280 behavior.FirstConsumeTime = await _db.Queryable<LqXhHyhkEntity>()
275   - .Where(x => x.IsEffective == 1 && x.Hy == member.Id)
  281 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Hy))
276 282 .MinAsync(x => x.Hksj);
277 283  
278 284 behavior.LastConsumeTime = await _db.Queryable<LqXhHyhkEntity>()
279   - .Where(x => x.IsEffective == 1 && x.Hy == member.Id)
  285 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Hy))
280 286 .MaxAsync(x => x.Hksj);
281 287  
282 288 behavior.AvgConsumeAmount = behavior.TotalConsumeAmount > 0 && behavior.ConsumeCount > 0
... ... @@ -293,7 +299,7 @@ namespace NCC.Extend
293 299 // 退卡次数
294 300 behavior.RefundCount = await _db.Queryable<LqHytkHytkEntity>()
295 301 .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
296   - .Where(x => x.Hy == member.Id)
  302 + .Where(x => memberKeys.Contains(x.Hy))
297 303 .CountAsync();
298 304  
299 305 // 近12个月趋势(以当前月份往前推12个月)
... ... @@ -310,11 +316,11 @@ namespace NCC.Extend
310 316 WHERE kd.F_IsEffective = 1
311 317 AND kd.kdrq >= @trendStart
312 318 AND kd.kdrq <= @trendEnd
313   - AND kd.kdhy = @memberId
  319 + AND kd.kdhy IN (@memberId1, @memberId2)
314 320 GROUP BY DATE_FORMAT(kd.kdrq, '%Y-%m')";
315 321  
316 322 var billingTrend = await _db.Ado.SqlQueryAsync<dynamic>(billingTrendSql,
317   - new { trendStart, trendEnd, memberId });
  323 + new { trendStart, trendEnd, memberId1 = member.Id, memberId2 = member.Dah });
318 324  
319 325 // 耗卡趋势
320 326 var consumeTrendSql = $@"
... ... @@ -325,11 +331,11 @@ namespace NCC.Extend
325 331 WHERE xh.F_IsEffective = 1
326 332 AND xh.hksj >= @trendStart
327 333 AND xh.hksj <= @trendEnd
328   - AND xh.hy = @memberId
  334 + AND xh.hy IN (@memberId1, @memberId2)
329 335 GROUP BY DATE_FORMAT(xh.hksj, '%Y-%m')";
330 336  
331 337 var consumeTrend = await _db.Ado.SqlQueryAsync<dynamic>(consumeTrendSql,
332   - new { trendStart, trendEnd, memberId });
  338 + new { trendStart, trendEnd, memberId1 = member.Id, memberId2 = member.Dah });
333 339  
334 340 // 退卡趋势
335 341 var refundTrendSql = $@"
... ... @@ -340,11 +346,11 @@ namespace NCC.Extend
340 346 WHERE hytk.F_IsEffective = 1
341 347 AND hytk.tksj >= @trendStart
342 348 AND hytk.tksj <= @trendEnd
343   - AND hytk.hy = @memberId
  349 + AND hytk.hy IN (@memberId1, @memberId2)
344 350 GROUP BY DATE_FORMAT(hytk.tksj, '%Y-%m')";
345 351  
346 352 var refundTrend = await _db.Ado.SqlQueryAsync<dynamic>(refundTrendSql,
347   - new { trendStart, trendEnd, memberId });
  353 + new { trendStart, trendEnd, memberId1 = member.Id, memberId2 = member.Dah });
348 354  
349 355 // 组合趋势数据,补全没有数据的月份
350 356 var trendDict = new Dictionary<string, MemberMonthlyTrendPoint>();
... ... @@ -389,7 +395,7 @@ namespace NCC.Extend
389 395  
390 396 // 查询权益明细
391 397 var baseItems = await _db.Queryable<LqKdPxmxEntity>()
392   - .Where(x => x.MemberId == memberId)
  398 + .Where(x => memberKeys.Contains(x.MemberId))
393 399 .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
394 400 .Select(x => new
395 401 {
... ... @@ -484,7 +490,7 @@ namespace NCC.Extend
484 490  
485 491 // 品项类型偏好(按品项类型统计消费金额和次数)
486 492 var itemTypeStats = await _db.Queryable<LqXhPxmxEntity>()
487   - .Where(x => x.MemberId == memberId)
  493 + .Where(x => memberKeys.Contains(x.MemberId))
488 494 .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
489 495 .GroupBy(x => x.SourceType)
490 496 .Select(x => new
... ... @@ -506,7 +512,7 @@ namespace NCC.Extend
506 512  
507 513 // 门店偏好(按门店统计消费金额和次数)
508 514 var storeStats = await _db.Queryable<LqXhHyhkEntity>()
509   - .Where(x => x.Hy == memberId)
  515 + .Where(x => memberKeys.Contains(x.Hy))
510 516 .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
511 517 .GroupBy(x => x.Md)
512 518 .Select(x => new
... ... @@ -517,6 +523,22 @@ namespace NCC.Extend
517 523 })
518 524 .ToListAsync();
519 525  
  526 + // 兜底:累计字段为0时,按明细实时汇总(口径与次数统计一致)
  527 + if (behavior.TotalBillingAmount <= 0m)
  528 + {
  529 + behavior.TotalBillingAmount = await _db.Queryable<LqKdKdjlbEntity>()
  530 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Kdhy))
  531 + .SumAsync(x => (decimal?)x.Sfyj) ?? 0m;
  532 + }
  533 + if (behavior.TotalConsumeAmount <= 0m)
  534 + {
  535 + behavior.TotalConsumeAmount = await _db.Queryable<LqXhHyhkEntity>()
  536 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode() && memberKeys.Contains(x.Hy))
  537 + .SumAsync(x => (decimal?)x.Xfje) ?? 0m;
  538 + }
  539 + // 剩余权益金额优先按权益明细计算(更可靠)
  540 + behavior.RemainingRightsAmount = remainingItems.Sum(x => x.RemainingValue);
  541 +
520 542 // 查询门店名称
521 543 var storeIds = storeStats.Select(x => x.StoreId).ToList();
522 544 var storeNames = await _db.Queryable<LqMdxxEntity>()
... ...