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 | using System; | 1 | using System; |
| 2 | +using System.Collections.Generic; | ||
| 2 | using NCC.Code; | 3 | using NCC.Code; |
| 3 | using NCC.Extend.Entitys.Enum; | 4 | using NCC.Extend.Entitys.Enum; |
| 4 | 5 | ||
| @@ -253,6 +254,26 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx | @@ -253,6 +254,26 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx | ||
| 253 | public int consumeLevel { get; set; } | 254 | public int consumeLevel { get; set; } |
| 254 | 255 | ||
| 255 | /// <summary> | 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 | /// </summary> | 278 | /// </summary> |
| 258 | public DateTime? consumeLevelUpdateTime { get; set; } | 279 | public DateTime? consumeLevelUpdateTime { get; set; } |
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
| 1 | using System; | 1 | using System; |
| 2 | using System.Collections.Generic; | 2 | using System.Collections.Generic; |
| 3 | +using System.IO; | ||
| 3 | using System.Linq; | 4 | using System.Linq; |
| 5 | +using System.Reflection; | ||
| 4 | using System.Threading.Tasks; | 6 | using System.Threading.Tasks; |
| 5 | using Mapster; | 7 | using Mapster; |
| 6 | using Microsoft.AspNetCore.Mvc; | 8 | using Microsoft.AspNetCore.Mvc; |
| @@ -26,6 +28,8 @@ using NCC.Extend.Entitys.lq_hytk_kjbsyj; | @@ -26,6 +28,8 @@ using NCC.Extend.Entitys.lq_hytk_kjbsyj; | ||
| 26 | using NCC.Extend.Entitys.lq_hytk_mx; | 28 | using NCC.Extend.Entitys.lq_hytk_mx; |
| 27 | using NCC.Extend.Entitys.lq_kd_pxmx; | 29 | using NCC.Extend.Entitys.lq_kd_pxmx; |
| 28 | using NCC.Extend.Entitys.lq_xmzl; | 30 | using NCC.Extend.Entitys.lq_xmzl; |
| 31 | +using NCC.Extend.Entitys.lq_khxx; | ||
| 32 | +using NCC.Extend.Entitys.lq_mdxx; | ||
| 29 | using NCC.Extend.Interfaces.LqHytkHytk; | 33 | using NCC.Extend.Interfaces.LqHytkHytk; |
| 30 | using NCC.FriendlyException; | 34 | using NCC.FriendlyException; |
| 31 | using NCC.JsonSerialization; | 35 | using NCC.JsonSerialization; |
| @@ -810,5 +814,445 @@ namespace NCC.Extend.LqHytkHytk | @@ -810,5 +814,445 @@ namespace NCC.Extend.LqHytkHytk | ||
| 810 | } | 814 | } |
| 811 | } | 815 | } |
| 812 | #endregion | 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,7 +562,7 @@ namespace NCC.Extend.LqKhxx | ||
| 562 | exportData = await this.GetNoPagingList(input); | 562 | exportData = await this.GetNoPagingList(input); |
| 563 | } | 563 | } |
| 564 | List<ParamsModel> paramList = | 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 | ExcelConfig excelconfig = new ExcelConfig(); | 566 | ExcelConfig excelconfig = new ExcelConfig(); |
| 567 | excelconfig.FileName = "客户资料.xls"; | 567 | excelconfig.FileName = "客户资料.xls"; |
| 568 | excelconfig.HeadFont = "微软雅黑"; | 568 | excelconfig.HeadFont = "微软雅黑"; |
netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
| @@ -4351,11 +4351,11 @@ namespace NCC.Extend | @@ -4351,11 +4351,11 @@ namespace NCC.Extend | ||
| 4351 | storeFilter = $" AND hytk.md IN ('{storeIdsStr}')"; | 4351 | storeFilter = $" AND hytk.md IN ('{storeIdsStr}')"; |
| 4352 | } | 4352 | } |
| 4353 | 4353 | ||
| 4354 | - // 1. 各门店退卡金额分布(不包含转卡)- 使用原生SQL确保正确 | 4354 | + // 1. 各门店退卡金额分布(不包含转卡)- 使用原生SQL确保正确,使用实退金额 |
| 4355 | var storeDistributionSql = $@" | 4355 | var storeDistributionSql = $@" |
| 4356 | SELECT | 4356 | SELECT |
| 4357 | hytk.md as StoreId, | 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 | COUNT(hytk.F_Id) as RefundCount | 4359 | COUNT(hytk.F_Id) as RefundCount |
| 4360 | FROM lq_hytk_hytk hytk | 4360 | FROM lq_hytk_hytk hytk |
| 4361 | WHERE hytk.F_IsEffective = 1 | 4361 | WHERE hytk.F_IsEffective = 1 |
| @@ -4390,10 +4390,10 @@ namespace NCC.Extend | @@ -4390,10 +4390,10 @@ namespace NCC.Extend | ||
| 4390 | RefundCount = Convert.ToInt32(x.RefundCount ?? 0) | 4390 | RefundCount = Convert.ToInt32(x.RefundCount ?? 0) |
| 4391 | }).OrderByDescending(x => x.RefundAmount).ToList(); | 4391 | }).OrderByDescending(x => x.RefundAmount).ToList(); |
| 4392 | 4392 | ||
| 4393 | - // 2. 总计数据 - 使用原生SQL确保正确(包含转卡,与驾驶舱保持一致) | 4393 | + // 2. 总计数据 - 使用原生SQL确保正确(包含转卡,与驾驶舱保持一致),使用实退金额 |
| 4394 | var totalStatsSql = $@" | 4394 | var totalStatsSql = $@" |
| 4395 | SELECT | 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 | COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalActualRefundAmount, | 4397 | COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalActualRefundAmount, |
| 4398 | COUNT(F_Id) as TotalRefundCount | 4398 | COUNT(F_Id) as TotalRefundCount |
| 4399 | FROM lq_hytk_hytk | 4399 | FROM lq_hytk_hytk |
| @@ -4409,10 +4409,10 @@ namespace NCC.Extend | @@ -4409,10 +4409,10 @@ namespace NCC.Extend | ||
| 4409 | var totalActualRefundAmount = totalStats != null ? Convert.ToDecimal(totalStats.TotalActualRefundAmount ?? 0) : 0m; | 4409 | var totalActualRefundAmount = totalStats != null ? Convert.ToDecimal(totalStats.TotalActualRefundAmount ?? 0) : 0m; |
| 4410 | var totalRefundCount = totalStats != null ? Convert.ToInt32(totalStats.TotalRefundCount ?? 0) : 0; | 4410 | var totalRefundCount = totalStats != null ? Convert.ToInt32(totalStats.TotalRefundCount ?? 0) : 0; |
| 4411 | 4411 | ||
| 4412 | - // 转卡总计(单独统计)- 使用原生SQL | 4412 | + // 转卡总计(单独统计)- 使用原生SQL,使用实退金额 |
| 4413 | var transferCardSql = $@" | 4413 | var transferCardSql = $@" |
| 4414 | SELECT | 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 | COUNT(F_Id) as TotalTransferCount | 4416 | COUNT(F_Id) as TotalTransferCount |
| 4417 | FROM lq_hytk_hytk | 4417 | FROM lq_hytk_hytk |
| 4418 | WHERE F_IsEffective = 1 | 4418 | WHERE F_IsEffective = 1 |
| @@ -4427,13 +4427,13 @@ namespace NCC.Extend | @@ -4427,13 +4427,13 @@ namespace NCC.Extend | ||
| 4427 | var totalTransferAmount = transferCardStats != null ? Convert.ToDecimal(transferCardStats.TotalTransferAmount ?? 0) : 0m; | 4427 | var totalTransferAmount = transferCardStats != null ? Convert.ToDecimal(transferCardStats.TotalTransferAmount ?? 0) : 0m; |
| 4428 | var totalTransferCount = transferCardStats != null ? Convert.ToInt32(transferCardStats.TotalTransferCount ?? 0) : 0; | 4428 | var totalTransferCount = transferCardStats != null ? Convert.ToInt32(transferCardStats.TotalTransferCount ?? 0) : 0; |
| 4429 | 4429 | ||
| 4430 | - // 3. 退卡金额最大的人 - 使用原生SQL | 4430 | + // 3. 退卡金额最大的人 - 使用原生SQL,使用实退金额 |
| 4431 | // 注意:按 hymc(会员名称)分组,因为同一个会员可能有不同的 hy(会员ID) | 4431 | // 注意:按 hymc(会员名称)分组,因为同一个会员可能有不同的 hy(会员ID) |
| 4432 | var maxAmountPersonSql = $@" | 4432 | var maxAmountPersonSql = $@" |
| 4433 | SELECT | 4433 | SELECT |
| 4434 | MAX(hy) as MemberId, | 4434 | MAX(hy) as MemberId, |
| 4435 | hymc as MemberName, | 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 | COUNT(F_Id) as RefundCount | 4437 | COUNT(F_Id) as RefundCount |
| 4438 | FROM lq_hytk_hytk | 4438 | FROM lq_hytk_hytk |
| 4439 | WHERE F_IsEffective = 1 | 4439 | WHERE F_IsEffective = 1 |
| @@ -4451,13 +4451,13 @@ namespace NCC.Extend | @@ -4451,13 +4451,13 @@ namespace NCC.Extend | ||
| 4451 | var maxAmountPersonResult = await _db.Ado.SqlQueryAsync<dynamic>(maxAmountPersonSql); | 4451 | var maxAmountPersonResult = await _db.Ado.SqlQueryAsync<dynamic>(maxAmountPersonSql); |
| 4452 | var maxAmountPerson = maxAmountPersonResult?.FirstOrDefault(); | 4452 | var maxAmountPerson = maxAmountPersonResult?.FirstOrDefault(); |
| 4453 | 4453 | ||
| 4454 | - // 4. 退卡次数最多的人 - 使用原生SQL(按退卡单数统计,一个退卡单算一次) | 4454 | + // 4. 退卡次数最多的人 - 使用原生SQL(按退卡单数统计,一个退卡单算一次),使用实退金额 |
| 4455 | // 注意:按 hymc(会员名称)分组,因为同一个会员可能有不同的 hy(会员ID) | 4455 | // 注意:按 hymc(会员名称)分组,因为同一个会员可能有不同的 hy(会员ID) |
| 4456 | var maxCountPersonSql = $@" | 4456 | var maxCountPersonSql = $@" |
| 4457 | SELECT | 4457 | SELECT |
| 4458 | MAX(hy) as MemberId, | 4458 | MAX(hy) as MemberId, |
| 4459 | hymc as MemberName, | 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 | COUNT(F_Id) as RefundCount | 4461 | COUNT(F_Id) as RefundCount |
| 4462 | FROM lq_hytk_hytk | 4462 | FROM lq_hytk_hytk |
| 4463 | WHERE F_IsEffective = 1 | 4463 | WHERE F_IsEffective = 1 |