diff --git a/ExportFiles/退卡明细_20251228193529.xls b/ExportFiles/退卡明细_20251228193529.xls
new file mode 100644
index 0000000..df7e27d
--- /dev/null
+++ b/ExportFiles/退卡明细_20251228193529.xls
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListOutput.cs
new file mode 100644
index 0000000..8b6f121
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListOutput.cs
@@ -0,0 +1,86 @@
+using System;
+
+namespace NCC.Extend.Entitys.Dto.LqHytkHytk
+{
+ ///
+ /// 退卡明细列表输出参数
+ ///
+ public class RefundDetailListOutput
+ {
+ ///
+ /// 门店ID
+ ///
+ public string storeId { get; set; }
+
+ ///
+ /// 门店名称
+ ///
+ public string storeName { get; set; }
+
+ ///
+ /// 会员ID
+ ///
+ public string memberId { get; set; }
+
+ ///
+ /// 会员名称
+ ///
+ public string memberName { get; set; }
+
+ ///
+ /// 会员电话
+ ///
+ public string memberPhone { get; set; }
+
+ ///
+ /// 退卡时间
+ ///
+ public DateTime? refundTime { get; set; }
+
+ ///
+ /// 退卡品项ID
+ ///
+ public string itemId { get; set; }
+
+ ///
+ /// 退卡品项名称
+ ///
+ public string itemName { get; set; }
+
+ ///
+ /// 退卡数量
+ ///
+ public decimal refundQuantity { get; set; }
+
+ ///
+ /// 单价
+ ///
+ public decimal unitPrice { get; set; }
+
+ ///
+ /// 总价
+ ///
+ public decimal totalPrice { get; set; }
+
+ ///
+ /// 业绩类型
+ ///
+ public string performanceType { get; set; }
+
+ ///
+ /// 科美类型
+ ///
+ public string beautyType { get; set; }
+
+ ///
+ /// 来源类型
+ ///
+ public string sourceType { get; set; }
+
+ ///
+ /// 品项分类
+ ///
+ public string itemCategory { get; set; }
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListQueryInput.cs
new file mode 100644
index 0000000..fe0c7c5
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListQueryInput.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+
+namespace NCC.Extend.Entitys.Dto.LqHytkHytk
+{
+ ///
+ /// 退卡明细列表查询输入参数
+ ///
+ public class RefundDetailListQueryInput
+ {
+ ///
+ /// 当前页码
+ ///
+ public int currentPage { get; set; } = 1;
+
+ ///
+ /// 每页数量
+ ///
+ public int pageSize { get; set; } = 20;
+
+ ///
+ /// 排序字段
+ ///
+ public string sidx { get; set; } = "tksj";
+
+ ///
+ /// 排序方式(asc/desc)
+ ///
+ public string sort { get; set; } = "desc";
+
+ ///
+ /// 门店ID列表(多门店筛选)
+ ///
+ public List storeIds { get; set; }
+
+ ///
+ /// 会员ID(会员筛选)
+ ///
+ public string memberId { get; set; }
+
+ ///
+ /// 开始时间(时间范围筛选)
+ ///
+ public DateTime? startTime { get; set; }
+
+ ///
+ /// 结束时间(时间范围筛选)
+ ///
+ public DateTime? endTime { get; set; }
+
+ ///
+ /// 品项ID(品项筛选)
+ ///
+ public string itemId { get; set; }
+
+ ///
+ /// 业绩类型(业绩类型筛选)
+ ///
+ public string performanceType { get; set; }
+
+ ///
+ /// 科美类型(科美类型筛选)
+ ///
+ public string beautyType { get; set; }
+
+ ///
+ /// 来源类型(来源类型筛选)
+ ///
+ public string sourceType { get; set; }
+
+ ///
+ /// 品项分类(品项分类筛选)
+ ///
+ public string itemCategory { get; set; }
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs
index 796345d..62ca416 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using NCC.Code;
using NCC.Extend.Entitys.Enum;
@@ -253,6 +254,26 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
public int consumeLevel { get; set; }
///
+ /// 消费等级名称
+ ///
+ public string consumeLevelName
+ {
+ get
+ {
+ var levelMap = new Dictionary
+ {
+ { 0, "D" },
+ { 1, "C" },
+ { 2, "B" },
+ { 3, "A" },
+ { 4, "A+" },
+ { 5, "A++" }
+ };
+ return levelMap.ContainsKey(consumeLevel) ? levelMap[consumeLevel] : "D";
+ }
+ }
+
+ ///
/// 消费等级更新时间
///
public DateTime? consumeLevelUpdateTime { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
index 6ebe09a..6731e6a 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Reflection;
using System.Threading.Tasks;
using Mapster;
using Microsoft.AspNetCore.Mvc;
@@ -26,6 +28,8 @@ using NCC.Extend.Entitys.lq_hytk_kjbsyj;
using NCC.Extend.Entitys.lq_hytk_mx;
using NCC.Extend.Entitys.lq_kd_pxmx;
using NCC.Extend.Entitys.lq_xmzl;
+using NCC.Extend.Entitys.lq_khxx;
+using NCC.Extend.Entitys.lq_mdxx;
using NCC.Extend.Interfaces.LqHytkHytk;
using NCC.FriendlyException;
using NCC.JsonSerialization;
@@ -810,5 +814,445 @@ namespace NCC.Extend.LqHytkHytk
}
}
#endregion
+
+ #region 退卡明细查询
+ ///
+ /// 获取退卡明细列表
+ ///
+ ///
+ /// 获取退卡明细列表,支持多门店、会员、时间范围、品项、业绩类型、科美类型、来源类型、品项分类等筛选
+ ///
+ /// 示例请求:
+ /// ```json
+ /// GET /api/Extend/LqHytkHytk/refund-detail-list?storeIds=门店ID1,门店ID2&memberId=会员ID&startTime=2025-01-01&endTime=2025-12-31¤tPage=1&pageSize=20
+ /// ```
+ ///
+ /// 参数说明:
+ /// - storeIds: 门店ID列表(可选,多门店筛选)
+ /// - memberId: 会员ID(可选)
+ /// - startTime: 开始时间(可选,时间范围筛选)
+ /// - endTime: 结束时间(可选,时间范围筛选)
+ /// - itemId: 品项ID(可选)
+ /// - performanceType: 业绩类型(可选)
+ /// - beautyType: 科美类型(可选)
+ /// - sourceType: 来源类型(可选)
+ /// - itemCategory: 品项分类(可选)
+ /// - currentPage: 当前页码(必填)
+ /// - pageSize: 每页数量(必填)
+ ///
+ /// 查询参数
+ /// 分页的退卡明细列表
+ /// 成功返回退卡明细列表
+ /// 参数错误
+ /// 服务器内部错误
+ [HttpPost("refund-detail-list")]
+ public async Task GetRefundDetailList([FromBody] RefundDetailListQueryInput input)
+ {
+ try
+ {
+ if (input == null)
+ {
+ throw NCCException.Oh("参数不能为空");
+ }
+
+ // 1. 先查询退卡明细表
+ var mxQuery = _db.Queryable()
+ .Where(mx => mx.IsEffective == StatusEnum.有效.GetHashCode())
+ .WhereIF(!string.IsNullOrEmpty(input.itemId), mx => mx.Px == input.itemId)
+ .WhereIF(!string.IsNullOrEmpty(input.performanceType), mx => mx.PerformanceType == input.performanceType)
+ .WhereIF(!string.IsNullOrEmpty(input.beautyType), mx => mx.BeautyType == input.beautyType)
+ .WhereIF(!string.IsNullOrEmpty(input.sourceType), mx => mx.SourceType == input.sourceType)
+ .WhereIF(!string.IsNullOrEmpty(input.itemCategory), mx => mx.ItemCategory == input.itemCategory)
+ .WhereIF(input.startTime.HasValue, mx => mx.Tksj.HasValue && mx.Tksj >= input.startTime.Value)
+ .WhereIF(input.endTime.HasValue, mx => mx.Tksj.HasValue && mx.Tksj <= input.endTime.Value);
+
+ var mxList = await mxQuery.ToListAsync();
+
+ if (!mxList.Any())
+ {
+ return PageResult.SqlSugarPageResult(new SqlSugarPagedList());
+ }
+
+ // 2. 批量查询退卡信息表
+ var refundInfoIds = mxList.Select(x => x.RefundInfoId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
+ var refundInfoList = new List();
+ if (refundInfoIds.Any())
+ {
+ refundInfoList = await _db.Queryable()
+ .Where(x => refundInfoIds.Contains(x.Id))
+ .WhereIF(input.storeIds != null && input.storeIds.Any(), x => input.storeIds.Contains(x.Md))
+ .WhereIF(!string.IsNullOrEmpty(input.memberId), x => x.Hy == input.memberId)
+ .WhereIF(input.startTime.HasValue && !mxList.Any(m => m.Tksj.HasValue), x => x.Tksj.HasValue && x.Tksj >= input.startTime.Value)
+ .WhereIF(input.endTime.HasValue && !mxList.Any(m => m.Tksj.HasValue), x => x.Tksj.HasValue && x.Tksj <= input.endTime.Value)
+ .ToListAsync();
+ }
+
+ var refundInfoDict = refundInfoList.ToDictionary(x => x.Id, x => x);
+
+ // 3. 批量查询门店信息
+ var storeIds = refundInfoList.Select(x => x.Md).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
+ var storeDict = new Dictionary();
+ if (storeIds.Any())
+ {
+ var stores = await _db.Queryable()
+ .Where(x => storeIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.Dm })
+ .ToListAsync();
+ storeDict = stores.ToDictionary(x => x.Id, x => x.Dm ?? "");
+ }
+
+ // 4. 批量查询会员信息
+ var memberIds = mxList.Select(x => x.MemberId).Where(x => !string.IsNullOrEmpty(x))
+ .Concat(refundInfoList.Select(x => x.Hy).Where(x => !string.IsNullOrEmpty(x)))
+ .Distinct().ToList();
+ var memberDict = new Dictionary();
+ if (memberIds.Any())
+ {
+ var members = await _db.Queryable()
+ .Where(x => memberIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.Sjh })
+ .ToListAsync();
+ memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? "");
+ }
+
+ // 5. 组装数据
+ var resultList = new List();
+ foreach (var mx in mxList)
+ {
+ var refundInfo = refundInfoDict.ContainsKey(mx.RefundInfoId) ? refundInfoDict[mx.RefundInfoId] : null;
+
+ // 应用筛选条件
+ if (input.storeIds != null && input.storeIds.Any())
+ {
+ if (refundInfo == null || !input.storeIds.Contains(refundInfo.Md))
+ continue;
+ }
+ if (!string.IsNullOrEmpty(input.memberId))
+ {
+ if (mx.MemberId != input.memberId && (refundInfo == null || refundInfo.Hy != input.memberId))
+ continue;
+ }
+
+ var memberId = mx.MemberId ?? (refundInfo?.Hy);
+ var refundTime = mx.Tksj ?? refundInfo?.Tksj;
+
+ resultList.Add(new RefundDetailListOutput
+ {
+ storeId = refundInfo?.Md,
+ storeName = refundInfo != null && storeDict.ContainsKey(refundInfo.Md) ? storeDict[refundInfo.Md] : "",
+ memberId = memberId,
+ memberName = refundInfo?.Hymc,
+ memberPhone = !string.IsNullOrEmpty(memberId) && memberDict.ContainsKey(memberId) ? memberDict[memberId] : "",
+ refundTime = refundTime,
+ itemId = mx.Px,
+ itemName = mx.Pxmc,
+ refundQuantity = mx.ProjectNumber,
+ unitPrice = mx.Pxjg ?? 0,
+ totalPrice = mx.Tkje ?? mx.TotalPrice ?? 0,
+ performanceType = mx.PerformanceType,
+ beautyType = mx.BeautyType,
+ sourceType = mx.SourceType,
+ itemCategory = mx.ItemCategory
+ });
+ }
+
+ // 6. 排序
+ 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();
+ }
+ else
+ {
+ resultList = resultList.OrderBy(x => GetPropertyValue(x, sidx)).ToList();
+ }
+
+ // 7. 分页
+ var total = resultList.Count;
+ var skip = (input.currentPage - 1) * input.pageSize;
+ var pagedList = resultList.Skip(skip).Take(input.pageSize).ToList();
+
+ var pageList = new SqlSugarPagedList
+ {
+ list = pagedList,
+ pagination = new PagedModel
+ {
+ PageIndex = input.currentPage,
+ PageSize = input.pageSize,
+ Total = total
+ }
+ };
+
+ return PageResult.SqlSugarPageResult(pageList);
+ }
+ catch (Exception ex)
+ {
+ throw NCCException.Oh($"获取退卡明细列表失败: {ex.Message}");
+ }
+ }
+
+ private object GetPropertyValue(RefundDetailListOutput obj, string propertyName)
+ {
+ var prop = typeof(RefundDetailListOutput).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
+ return prop?.GetValue(obj) ?? "";
+ }
+
+ ///
+ /// 获取退卡明细列表(无分页,用于导出)
+ ///
+ /// 查询参数
+ /// 退卡明细列表
+ [NonAction]
+ public async Task> GetRefundDetailListNoPaging(RefundDetailListQueryInput input)
+ {
+ try
+ {
+ // 1. 先查询退卡明细表
+ var mxQuery = _db.Queryable()
+ .Where(mx => mx.IsEffective == StatusEnum.有效.GetHashCode())
+ .WhereIF(!string.IsNullOrEmpty(input.itemId), mx => mx.Px == input.itemId)
+ .WhereIF(!string.IsNullOrEmpty(input.performanceType), mx => mx.PerformanceType == input.performanceType)
+ .WhereIF(!string.IsNullOrEmpty(input.beautyType), mx => mx.BeautyType == input.beautyType)
+ .WhereIF(!string.IsNullOrEmpty(input.sourceType), mx => mx.SourceType == input.sourceType)
+ .WhereIF(!string.IsNullOrEmpty(input.itemCategory), mx => mx.ItemCategory == input.itemCategory)
+ .WhereIF(input.startTime.HasValue, mx => mx.Tksj.HasValue && mx.Tksj >= input.startTime.Value)
+ .WhereIF(input.endTime.HasValue, mx => mx.Tksj.HasValue && mx.Tksj <= input.endTime.Value);
+
+ var mxList = await mxQuery.ToListAsync();
+
+ if (!mxList.Any())
+ {
+ return new List();
+ }
+
+ // 2. 批量查询退卡信息表
+ var refundInfoIds = mxList.Select(x => x.RefundInfoId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
+ var refundInfoList = new List();
+ if (refundInfoIds.Any())
+ {
+ refundInfoList = await _db.Queryable()
+ .Where(x => refundInfoIds.Contains(x.Id))
+ .WhereIF(input.storeIds != null && input.storeIds.Any(), x => input.storeIds.Contains(x.Md))
+ .WhereIF(!string.IsNullOrEmpty(input.memberId), x => x.Hy == input.memberId)
+ .WhereIF(input.startTime.HasValue && !mxList.Any(m => m.Tksj.HasValue), x => x.Tksj.HasValue && x.Tksj >= input.startTime.Value)
+ .WhereIF(input.endTime.HasValue && !mxList.Any(m => m.Tksj.HasValue), x => x.Tksj.HasValue && x.Tksj <= input.endTime.Value)
+ .ToListAsync();
+ }
+
+ var refundInfoDict = refundInfoList.ToDictionary(x => x.Id, x => x);
+
+ // 3. 批量查询门店信息
+ var storeIds = refundInfoList.Select(x => x.Md).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
+ var storeDict = new Dictionary();
+ if (storeIds.Any())
+ {
+ var stores = await _db.Queryable()
+ .Where(x => storeIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.Dm })
+ .ToListAsync();
+ storeDict = stores.ToDictionary(x => x.Id, x => x.Dm ?? "");
+ }
+
+ // 4. 批量查询会员信息
+ var memberIds = mxList.Select(x => x.MemberId).Where(x => !string.IsNullOrEmpty(x))
+ .Concat(refundInfoList.Select(x => x.Hy).Where(x => !string.IsNullOrEmpty(x)))
+ .Distinct().ToList();
+ var memberDict = new Dictionary();
+ if (memberIds.Any())
+ {
+ var members = await _db.Queryable()
+ .Where(x => memberIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.Sjh })
+ .ToListAsync();
+ memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? "");
+ }
+
+ // 5. 组装数据
+ var resultList = new List();
+ foreach (var mx in mxList)
+ {
+ var refundInfo = refundInfoDict.ContainsKey(mx.RefundInfoId) ? refundInfoDict[mx.RefundInfoId] : null;
+
+ // 应用筛选条件
+ if (input.storeIds != null && input.storeIds.Any())
+ {
+ if (refundInfo == null || !input.storeIds.Contains(refundInfo.Md))
+ continue;
+ }
+ if (!string.IsNullOrEmpty(input.memberId))
+ {
+ if (mx.MemberId != input.memberId && (refundInfo == null || refundInfo.Hy != input.memberId))
+ continue;
+ }
+
+ var memberId = mx.MemberId ?? (refundInfo?.Hy);
+ var refundTime = mx.Tksj ?? refundInfo?.Tksj;
+
+ resultList.Add(new RefundDetailListOutput
+ {
+ storeId = refundInfo?.Md,
+ storeName = refundInfo != null && storeDict.ContainsKey(refundInfo.Md) ? storeDict[refundInfo.Md] : "",
+ memberId = memberId,
+ memberName = refundInfo?.Hymc,
+ memberPhone = !string.IsNullOrEmpty(memberId) && memberDict.ContainsKey(memberId) ? memberDict[memberId] : "",
+ refundTime = refundTime,
+ itemId = mx.Px,
+ itemName = mx.Pxmc,
+ refundQuantity = mx.ProjectNumber,
+ unitPrice = mx.Pxjg ?? 0,
+ totalPrice = mx.Tkje ?? mx.TotalPrice ?? 0,
+ performanceType = mx.PerformanceType,
+ beautyType = mx.BeautyType,
+ sourceType = mx.SourceType,
+ itemCategory = mx.ItemCategory
+ });
+ }
+
+ // 6. 排序
+ 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();
+ }
+ else
+ {
+ resultList = resultList.OrderBy(x => GetPropertyValue(x, sidx)).ToList();
+ }
+
+ return resultList;
+ }
+ catch (Exception ex)
+ {
+ throw NCCException.Oh($"获取退卡明细列表失败: {ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 退卡明细导出
+ ///
+ /// 导出退卡明细
+ ///
+ ///
+ /// 导出退卡明细到Excel,支持多门店、会员、时间范围、品项、业绩类型、科美类型、来源类型、品项分类等筛选
+ ///
+ /// 示例请求:
+ /// ```json
+ /// POST /api/Extend/LqHytkHytk/refund-detail-export
+ /// {
+ /// "storeIds": ["门店ID1", "门店ID2"],
+ /// "memberId": "会员ID",
+ /// "startTime": "2025-01-01T00:00:00",
+ /// "endTime": "2025-12-31T23:59:59",
+ /// "itemId": "品项ID",
+ /// "performanceType": "业绩类型",
+ /// "beautyType": "科美类型",
+ /// "sourceType": "来源类型",
+ /// "itemCategory": "品项分类"
+ /// }
+ /// ```
+ ///
+ /// 查询参数
+ /// Excel文件下载信息
+ /// 成功返回Excel文件下载链接
+ /// 参数错误
+ /// 服务器内部错误
+ [HttpPost("refund-detail-export")]
+ public async Task ExportRefundDetail([FromBody] RefundDetailListQueryInput input)
+ {
+ try
+ {
+ var userInfo = await _userManager.GetUserInfo();
+ var exportData = await GetRefundDetailListNoPaging(input);
+
+ if (exportData == null || !exportData.Any())
+ {
+ return new { name = "退卡明细.xls", url = "", message = "没有找到符合条件的数据" };
+ }
+
+ // 定义导出字段
+ 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();
+
+ ExcelConfig excelconfig = new ExcelConfig();
+ excelconfig.FileName = "退卡明细_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xls";
+ excelconfig.HeadFont = "微软雅黑";
+ excelconfig.HeadPoint = 10;
+ excelconfig.IsAllSizeColumn = true;
+ excelconfig.ColumnModel = new List();
+
+ // 添加所有字段到导出列
+ foreach (var param in paramList)
+ {
+ excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = param.field, ExcelColumn = param.value });
+ }
+
+ // 查找项目根目录
+ var baseDir = AppContext.BaseDirectory;
+ var projectRoot = baseDir;
+ var dir = new DirectoryInfo(baseDir);
+
+ // 优先查找包含 .git 目录的目录(真正的项目根目录)
+ while (dir != null && dir.Parent != null)
+ {
+ try
+ {
+ if (dir.GetDirectories(".git").Any())
+ {
+ projectRoot = dir.FullName;
+ break;
+ }
+ }
+ catch
+ {
+ // 忽略访问错误,继续向上查找
+ }
+ dir = dir.Parent;
+ }
+
+ // 如果没找到 .git 目录,再查找包含 .sln 文件的目录
+ if (projectRoot == baseDir)
+ {
+ dir = new DirectoryInfo(baseDir);
+ while (dir != null && dir.Parent != null)
+ {
+ try
+ {
+ if (dir.GetFiles("*.sln").Any())
+ {
+ projectRoot = dir.FullName;
+ break;
+ }
+ }
+ catch
+ {
+ // 忽略访问错误,继续向上查找
+ }
+ dir = dir.Parent;
+ }
+ }
+
+ // 在项目根目录下创建 ExportFiles 文件夹
+ var exportFilesPath = Path.Combine(projectRoot, "ExportFiles");
+ if (!Directory.Exists(exportFilesPath))
+ {
+ Directory.CreateDirectory(exportFilesPath);
+ }
+ var addPath = Path.Combine(exportFilesPath, excelconfig.FileName);
+
+ ExcelExportHelper.Export(exportData, excelconfig, addPath);
+ var fileName = _userManager.UserId + "|" + addPath + "|xls";
+ var output = new { name = excelconfig.FileName, url = "/api/File/Download?encryption=" + DESCEncryption.Encrypt(fileName, "NCC") };
+ return output;
+ }
+ catch (Exception ex)
+ {
+ throw NCCException.Oh($"导出退卡明细失败: {ex.Message}");
+ }
+ }
+ #endregion
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
index 6fbeb1a..0feff54 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
@@ -562,7 +562,7 @@ namespace NCC.Extend.LqKhxx
exportData = await this.GetNoPagingList(input);
}
List paramList =
- "[{\"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();
+ "[{\"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();
ExcelConfig excelconfig = new ExcelConfig();
excelconfig.FileName = "客户资料.xls";
excelconfig.HeadFont = "微软雅黑";
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
index e5e9391..9dca7ff 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
@@ -4351,11 +4351,11 @@ namespace NCC.Extend
storeFilter = $" AND hytk.md IN ('{storeIdsStr}')";
}
- // 1. 各门店退卡金额分布(不包含转卡)- 使用原生SQL确保正确
+ // 1. 各门店退卡金额分布(不包含转卡)- 使用原生SQL确保正确,使用实退金额
var storeDistributionSql = $@"
SELECT
hytk.md as StoreId,
- COALESCE(SUM(CAST(hytk.tkje AS DECIMAL(18,2))), 0) as RefundAmount,
+ COALESCE(SUM(CAST(COALESCE(hytk.F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as RefundAmount,
COUNT(hytk.F_Id) as RefundCount
FROM lq_hytk_hytk hytk
WHERE hytk.F_IsEffective = 1
@@ -4390,10 +4390,10 @@ namespace NCC.Extend
RefundCount = Convert.ToInt32(x.RefundCount ?? 0)
}).OrderByDescending(x => x.RefundAmount).ToList();
- // 2. 总计数据 - 使用原生SQL确保正确(包含转卡,与驾驶舱保持一致)
+ // 2. 总计数据 - 使用原生SQL确保正确(包含转卡,与驾驶舱保持一致),使用实退金额
var totalStatsSql = $@"
SELECT
- COALESCE(SUM(CAST(tkje AS DECIMAL(18,2))), 0) as TotalRefundAmount,
+ COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalRefundAmount,
COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalActualRefundAmount,
COUNT(F_Id) as TotalRefundCount
FROM lq_hytk_hytk
@@ -4409,10 +4409,10 @@ namespace NCC.Extend
var totalActualRefundAmount = totalStats != null ? Convert.ToDecimal(totalStats.TotalActualRefundAmount ?? 0) : 0m;
var totalRefundCount = totalStats != null ? Convert.ToInt32(totalStats.TotalRefundCount ?? 0) : 0;
- // 转卡总计(单独统计)- 使用原生SQL
+ // 转卡总计(单独统计)- 使用原生SQL,使用实退金额
var transferCardSql = $@"
SELECT
- COALESCE(SUM(CAST(tkje AS DECIMAL(18,2))), 0) as TotalTransferAmount,
+ COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalTransferAmount,
COUNT(F_Id) as TotalTransferCount
FROM lq_hytk_hytk
WHERE F_IsEffective = 1
@@ -4427,13 +4427,13 @@ namespace NCC.Extend
var totalTransferAmount = transferCardStats != null ? Convert.ToDecimal(transferCardStats.TotalTransferAmount ?? 0) : 0m;
var totalTransferCount = transferCardStats != null ? Convert.ToInt32(transferCardStats.TotalTransferCount ?? 0) : 0;
- // 3. 退卡金额最大的人 - 使用原生SQL
+ // 3. 退卡金额最大的人 - 使用原生SQL,使用实退金额
// 注意:按 hymc(会员名称)分组,因为同一个会员可能有不同的 hy(会员ID)
var maxAmountPersonSql = $@"
SELECT
MAX(hy) as MemberId,
hymc as MemberName,
- COALESCE(SUM(CAST(tkje AS DECIMAL(18,2))), 0) as TotalRefundAmount,
+ COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalRefundAmount,
COUNT(F_Id) as RefundCount
FROM lq_hytk_hytk
WHERE F_IsEffective = 1
@@ -4451,13 +4451,13 @@ namespace NCC.Extend
var maxAmountPersonResult = await _db.Ado.SqlQueryAsync(maxAmountPersonSql);
var maxAmountPerson = maxAmountPersonResult?.FirstOrDefault();
- // 4. 退卡次数最多的人 - 使用原生SQL(按退卡单数统计,一个退卡单算一次)
+ // 4. 退卡次数最多的人 - 使用原生SQL(按退卡单数统计,一个退卡单算一次),使用实退金额
// 注意:按 hymc(会员名称)分组,因为同一个会员可能有不同的 hy(会员ID)
var maxCountPersonSql = $@"
SELECT
MAX(hy) as MemberId,
hymc as MemberName,
- COALESCE(SUM(CAST(tkje AS DECIMAL(18,2))), 0) as TotalRefundAmount,
+ COALESCE(SUM(CAST(COALESCE(F_ActualRefundAmount, 0) AS DECIMAL(18,2))), 0) as TotalRefundAmount,
COUNT(F_Id) as RefundCount
FROM lq_hytk_hytk
WHERE F_IsEffective = 1