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; using NCC.ClayObject; using NCC.Common.Configuration; using NCC.Common.Core.Manager; using NCC.Common.Enum; using NCC.Common.Extension; using NCC.Common.Filter; using NCC.Common.Helper; using NCC.Common.Model.NPOI; using NCC.DataEncryption; using NCC.Dependency; using NCC.DynamicApiController; using NCC.Extend.Entitys.Dto.LqHytkHytk; using NCC.Extend.Entitys.Dto.LqHytkJksyj; using NCC.Extend.Entitys.Dto.LqHytkKjbsyj; using NCC.Extend.Entitys.Dto.LqHytkMx; using NCC.Extend.Entitys.Enum; using NCC.Extend.Entitys.lq_hytk_hytk; using NCC.Extend.Entitys.lq_hytk_jksyj; 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_kd_jksyj; 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; using SqlSugar; using Yitter.IdGenerator; namespace NCC.Extend.LqHytkHytk { /// /// 退卡_信息表服务 /// [ApiDescriptionSettings(Tag = "绿纤退卡信息表服务", Name = "LqHytkHytk", Order = 200)] [Route("api/Extend/[controller]")] public class LqHytkHytkService : ILqHytkHytkService, IDynamicApiController, ITransient { private readonly ISqlSugarRepository _lqHytkHytkRepository; private readonly ISqlSugarRepository _lqHytkMxRepository; private readonly ISqlSugarRepository _lqHytkJksyjRepository; private readonly ISqlSugarRepository _lqHytkKjbsyjRepository; private readonly SqlSugarScope _db; private readonly IUserManager _userManager; /// /// 初始化一个类型的新实例 /// public LqHytkHytkService( ISqlSugarRepository lqHytkHytkRepository, ISqlSugarRepository lqHytkMxRepository, ISqlSugarRepository lqHytkJksyjRepository, ISqlSugarRepository lqHytkKjbsyjRepository, IUserManager userManager ) { _lqHytkHytkRepository = lqHytkHytkRepository; _lqHytkMxRepository = lqHytkMxRepository; _lqHytkJksyjRepository = lqHytkJksyjRepository; _lqHytkKjbsyjRepository = lqHytkKjbsyjRepository; _db = _lqHytkHytkRepository.Context; _userManager = userManager; } #region 获取退卡信息列表 /// /// 获取退卡信息列表 /// /// /// 获取退卡记录列表,支持根据健康师ID或科技部老师ID筛选,返回品项明细、健康师业绩和科技部老师业绩 /// /// 示例请求: /// ```json /// GET /api/Extend/LqHytkHytk?jksId=健康师ID&kjblsId=科技部老师ID&currentPage=1&pageSize=10 /// ``` /// /// 参数说明: /// - jksId: 健康师ID(可选,传入后只返回该健康师参与的退卡记录) /// - kjblsId: 科技部老师ID(可选,传入后只返回该老师参与的退卡记录) /// - id: 退卡编号(可选) /// - md: 门店ID(可选) /// - hy: 会员ID(可选) /// - tksj: 退卡时间(可选,格式:yyyy-MM-dd,yyyy-MM-dd) /// - currentPage: 当前页码(必填) /// - pageSize: 每页数量(必填) /// /// 返回数据说明: /// - 退卡基本信息:id、门店信息、会员信息、退卡金额、退卡时间等 /// - lqHytkMxList: 退卡品项明细列表,每个明细包含品项信息、退款金额、项目次数等 /// - lqHytkJksyjList: 健康师业绩列表,每个业绩包含健康师信息、业绩金额、手工费、退卡品项次数等 /// - lqHytkKjbsyjList: 科技部老师业绩列表,每个业绩包含科技部老师信息、业绩金额、手工费、退卡品项次数等 /// /// 查询参数 /// 分页的退卡记录列表,包含退卡基本信息、品项明细、健康师业绩、科技部老师业绩 /// 成功返回退卡列表 /// 参数错误 /// 服务器内部错误 [HttpGet("")] public async Task GetList([FromQuery] LqHytkHytkListQueryInput input) { var sidx = input.sidx == null ? "id" : input.sidx; List queryTksj = input.tksj != null ? input.tksj.Split(',').ToObeject>() : null; DateTime? startTksj = queryTksj != null ? Ext.GetDateTime(queryTksj.First()) : null; DateTime? endTksj = queryTksj != null ? Ext.GetDateTime(queryTksj.Last()) : null; // 根据是否传入健康师ID或科技部老师ID,动态构建查询 ISugarQueryable baseQuery = null; if (!string.IsNullOrEmpty(input.jksId) && !string.IsNullOrEmpty(input.kjblsId)) { // 同时传入健康师ID和科技部老师ID,需要同时关联两个业绩表(不过滤有效性) baseQuery = _db.Queryable( (jksyj, kjbsyj, hytk) => jksyj.Gltkbh == hytk.Id && kjbsyj.Gltkbh == hytk.Id) .Where((jksyj, kjbsyj, hytk) => jksyj.Jkszh == input.jksId) .Where((jksyj, kjbsyj, hytk) => kjbsyj.Kjblszh == input.kjblsId) .Select((jksyj, kjbsyj, hytk) => hytk) .Distinct() .MergeTable(); } else if (!string.IsNullOrEmpty(input.jksId)) { // 只传入健康师ID,关联健康师业绩表(不过滤有效性) baseQuery = _db.Queryable( (jksyj, hytk) => jksyj.Gltkbh == hytk.Id) .Where((jksyj, hytk) => jksyj.Jkszh == input.jksId) .Select((jksyj, hytk) => hytk) .Distinct() .MergeTable(); } else if (!string.IsNullOrEmpty(input.kjblsId)) { // 只传入科技部老师ID,关联科技部老师业绩表(不过滤有效性) baseQuery = _db.Queryable( (kjbsyj, hytk) => kjbsyj.Gltkbh == hytk.Id) .Where((kjbsyj, hytk) => kjbsyj.Kjblszh == input.kjblsId) .Select((kjbsyj, hytk) => hytk) .Distinct() .MergeTable(); } else { // 没有传入健康师ID或科技部老师ID,使用原来的查询逻辑 baseQuery = _db.Queryable(); } var data = await baseQuery .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id)) .WhereIF(!string.IsNullOrEmpty(input.md), p => p.Md.Equals(input.md)) .WhereIF(!string.IsNullOrEmpty(input.mdbh), p => p.Mdbh.Contains(input.mdbh)) .WhereIF(!string.IsNullOrEmpty(input.mdmc), p => p.Mdmc.Contains(input.mdmc)) .WhereIF(!string.IsNullOrEmpty(input.hy), p => p.Hy.Equals(input.hy)) .WhereIF(!string.IsNullOrEmpty(input.hymc), p => p.Hymc.Contains(input.hymc)) .WhereIF(!string.IsNullOrEmpty(input.hyzh), p => p.Hyzh.Contains(input.hyzh)) .WhereIF(!string.IsNullOrEmpty(input.gklx), p => p.Gklx.Contains(input.gklx)) .WhereIF(input.tkje.HasValue, p => p.Tkje == input.tkje) .WhereIF(input.sgfy.HasValue, p => p.Sgfy == input.sgfy) .WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz)) .WhereIF(!string.IsNullOrEmpty(input.tkzt), p => p.Tkzt.Contains(input.tkzt)) .WhereIF(!string.IsNullOrEmpty(input.tkyy), p => p.Tkyy.Contains(input.tkyy)) .WhereIF(!string.IsNullOrEmpty(input.fileUrl), p => p.FileUrl.Contains(input.fileUrl)) .WhereIF(startTksj.HasValue, p => p.Tksj >= new DateTime(startTksj.Value.Year, startTksj.Value.Month, startTksj.Value.Day, 0, 0, 0)) .WhereIF(endTksj.HasValue, p => p.Tksj <= new DateTime(endTksj.Value.Year, endTksj.Value.Month, endTksj.Value.Day, 23, 59, 59)) .WhereIF(!string.IsNullOrEmpty(input.czry), p => p.Czry.Equals(input.czry)) .WhereIF(input.isEffective != 0, p => p.IsEffective == input.isEffective) .WhereIF(input.isEffective == 0, p => p.IsEffective == StatusEnum.有效.GetHashCode()) // 如果未指定,默认只返回有效的 .Select(it => new LqHytkHytkListOutput { id = it.Id, md = it.Md, mdbh = it.Mdbh, mdmc = it.Mdmc, hy = it.Hy, hymc = it.Hymc, hyzh = it.Hyzh, gklx = it.Gklx, tkje = it.Tkje, sgfy = it.Sgfy, bz = it.Bz, tkzt = it.Tkzt, tkyy = it.Tkyy, tksj = it.Tksj, czry = it.Czry, fileUrl = it.FileUrl, isEffective = it.IsEffective, cancelRemark = it.CancelRemark, actualRefundAmount = it.ActualRefundAmount }) .MergeTable() .OrderBy(sidx + " " + input.sort) .ToPagedListAsync(input.currentPage, input.pageSize); // 获取当前页的退卡记录ID列表 var refundIds = data.list.Select(x => x.id).ToList(); // 批量查询会员电话(性能优化:一次性查询所有会员的手机号) var memberPhoneDict = new Dictionary(); var memberIds = data.list.Where(x => !string.IsNullOrEmpty(x.hy)).Select(x => x.hy).Distinct().ToList(); if (memberIds.Any()) { var members = await _db.Queryable() .Where(x => memberIds.Contains(x.Id)) .Select(x => new { x.Id, x.Sjh }) .ToListAsync(); memberPhoneDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? string.Empty); } // 填充会员电话 foreach (var item in data.list) { if (!string.IsNullOrEmpty(item.hy) && memberPhoneDict.ContainsKey(item.hy)) { item.hyPhone = memberPhoneDict[item.hy]; } } // 批量查询品项明细(不过滤有效性,返回所有记录) var itemDetails = new List(); if (refundIds.Any()) { itemDetails = await _db.Queryable() .Where(x => refundIds.Contains(x.RefundInfoId)) .Select(x => new LqHytkMxInfoOutput { id = x.Id, refundInfoId = x.RefundInfoId, billingItemId = x.BillingItemId, px = x.Px, pxmc = x.Pxmc, pxjg = x.Pxjg, tkje = x.Tkje, projectNumber = x.ProjectNumber, isEffective = x.IsEffective, sourceType = x.SourceType, totalPrice = x.TotalPrice }) .ToListAsync(); } // 批量查询健康师业绩(性能优化:一次性查询所有退卡的健康师业绩,不过滤有效性) var jksyjList = new List(); if (refundIds.Any()) { jksyjList = await _db.Queryable() .Where(x => refundIds.Contains(x.Gltkbh)) .Select(x => new LqHytkJksyjInfoOutput { id = x.Id, gltkbh = x.Gltkbh, jks = x.Jks, jksxm = x.Jksxm, jkszh = x.Jkszh, jksyj = x.Jksyj, tksj = x.Tksj, F_jsjid = x.F_jsjid, F_tkpxid = x.F_tkpxid, F_LaborCost = x.F_LaborCost, F_tkpxNumber = x.F_tkpxNumber }) .ToListAsync(); } // 批量查询科技部老师业绩(性能优化:一次性查询所有退卡的科技部老师业绩,不过滤有效性) var kjbsyjList = new List(); if (refundIds.Any()) { kjbsyjList = await _db.Queryable() .Where(x => refundIds.Contains(x.Gltkbh)) .Select(x => new LqHytkKjbsyjInfoOutput { id = x.Id, gltkbh = x.Gltkbh, kjbls = x.Kjbls, kjblsxm = x.Kjblsxm, kjblszh = x.Kjblszh, kjblsyj = x.Kjblsyj, tksj = x.Tksj, F_tkpxid = x.F_tkpxid, F_LaborCost = x.F_LaborCost, F_tkpxNumber = x.F_tkpxNumber }) .ToListAsync(); } // 按退卡ID分组品项明细 var itemDetailsGrouped = itemDetails.GroupBy(x => x.refundInfoId) .ToDictionary(g => g.Key, g => g.ToList()); // 按退卡ID分组健康师业绩 var jksyjGrouped = jksyjList.GroupBy(x => x.gltkbh) .ToDictionary(g => g.Key, g => g.ToList()); // 按退卡ID分组科技部老师业绩 var kjbsyjGrouped = kjbsyjList.GroupBy(x => x.gltkbh) .ToDictionary(g => g.Key, g => g.ToList()); // 为每个退卡记录分配品项明细、健康师业绩和科技部老师业绩 foreach (var item in data.list) { item.lqHytkMxList = itemDetailsGrouped.ContainsKey(item.id) ? itemDetailsGrouped[item.id] : new List(); item.lqHytkJksyjList = jksyjGrouped.ContainsKey(item.id) ? jksyjGrouped[item.id] : new List(); item.lqHytkKjbsyjList = kjbsyjGrouped.ContainsKey(item.id) ? kjbsyjGrouped[item.id] : new List(); } return PageResult.SqlSugarPageResult(data); } #endregion #region 创建退卡信息及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// /// 创建退卡信息及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// /// /// 参数说明: /// - md: 门店ID /// - hy: 会员ID /// - lqHytkMxList: 退卡品项明细列表 /// /// 退卡创建参数 /// 无返回值 /// 创建成功 /// 参数错误或数据验证失败 /// 服务器内部错误 [HttpPost("")] public async Task Create([FromBody] LqHytkHytkCrInput input) { var userInfo = await _userManager.GetUserInfo(); var entity = input.Adapt(); entity.Id = YitIdHelper.NextId().ToString(); entity.F_CreateTime = DateTime.Now; entity.F_CreateUser = userInfo.userId; entity.IsEffective = StatusEnum.有效.GetHashCode(); entity.Czry = userInfo.userId; try { // 开启事务 _db.BeginTran(); // 新增退卡主表记录 var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); // 收集所有需要插入的实体,然后批量插入 var allMxEntities = new List(); var allJksyjEntities = new List(); var allKjbsyjEntities = new List(); // 处理品项明细列表 if (input.lqHytkMxList != null && input.lqHytkMxList.Any()) { foreach (var item in input.lqHytkMxList) { // 创建品项明细实体 var lqHytkMxEntity = new LqHytkMxEntity { Id = YitIdHelper.NextId().ToString(), RefundInfoId = newEntity.Id, BillingItemId = item.billingItemId, MemberId = newEntity.Hy, CreateTime = DateTime.Now, CreateUser = userInfo.userId, Px = item.px, Pxmc = item.pxmc, Pxjg = item.pxjg, Tkje = item.tkje, Tksj = input.tksj, ProjectNumber = item.F_ProjectNumber ?? 1, SourceType = item.F_SourceType, TotalPrice = item.F_TotalPrice ?? (item.pxjg * (item.F_ProjectNumber ?? 1)), IsEffective = StatusEnum.有效.GetHashCode(), ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", BeautyType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), }; allMxEntities.Add(lqHytkMxEntity); // 收集该品项关联的健康师业绩 if (item.lqHytkJksyjList != null && item.lqHytkJksyjList.Any()) { foreach (var ijks_tem in item.lqHytkJksyjList) { allJksyjEntities.Add(new LqHytkJksyjEntity { Id = YitIdHelper.NextId().ToString(), Gltkbh = newEntity.Id, Jks = ijks_tem.jks, Jksxm = ijks_tem.jksxm, Jkszh = ijks_tem.jkszh, Jksyj = ijks_tem.jksyj, Tksj = input.tksj, F_jsjid = ijks_tem.F_jsjid, F_tkpxid = ijks_tem.F_tkpxid, F_LaborCost = ijks_tem.F_LaborCost, F_tkpxNumber = ijks_tem.F_tkpxNumber, F_CreateTime = DateTime.Now, F_CreateUser = userInfo.userId, CardReturn = lqHytkMxEntity.Id, IsEffective = StatusEnum.有效.GetHashCode(), ItemCategory = lqHytkMxEntity.ItemCategory, ItemId = lqHytkMxEntity.Px, StoreId = newEntity.Md, ItemName = lqHytkMxEntity.Pxmc, PerformanceType = lqHytkMxEntity.PerformanceType, BeautyType = lqHytkMxEntity.BeautyType, } ); } } // 收集该品项关联的科技部老师业绩 if (item.lqHytkKjbsyjList != null && item.lqHytkKjbsyjList.Any()) { foreach (var ikjbs_tem in item.lqHytkKjbsyjList) { allKjbsyjEntities.Add( new LqHytkKjbsyjEntity { Id = YitIdHelper.NextId().ToString(), Gltkbh = newEntity.Id, Kjbls = ikjbs_tem.kjbls, Kjblsxm = ikjbs_tem.kjblsxm, Kjblszh = ikjbs_tem.kjblszh, Kjblsyj = ikjbs_tem.kjblsyj, Tksj = input.tksj, F_tkpxid = ikjbs_tem.F_tkpxid, F_LaborCost = ikjbs_tem.F_LaborCost, F_tkpxNumber = ikjbs_tem.F_tkpxNumber, F_CreateTime = DateTime.Now, F_CreateUser = userInfo.userId, CardReturn = lqHytkMxEntity.Id, IsEffective = StatusEnum.有效.GetHashCode(), ItemCategory = lqHytkMxEntity.ItemCategory, ItemId = lqHytkMxEntity.Px, StoreId = newEntity.Md, ItemName = lqHytkMxEntity.Pxmc, PerformanceType = lqHytkMxEntity.PerformanceType, BeautyType = lqHytkMxEntity.BeautyType, } ); } } } } // 批量插入品项明细 if (allMxEntities.Any()) { await _db.Insertable(allMxEntities).ExecuteCommandAsync(); } // 批量插入健康师业绩 if (allJksyjEntities.Any()) { await _db.Insertable(allJksyjEntities).ExecuteCommandAsync(); } // 批量插入科技部老师业绩 if (allKjbsyjEntities.Any()) { await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync(); } // 关闭事务 _db.CommitTran(); } catch (Exception ex) { _db.RollbackTran(); throw NCCException.Oh(ErrorCode.COM1005, ex.Message); } } #endregion #region 更新退卡信息及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// /// 更新退卡信息及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// /// /// 更新退卡记录及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// 先删除原有的关联数据,再插入新的数据 /// /// 退卡ID /// 退卡更新参数 /// 无返回值 /// 更新成功 /// 参数错误或数据验证失败 /// 服务器内部错误 [HttpPut("{id}")] public async Task Update(string id, [FromBody] LqHytkHytkUpInput input) { var userInfo = await _userManager.GetUserInfo(); var entity = input.Adapt(); entity.Id = id; entity.F_ModifyTime = DateTime.Now; entity.F_ModifyUser = userInfo.userId; try { // 开启事务 _db.BeginTran(); // 更新退卡主表记录 await _db.Updateable(entity).IgnoreColumns(true).ExecuteCommandAsync(); // 删除原有的关联数据 await _db.Deleteable().Where(x => x.RefundInfoId == id).ExecuteCommandAsync(); await _db.Deleteable().Where(x => x.Gltkbh == id).ExecuteCommandAsync(); await _db.Deleteable().Where(x => x.Gltkbh == id).ExecuteCommandAsync(); // 收集所有需要插入的实体,然后批量插入 var allMxEntities = new List(); var allJksyjEntities = new List(); var allKjbsyjEntities = new List(); // 处理品项明细列表 if (input.lqHytkMxList != null && input.lqHytkMxList.Any()) { foreach (var item in input.lqHytkMxList) { // 创建品项明细实体 var lqHytkMxEntity = new LqHytkMxEntity { Id = YitIdHelper.NextId().ToString(), RefundInfoId = id, BillingItemId = item.billingItemId, MemberId = entity.Hy, CreateTime = DateTime.Now, CreateUser = userInfo.userId, Tksj = input.tksj, Px = item.px, Pxmc = item.pxmc, Pxjg = item.pxjg, Tkje = item.tkje, ProjectNumber = item.F_ProjectNumber ?? 1, SourceType = item.F_SourceType, TotalPrice = item.F_TotalPrice ?? (item.pxjg * (item.F_ProjectNumber ?? 1)), ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", BeautyType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), }; allMxEntities.Add(lqHytkMxEntity); // 收集该品项关联的健康师业绩 if (item.lqHytkJksyjList != null && item.lqHytkJksyjList.Any()) { foreach (var ijks_tem in item.lqHytkJksyjList) { allJksyjEntities.Add( new LqHytkJksyjEntity { Id = YitIdHelper.NextId().ToString(), Gltkbh = id, Jks = ijks_tem.jks, Jksxm = ijks_tem.jksxm, Jkszh = ijks_tem.jkszh, Jksyj = ijks_tem.jksyj, Tksj = input.tksj, F_jsjid = ijks_tem.F_jsjid, F_tkpxid = ijks_tem.F_tkpxid, F_LaborCost = ijks_tem.F_LaborCost, F_tkpxNumber = ijks_tem.F_tkpxNumber, F_CreateTime = DateTime.Now, F_CreateUser = userInfo.userId, ItemCategory = lqHytkMxEntity.ItemCategory, ItemId = lqHytkMxEntity.Px, StoreId = entity.Md, ItemName = lqHytkMxEntity.Pxmc, PerformanceType = lqHytkMxEntity.PerformanceType, BeautyType = lqHytkMxEntity.BeautyType, } ); } } // 收集该品项关联的科技部老师业绩 if (item.lqHytkKjbsyjList != null && item.lqHytkKjbsyjList.Any()) { foreach (var ikjbs_tem in item.lqHytkKjbsyjList) { allKjbsyjEntities.Add( new LqHytkKjbsyjEntity { Id = YitIdHelper.NextId().ToString(), Gltkbh = id, Kjbls = ikjbs_tem.kjbls, Kjblsxm = ikjbs_tem.kjblsxm, Kjblszh = ikjbs_tem.kjblszh, Kjblsyj = ikjbs_tem.kjblsyj, Tksj = input.tksj, F_tkpxid = ikjbs_tem.F_tkpxid, F_LaborCost = ikjbs_tem.F_LaborCost, F_tkpxNumber = ikjbs_tem.F_tkpxNumber, F_CreateTime = DateTime.Now, F_CreateUser = userInfo.userId, ItemCategory = lqHytkMxEntity.ItemCategory, ItemId = lqHytkMxEntity.Px, StoreId = entity.Md, ItemName = lqHytkMxEntity.Pxmc, PerformanceType = lqHytkMxEntity.PerformanceType, BeautyType = lqHytkMxEntity.BeautyType, } ); } } } } // 批量插入品项明细 if (allMxEntities.Any()) { await _db.Insertable(allMxEntities).ExecuteCommandAsync(); } // 批量插入健康师业绩 if (allJksyjEntities.Any()) { await _db.Insertable(allJksyjEntities).ExecuteCommandAsync(); } // 批量插入科技部老师业绩 if (allKjbsyjEntities.Any()) { await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync(); } // 关闭事务 _db.CommitTran(); } catch (Exception ex) { _db.RollbackTran(); throw NCCException.Oh(ErrorCode.COM1005, ex.Message); } } #endregion #region 作废退卡信息 /// /// 作废退卡信息 /// /// 主键 /// [HttpPut("VoidRefundCardInfo/{id}")] public async Task VoidRefundCardInfo(string id, [FromQuery] string remarks = null) { try { var entity = await _db.Queryable().FirstAsync(p => p.Id == id && p.IsEffective == StatusEnum.有效.GetHashCode()); if (entity == null) { throw NCCException.Oh("退卡信息不存在或已被作废"); } // 开启事务 _db.BeginTran(); // 更新主表 entity.IsEffective = StatusEnum.无效.GetHashCode(); entity.F_ModifyTime = DateTime.Now; entity.CancelRemark = remarks; await _db.Updateable(entity).ExecuteCommandAsync(); // 更新明细表 await _db.Updateable().SetColumns(it => new LqHytkMxEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(w => w.RefundInfoId == id).ExecuteCommandAsync(); // 更新健康师业绩表 await _db.Updateable().SetColumns(it => new LqHytkJksyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(w => w.Gltkbh == id).ExecuteCommandAsync(); // 更新科技部业绩表 await _db.Updateable().SetColumns(it => new LqHytkKjbsyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(w => w.Gltkbh == id).ExecuteCommandAsync(); // 提交事务 _db.CommitTran(); return entity; } catch (Exception ex) { _db.RollbackTran(); throw NCCException.Oh($"删除退卡信息失败: {ex.Message}"); } } #endregion #region 物理删除退卡信息 /// /// 物理删除退卡信息 /// /// /// 彻底删除退卡记录及其所有关联数据,包括: /// - 退卡主表记录 /// - 退卡品项明细记录 /// - 退卡健康师业绩记录 /// - 退卡科技部老师业绩记录 /// /// 注意:此操作不可逆,请谨慎使用 /// /// 退卡记录主键ID /// 删除结果 /// 删除成功 /// 退卡记录不存在 /// 服务器内部错误 [HttpDelete("physical/{id}")] public async Task PhysicalDelete(string id) { try { // 检查退卡记录是否存在 var entity = await _db.Queryable().FirstAsync(p => p.Id == id); if (entity == null) { throw NCCException.Oh("退卡记录不存在"); } // 开启事务 _db.BeginTran(); // 1. 删除退卡品项明细记录 await _db.Deleteable().Where(x => x.RefundInfoId == id).ExecuteCommandAsync(); // 2. 删除退卡健康师业绩记录 await _db.Deleteable().Where(x => x.Gltkbh == id).ExecuteCommandAsync(); // 3. 删除退卡科技部老师业绩记录 await _db.Deleteable().Where(x => x.Gltkbh == id).ExecuteCommandAsync(); // 4. 删除退卡主表记录 await _db.Deleteable().Where(x => x.Id == id).ExecuteCommandAsync(); // 提交事务 _db.CommitTran(); return new { success = true, message = "退卡记录已彻底删除", deletedId = id, deletedTime = DateTime.Now }; } catch (Exception ex) { // 回滚事务 _db.RollbackTran(); throw NCCException.Oh($"物理删除退卡记录失败: {ex.Message}"); } } #endregion #region 获取退卡信息详情 /// /// 获取退卡信息详情 /// /// /// 获取退卡记录及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// 按照退卡的完整格式返回数据,不包含汇总信息 /// /// 返回数据结构: /// - 主表信息:退卡基础信息、门店信息、会员信息、退卡金额等 /// - 品项明细列表:每个品项包含完整的项目信息(项目次数、是否有效、来源类型等) /// - 健康师业绩列表:按品项关联的健康师业绩信息 /// - 科技部老师业绩列表:按品项关联的科技部老师业绩信息 /// /// 退卡记录主键ID /// 退卡记录完整信息 /// 查询成功 /// 退卡记录不存在 /// 服务器内部错误 [HttpGet("{id}")] public async Task GetInfo(string id) { try { // 1. 查询主表信息 var entity = await _db.Queryable().Where(p => p.Id == id).FirstAsync(); if (entity == null) { throw NCCException.Oh("退卡记录不存在"); } var output = entity.Adapt(); // 2. 查询品项明细列表 var lqHytkMxList = await _db.Queryable().Where(w => w.RefundInfoId == entity.Id).ToListAsync(); // 3. 查询健康师业绩列表 var lqHytkJksyjList = await _db.Queryable().Where(w => w.Gltkbh == entity.Id).ToListAsync(); // 4. 查询科技部老师业绩列表 var lqHytkKjbsyjList = await _db.Queryable().Where(w => w.Gltkbh == entity.Id).ToListAsync(); // 5. 构建品项明细输出,每个品项关联对应的业绩信息 var mxOutputList = new List(); foreach (var mx in lqHytkMxList) { var mxOutput = new LqHytkMxInfoOutput { id = mx.Id, refundInfoId = mx.RefundInfoId, billingItemId = mx.BillingItemId, px = mx.Px, pxmc = mx.Pxmc, pxjg = mx.Pxjg, tkje = mx.Tkje, projectNumber = mx.ProjectNumber, sourceType = mx.SourceType, totalPrice = mx.TotalPrice, isEffective = mx.IsEffective, }; // 关联该品项的健康师业绩 var jksyjForMx = lqHytkJksyjList.Where(j => j.CardReturn == mx.Id).ToList(); mxOutput.lqHytkJksyjList = jksyjForMx.Adapt>(); // 关联该品项的科技部老师业绩 var kjbsyjForMx = lqHytkKjbsyjList.Where(k => k.CardReturn == mx.Id).ToList(); mxOutput.lqHytkKjbsyjList = kjbsyjForMx.Adapt>(); mxOutputList.Add(mxOutput); } // 6. 设置输出结果 output.lqHytkMxList = mxOutputList; // 7. 设置全局业绩列表(用于兼容性,但主要使用品项关联的业绩) output.lqHytkJksyjList = lqHytkJksyjList.Adapt>(); output.lqHytkKjbsyjList = lqHytkKjbsyjList.Adapt>(); return output; } catch (Exception ex) { throw NCCException.Oh($"获取退卡记录详情失败: {ex.Message}"); } } #endregion #region 退卡明细查询 /// /// 获取退卡明细列表 /// /// /// 获取退卡明细列表,支持多门店、会员、时间范围、品项、业绩类型、科美类型、来源类型、品项分类等筛选 /// /// 示例请求: /// ```json /// GET /api/Extend/LqHytkHytk/refund-detail-list?storeIds=门店ID1,门店ID2&memberId=会员ID&startTime=2025-01-01&endTime=2025-12-31&currentPage=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. 先查询退卡明细表(不使用时间筛选,因为明细表的tksj可能为null,时间筛选在退卡信息表进行) 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); 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, x => x.Tksj.HasValue && x.Tksj >= input.startTime.Value) .WhereIF(input.endTime.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 mxIds = mxList.Select(x => x.Id).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); // 使用字典存储每个退款明细对应的多个健康师信息 var healthCoachDict = new Dictionary>(); var actualRefundAmountDict = new Dictionary(); if (mxIds.Any()) { // 通过退卡品相表ID(F_CardReturn)查询退款健康师业绩 var jksyjList = await _db.Queryable() .Where(x => mxIds.Contains(x.CardReturn) && x.IsEffective == StatusEnum.有效.GetHashCode()) .Select(x => new { x.CardReturn, x.Jkszh, x.Jksxm, x.Jksyj }) .ToListAsync(); // 按退款明细ID分组,建立映射 foreach (var jksyj in jksyjList) { if (string.IsNullOrEmpty(jksyj.CardReturn)) continue; var jksyjValue = jksyj.Jksyj ?? 0m; if (!healthCoachDict.ContainsKey(jksyj.CardReturn)) { healthCoachDict[jksyj.CardReturn] = new List<(string Id, string Name, decimal Performance)>(); actualRefundAmountDict[jksyj.CardReturn] = 0m; } healthCoachDict[jksyj.CardReturn].Add((jksyj.Jkszh ?? "", jksyj.Jksxm ?? "", jksyjValue)); actualRefundAmountDict[jksyj.CardReturn] += jksyjValue; } } // 6. 组装数据 var resultList = new List(); foreach (var mx in mxList) { var refundInfo = refundInfoDict.ContainsKey(mx.RefundInfoId) ? refundInfoDict[mx.RefundInfoId] : null; // 如果退卡信息不在筛选范围内(时间、门店、会员等),跳过这条明细 if (refundInfo == null) { continue; } // 应用筛选条件 if (input.storeIds != null && input.storeIds.Any()) { if (!input.storeIds.Contains(refundInfo.Md)) continue; } if (!string.IsNullOrEmpty(input.memberId)) { if (mx.MemberId != input.memberId && refundInfo.Hy != input.memberId) continue; } var memberId = mx.MemberId ?? (refundInfo?.Hy); var refundTime = mx.Tksj ?? refundInfo?.Tksj; // 获取健康师信息(支持多个健康师) var healthCoachList = healthCoachDict.ContainsKey(mx.Id) ? healthCoachDict[mx.Id] : new List<(string Id, string Name, decimal Performance)>(); // 合并多个健康师姓名和业绩(格式:姓名(业绩),用顿号分隔) var healthCoachNames = ""; if (healthCoachList.Any()) { var healthCoachItems = healthCoachList .Where(h => !string.IsNullOrEmpty(h.Name)) .Select(h => $"{h.Name}({h.Performance:F2})") .ToList(); healthCoachNames = string.Join("、", healthCoachItems); } // 获取第一个健康师信息(兼容旧版本) var firstHealthCoach = healthCoachList.FirstOrDefault(); // 获取实际退款金额(所有健康师业绩之和) var actualRefundAmount = actualRefundAmountDict.ContainsKey(mx.Id) ? actualRefundAmountDict[mx.Id] : 0m; resultList.Add(new RefundDetailListOutput { 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, healthCoachId = firstHealthCoach.Id, healthCoachName = healthCoachNames, healthCoachPerformance = firstHealthCoach.Performance, actualRefundAmount = actualRefundAmount }); } // 7. 排序 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx; var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; if (sort.ToLower() == "desc") { resultList = resultList.OrderByDescending(x => GetPropertyValue(x, sidx)).ToList(); } else { resultList = resultList.OrderBy(x => GetPropertyValue(x, sidx)).ToList(); } // 8. 分页 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. 先查询退卡明细表(不使用时间筛选,因为明细表的tksj可能为null,时间筛选在退卡信息表进行) 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); 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, x => x.Tksj.HasValue && x.Tksj >= input.startTime.Value) .WhereIF(input.endTime.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 mxIds = mxList.Select(x => x.Id).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); // 使用字典存储每个退款明细对应的多个健康师信息 var healthCoachDict = new Dictionary>(); var actualRefundAmountDict = new Dictionary(); if (mxIds.Any()) { // 通过退卡品相表ID(F_CardReturn)查询退款健康师业绩 var jksyjList = await _db.Queryable() .Where(x => mxIds.Contains(x.CardReturn) && x.IsEffective == StatusEnum.有效.GetHashCode()) .Select(x => new { x.CardReturn, x.Jkszh, x.Jksxm, x.Jksyj }) .ToListAsync(); // 按退款明细ID分组,建立映射 foreach (var jksyj in jksyjList) { if (string.IsNullOrEmpty(jksyj.CardReturn)) continue; var jksyjValue = jksyj.Jksyj ?? 0m; if (!healthCoachDict.ContainsKey(jksyj.CardReturn)) { healthCoachDict[jksyj.CardReturn] = new List<(string Id, string Name, decimal Performance)>(); actualRefundAmountDict[jksyj.CardReturn] = 0m; } healthCoachDict[jksyj.CardReturn].Add((jksyj.Jkszh ?? "", jksyj.Jksxm ?? "", jksyjValue)); actualRefundAmountDict[jksyj.CardReturn] += jksyjValue; } } // 6. 组装数据 var resultList = new List(); foreach (var mx in mxList) { var refundInfo = refundInfoDict.ContainsKey(mx.RefundInfoId) ? refundInfoDict[mx.RefundInfoId] : null; // 如果退卡信息不在筛选范围内(时间、门店、会员等),跳过这条明细 if (refundInfo == null) { continue; } // 应用筛选条件 if (input.storeIds != null && input.storeIds.Any()) { if (!input.storeIds.Contains(refundInfo.Md)) continue; } if (!string.IsNullOrEmpty(input.memberId)) { if (mx.MemberId != input.memberId && refundInfo.Hy != input.memberId) continue; } var memberId = mx.MemberId ?? (refundInfo?.Hy); var refundTime = mx.Tksj ?? refundInfo?.Tksj; // 获取健康师信息(支持多个健康师) var healthCoachList = healthCoachDict.ContainsKey(mx.Id) ? healthCoachDict[mx.Id] : new List<(string Id, string Name, decimal Performance)>(); // 合并多个健康师姓名和业绩(格式:姓名(业绩),用顿号分隔) var healthCoachNames = ""; if (healthCoachList.Any()) { var healthCoachItems = healthCoachList .Where(h => !string.IsNullOrEmpty(h.Name)) .Select(h => $"{h.Name}({h.Performance:F2})") .ToList(); healthCoachNames = string.Join("、", healthCoachItems); } // 获取第一个健康师信息(兼容旧版本) var firstHealthCoach = healthCoachList.FirstOrDefault(); // 获取实际退款金额(所有健康师业绩之和) var actualRefundAmount = actualRefundAmountDict.ContainsKey(mx.Id) ? actualRefundAmountDict[mx.Id] : 0m; resultList.Add(new RefundDetailListOutput { 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, healthCoachId = firstHealthCoach.Id, healthCoachName = healthCoachNames, healthCoachPerformance = firstHealthCoach.Performance, actualRefundAmount = actualRefundAmount }); } // 7. 排序 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx; var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; if (sort.ToLower() == "desc") { resultList = resultList.OrderByDescending(x => GetPropertyValue(x, sidx)).ToList(); } 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\"},{\"value\":\"健康师\",\"field\":\"healthCoachName\"},{\"value\":\"健康师业绩\",\"field\":\"healthCoachPerformance\"},]".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 } }