diff --git a/antis-ncc-admin/src/views/statisticsList/form21.vue b/antis-ncc-admin/src/views/statisticsList/form21.vue index 54b8fff..f831533 100644 --- a/antis-ncc-admin/src/views/statisticsList/form21.vue +++ b/antis-ncc-admin/src/views/statisticsList/form21.vue @@ -270,10 +270,10 @@ - + @@ -281,6 +281,12 @@ ¥{{ formatMoney(scope.row.healthCoachPerformance) }} + + + - /// 健康师账号 + /// 健康师账号(保留字段,兼容旧版本,显示第一个健康师) /// public string healthCoachId { get; set; } /// - /// 健康师姓名 + /// 健康师姓名(多个健康师用顿号分隔) /// public string healthCoachName { get; set; } /// - /// 健康师业绩 + /// 健康师业绩(保留字段,兼容旧版本,显示第一个健康师业绩) /// public decimal healthCoachPerformance { get; set; } + + /// + /// 实际退款金额(所有健康师业绩之和) + /// + public decimal actualRefundAmount { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs index f2c9210..452ff99 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs @@ -916,39 +916,36 @@ namespace NCC.Extend.LqHytkHytk memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? ""); } - // 5. 批量查询健康师业绩信息 - var billingItemIds = mxList.Select(x => x.BillingItemId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); - var healthCoachDict = new Dictionary(); - if (billingItemIds.Any()) + // 5. 批量查询退款健康师业绩信息(从退款健康师业绩表查询,而不是开单健康师业绩表) + var mxIds = mxList.Select(x => x.Id).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); + // 使用字典存储每个退款明细对应的多个健康师信息 + var healthCoachDict = new Dictionary>(); + var actualRefundAmountDict = new Dictionary(); + + if (mxIds.Any()) { - // 通过开单品项明细ID查询开单品项明细,获取开单编号和品项明细ID - var pxmxList = await _db.Queryable() - .Where(x => billingItemIds.Contains(x.Id)) - .Select(x => new { x.Id, x.Glkdbh }) + // 通过退卡品相表ID(F_CardReturn)查询退款健康师业绩 + var jksyjList = await _db.Queryable() + .Where(x => mxIds.Contains(x.CardReturn) && x.IsEffective == StatusEnum.有效.GetHashCode()) + .Select(x => new { x.CardReturn, x.Jkszh, x.Jksxm, x.Jksyj }) .ToListAsync(); - if (pxmxList.Any()) + // 按退款明细ID分组,建立映射 + foreach (var jksyj in jksyjList) { - var glkdbhList = pxmxList.Select(x => x.Glkdbh).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); - var pxmxIdList = pxmxList.Select(x => x.Id).Distinct().ToList(); + if (string.IsNullOrEmpty(jksyj.CardReturn)) + continue; - // 通过开单编号和品项明细ID查询健康师业绩 - var jksyjList = await _db.Queryable() - .Where(x => glkdbhList.Contains(x.Glkdbh) && pxmxIdList.Contains(x.Kdpxid) && x.IsEffective == 1) - .Select(x => new { x.Glkdbh, x.Kdpxid, x.Jkszh, x.Jksxm, x.Jksyj }) - .ToListAsync(); + var jksyjValue = jksyj.Jksyj ?? 0m; - // 建立开单品项明细ID到健康师信息的映射 - foreach (var jksyj in jksyjList) + if (!healthCoachDict.ContainsKey(jksyj.CardReturn)) { - // 找到对应的开单品项明细ID - var pxmxId = pxmxList.FirstOrDefault(x => x.Glkdbh == jksyj.Glkdbh && x.Id == jksyj.Kdpxid)?.Id; - if (!string.IsNullOrEmpty(pxmxId)) - { - var jksyjValue = decimal.TryParse(jksyj.Jksyj, out var perf) ? perf : 0; - healthCoachDict[pxmxId] = (jksyj.Jkszh ?? "", jksyj.Jksxm ?? "", jksyjValue); - } + healthCoachDict[jksyj.CardReturn] = new List<(string Id, string Name, decimal Performance)>(); + actualRefundAmountDict[jksyj.CardReturn] = 0m; } + + healthCoachDict[jksyj.CardReturn].Add((jksyj.Jkszh ?? "", jksyj.Jksxm ?? "", jksyjValue)); + actualRefundAmountDict[jksyj.CardReturn] += jksyjValue; } } @@ -957,7 +954,7 @@ namespace NCC.Extend.LqHytkHytk foreach (var mx in mxList) { var refundInfo = refundInfoDict.ContainsKey(mx.RefundInfoId) ? refundInfoDict[mx.RefundInfoId] : null; - + // 应用筛选条件 if (input.storeIds != null && input.storeIds.Any()) { @@ -973,10 +970,29 @@ namespace NCC.Extend.LqHytkHytk var memberId = mx.MemberId ?? (refundInfo?.Hy); var refundTime = mx.Tksj ?? refundInfo?.Tksj; - // 获取健康师信息 - var healthCoachInfo = !string.IsNullOrEmpty(mx.BillingItemId) && healthCoachDict.ContainsKey(mx.BillingItemId) - ? healthCoachDict[mx.BillingItemId] - : (Id: "", Name: "", Performance: 0m); + // 获取健康师信息(支持多个健康师) + var healthCoachList = healthCoachDict.ContainsKey(mx.Id) + ? healthCoachDict[mx.Id] + : new List<(string Id, string Name, decimal Performance)>(); + + // 合并多个健康师姓名和业绩(格式:姓名(业绩),用顿号分隔) + var healthCoachNames = ""; + if (healthCoachList.Any()) + { + var healthCoachItems = healthCoachList + .Where(h => !string.IsNullOrEmpty(h.Name)) + .Select(h => $"{h.Name}({h.Performance:F2})") + .ToList(); + healthCoachNames = string.Join("、", healthCoachItems); + } + + // 获取第一个健康师信息(兼容旧版本) + var firstHealthCoach = healthCoachList.FirstOrDefault(); + + // 获取实际退款金额(所有健康师业绩之和) + var actualRefundAmount = actualRefundAmountDict.ContainsKey(mx.Id) + ? actualRefundAmountDict[mx.Id] + : 0m; resultList.Add(new RefundDetailListOutput { @@ -995,16 +1011,17 @@ namespace NCC.Extend.LqHytkHytk beautyType = mx.BeautyType, sourceType = mx.SourceType, itemCategory = mx.ItemCategory, - healthCoachId = healthCoachInfo.Id, - healthCoachName = healthCoachInfo.Name, - healthCoachPerformance = healthCoachInfo.Performance + healthCoachId = firstHealthCoach.Id, + healthCoachName = healthCoachNames, + healthCoachPerformance = firstHealthCoach.Performance, + actualRefundAmount = actualRefundAmount }); } // 7. 排序 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx; var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; - + if (sort.ToLower() == "desc") { resultList = resultList.OrderByDescending(x => GetPropertyValue(x, sidx)).ToList(); @@ -1114,39 +1131,36 @@ namespace NCC.Extend.LqHytkHytk memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? ""); } - // 5. 批量查询健康师业绩信息 - var billingItemIds = mxList.Select(x => x.BillingItemId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); - var healthCoachDict = new Dictionary(); - if (billingItemIds.Any()) + // 5. 批量查询退款健康师业绩信息(从退款健康师业绩表查询,而不是开单健康师业绩表) + var mxIds = mxList.Select(x => x.Id).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); + // 使用字典存储每个退款明细对应的多个健康师信息 + var healthCoachDict = new Dictionary>(); + var actualRefundAmountDict = new Dictionary(); + + if (mxIds.Any()) { - // 通过开单品项明细ID查询开单品项明细,获取开单编号和品项明细ID - var pxmxList = await _db.Queryable() - .Where(x => billingItemIds.Contains(x.Id)) - .Select(x => new { x.Id, x.Glkdbh }) + // 通过退卡品相表ID(F_CardReturn)查询退款健康师业绩 + var jksyjList = await _db.Queryable() + .Where(x => mxIds.Contains(x.CardReturn) && x.IsEffective == StatusEnum.有效.GetHashCode()) + .Select(x => new { x.CardReturn, x.Jkszh, x.Jksxm, x.Jksyj }) .ToListAsync(); - if (pxmxList.Any()) + // 按退款明细ID分组,建立映射 + foreach (var jksyj in jksyjList) { - var glkdbhList = pxmxList.Select(x => x.Glkdbh).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); - var pxmxIdList = pxmxList.Select(x => x.Id).Distinct().ToList(); + if (string.IsNullOrEmpty(jksyj.CardReturn)) + continue; - // 通过开单编号和品项明细ID查询健康师业绩 - var jksyjList = await _db.Queryable() - .Where(x => glkdbhList.Contains(x.Glkdbh) && pxmxIdList.Contains(x.Kdpxid) && x.IsEffective == 1) - .Select(x => new { x.Glkdbh, x.Kdpxid, x.Jkszh, x.Jksxm, x.Jksyj }) - .ToListAsync(); + var jksyjValue = jksyj.Jksyj ?? 0m; - // 建立开单品项明细ID到健康师信息的映射 - foreach (var jksyj in jksyjList) + if (!healthCoachDict.ContainsKey(jksyj.CardReturn)) { - // 找到对应的开单品项明细ID - var pxmxId = pxmxList.FirstOrDefault(x => x.Glkdbh == jksyj.Glkdbh && x.Id == jksyj.Kdpxid)?.Id; - if (!string.IsNullOrEmpty(pxmxId)) - { - var jksyjValue = decimal.TryParse(jksyj.Jksyj, out var perf) ? perf : 0; - healthCoachDict[pxmxId] = (jksyj.Jkszh ?? "", jksyj.Jksxm ?? "", jksyjValue); - } + healthCoachDict[jksyj.CardReturn] = new List<(string Id, string Name, decimal Performance)>(); + actualRefundAmountDict[jksyj.CardReturn] = 0m; } + + healthCoachDict[jksyj.CardReturn].Add((jksyj.Jkszh ?? "", jksyj.Jksxm ?? "", jksyjValue)); + actualRefundAmountDict[jksyj.CardReturn] += jksyjValue; } } @@ -1155,7 +1169,7 @@ namespace NCC.Extend.LqHytkHytk foreach (var mx in mxList) { var refundInfo = refundInfoDict.ContainsKey(mx.RefundInfoId) ? refundInfoDict[mx.RefundInfoId] : null; - + // 应用筛选条件 if (input.storeIds != null && input.storeIds.Any()) { @@ -1171,10 +1185,29 @@ namespace NCC.Extend.LqHytkHytk var memberId = mx.MemberId ?? (refundInfo?.Hy); var refundTime = mx.Tksj ?? refundInfo?.Tksj; - // 获取健康师信息 - var healthCoachInfo = !string.IsNullOrEmpty(mx.BillingItemId) && healthCoachDict.ContainsKey(mx.BillingItemId) - ? healthCoachDict[mx.BillingItemId] - : (Id: "", Name: "", Performance: 0m); + // 获取健康师信息(支持多个健康师) + var healthCoachList = healthCoachDict.ContainsKey(mx.Id) + ? healthCoachDict[mx.Id] + : new List<(string Id, string Name, decimal Performance)>(); + + // 合并多个健康师姓名和业绩(格式:姓名(业绩),用顿号分隔) + var healthCoachNames = ""; + if (healthCoachList.Any()) + { + var healthCoachItems = healthCoachList + .Where(h => !string.IsNullOrEmpty(h.Name)) + .Select(h => $"{h.Name}({h.Performance:F2})") + .ToList(); + healthCoachNames = string.Join("、", healthCoachItems); + } + + // 获取第一个健康师信息(兼容旧版本) + var firstHealthCoach = healthCoachList.FirstOrDefault(); + + // 获取实际退款金额(所有健康师业绩之和) + var actualRefundAmount = actualRefundAmountDict.ContainsKey(mx.Id) + ? actualRefundAmountDict[mx.Id] + : 0m; resultList.Add(new RefundDetailListOutput { @@ -1193,16 +1226,17 @@ namespace NCC.Extend.LqHytkHytk beautyType = mx.BeautyType, sourceType = mx.SourceType, itemCategory = mx.ItemCategory, - healthCoachId = healthCoachInfo.Id, - healthCoachName = healthCoachInfo.Name, - healthCoachPerformance = healthCoachInfo.Performance + healthCoachId = firstHealthCoach.Id, + healthCoachName = healthCoachNames, + healthCoachPerformance = firstHealthCoach.Performance, + actualRefundAmount = actualRefundAmount }); } // 7. 排序 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx; var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; - + if (sort.ToLower() == "desc") { resultList = resultList.OrderByDescending(x => GetPropertyValue(x, sidx)).ToList(); @@ -1264,7 +1298,7 @@ namespace NCC.Extend.LqHytkHytk // 定义导出字段 List paramList = - "[{\"value\":\"门店\",\"field\":\"storeName\"},{\"value\":\"会员ID\",\"field\":\"memberId\"},{\"value\":\"会员名称\",\"field\":\"memberName\"},{\"value\":\"会员电话\",\"field\":\"memberPhone\"},{\"value\":\"退卡时间\",\"field\":\"refundTime\"},{\"value\":\"退卡品项ID\",\"field\":\"itemId\"},{\"value\":\"退卡品项名称\",\"field\":\"itemName\"},{\"value\":\"退卡数量\",\"field\":\"refundQuantity\"},{\"value\":\"单价\",\"field\":\"unitPrice\"},{\"value\":\"总价\",\"field\":\"totalPrice\"},{\"value\":\"业绩类型\",\"field\":\"performanceType\"},{\"value\":\"科美类型\",\"field\":\"beautyType\"},{\"value\":\"来源类型\",\"field\":\"sourceType\"},{\"value\":\"品项分类\",\"field\":\"itemCategory\"},]".ToList(); + "[{\"value\":\"门店\",\"field\":\"storeName\"},{\"value\":\"会员ID\",\"field\":\"memberId\"},{\"value\":\"会员名称\",\"field\":\"memberName\"},{\"value\":\"会员电话\",\"field\":\"memberPhone\"},{\"value\":\"退卡时间\",\"field\":\"refundTime\"},{\"value\":\"退卡品项ID\",\"field\":\"itemId\"},{\"value\":\"退卡品项名称\",\"field\":\"itemName\"},{\"value\":\"退卡数量\",\"field\":\"refundQuantity\"},{\"value\":\"单价\",\"field\":\"unitPrice\"},{\"value\":\"总价\",\"field\":\"totalPrice\"},{\"value\":\"业绩类型\",\"field\":\"performanceType\"},{\"value\":\"科美类型\",\"field\":\"beautyType\"},{\"value\":\"来源类型\",\"field\":\"sourceType\"},{\"value\":\"品项分类\",\"field\":\"itemCategory\"},{\"value\":\"健康师\",\"field\":\"healthCoachName\"},{\"value\":\"健康师业绩\",\"field\":\"healthCoachPerformance\"},]".ToList(); ExcelConfig excelconfig = new ExcelConfig(); excelconfig.FileName = "退卡明细_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xls"; diff --git a/test_refund_detail.sh b/test_refund_detail.sh new file mode 100755 index 0000000..cd2c406 --- /dev/null +++ b/test_refund_detail.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +# 测试退款明细列表接口 + +echo "=== 测试退款明细列表接口 ===" +echo "" + +# 步骤1: 获取Token +echo "步骤1: 获取Token..." +TOKEN_RESPONSE=$(curl -s -X POST "http://localhost:2011/api/oauth/Login" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "account=admin&password=e10adc3949ba59abbe56e057f20f883e") + +TOKEN=$(echo "$TOKEN_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data['data']['token'])" 2>/dev/null) + +if [ -z "$TOKEN" ]; then + echo "❌ Token获取失败" + exit 1 +fi + +echo "✅ Token获取成功" +echo "" + +# 步骤2: 调用退款明细列表接口(2025年11月数据) +echo "步骤2: 调用退款明细列表接口(2025年11月数据)..." +echo "接口: POST /api/Extend/LqHytkHytk/refund-detail-list" + +RESPONSE=$(curl -s -X POST "http://localhost:2011/api/Extend/LqHytkHytk/refund-detail-list" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "currentPage": 1, + "pageSize": 5, + "startTime": "2025-11-01T00:00:00", + "endTime": "2025-11-30T23:59:59" + }') + +echo "$RESPONSE" | python3 -c " +import sys, json +try: + data = json.load(sys.stdin) + print('状态码:', data.get('code')) + print('消息:', data.get('msg')) + items = data.get('data', {}).get('list', []) + print('返回数据条数:', len(items)) + if items: + print('\\n前3条数据详情:') + for i, item in enumerate(items[:3], 1): + print(f'\\n第{i}条:') + print(f' 门店名称: {item.get(\"storeName\", \"N/A\")}') + print(f' 会员名称: {item.get(\"memberName\", \"N/A\")}') + print(f' 品项名称: {item.get(\"itemName\", \"N/A\")}') + print(f' 健康师姓名: {item.get(\"healthCoachName\", \"N/A\")}') + print(f' 健康师业绩: {item.get(\"healthCoachPerformance\", \"N/A\")}') + print(f' 实际退款金额: {item.get(\"actualRefundAmount\", \"N/A\")}') +except Exception as e: + print('解析错误:', str(e)) + print('原始响应:') + print(sys.stdin.read()) +" 2>/dev/null + +echo "" +echo "=== 测试完成 ===" +