Commit d64f9fbdca5a77c3e94b21166fff22e0991fb268
1 parent
b1146e00
feat: 添加退卡明细查询和导出功能
- 新增退卡明细查询接口 - 支持多门店、会员、时间范围、品项、业绩类型、科美类型、来源类型、品项分类等筛选 - 返回字段:门店、会员信息、退卡时间、品项信息、金额、类型等 - 支持分页和排序 - 新增退卡明细导出接口 - 导出Excel文件,包含所有筛选条件 - 文件保存到项目根目录ExportFiles文件夹 - 优化退卡穿透统计接口 - 所有金额字段改为使用实退金额(F_ActualRefundAmount) - 包括门店分布、总计、转卡总计、金额最大/次数最多的人等统计 - 优化客户资料导出接口 - 添加消费等级、开单总金额、剩余权益总金额字段 - 添加首次到店时间、最后到店时间、到店天数、沉睡天数字段 - 消费等级显示为名称(D/C/B/A/A+/A++)
Showing
7 changed files
with
639 additions
and
11 deletions
ExportFiles/退卡明细_20251228193529.xls
0 → 100644
No preview for this file type
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqHytkHytk | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 退卡明细列表输出参数 | |
| 7 | + /// </summary> | |
| 8 | + public class RefundDetailListOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 门店ID | |
| 12 | + /// </summary> | |
| 13 | + public string storeId { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 门店名称 | |
| 17 | + /// </summary> | |
| 18 | + public string storeName { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 会员ID | |
| 22 | + /// </summary> | |
| 23 | + public string memberId { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 会员名称 | |
| 27 | + /// </summary> | |
| 28 | + public string memberName { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 会员电话 | |
| 32 | + /// </summary> | |
| 33 | + public string memberPhone { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 退卡时间 | |
| 37 | + /// </summary> | |
| 38 | + public DateTime? refundTime { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 退卡品项ID | |
| 42 | + /// </summary> | |
| 43 | + public string itemId { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 退卡品项名称 | |
| 47 | + /// </summary> | |
| 48 | + public string itemName { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 退卡数量 | |
| 52 | + /// </summary> | |
| 53 | + public decimal refundQuantity { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 单价 | |
| 57 | + /// </summary> | |
| 58 | + public decimal unitPrice { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 总价 | |
| 62 | + /// </summary> | |
| 63 | + public decimal totalPrice { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 业绩类型 | |
| 67 | + /// </summary> | |
| 68 | + public string performanceType { get; set; } | |
| 69 | + | |
| 70 | + /// <summary> | |
| 71 | + /// 科美类型 | |
| 72 | + /// </summary> | |
| 73 | + public string beautyType { get; set; } | |
| 74 | + | |
| 75 | + /// <summary> | |
| 76 | + /// 来源类型 | |
| 77 | + /// </summary> | |
| 78 | + public string sourceType { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 品项分类 | |
| 82 | + /// </summary> | |
| 83 | + public string itemCategory { get; set; } | |
| 84 | + } | |
| 85 | +} | |
| 86 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListQueryInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.LqHytkHytk | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 退卡明细列表查询输入参数 | |
| 8 | + /// </summary> | |
| 9 | + public class RefundDetailListQueryInput | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 当前页码 | |
| 13 | + /// </summary> | |
| 14 | + public int currentPage { get; set; } = 1; | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 每页数量 | |
| 18 | + /// </summary> | |
| 19 | + public int pageSize { get; set; } = 20; | |
| 20 | + | |
| 21 | + /// <summary> | |
| 22 | + /// 排序字段 | |
| 23 | + /// </summary> | |
| 24 | + public string sidx { get; set; } = "tksj"; | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 排序方式(asc/desc) | |
| 28 | + /// </summary> | |
| 29 | + public string sort { get; set; } = "desc"; | |
| 30 | + | |
| 31 | + /// <summary> | |
| 32 | + /// 门店ID列表(多门店筛选) | |
| 33 | + /// </summary> | |
| 34 | + public List<string> storeIds { get; set; } | |
| 35 | + | |
| 36 | + /// <summary> | |
| 37 | + /// 会员ID(会员筛选) | |
| 38 | + /// </summary> | |
| 39 | + public string memberId { get; set; } | |
| 40 | + | |
| 41 | + /// <summary> | |
| 42 | + /// 开始时间(时间范围筛选) | |
| 43 | + /// </summary> | |
| 44 | + public DateTime? startTime { get; set; } | |
| 45 | + | |
| 46 | + /// <summary> | |
| 47 | + /// 结束时间(时间范围筛选) | |
| 48 | + /// </summary> | |
| 49 | + public DateTime? endTime { get; set; } | |
| 50 | + | |
| 51 | + /// <summary> | |
| 52 | + /// 品项ID(品项筛选) | |
| 53 | + /// </summary> | |
| 54 | + public string itemId { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 业绩类型(业绩类型筛选) | |
| 58 | + /// </summary> | |
| 59 | + public string performanceType { get; set; } | |
| 60 | + | |
| 61 | + /// <summary> | |
| 62 | + /// 科美类型(科美类型筛选) | |
| 63 | + /// </summary> | |
| 64 | + public string beautyType { get; set; } | |
| 65 | + | |
| 66 | + /// <summary> | |
| 67 | + /// 来源类型(来源类型筛选) | |
| 68 | + /// </summary> | |
| 69 | + public string sourceType { get; set; } | |
| 70 | + | |
| 71 | + /// <summary> | |
| 72 | + /// 品项分类(品项分类筛选) | |
| 73 | + /// </summary> | |
| 74 | + public string itemCategory { get; set; } | |
| 75 | + } | |
| 76 | +} | |
| 77 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs
| 1 | 1 | using System; |
| 2 | +using System.Collections.Generic; | |
| 2 | 3 | using NCC.Code; |
| 3 | 4 | using NCC.Extend.Entitys.Enum; |
| 4 | 5 | |
| ... | ... | @@ -253,6 +254,26 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx |
| 253 | 254 | public int consumeLevel { get; set; } |
| 254 | 255 | |
| 255 | 256 | /// <summary> |
| 257 | + /// 消费等级名称 | |
| 258 | + /// </summary> | |
| 259 | + public string consumeLevelName | |
| 260 | + { | |
| 261 | + get | |
| 262 | + { | |
| 263 | + var levelMap = new Dictionary<int, string> | |
| 264 | + { | |
| 265 | + { 0, "D" }, | |
| 266 | + { 1, "C" }, | |
| 267 | + { 2, "B" }, | |
| 268 | + { 3, "A" }, | |
| 269 | + { 4, "A+" }, | |
| 270 | + { 5, "A++" } | |
| 271 | + }; | |
| 272 | + return levelMap.ContainsKey(consumeLevel) ? levelMap[consumeLevel] : "D"; | |
| 273 | + } | |
| 274 | + } | |
| 275 | + | |
| 276 | + /// <summary> | |
| 256 | 277 | /// 消费等级更新时间 |
| 257 | 278 | /// </summary> |
| 258 | 279 | public DateTime? consumeLevelUpdateTime { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
| 1 | 1 | using System; |
| 2 | 2 | using System.Collections.Generic; |
| 3 | +using System.IO; | |
| 3 | 4 | using System.Linq; |
| 5 | +using System.Reflection; | |
| 4 | 6 | using System.Threading.Tasks; |
| 5 | 7 | using Mapster; |
| 6 | 8 | using Microsoft.AspNetCore.Mvc; |
| ... | ... | @@ -26,6 +28,8 @@ using NCC.Extend.Entitys.lq_hytk_kjbsyj; |
| 26 | 28 | using NCC.Extend.Entitys.lq_hytk_mx; |
| 27 | 29 | using NCC.Extend.Entitys.lq_kd_pxmx; |
| 28 | 30 | using NCC.Extend.Entitys.lq_xmzl; |
| 31 | +using NCC.Extend.Entitys.lq_khxx; | |
| 32 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 29 | 33 | using NCC.Extend.Interfaces.LqHytkHytk; |
| 30 | 34 | using NCC.FriendlyException; |
| 31 | 35 | using NCC.JsonSerialization; |
| ... | ... | @@ -810,5 +814,445 @@ namespace NCC.Extend.LqHytkHytk |
| 810 | 814 | } |
| 811 | 815 | } |
| 812 | 816 | #endregion |
| 817 | + | |
| 818 | + #region 退卡明细查询 | |
| 819 | + /// <summary> | |
| 820 | + /// 获取退卡明细列表 | |
| 821 | + /// </summary> | |
| 822 | + /// <remarks> | |
| 823 | + /// 获取退卡明细列表,支持多门店、会员、时间范围、品项、业绩类型、科美类型、来源类型、品项分类等筛选 | |
| 824 | + /// | |
| 825 | + /// 示例请求: | |
| 826 | + /// ```json | |
| 827 | + /// GET /api/Extend/LqHytkHytk/refund-detail-list?storeIds=门店ID1,门店ID2&memberId=会员ID&startTime=2025-01-01&endTime=2025-12-31&currentPage=1&pageSize=20 | |
| 828 | + /// ``` | |
| 829 | + /// | |
| 830 | + /// 参数说明: | |
| 831 | + /// - storeIds: 门店ID列表(可选,多门店筛选) | |
| 832 | + /// - memberId: 会员ID(可选) | |
| 833 | + /// - startTime: 开始时间(可选,时间范围筛选) | |
| 834 | + /// - endTime: 结束时间(可选,时间范围筛选) | |
| 835 | + /// - itemId: 品项ID(可选) | |
| 836 | + /// - performanceType: 业绩类型(可选) | |
| 837 | + /// - beautyType: 科美类型(可选) | |
| 838 | + /// - sourceType: 来源类型(可选) | |
| 839 | + /// - itemCategory: 品项分类(可选) | |
| 840 | + /// - currentPage: 当前页码(必填) | |
| 841 | + /// - pageSize: 每页数量(必填) | |
| 842 | + /// </remarks> | |
| 843 | + /// <param name="input">查询参数</param> | |
| 844 | + /// <returns>分页的退卡明细列表</returns> | |
| 845 | + /// <response code="200">成功返回退卡明细列表</response> | |
| 846 | + /// <response code="400">参数错误</response> | |
| 847 | + /// <response code="500">服务器内部错误</response> | |
| 848 | + [HttpPost("refund-detail-list")] | |
| 849 | + public async Task<dynamic> GetRefundDetailList([FromBody] RefundDetailListQueryInput input) | |
| 850 | + { | |
| 851 | + try | |
| 852 | + { | |
| 853 | + if (input == null) | |
| 854 | + { | |
| 855 | + throw NCCException.Oh("参数不能为空"); | |
| 856 | + } | |
| 857 | + | |
| 858 | + // 1. 先查询退卡明细表 | |
| 859 | + var mxQuery = _db.Queryable<LqHytkMxEntity>() | |
| 860 | + .Where(mx => mx.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 861 | + .WhereIF(!string.IsNullOrEmpty(input.itemId), mx => mx.Px == input.itemId) | |
| 862 | + .WhereIF(!string.IsNullOrEmpty(input.performanceType), mx => mx.PerformanceType == input.performanceType) | |
| 863 | + .WhereIF(!string.IsNullOrEmpty(input.beautyType), mx => mx.BeautyType == input.beautyType) | |
| 864 | + .WhereIF(!string.IsNullOrEmpty(input.sourceType), mx => mx.SourceType == input.sourceType) | |
| 865 | + .WhereIF(!string.IsNullOrEmpty(input.itemCategory), mx => mx.ItemCategory == input.itemCategory) | |
| 866 | + .WhereIF(input.startTime.HasValue, mx => mx.Tksj.HasValue && mx.Tksj >= input.startTime.Value) | |
| 867 | + .WhereIF(input.endTime.HasValue, mx => mx.Tksj.HasValue && mx.Tksj <= input.endTime.Value); | |
| 868 | + | |
| 869 | + var mxList = await mxQuery.ToListAsync(); | |
| 870 | + | |
| 871 | + if (!mxList.Any()) | |
| 872 | + { | |
| 873 | + return PageResult<RefundDetailListOutput>.SqlSugarPageResult(new SqlSugarPagedList<RefundDetailListOutput>()); | |
| 874 | + } | |
| 875 | + | |
| 876 | + // 2. 批量查询退卡信息表 | |
| 877 | + var refundInfoIds = mxList.Select(x => x.RefundInfoId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); | |
| 878 | + var refundInfoList = new List<LqHytkHytkEntity>(); | |
| 879 | + if (refundInfoIds.Any()) | |
| 880 | + { | |
| 881 | + refundInfoList = await _db.Queryable<LqHytkHytkEntity>() | |
| 882 | + .Where(x => refundInfoIds.Contains(x.Id)) | |
| 883 | + .WhereIF(input.storeIds != null && input.storeIds.Any(), x => input.storeIds.Contains(x.Md)) | |
| 884 | + .WhereIF(!string.IsNullOrEmpty(input.memberId), x => x.Hy == input.memberId) | |
| 885 | + .WhereIF(input.startTime.HasValue && !mxList.Any(m => m.Tksj.HasValue), x => x.Tksj.HasValue && x.Tksj >= input.startTime.Value) | |
| 886 | + .WhereIF(input.endTime.HasValue && !mxList.Any(m => m.Tksj.HasValue), x => x.Tksj.HasValue && x.Tksj <= input.endTime.Value) | |
| 887 | + .ToListAsync(); | |
| 888 | + } | |
| 889 | + | |
| 890 | + var refundInfoDict = refundInfoList.ToDictionary(x => x.Id, x => x); | |
| 891 | + | |
| 892 | + // 3. 批量查询门店信息 | |
| 893 | + var storeIds = refundInfoList.Select(x => x.Md).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); | |
| 894 | + var storeDict = new Dictionary<string, string>(); | |
| 895 | + if (storeIds.Any()) | |
| 896 | + { | |
| 897 | + var stores = await _db.Queryable<LqMdxxEntity>() | |
| 898 | + .Where(x => storeIds.Contains(x.Id)) | |
| 899 | + .Select(x => new { x.Id, x.Dm }) | |
| 900 | + .ToListAsync(); | |
| 901 | + storeDict = stores.ToDictionary(x => x.Id, x => x.Dm ?? ""); | |
| 902 | + } | |
| 903 | + | |
| 904 | + // 4. 批量查询会员信息 | |
| 905 | + var memberIds = mxList.Select(x => x.MemberId).Where(x => !string.IsNullOrEmpty(x)) | |
| 906 | + .Concat(refundInfoList.Select(x => x.Hy).Where(x => !string.IsNullOrEmpty(x))) | |
| 907 | + .Distinct().ToList(); | |
| 908 | + var memberDict = new Dictionary<string, string>(); | |
| 909 | + if (memberIds.Any()) | |
| 910 | + { | |
| 911 | + var members = await _db.Queryable<LqKhxxEntity>() | |
| 912 | + .Where(x => memberIds.Contains(x.Id)) | |
| 913 | + .Select(x => new { x.Id, x.Sjh }) | |
| 914 | + .ToListAsync(); | |
| 915 | + memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? ""); | |
| 916 | + } | |
| 917 | + | |
| 918 | + // 5. 组装数据 | |
| 919 | + var resultList = new List<RefundDetailListOutput>(); | |
| 920 | + foreach (var mx in mxList) | |
| 921 | + { | |
| 922 | + var refundInfo = refundInfoDict.ContainsKey(mx.RefundInfoId) ? refundInfoDict[mx.RefundInfoId] : null; | |
| 923 | + | |
| 924 | + // 应用筛选条件 | |
| 925 | + if (input.storeIds != null && input.storeIds.Any()) | |
| 926 | + { | |
| 927 | + if (refundInfo == null || !input.storeIds.Contains(refundInfo.Md)) | |
| 928 | + continue; | |
| 929 | + } | |
| 930 | + if (!string.IsNullOrEmpty(input.memberId)) | |
| 931 | + { | |
| 932 | + if (mx.MemberId != input.memberId && (refundInfo == null || refundInfo.Hy != input.memberId)) | |
| 933 | + continue; | |
| 934 | + } | |
| 935 | + | |
| 936 | + var memberId = mx.MemberId ?? (refundInfo?.Hy); | |
| 937 | + var refundTime = mx.Tksj ?? refundInfo?.Tksj; | |
| 938 | + | |
| 939 | + resultList.Add(new RefundDetailListOutput | |
| 940 | + { | |
| 941 | + storeId = refundInfo?.Md, | |
| 942 | + storeName = refundInfo != null && storeDict.ContainsKey(refundInfo.Md) ? storeDict[refundInfo.Md] : "", | |
| 943 | + memberId = memberId, | |
| 944 | + memberName = refundInfo?.Hymc, | |
| 945 | + memberPhone = !string.IsNullOrEmpty(memberId) && memberDict.ContainsKey(memberId) ? memberDict[memberId] : "", | |
| 946 | + refundTime = refundTime, | |
| 947 | + itemId = mx.Px, | |
| 948 | + itemName = mx.Pxmc, | |
| 949 | + refundQuantity = mx.ProjectNumber, | |
| 950 | + unitPrice = mx.Pxjg ?? 0, | |
| 951 | + totalPrice = mx.Tkje ?? mx.TotalPrice ?? 0, | |
| 952 | + performanceType = mx.PerformanceType, | |
| 953 | + beautyType = mx.BeautyType, | |
| 954 | + sourceType = mx.SourceType, | |
| 955 | + itemCategory = mx.ItemCategory | |
| 956 | + }); | |
| 957 | + } | |
| 958 | + | |
| 959 | + // 6. 排序 | |
| 960 | + var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx; | |
| 961 | + var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; | |
| 962 | + | |
| 963 | + if (sort.ToLower() == "desc") | |
| 964 | + { | |
| 965 | + resultList = resultList.OrderByDescending(x => GetPropertyValue(x, sidx)).ToList(); | |
| 966 | + } | |
| 967 | + else | |
| 968 | + { | |
| 969 | + resultList = resultList.OrderBy(x => GetPropertyValue(x, sidx)).ToList(); | |
| 970 | + } | |
| 971 | + | |
| 972 | + // 7. 分页 | |
| 973 | + var total = resultList.Count; | |
| 974 | + var skip = (input.currentPage - 1) * input.pageSize; | |
| 975 | + var pagedList = resultList.Skip(skip).Take(input.pageSize).ToList(); | |
| 976 | + | |
| 977 | + var pageList = new SqlSugarPagedList<RefundDetailListOutput> | |
| 978 | + { | |
| 979 | + list = pagedList, | |
| 980 | + pagination = new PagedModel | |
| 981 | + { | |
| 982 | + PageIndex = input.currentPage, | |
| 983 | + PageSize = input.pageSize, | |
| 984 | + Total = total | |
| 985 | + } | |
| 986 | + }; | |
| 987 | + | |
| 988 | + return PageResult<RefundDetailListOutput>.SqlSugarPageResult(pageList); | |
| 989 | + } | |
| 990 | + catch (Exception ex) | |
| 991 | + { | |
| 992 | + throw NCCException.Oh($"获取退卡明细列表失败: {ex.Message}"); | |
| 993 | + } | |
| 994 | + } | |
| 995 | + | |
| 996 | + private object GetPropertyValue(RefundDetailListOutput obj, string propertyName) | |
| 997 | + { | |
| 998 | + var prop = typeof(RefundDetailListOutput).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); | |
| 999 | + return prop?.GetValue(obj) ?? ""; | |
| 1000 | + } | |
| 1001 | + | |
| 1002 | + /// <summary> | |
| 1003 | + /// 获取退卡明细列表(无分页,用于导出) | |
| 1004 | + /// </summary> | |
| 1005 | + /// <param name="input">查询参数</param> | |
| 1006 | + /// <returns>退卡明细列表</returns> | |
| 1007 | + [NonAction] | |
| 1008 | + public async Task<List<RefundDetailListOutput>> GetRefundDetailListNoPaging(RefundDetailListQueryInput input) | |
| 1009 | + { | |
| 1010 | + try | |
| 1011 | + { | |
| 1012 | + // 1. 先查询退卡明细表 | |
| 1013 | + var mxQuery = _db.Queryable<LqHytkMxEntity>() | |
| 1014 | + .Where(mx => mx.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1015 | + .WhereIF(!string.IsNullOrEmpty(input.itemId), mx => mx.Px == input.itemId) | |
| 1016 | + .WhereIF(!string.IsNullOrEmpty(input.performanceType), mx => mx.PerformanceType == input.performanceType) | |
| 1017 | + .WhereIF(!string.IsNullOrEmpty(input.beautyType), mx => mx.BeautyType == input.beautyType) | |
| 1018 | + .WhereIF(!string.IsNullOrEmpty(input.sourceType), mx => mx.SourceType == input.sourceType) | |
| 1019 | + .WhereIF(!string.IsNullOrEmpty(input.itemCategory), mx => mx.ItemCategory == input.itemCategory) | |
| 1020 | + .WhereIF(input.startTime.HasValue, mx => mx.Tksj.HasValue && mx.Tksj >= input.startTime.Value) | |
| 1021 | + .WhereIF(input.endTime.HasValue, mx => mx.Tksj.HasValue && mx.Tksj <= input.endTime.Value); | |
| 1022 | + | |
| 1023 | + var mxList = await mxQuery.ToListAsync(); | |
| 1024 | + | |
| 1025 | + if (!mxList.Any()) | |
| 1026 | + { | |
| 1027 | + return new List<RefundDetailListOutput>(); | |
| 1028 | + } | |
| 1029 | + | |
| 1030 | + // 2. 批量查询退卡信息表 | |
| 1031 | + var refundInfoIds = mxList.Select(x => x.RefundInfoId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); | |
| 1032 | + var refundInfoList = new List<LqHytkHytkEntity>(); | |
| 1033 | + if (refundInfoIds.Any()) | |
| 1034 | + { | |
| 1035 | + refundInfoList = await _db.Queryable<LqHytkHytkEntity>() | |
| 1036 | + .Where(x => refundInfoIds.Contains(x.Id)) | |
| 1037 | + .WhereIF(input.storeIds != null && input.storeIds.Any(), x => input.storeIds.Contains(x.Md)) | |
| 1038 | + .WhereIF(!string.IsNullOrEmpty(input.memberId), x => x.Hy == input.memberId) | |
| 1039 | + .WhereIF(input.startTime.HasValue && !mxList.Any(m => m.Tksj.HasValue), x => x.Tksj.HasValue && x.Tksj >= input.startTime.Value) | |
| 1040 | + .WhereIF(input.endTime.HasValue && !mxList.Any(m => m.Tksj.HasValue), x => x.Tksj.HasValue && x.Tksj <= input.endTime.Value) | |
| 1041 | + .ToListAsync(); | |
| 1042 | + } | |
| 1043 | + | |
| 1044 | + var refundInfoDict = refundInfoList.ToDictionary(x => x.Id, x => x); | |
| 1045 | + | |
| 1046 | + // 3. 批量查询门店信息 | |
| 1047 | + var storeIds = refundInfoList.Select(x => x.Md).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); | |
| 1048 | + var storeDict = new Dictionary<string, string>(); | |
| 1049 | + if (storeIds.Any()) | |
| 1050 | + { | |
| 1051 | + var stores = await _db.Queryable<LqMdxxEntity>() | |
| 1052 | + .Where(x => storeIds.Contains(x.Id)) | |
| 1053 | + .Select(x => new { x.Id, x.Dm }) | |
| 1054 | + .ToListAsync(); | |
| 1055 | + storeDict = stores.ToDictionary(x => x.Id, x => x.Dm ?? ""); | |
| 1056 | + } | |
| 1057 | + | |
| 1058 | + // 4. 批量查询会员信息 | |
| 1059 | + var memberIds = mxList.Select(x => x.MemberId).Where(x => !string.IsNullOrEmpty(x)) | |
| 1060 | + .Concat(refundInfoList.Select(x => x.Hy).Where(x => !string.IsNullOrEmpty(x))) | |
| 1061 | + .Distinct().ToList(); | |
| 1062 | + var memberDict = new Dictionary<string, string>(); | |
| 1063 | + if (memberIds.Any()) | |
| 1064 | + { | |
| 1065 | + var members = await _db.Queryable<LqKhxxEntity>() | |
| 1066 | + .Where(x => memberIds.Contains(x.Id)) | |
| 1067 | + .Select(x => new { x.Id, x.Sjh }) | |
| 1068 | + .ToListAsync(); | |
| 1069 | + memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? ""); | |
| 1070 | + } | |
| 1071 | + | |
| 1072 | + // 5. 组装数据 | |
| 1073 | + var resultList = new List<RefundDetailListOutput>(); | |
| 1074 | + foreach (var mx in mxList) | |
| 1075 | + { | |
| 1076 | + var refundInfo = refundInfoDict.ContainsKey(mx.RefundInfoId) ? refundInfoDict[mx.RefundInfoId] : null; | |
| 1077 | + | |
| 1078 | + // 应用筛选条件 | |
| 1079 | + if (input.storeIds != null && input.storeIds.Any()) | |
| 1080 | + { | |
| 1081 | + if (refundInfo == null || !input.storeIds.Contains(refundInfo.Md)) | |
| 1082 | + continue; | |
| 1083 | + } | |
| 1084 | + if (!string.IsNullOrEmpty(input.memberId)) | |
| 1085 | + { | |
| 1086 | + if (mx.MemberId != input.memberId && (refundInfo == null || refundInfo.Hy != input.memberId)) | |
| 1087 | + continue; | |
| 1088 | + } | |
| 1089 | + | |
| 1090 | + var memberId = mx.MemberId ?? (refundInfo?.Hy); | |
| 1091 | + var refundTime = mx.Tksj ?? refundInfo?.Tksj; | |
| 1092 | + | |
| 1093 | + resultList.Add(new RefundDetailListOutput | |
| 1094 | + { | |
| 1095 | + storeId = refundInfo?.Md, | |
| 1096 | + storeName = refundInfo != null && storeDict.ContainsKey(refundInfo.Md) ? storeDict[refundInfo.Md] : "", | |
| 1097 | + memberId = memberId, | |
| 1098 | + memberName = refundInfo?.Hymc, | |
| 1099 | + memberPhone = !string.IsNullOrEmpty(memberId) && memberDict.ContainsKey(memberId) ? memberDict[memberId] : "", | |
| 1100 | + refundTime = refundTime, | |
| 1101 | + itemId = mx.Px, | |
| 1102 | + itemName = mx.Pxmc, | |
| 1103 | + refundQuantity = mx.ProjectNumber, | |
| 1104 | + unitPrice = mx.Pxjg ?? 0, | |
| 1105 | + totalPrice = mx.Tkje ?? mx.TotalPrice ?? 0, | |
| 1106 | + performanceType = mx.PerformanceType, | |
| 1107 | + beautyType = mx.BeautyType, | |
| 1108 | + sourceType = mx.SourceType, | |
| 1109 | + itemCategory = mx.ItemCategory | |
| 1110 | + }); | |
| 1111 | + } | |
| 1112 | + | |
| 1113 | + // 6. 排序 | |
| 1114 | + var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx; | |
| 1115 | + var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; | |
| 1116 | + | |
| 1117 | + if (sort.ToLower() == "desc") | |
| 1118 | + { | |
| 1119 | + resultList = resultList.OrderByDescending(x => GetPropertyValue(x, sidx)).ToList(); | |
| 1120 | + } | |
| 1121 | + else | |
| 1122 | + { | |
| 1123 | + resultList = resultList.OrderBy(x => GetPropertyValue(x, sidx)).ToList(); | |
| 1124 | + } | |
| 1125 | + | |
| 1126 | + return resultList; | |
| 1127 | + } | |
| 1128 | + catch (Exception ex) | |
| 1129 | + { | |
| 1130 | + throw NCCException.Oh($"获取退卡明细列表失败: {ex.Message}"); | |
| 1131 | + } | |
| 1132 | + } | |
| 1133 | + #endregion | |
| 1134 | + | |
| 1135 | + #region 退卡明细导出 | |
| 1136 | + /// <summary> | |
| 1137 | + /// 导出退卡明细 | |
| 1138 | + /// </summary> | |
| 1139 | + /// <remarks> | |
| 1140 | + /// 导出退卡明细到Excel,支持多门店、会员、时间范围、品项、业绩类型、科美类型、来源类型、品项分类等筛选 | |
| 1141 | + /// | |
| 1142 | + /// 示例请求: | |
| 1143 | + /// ```json | |
| 1144 | + /// POST /api/Extend/LqHytkHytk/refund-detail-export | |
| 1145 | + /// { | |
| 1146 | + /// "storeIds": ["门店ID1", "门店ID2"], | |
| 1147 | + /// "memberId": "会员ID", | |
| 1148 | + /// "startTime": "2025-01-01T00:00:00", | |
| 1149 | + /// "endTime": "2025-12-31T23:59:59", | |
| 1150 | + /// "itemId": "品项ID", | |
| 1151 | + /// "performanceType": "业绩类型", | |
| 1152 | + /// "beautyType": "科美类型", | |
| 1153 | + /// "sourceType": "来源类型", | |
| 1154 | + /// "itemCategory": "品项分类" | |
| 1155 | + /// } | |
| 1156 | + /// ``` | |
| 1157 | + /// </remarks> | |
| 1158 | + /// <param name="input">查询参数</param> | |
| 1159 | + /// <returns>Excel文件下载信息</returns> | |
| 1160 | + /// <response code="200">成功返回Excel文件下载链接</response> | |
| 1161 | + /// <response code="400">参数错误</response> | |
| 1162 | + /// <response code="500">服务器内部错误</response> | |
| 1163 | + [HttpPost("refund-detail-export")] | |
| 1164 | + public async Task<dynamic> ExportRefundDetail([FromBody] RefundDetailListQueryInput input) | |
| 1165 | + { | |
| 1166 | + try | |
| 1167 | + { | |
| 1168 | + var userInfo = await _userManager.GetUserInfo(); | |
| 1169 | + var exportData = await GetRefundDetailListNoPaging(input); | |
| 1170 | + | |
| 1171 | + if (exportData == null || !exportData.Any()) | |
| 1172 | + { | |
| 1173 | + return new { name = "退卡明细.xls", url = "", message = "没有找到符合条件的数据" }; | |
| 1174 | + } | |
| 1175 | + | |
| 1176 | + // 定义导出字段 | |
| 1177 | + List<ParamsModel> paramList = | |
| 1178 | + "[{\"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<ParamsModel>(); | |
| 1179 | + | |
| 1180 | + ExcelConfig excelconfig = new ExcelConfig(); | |
| 1181 | + excelconfig.FileName = "退卡明细_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xls"; | |
| 1182 | + excelconfig.HeadFont = "微软雅黑"; | |
| 1183 | + excelconfig.HeadPoint = 10; | |
| 1184 | + excelconfig.IsAllSizeColumn = true; | |
| 1185 | + excelconfig.ColumnModel = new List<ExcelColumnModel>(); | |
| 1186 | + | |
| 1187 | + // 添加所有字段到导出列 | |
| 1188 | + foreach (var param in paramList) | |
| 1189 | + { | |
| 1190 | + excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = param.field, ExcelColumn = param.value }); | |
| 1191 | + } | |
| 1192 | + | |
| 1193 | + // 查找项目根目录 | |
| 1194 | + var baseDir = AppContext.BaseDirectory; | |
| 1195 | + var projectRoot = baseDir; | |
| 1196 | + var dir = new DirectoryInfo(baseDir); | |
| 1197 | + | |
| 1198 | + // 优先查找包含 .git 目录的目录(真正的项目根目录) | |
| 1199 | + while (dir != null && dir.Parent != null) | |
| 1200 | + { | |
| 1201 | + try | |
| 1202 | + { | |
| 1203 | + if (dir.GetDirectories(".git").Any()) | |
| 1204 | + { | |
| 1205 | + projectRoot = dir.FullName; | |
| 1206 | + break; | |
| 1207 | + } | |
| 1208 | + } | |
| 1209 | + catch | |
| 1210 | + { | |
| 1211 | + // 忽略访问错误,继续向上查找 | |
| 1212 | + } | |
| 1213 | + dir = dir.Parent; | |
| 1214 | + } | |
| 1215 | + | |
| 1216 | + // 如果没找到 .git 目录,再查找包含 .sln 文件的目录 | |
| 1217 | + if (projectRoot == baseDir) | |
| 1218 | + { | |
| 1219 | + dir = new DirectoryInfo(baseDir); | |
| 1220 | + while (dir != null && dir.Parent != null) | |
| 1221 | + { | |
| 1222 | + try | |
| 1223 | + { | |
| 1224 | + if (dir.GetFiles("*.sln").Any()) | |
| 1225 | + { | |
| 1226 | + projectRoot = dir.FullName; | |
| 1227 | + break; | |
| 1228 | + } | |
| 1229 | + } | |
| 1230 | + catch | |
| 1231 | + { | |
| 1232 | + // 忽略访问错误,继续向上查找 | |
| 1233 | + } | |
| 1234 | + dir = dir.Parent; | |
| 1235 | + } | |
| 1236 | + } | |
| 1237 | + | |
| 1238 | + // 在项目根目录下创建 ExportFiles 文件夹 | |
| 1239 | + var exportFilesPath = Path.Combine(projectRoot, "ExportFiles"); | |
| 1240 | + if (!Directory.Exists(exportFilesPath)) | |
| 1241 | + { | |
| 1242 | + Directory.CreateDirectory(exportFilesPath); | |
| 1243 | + } | |
| 1244 | + var addPath = Path.Combine(exportFilesPath, excelconfig.FileName); | |
| 1245 | + | |
| 1246 | + ExcelExportHelper<RefundDetailListOutput>.Export(exportData, excelconfig, addPath); | |
| 1247 | + var fileName = _userManager.UserId + "|" + addPath + "|xls"; | |
| 1248 | + var output = new { name = excelconfig.FileName, url = "/api/File/Download?encryption=" + DESCEncryption.Encrypt(fileName, "NCC") }; | |
| 1249 | + return output; | |
| 1250 | + } | |
| 1251 | + catch (Exception ex) | |
| 1252 | + { | |
| 1253 | + throw NCCException.Oh($"导出退卡明细失败: {ex.Message}"); | |
| 1254 | + } | |
| 1255 | + } | |
| 1256 | + #endregion | |
| 813 | 1257 | } |
| 814 | 1258 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
| ... | ... | @@ -562,7 +562,7 @@ namespace NCC.Extend.LqKhxx |
| 562 | 562 | exportData = await this.GetNoPagingList(input); |
| 563 | 563 | } |
| 564 | 564 | List<ParamsModel> paramList = |
| 565 | - "[{\"value\":\"客户编码\",\"field\":\"id\"},{\"value\":\"客户名称\",\"field\":\"khmc\"},{\"value\":\"手机号\",\"field\":\"sjh\"},{\"value\":\"档案号\",\"field\":\"dah\"},{\"value\":\"性别\",\"field\":\"xb\"},{\"value\":\"公众号状态\",\"field\":\"gzhzt\"},{\"value\":\"微信昵称\",\"field\":\"wxnc\"},{\"value\":\"小程序状态\",\"field\":\"wxxcxzt\"},{\"value\":\"最近登录时间\",\"field\":\"zjdlsj\"},{\"value\":\"客户目前归属\",\"field\":\"khmqgs\"},{\"value\":\"归属门店\",\"field\":\"gsmd\"},{\"value\":\"注册时间\",\"field\":\"zcsj\"},{\"value\":\"客户类型\",\"field\":\"khlx\"},{\"value\":\"客户阶段\",\"field\":\"khjd\"},{\"value\":\"客户消费\",\"field\":\"khxf\"},{\"value\":\"消费频次\",\"field\":\"xfpc\"},{\"value\":\"推荐人\",\"field\":\"tjr\"},{\"value\":\"负责顾问\",\"field\":\"fzgw\"},{\"value\":\"美容师\",\"field\":\"mrs\"},{\"value\":\"进店渠道\",\"field\":\"jdqd\"},{\"value\":\"联系地址\",\"field\":\"lxdz\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"阳历生日\",\"field\":\"yanglsr\"},{\"value\":\"阴历生日\",\"field\":\"yinlsr\"},{\"value\":\"年龄\",\"field\":\"ml\"},]".ToList<ParamsModel>(); | |
| 565 | + "[{\"value\":\"客户编码\",\"field\":\"id\"},{\"value\":\"客户名称\",\"field\":\"khmc\"},{\"value\":\"手机号\",\"field\":\"sjh\"},{\"value\":\"档案号\",\"field\":\"dah\"},{\"value\":\"性别\",\"field\":\"xb\"},{\"value\":\"公众号状态\",\"field\":\"gzhzt\"},{\"value\":\"微信昵称\",\"field\":\"wxnc\"},{\"value\":\"小程序状态\",\"field\":\"wxxcxzt\"},{\"value\":\"最近登录时间\",\"field\":\"zjdlsj\"},{\"value\":\"客户目前归属\",\"field\":\"khmqgs\"},{\"value\":\"归属门店\",\"field\":\"gsmd\"},{\"value\":\"注册时间\",\"field\":\"zcsj\"},{\"value\":\"客户类型\",\"field\":\"khlx\"},{\"value\":\"客户阶段\",\"field\":\"khjd\"},{\"value\":\"客户消费\",\"field\":\"khxf\"},{\"value\":\"消费频次\",\"field\":\"xfpc\"},{\"value\":\"推荐人\",\"field\":\"tjr\"},{\"value\":\"负责顾问\",\"field\":\"fzgw\"},{\"value\":\"美容师\",\"field\":\"mrs\"},{\"value\":\"进店渠道\",\"field\":\"jdqd\"},{\"value\":\"联系地址\",\"field\":\"lxdz\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"阳历生日\",\"field\":\"yanglsr\"},{\"value\":\"阴历生日\",\"field\":\"yinlsr\"},{\"value\":\"年龄\",\"field\":\"ml\"},{\"value\":\"消费等级\",\"field\":\"consumeLevelName\"},{\"value\":\"开单总金额\",\"field\":\"totalBillingAmount\"},{\"value\":\"剩余权益总金额\",\"field\":\"remainingRightsAmount\"},{\"value\":\"首次到店时间\",\"field\":\"firstVisitTime\"},{\"value\":\"最后到店时间\",\"field\":\"lastVisitTime\"},{\"value\":\"到店天数\",\"field\":\"visitDays\"},{\"value\":\"沉睡天数\",\"field\":\"sleepDays\"},]".ToList<ParamsModel>(); | |
| 566 | 566 | ExcelConfig excelconfig = new ExcelConfig(); |
| 567 | 567 | excelconfig.FileName = "客户资料.xls"; |
| 568 | 568 | excelconfig.HeadFont = "微软雅黑"; | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
| ... | ... | @@ -4351,11 +4351,11 @@ namespace NCC.Extend |
| 4351 | 4351 | storeFilter = $" AND hytk.md IN ('{storeIdsStr}')"; |
| 4352 | 4352 | } |
| 4353 | 4353 | |
| 4354 | - // 1. 各门店退卡金额分布(不包含转卡)- 使用原生SQL确保正确 | |
| 4354 | + // 1. 各门店退卡金额分布(不包含转卡)- 使用原生SQL确保正确,使用实退金额 | |
| 4355 | 4355 | var storeDistributionSql = $@" |
| 4356 | 4356 | SELECT |
| 4357 | 4357 | hytk.md as StoreId, |
| 4358 | - COALESCE(SUM(CAST(hytk.tkje AS DECIMAL(18,2))), 0) as RefundAmount, | |
| 4358 | + COALESCE(SUM(CAST(COALESCE(hytk.F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as RefundAmount, | |
| 4359 | 4359 | COUNT(hytk.F_Id) as RefundCount |
| 4360 | 4360 | FROM lq_hytk_hytk hytk |
| 4361 | 4361 | WHERE hytk.F_IsEffective = 1 |
| ... | ... | @@ -4390,10 +4390,10 @@ namespace NCC.Extend |
| 4390 | 4390 | RefundCount = Convert.ToInt32(x.RefundCount ?? 0) |
| 4391 | 4391 | }).OrderByDescending(x => x.RefundAmount).ToList(); |
| 4392 | 4392 | |
| 4393 | - // 2. 总计数据 - 使用原生SQL确保正确(包含转卡,与驾驶舱保持一致) | |
| 4393 | + // 2. 总计数据 - 使用原生SQL确保正确(包含转卡,与驾驶舱保持一致),使用实退金额 | |
| 4394 | 4394 | var totalStatsSql = $@" |
| 4395 | 4395 | SELECT |
| 4396 | - COALESCE(SUM(CAST(tkje AS DECIMAL(18,2))), 0) as TotalRefundAmount, | |
| 4396 | + COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalRefundAmount, | |
| 4397 | 4397 | COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalActualRefundAmount, |
| 4398 | 4398 | COUNT(F_Id) as TotalRefundCount |
| 4399 | 4399 | FROM lq_hytk_hytk |
| ... | ... | @@ -4409,10 +4409,10 @@ namespace NCC.Extend |
| 4409 | 4409 | var totalActualRefundAmount = totalStats != null ? Convert.ToDecimal(totalStats.TotalActualRefundAmount ?? 0) : 0m; |
| 4410 | 4410 | var totalRefundCount = totalStats != null ? Convert.ToInt32(totalStats.TotalRefundCount ?? 0) : 0; |
| 4411 | 4411 | |
| 4412 | - // 转卡总计(单独统计)- 使用原生SQL | |
| 4412 | + // 转卡总计(单独统计)- 使用原生SQL,使用实退金额 | |
| 4413 | 4413 | var transferCardSql = $@" |
| 4414 | 4414 | SELECT |
| 4415 | - COALESCE(SUM(CAST(tkje AS DECIMAL(18,2))), 0) as TotalTransferAmount, | |
| 4415 | + COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalTransferAmount, | |
| 4416 | 4416 | COUNT(F_Id) as TotalTransferCount |
| 4417 | 4417 | FROM lq_hytk_hytk |
| 4418 | 4418 | WHERE F_IsEffective = 1 |
| ... | ... | @@ -4427,13 +4427,13 @@ namespace NCC.Extend |
| 4427 | 4427 | var totalTransferAmount = transferCardStats != null ? Convert.ToDecimal(transferCardStats.TotalTransferAmount ?? 0) : 0m; |
| 4428 | 4428 | var totalTransferCount = transferCardStats != null ? Convert.ToInt32(transferCardStats.TotalTransferCount ?? 0) : 0; |
| 4429 | 4429 | |
| 4430 | - // 3. 退卡金额最大的人 - 使用原生SQL | |
| 4430 | + // 3. 退卡金额最大的人 - 使用原生SQL,使用实退金额 | |
| 4431 | 4431 | // 注意:按 hymc(会员名称)分组,因为同一个会员可能有不同的 hy(会员ID) |
| 4432 | 4432 | var maxAmountPersonSql = $@" |
| 4433 | 4433 | SELECT |
| 4434 | 4434 | MAX(hy) as MemberId, |
| 4435 | 4435 | hymc as MemberName, |
| 4436 | - COALESCE(SUM(CAST(tkje AS DECIMAL(18,2))), 0) as TotalRefundAmount, | |
| 4436 | + COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalRefundAmount, | |
| 4437 | 4437 | COUNT(F_Id) as RefundCount |
| 4438 | 4438 | FROM lq_hytk_hytk |
| 4439 | 4439 | WHERE F_IsEffective = 1 |
| ... | ... | @@ -4451,13 +4451,13 @@ namespace NCC.Extend |
| 4451 | 4451 | var maxAmountPersonResult = await _db.Ado.SqlQueryAsync<dynamic>(maxAmountPersonSql); |
| 4452 | 4452 | var maxAmountPerson = maxAmountPersonResult?.FirstOrDefault(); |
| 4453 | 4453 | |
| 4454 | - // 4. 退卡次数最多的人 - 使用原生SQL(按退卡单数统计,一个退卡单算一次) | |
| 4454 | + // 4. 退卡次数最多的人 - 使用原生SQL(按退卡单数统计,一个退卡单算一次),使用实退金额 | |
| 4455 | 4455 | // 注意:按 hymc(会员名称)分组,因为同一个会员可能有不同的 hy(会员ID) |
| 4456 | 4456 | var maxCountPersonSql = $@" |
| 4457 | 4457 | SELECT |
| 4458 | 4458 | MAX(hy) as MemberId, |
| 4459 | 4459 | hymc as MemberName, |
| 4460 | - COALESCE(SUM(CAST(tkje AS DECIMAL(18,2))), 0) as TotalRefundAmount, | |
| 4460 | + COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalRefundAmount, | |
| 4461 | 4461 | COUNT(F_Id) as RefundCount |
| 4462 | 4462 | FROM lq_hytk_hytk |
| 4463 | 4463 | WHERE F_IsEffective = 1 | ... | ... |