using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; 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.Common; using NCC.Extend.Entitys.Dto.LqKdDeductinfo; using NCC.Extend.Entitys.Dto.LqKdKdjlb; using NCC.Extend.Entitys.Enum; using NCC.Extend.Entitys.lq_hytk_hytk; using NCC.Extend.Entitys.lq_hytk_mx; using NCC.Extend.Entitys.lq_jinsanjiao_user; using NCC.Extend.Entitys.lq_kd_deductinfo; using NCC.Extend.Entitys.lq_xh_hyhk; using NCC.Extend.Entitys.lq_xh_pxmx; using NCC.Extend.Entitys.lq_kd_jksyj; using NCC.Extend.Entitys.lq_kd_kdjlb; using NCC.Extend.Entitys.lq_kd_kjbsyj; using NCC.Extend.Entitys.lq_kd_pxmx; using NCC.Extend.Entitys.lq_khxx; using NCC.Extend.Entitys.lq_xmzl; using NCC.Extend.Interfaces.LqKdKdjlb; using NCC.Extend.Utils; using NCC.FriendlyException; using NCC.JsonSerialization; using NCC.System.Entitys.Permission; using SqlSugar; using Yitter.IdGenerator; namespace NCC.Extend.LqKdKdjlb { /// /// 开单记录表服务 /// [ApiDescriptionSettings(Tag = "绿纤开单记录表服务", Name = "LqKdKdjlb", Order = 200)] [Route("api/Extend/[controller]")] public class LqKdKdjlbService : ILqKdKdjlbService, IDynamicApiController, ITransient { private readonly ISqlSugarRepository _lqKdKdjlbRepository; private readonly ISqlSugarRepository _lqKdJksyjRepository; private readonly ISqlSugarRepository _lqKdKjbsyjRepository; private readonly ISqlSugarRepository _lqKdPxmxRepository; private readonly SqlSugarScope _db; private readonly IUserManager _userManager; private readonly WeChatBotService _weChatBotService; private readonly LqKdKdjlbStringGenerator _stringGenerator; /// /// 初始化一个类型的新实例 /// public LqKdKdjlbService( ISqlSugarRepository lqKdKdjlbRepository, ISqlSugarRepository lqKdJksyjRepository, ISqlSugarRepository lqKdKjbsyjRepository, ISqlSugarRepository lqKdPxmxRepository, IUserManager userManager, WeChatBotService weChatBotService, LqKdKdjlbStringGenerator stringGenerator ) { _lqKdKdjlbRepository = lqKdKdjlbRepository; _db = _lqKdKdjlbRepository.Context; _lqKdJksyjRepository = lqKdJksyjRepository; _lqKdKjbsyjRepository = lqKdKjbsyjRepository; _lqKdPxmxRepository = lqKdPxmxRepository; _userManager = userManager; _weChatBotService = weChatBotService; _stringGenerator = stringGenerator; } #region 获取开单记录表 /// /// 获取开单记录表 /// /// /// 获取开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// 按照开单的完整格式返回数据,不包含汇总信息 /// /// 返回数据结构: /// - 主表信息:开单基础信息、业绩信息、支付信息、会员信息等 /// - 品项明细列表:每个品项包含完整的项目信息(项目次数、是否有效、来源类型等) /// - 健康师业绩列表:按品项关联的健康师业绩信息 /// - 科技部老师业绩列表:按品项关联的科技部老师业绩信息 /// /// 开单记录主键ID /// 开单记录完整信息 /// 查询成功 /// 开单记录不存在 /// 服务器内部错误 [HttpGet("{id}")] public async Task GetInfo(string id) { try { // 1. 查询主表信息 var entity = await _db.Queryable().FirstAsync(p => p.Id == id); if (entity == null) { throw NCCException.Oh(ErrorCode.COM1005, "开单记录不存在"); } var output = entity.Adapt(); //获取会员信息 var MemberInfo = await _db.Queryable().FirstAsync(p => p.Id == entity.Kdhy); output.kdhyc = MemberInfo.Khmc; output.kdhysjh = MemberInfo.Sjh; // 2. 查询品项明细列表 var lqKdPxmxList = await _db.Queryable().Where(w => w.Glkdbh == entity.Id).ToListAsync(); // 3. 查询健康师业绩列表 var lqKdJksyjList = await _db.Queryable().Where(w => w.Glkdbh == entity.Id).ToListAsync(); // 4. 查询科技部老师业绩列表 var lqKdKjbsyjList = await _db.Queryable().Where(w => w.Glkdbh == entity.Id).ToListAsync(); // 5. 查询扣款信息列表 var lqKdDeductList = await _db.Queryable().Where(w => w.BillingId == entity.Id).ToListAsync(); // 5. 构建品项明细输出,每个品项关联对应的业绩信息 var pxmxOutputList = new List(); foreach (var pxmx in lqKdPxmxList) { var pxmxOutput = new LqKdPxmxInfoOutput { id = pxmx.Id, glkdbh = pxmx.Glkdbh, px = pxmx.Px, pxmc = pxmx.Pxmc, pxjg = pxmx.Pxjg, projectNumber = pxmx.ProjectNumber, isEnabled = pxmx.IsEnabled, sourceType = pxmx.SourceType, memberId = pxmx.MemberId, createTime = pxmx.CreateTIme, totalPrice = pxmx.TotalPrice, actualPrice = pxmx.ActualPrice, remark = pxmx.Remark, isEffective = pxmx.IsEffective, }; // 关联该品项的健康师业绩 var jksyjForPx = lqKdJksyjList.Where(j => j.Kdpxid == pxmx.Id).ToList(); pxmxOutput.lqKdJksyjList = jksyjForPx.Adapt>(); // 关联该品项的科技部老师业绩 var kjbsyjForPx = lqKdKjbsyjList.Where(k => k.Kdpxid == pxmx.Id).ToList(); pxmxOutput.lqKdKjbsyjList = kjbsyjForPx.Adapt>(); pxmxOutputList.Add(pxmxOutput); } // 6. 设置输出结果 output.lqKdPxmxList = pxmxOutputList; // 7. 设置全局业绩列表(用于兼容性,但主要使用品项关联的业绩) output.lqKdJksyjList = lqKdJksyjList.Adapt>(); output.lqKdKjbsyjList = lqKdKjbsyjList.Adapt>(); // 8. 设置扣款信息列表 output.lqKdDeductList = lqKdDeductList.Adapt>(); return output; } catch (Exception ex) { Console.WriteLine($"获取开单记录失败: {ex.Message}"); throw NCCException.Oh(ErrorCode.COM1000, "获取开单记录失败"); } } #endregion #region 获取开单记录表列表 /// /// 获取开单记录表列表 /// /// 请求参数 /// [HttpGet("")] public async Task GetList([FromQuery] LqKdKdjlbListQueryInput input) { var sidx = input.sidx == null ? "kdrq" : input.sidx; List queryKdrq = input.kdrq != null ? input.kdrq.Split(',').ToObeject>() : null; DateTime? startKdrq = queryKdrq != null ? Ext.GetDateTime(queryKdrq.First()) : null; DateTime? endKdrq = queryKdrq != null ? Ext.GetDateTime(queryKdrq.Last()) : null; var data = await _db.Queryable() .WhereIF(!string.IsNullOrEmpty(input.keyword), p => p.Kdhyc.Contains(input.keyword) || p.Kdhysjh.Contains(input.keyword)) .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id)) .WhereIF(!string.IsNullOrEmpty(input.djmd), p => p.Djmd.Equals(input.djmd)) .WhereIF(!string.IsNullOrEmpty(input.jsj), p => p.Jsj.Equals(input.jsj)) .WhereIF(queryKdrq != null, p => p.Kdrq >= new DateTime(startKdrq.ToDate().Year, startKdrq.ToDate().Month, startKdrq.ToDate().Day, 0, 0, 0)) .WhereIF(queryKdrq != null, p => p.Kdrq <= new DateTime(endKdrq.ToDate().Year, endKdrq.ToDate().Month, endKdrq.ToDate().Day, 23, 59, 59)) .WhereIF(!string.IsNullOrEmpty(input.gjlx), p => p.Gjlx.Equals(input.gjlx)) .WhereIF(!string.IsNullOrEmpty(input.hgjg), p => p.Hgjg.Equals(input.hgjg)) .WhereIF(!string.IsNullOrEmpty(input.zdyj), p => p.Zdyj.Equals(input.zdyj)) .WhereIF(!string.IsNullOrEmpty(input.sfyj), p => p.Sfyj.Equals(input.sfyj)) .WhereIF(!string.IsNullOrEmpty(input.qk), p => p.Qk.Equals(input.qk)) .WhereIF(!string.IsNullOrEmpty(input.ckfs), p => p.Ckfs.Equals(input.ckfs)) .WhereIF(!string.IsNullOrEmpty(input.fkfs), p => p.Fkfs.Equals(input.fkfs)) .WhereIF(!string.IsNullOrEmpty(input.fkyy), p => p.Fkyy.Equals(input.fkyy)) .WhereIF(!string.IsNullOrEmpty(input.fkpd), p => p.Fkpd.Contains(input.fkpd)) .WhereIF(!string.IsNullOrEmpty(input.khly), p => p.Khly.Equals(input.khly)) .WhereIF(!string.IsNullOrEmpty(input.tjr), p => p.Tjr.Contains(input.tjr)) .WhereIF(!string.IsNullOrEmpty(input.sfskdd), p => p.Sfskdd.Equals(input.sfskdd)) .WhereIF(!string.IsNullOrEmpty(input.jj), p => p.Jj.Contains(input.jj)) .WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz)) .WhereIF(!string.IsNullOrEmpty(input.kdhy), p => p.Kdhy.Equals(input.kdhy)) .WhereIF(!string.IsNullOrEmpty(input.kdhyc), p => p.Kdhyc.Contains(input.kdhyc)) .WhereIF(!string.IsNullOrEmpty(input.kdhysjh), p => p.Kdhysjh.Contains(input.kdhysjh)) .WhereIF(!string.IsNullOrEmpty(input.F_FIleUrl), p => p.F_FIleUrl.Contains(input.F_FIleUrl)) .WhereIF(!string.IsNullOrEmpty(input.CreateUser), p => p.CreateUser.Equals(input.CreateUser)) .Select(it => new LqKdKdjlbListOutput { id = it.Id, djmd = it.Djmd, jsj = it.Jsj, kdrq = it.Kdrq, gjlx = it.Gjlx, hgjg = it.Hgjg, zdyj = it.Zdyj, sfyj = it.Sfyj, qk = it.Qk, ckfs = it.Ckfs, fkfs = it.Fkfs, fkyy = it.Fkyy, fkpd = it.Fkpd, khly = it.Khly, tjr = it.Tjr, deductAmount = it.DeductAmount, sfskdd = it.Sfskdd, jj = it.Jj, bz = it.Bz, kdhy = it.Kdhy, kdhyc = SqlFunc.Subqueryable().Where(x => x.Id == it.Kdhy).Select(x => x.Khmc), kdhysjh = SqlFunc.Subqueryable().Where(x => x.Id == it.Kdhy).Select(x => x.Sjh), IsEffective = it.IsEffective, CreateUser = it.CreateUser, CreateUserName = SqlFunc.Subqueryable().Where(x => x.Id == it.CreateUser).Select(x => x.RealName), }) .MergeTable() .OrderBy(sidx + " " + input.sort) .ToPagedListAsync(input.currentPage, input.pageSize); // 获取当前页的开单记录ID列表 var billingIds = data.list.Select(x => x.id).ToList(); // 批量查询品项明细 var itemDetails = new List(); if (billingIds.Any()) { itemDetails = await _db.Queryable() .Where(x => billingIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()) .Select(x => new LqKdPxmxInfoOutput { id = x.Id, glkdbh = x.Glkdbh, px = x.Px, pxmc = x.Pxmc, pxjg = x.Pxjg, projectNumber = x.ProjectNumber, isEnabled = x.IsEnabled, sourceType = x.SourceType, memberId = x.MemberId, createTime = x.CreateTIme, totalPrice = x.TotalPrice, actualPrice = x.ActualPrice, remark = x.Remark, isEffective = x.IsEffective }) .ToListAsync(); } // 按开单ID分组品项明细 var itemDetailsGrouped = itemDetails.GroupBy(x => x.glkdbh) .ToDictionary(g => g.Key, g => g.ToList()); // 为每个开单记录分配品项明细 foreach (var item in data.list) { item.ItemDetails = itemDetailsGrouped.ContainsKey(item.id) ? itemDetailsGrouped[item.id] : new List(); } return PageResult.SqlSugarPageResult(data); } #endregion #region 新建开单记录表 /// /// 新建开单记录表 /// /// 参数 /// [HttpPost("")] public async Task Create([FromBody] LqKdKdjlbCrInput input) { var userInfo = await _userManager.GetUserInfo(); var entity = input.Adapt(); var HealthInstructorNames = ""; entity.Id = YitIdHelper.NextId().ToString(); entity.CreateTime = DateTime.Now; entity.UpdateTime = DateTime.Now; entity.IsEffective = StatusEnum.有效.GetHashCode(); try { //开启事务 _db.BeginTran(); //判断是否有作废关联id //暂时先不需要 // if (!string.IsNullOrEmpty(input.cancelRefId)) // { // //查询作废关联id // var cancelRefEntity = await _db.Queryable().FirstAsync(p => p.Id == input.cancelRefId); // if (cancelRefEntity == null || cancelRefEntity.IsEffective == StatusEnum.无效.GetHashCode()) // { // throw NCCException.Oh("该开单记录已经作废"); // } // // 检查作废关联记录是否可以作废 // var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(input.cancelRefId); // if (!canCancel) // { // throw NCCException.Oh(errorMessage); // } // //将作废关联id的IsEffective设置为无效 // cancelRefEntity.IsEffective = StatusEnum.无效.GetHashCode(); // await _db.Updateable(cancelRefEntity).ExecuteCommandAsync(); // //把品项明细表的IsEffective设置为无效 // await _db.Updateable().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdPxmxEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync(); // //把健康师业绩表的IsEffective设置为无效 // await _db.Updateable().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdJksyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync(); // //把科技部老师业绩表的IsEffective设置为无效 // await _db.Updateable().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdKjbsyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync(); // //把扣款信息表的IsEffective设置为无效 // await _db.Updateable().Where(p => p.BillingId == input.cancelRefId).UpdateColumns(p => new LqKdDeductinfoEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync(); // } //判断是否有补缴开单ID if (!string.IsNullOrEmpty(input.supplementBillingId)) { //查询补缴开单ID var supplementBillingEntity = await _db.Queryable().FirstAsync(p => p.Id == input.supplementBillingId); if (supplementBillingEntity == null || supplementBillingEntity.IsEffective == StatusEnum.无效.GetHashCode()) { throw NCCException.Oh("补缴开单记录不存在或已作废"); } //然后对之前的开单表的补缴金额进行累加 //需要判断补缴金额是否超过欠款金额,只记录应缴金额 var supplementAmount = supplementBillingEntity.Qk - supplementBillingEntity.PaidDebt; entity.PaidDebt += input.supplementAmount > supplementAmount ? supplementAmount : input.supplementAmount; await _db.Updateable(entity).ExecuteCommandAsync(); } //新增开单记录表记录 entity.CreateUser = userInfo.userId; entity.DeductAmount = input.lqKdKdjlbDeductList.Sum(x => x.Amount ?? 0);//计算储扣总金额 var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); //循环品相信息 // 收集所有需要插入的实体,然后批量插入 var allPxmxEntities = new List(); var allJksyjEntities = new List(); var allKjbsyjEntities = new List(); var allDeductEntities = new List(); // 处理扣款信息列表 foreach (var item in input.lqKdKdjlbDeductList) { var lqKdDeductEntity = new LqKdDeductinfoEntity { Id = YitIdHelper.NextId().ToString(), BillingId = newEntity.Id, DeductId = item.DeductId, DeductType = item.DeductType, Amount = item.Amount, ProjectNumber = item.ProjectNumber, UnitPrice = item.UnitPrice, ItemName = item.ItemName, ItemId = item.ItemId, IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效 CreateTime = DateTime.Now, // 设置创建时间 }; allDeductEntities.Add(lqKdDeductEntity); } // 处理品项明细列表 foreach (var item in input.lqKdPxmxList) { // 创建品项明细实体 var lqKdPxmxEntity = new LqKdPxmxEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = newEntity.Id, Yjsj = input.kdrq, CreateTIme = DateTime.Now, MemberId = entity.Kdhy, IsEnabled = StatusEnum.有效.GetHashCode(), ProjectNumber = item.projectNumber, TotalPrice = (decimal)(item.pxjg * item.projectNumber), Px = item.px, Pxmc = item.pxmc, Pxjg = item.pxjg, SourceType = item.sourceType, ActualPrice = item.actualPrice, Remark = item.remark, IsEffective = StatusEnum.有效.GetHashCode(), ActivityId = input.activityId, }; allPxmxEntities.Add(lqKdPxmxEntity); // 收集该品项关联的健康师业绩 if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any()) { //把jksxm保存到HealthInstructorNames foreach (var ijks_tem in item.lqKdJksyjList) { HealthInstructorNames += ijks_tem.jksxm + ","; allJksyjEntities.Add(new LqKdJksyjEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = newEntity.Id, Jks = ijks_tem.jks, Jksxm = ijks_tem.jksxm, Jkszh = ijks_tem.jkszh, Jksyj = ijks_tem.jksyj, Yjsj = input.kdrq, Jsj_id = ijks_tem.jsj_id, Kdpxid = lqKdPxmxEntity.Id, IsEffective = StatusEnum.有效.GetHashCode(), ActivityId = input.activityId, }); } } // 收集该品项关联的科技部老师业绩 if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any()) { foreach (var ikjbs_tem in item.lqKdKjbsyjList) { allKjbsyjEntities.Add(new LqKdKjbsyjEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = newEntity.Id, Kjbls = ikjbs_tem.kjbls, Kjblsxm = ikjbs_tem.kjblsxm, Kjblszh = ikjbs_tem.kjblszh, Kjblsyj = ikjbs_tem.kjblsyj, Yjsj = input.kdrq, Kdpxid = lqKdPxmxEntity.Id, IsEffective = StatusEnum.有效.GetHashCode(), ActivityId = input.activityId, } ); } } } //通过会员id查询会员信息 var memberInfo = await _db.Queryable().Where(u => u.Id == entity.Kdhy).FirstAsync(); //通过开单记录表查询这个会员开单金额 var kdAmount = await _db.Queryable().Where(u => u.Kdhy == entity.Kdhy).SumAsync(u => u.Sfyj); //如果开单金额小于500,为散客,如果大于500,为会员 if (kdAmount < 500 && kdAmount > 0) { memberInfo.Khlx = MemberTypeEnum.散客.GetHashCode().ToString(); } else { memberInfo.Khlx = MemberTypeEnum.会员.GetHashCode().ToString(); } await _db.Updateable(memberInfo).ExecuteCommandAsync(); // 批量插入扣款信息 if (allDeductEntities.Any()) { await _db.Insertable(allDeductEntities).ExecuteCommandAsync(); } // 批量插入品项明细 if (allPxmxEntities.Any()) { await _db.Insertable(allPxmxEntities).ExecuteCommandAsync(); } // 批量插入健康师业绩 if (allJksyjEntities.Any()) { await _db.Insertable(allJksyjEntities).ExecuteCommandAsync(); } // 批量插入科技部老师业绩 if (allKjbsyjEntities.Any()) { await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync(); } //关闭事务 _db.CommitTran(); // 生成开单记录字符串并发送到企业微信 try { var entityInfo = await GetInfo(newEntity.Id); if (entityInfo != null) { var orderRecordString = _stringGenerator.GenerateOrderRecordString(entityInfo, HealthInstructorNames); Console.WriteLine("开单记录字符串生成成功:"); Console.WriteLine(orderRecordString); // 发送到企业微信群 try { var sendResult = await _weChatBotService.SendOrderRecordMessage(orderRecordString); if (sendResult) { Console.WriteLine("开单记录已成功发送到企业微信群"); } else { Console.WriteLine("开单记录发送到企业微信群失败"); } } catch (Exception wechatEx) { Console.WriteLine($"发送企业微信消息异常: {wechatEx.Message}"); } } } catch (Exception ex) { // 字符串生成失败不影响主流程,只记录日志 Console.WriteLine($"生成开单记录字符串失败: {ex.Message}"); } } catch (Exception ex) { //回滚事务 _db.RollbackTran(); Console.WriteLine($"开单创建失败: {ex.Message}"); Console.WriteLine($"异常堆栈: {ex.StackTrace}"); throw NCCException.Oh(ErrorCode.COM1000); } } #endregion #region 新建开单记录表_备份 /// /// 新建开单记录表_备份 /// /// 参数 /// [HttpPost("Create_Bak")] public async Task Create_Bak([FromBody] LqKdKdjlbCrInput input) { var userInfo = await _userManager.GetUserInfo(); var entity = input.Adapt(); var HealthInstructorNames = ""; entity.Id = YitIdHelper.NextId().ToString(); entity.CreateTime = DateTime.Now; entity.UpdateTime = DateTime.Now; try { //开启事务 _db.BeginTran(); //新增开单记录表记录 entity.CreateUser = userInfo.userId; entity.DeductAmount = input.lqKdKdjlbDeductList.Sum(x => x.Amount ?? 0);//计算储扣总金额 var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); //循环品相信息 // 收集所有需要插入的实体,然后批量插入 var allPxmxEntities = new List(); var allJksyjEntities = new List(); var allKjbsyjEntities = new List(); var allDeductEntities = new List(); // 处理扣款信息列表 foreach (var item in input.lqKdKdjlbDeductList) { var lqKdDeductEntity = new LqKdDeductinfoEntity { Id = YitIdHelper.NextId().ToString(), BillingId = newEntity.Id, DeductId = item.DeductId, DeductType = item.DeductType, Amount = item.Amount, }; allDeductEntities.Add(lqKdDeductEntity); } // 处理品项明细列表 foreach (var item in input.lqKdPxmxList) { // 创建品项明细实体 var lqKdPxmxEntity = new LqKdPxmxEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = newEntity.Id, Yjsj = input.kdrq, CreateTIme = DateTime.Now, MemberId = entity.Kdhy, IsEnabled = 0, ProjectNumber = item.projectNumber, TotalPrice = (decimal)(item.pxjg * item.projectNumber), Px = item.px, Pxmc = item.pxmc, Pxjg = item.pxjg, SourceType = item.sourceType, ActualPrice = item.actualPrice, }; allPxmxEntities.Add(lqKdPxmxEntity); // 收集该品项关联的健康师业绩 if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any()) { //把jksxm保存到HealthInstructorNames foreach (var ijks_tem in item.lqKdJksyjList) { HealthInstructorNames += ijks_tem.jksxm + ","; allJksyjEntities.Add(new LqKdJksyjEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = newEntity.Id, Jks = ijks_tem.jks, Jksxm = ijks_tem.jksxm, Jkszh = ijks_tem.jkszh, Jksyj = ijks_tem.jksyj, Yjsj = input.kdrq, Jsj_id = ijks_tem.jsj_id, Kdpxid = lqKdPxmxEntity.Id, }); } } // 收集该品项关联的科技部老师业绩 if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any()) { foreach (var ikjbs_tem in item.lqKdKjbsyjList) { allKjbsyjEntities.Add( new LqKdKjbsyjEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = newEntity.Id, Kjbls = ikjbs_tem.kjbls, Kjblsxm = ikjbs_tem.kjblsxm, Kjblszh = ikjbs_tem.kjblszh, Kjblsyj = ikjbs_tem.kjblsyj, Yjsj = input.kdrq, Kdpxid = lqKdPxmxEntity.Id, } ); } } } //通过会员id查询会员信息 var memberInfo = await _db.Queryable().Where(u => u.Id == entity.Kdhy).FirstAsync(); //通过开单记录表查询这个会员开单金额 var kdAmount = await _db.Queryable().Where(u => u.Kdhy == entity.Kdhy).SumAsync(u => u.Sfyj); //如果开单金额小于500,为散客,如果大于500,为会员 if (kdAmount < 500) { memberInfo.Khlx = MemberTypeEnum.散客.GetHashCode().ToString(); } else { memberInfo.Khlx = MemberTypeEnum.会员.GetHashCode().ToString(); } await _db.Updateable(memberInfo).ExecuteCommandAsync(); // 批量插入扣款信息 if (allDeductEntities.Any()) { await _db.Insertable(allDeductEntities).ExecuteCommandAsync(); } // 批量插入品项明细 if (allPxmxEntities.Any()) { await _db.Insertable(allPxmxEntities).ExecuteCommandAsync(); } // 批量插入健康师业绩 if (allJksyjEntities.Any()) { await _db.Insertable(allJksyjEntities).ExecuteCommandAsync(); } // 批量插入科技部老师业绩 if (allKjbsyjEntities.Any()) { await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync(); } //关闭事务 _db.CommitTran(); // 生成开单记录字符串并发送到企业微信 try { var entityInfo = await GetInfo(newEntity.Id); if (entityInfo != null) { var orderRecordString = _stringGenerator.GenerateOrderRecordString(entityInfo, HealthInstructorNames); Console.WriteLine("开单记录字符串生成成功:"); Console.WriteLine(orderRecordString); // 发送到企业微信群 try { var sendResult = await _weChatBotService.SendOrderRecordMessage(orderRecordString); if (sendResult) { Console.WriteLine("开单记录已成功发送到企业微信群"); } else { Console.WriteLine("开单记录发送到企业微信群失败"); } } catch (Exception wechatEx) { Console.WriteLine($"发送企业微信消息异常: {wechatEx.Message}"); } } } catch (Exception ex) { // 字符串生成失败不影响主流程,只记录日志 Console.WriteLine($"生成开单记录字符串失败: {ex.Message}"); } } catch (Exception ex) { //回滚事务 _db.RollbackTran(); Console.WriteLine($"开单创建失败: {ex.Message}"); Console.WriteLine($"异常堆栈: {ex.StackTrace}"); throw NCCException.Oh(ErrorCode.COM1000); } } #endregion #region 获取开单记录表无分页列表 /// /// 获取开单记录表无分页列表 /// /// 请求参数 /// [NonAction] public async Task GetNoPagingList([FromQuery] LqKdKdjlbListQueryInput input) { var sidx = input.sidx == null ? "id" : input.sidx; List queryKdrq = input.kdrq != null ? input.kdrq.Split(',').ToObeject>() : null; DateTime? startKdrq = queryKdrq != null ? Ext.GetDateTime(queryKdrq.First()) : null; DateTime? endKdrq = queryKdrq != null ? Ext.GetDateTime(queryKdrq.Last()) : null; var data = await _db.Queryable() .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id)) .WhereIF(!string.IsNullOrEmpty(input.djmd), p => p.Djmd.Equals(input.djmd)) .WhereIF(!string.IsNullOrEmpty(input.jsj), p => p.Jsj.Equals(input.jsj)) .WhereIF(queryKdrq != null, p => p.Kdrq >= new DateTime(startKdrq.ToDate().Year, startKdrq.ToDate().Month, startKdrq.ToDate().Day, 0, 0, 0)) .WhereIF(queryKdrq != null, p => p.Kdrq <= new DateTime(endKdrq.ToDate().Year, endKdrq.ToDate().Month, endKdrq.ToDate().Day, 23, 59, 59)) .WhereIF(!string.IsNullOrEmpty(input.gjlx), p => p.Gjlx.Equals(input.gjlx)) .WhereIF(!string.IsNullOrEmpty(input.hgjg), p => p.Hgjg.Equals(input.hgjg)) .WhereIF(!string.IsNullOrEmpty(input.zdyj), p => p.Zdyj.Equals(input.zdyj)) .WhereIF(!string.IsNullOrEmpty(input.sfyj), p => p.Sfyj.Equals(input.sfyj)) .WhereIF(!string.IsNullOrEmpty(input.qk), p => p.Qk.Equals(input.qk)) .WhereIF(!string.IsNullOrEmpty(input.ckfs), p => p.Ckfs.Equals(input.ckfs)) .WhereIF(!string.IsNullOrEmpty(input.fkfs), p => p.Fkfs.Equals(input.fkfs)) .WhereIF(!string.IsNullOrEmpty(input.fkyy), p => p.Fkyy.Equals(input.fkyy)) .WhereIF(!string.IsNullOrEmpty(input.fkpd), p => p.Fkpd.Contains(input.fkpd)) .WhereIF(!string.IsNullOrEmpty(input.khly), p => p.Khly.Equals(input.khly)) .WhereIF(!string.IsNullOrEmpty(input.tjr), p => p.Tjr.Contains(input.tjr)) .WhereIF(!string.IsNullOrEmpty(input.sfskdd), p => p.Sfskdd.Equals(input.sfskdd)) .WhereIF(!string.IsNullOrEmpty(input.jj), p => p.Jj.Contains(input.jj)) .WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz)) .WhereIF(!string.IsNullOrEmpty(input.kdhy), p => p.Kdhy.Equals(input.kdhy)) .WhereIF(!string.IsNullOrEmpty(input.kdhyc), p => p.Kdhyc.Contains(input.kdhyc)) .WhereIF(!string.IsNullOrEmpty(input.kdhysjh), p => p.Kdhysjh.Contains(input.kdhysjh)) .WhereIF(!string.IsNullOrEmpty(input.F_FIleUrl), p => p.F_FIleUrl.Contains(input.F_FIleUrl)) .Select(it => new LqKdKdjlbListOutput { id = it.Id, djmd = it.Djmd, jsj = it.Jsj, kdrq = it.Kdrq, gjlx = it.Gjlx, hgjg = it.Hgjg, zdyj = it.Zdyj, sfyj = it.Sfyj, qk = it.Qk, ckfs = it.Ckfs, deductAmount = it.DeductAmount, fkfs = it.Fkfs, fkyy = it.Fkyy, fkpd = it.Fkpd, khly = it.Khly, tjr = it.Tjr, sfskdd = it.Sfskdd, jj = it.Jj, bz = it.Bz, kdhy = it.Kdhy, kdhyc = it.Kdhyc, kdhysjh = it.Kdhysjh, F_FIleUrl = it.F_FIleUrl, }) .MergeTable() .OrderBy(sidx + " " + input.sort) .ToListAsync(); return data; } #endregion #region 导出开单记录表 /// /// 导出开单记录表 /// /// 请求参数 /// [HttpGet("Actions/Export")] public async Task Export([FromQuery] LqKdKdjlbListQueryInput input) { var userInfo = await _userManager.GetUserInfo(); var exportData = new List(); if (input.dataType == 0) { var data = Clay.Object(await this.GetList(input)); exportData = data.Solidify>().list; } else { exportData = await this.GetNoPagingList(input); } List paramList = "[{\"value\":\"开单编号\",\"field\":\"id\"},{\"value\":\"开单会员\",\"field\":\"kdhy\"},{\"value\":\"开单会员名称\",\"field\":\"kdhyc\"},{\"value\":\"会员手机号\",\"field\":\"kdhysjh\"},{\"value\":\"单据门店\",\"field\":\"djmd\"},{\"value\":\"金三角\",\"field\":\"jsj\"},{\"value\":\"开单日期\",\"field\":\"kdrq\"},{\"value\":\"顾客类型\",\"field\":\"gjlx\"},{\"value\":\"合作机构\",\"field\":\"hgjg\"},{\"value\":\"整单业绩\",\"field\":\"zdyj\"},{\"value\":\"实付业绩\",\"field\":\"sfyj\"},{\"value\":\"欠款\",\"field\":\"qk\"},{\"value\":\"储扣方式\",\"field\":\"ckfs\"},{\"value\":\"储扣明细\",\"field\":\"ckmx\"},{\"value\":\"付款方式\",\"field\":\"fkfs\"},{\"value\":\"付款医院\",\"field\":\"fkyy\"},{\"value\":\"付款判断\",\"field\":\"fkpd\"},{\"value\":\"客户来源\",\"field\":\"khly\"},{\"value\":\"推荐人\",\"field\":\"tjr\"},{\"value\":\"是否首开订单\",\"field\":\"sfskdd\"},{\"value\":\"简介\",\"field\":\"jj\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"健康师业绩\",\"field\":\"jksyj\"},{\"value\":\"科技部老师业绩\",\"field\":\"kjblsyj\"},{\"value\":\"品项信息\",\"field\":\"pxxx\"},{\"value\":\"方案其他\",\"field\":\"F_FIleUrl\"},]".ToList(); ExcelConfig excelconfig = new ExcelConfig(); excelconfig.FileName = "开单记录表.xls"; excelconfig.HeadFont = "微软雅黑"; excelconfig.HeadPoint = 10; excelconfig.IsAllSizeColumn = true; excelconfig.ColumnModel = new List(); List selectKeyList = input.selectKey.Split(',').ToList(); foreach (var item in selectKeyList) { var isExist = paramList.Find(p => p.field == item); if (isExist != null) { excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = isExist.field, ExcelColumn = isExist.value }); } } var addPath = FileVariable.TemporaryFilePath + 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; } #endregion #region 批量删除开单记录表 /// /// 批量删除开单记录表 /// /// 主键数组 /// [HttpPost("batchRemove")] public async Task BatchRemove([FromBody] List ids) { var entitys = await _db.Queryable().In(it => it.Id, ids).ToListAsync(); if (entitys.Count > 0) { try { //开启事务 _db.BeginTran(); // 检查是否有消耗记录 var billingItemIds = await _db.Queryable().Where(p => ids.Contains(p.Glkdbh)).Select(p => p.Id).ToListAsync(); if (billingItemIds.Any()) { var consumeRecords = await _db.Queryable().Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()).Where(x => billingItemIds.Contains(x.BillingItemId)).AnyAsync(); if (consumeRecords) { throw NCCException.Oh("选中的开单记录中有已消耗的记录,不能删除"); } } // 检查是否有储扣记录 var deductRecords = await _db.Queryable().Where(x => ids.Contains(x.BillingId) && x.IsEffective == StatusEnum.有效.GetHashCode()).AnyAsync(); if (deductRecords) { throw NCCException.Oh("选中的开单记录中有储扣记录,不能删除"); } //批量删除开单记录表 await _db.Deleteable().In(d => d.Id, ids).ExecuteCommandAsync(); //清空子表数据 await _db.Deleteable().In(u => u.Glkdbh, ids).ExecuteCommandAsync(); //清空子表数据 await _db.Deleteable().In(u => u.Glkdbh, ids).ExecuteCommandAsync(); //清空子表数据 await _db.Deleteable().In(u => u.Glkdbh, ids).ExecuteCommandAsync(); //关闭事务 _db.CommitTran(); } catch (Exception) { //回滚事务 _db.RollbackTran(); throw NCCException.Oh(ErrorCode.COM1002); } } } #endregion #region 更新开单记录表 /// /// 更新开单记录表 /// /// /// 更新开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// /// 示例请求: /// ```json /// { /// "id": "开单编号", /// "djmd": "单据门店", /// "jsj": "金三角", /// "kdrq": "2025-01-11", /// "lqKdPxmxList": [ /// { /// "px": "品项编号", /// "pxmc": "品项名称", /// "pxjg": 100.00, /// "projectNumber": 1, /// "sourceType": "购买", /// "lqKdJksyjList": [ /// { /// "jks": "健康师", /// "jksxm": "健康师姓名", /// "jksyj": "100" /// } /// ] /// } /// ] /// } /// ``` /// /// 参数说明: /// - id: 开单记录主键ID /// - input: 开单记录更新参数,包含品项明细和业绩信息 /// /// 开单记录主键ID /// 开单记录更新参数 /// 无返回值 /// 更新成功 /// 参数错误或数据验证失败 /// 服务器内部错误 [HttpPut("{id}")] public async Task Update(string id, [FromBody] LqKdKdjlbUpInput input) { var entity = input.Adapt(); try { //开启事务 _db.BeginTran(); //更新开单记录表记录 await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); //清空原有数据 await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); // 收集所有需要插入的实体,然后批量插入 var allPxmxEntities = new List(); var allJksyjEntities = new List(); var allKjbsyjEntities = new List(); // 处理品项明细列表 if (input.lqKdPxmxList != null && input.lqKdPxmxList.Any()) { foreach (var item in input.lqKdPxmxList) { // 创建品项明细实体 var lqKdPxmxEntity = new LqKdPxmxEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = entity.Id, CreateTIme = DateTime.Now, MemberId = entity.Kdhy, IsEnabled = 0, ProjectNumber = item.projectNumber == 0 ? 1 : item.projectNumber, TotalPrice = (decimal)(item.pxjg * (item.projectNumber == 0 ? 1 : item.projectNumber)), Px = item.px, Pxmc = item.pxmc, Pxjg = item.pxjg, SourceType = item.sourceType, }; allPxmxEntities.Add(lqKdPxmxEntity); // 收集该品项关联的健康师业绩 if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any()) { foreach (var ijks_tem in item.lqKdJksyjList) { allJksyjEntities.Add( new LqKdJksyjEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = entity.Id, Jks = ijks_tem.jks, Jksxm = ijks_tem.jksxm, Jkszh = ijks_tem.jkszh, Jksyj = ijks_tem.jksyj, Yjsj = DateTime.Now, Jsj_id = ijks_tem.jsj_id, Kdpxid = lqKdPxmxEntity.Id, } ); } } // 收集该品项关联的科技部老师业绩 if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any()) { foreach (var ikjbs_tem in item.lqKdKjbsyjList) { allKjbsyjEntities.Add( new LqKdKjbsyjEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = entity.Id, Kjbls = ikjbs_tem.kjbls, Kjblsxm = ikjbs_tem.kjblsxm, Kjblszh = ikjbs_tem.kjblszh, Kjblsyj = ikjbs_tem.kjblsyj, Yjsj = DateTime.Now, Kdpxid = lqKdPxmxEntity.Id, } ); } } } } // 批量插入品项明细 if (allPxmxEntities.Any()) { await _db.Insertable(allPxmxEntities).ExecuteCommandAsync(); } // 批量插入健康师业绩 if (allJksyjEntities.Any()) { await _db.Insertable(allJksyjEntities).ExecuteCommandAsync(); } // 批量插入科技部老师业绩 if (allKjbsyjEntities.Any()) { await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync(); } //关闭事务 _db.CommitTran(); } catch (Exception) { //回滚事务 _db.RollbackTran(); throw NCCException.Oh(ErrorCode.COM1001); } } #endregion #region 删除开单记录表 /// /// 删除开单记录表 /// /// [HttpDelete("{id}")] public async Task Delete(string id) { var entity = await _db.Queryable().FirstAsync(p => p.Id == id); _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); try { //开启事务 _db.BeginTran(); //先查询开单品项明细 var lqPxmxList = _db.Queryable().Where(x => x.Glkdbh == id).ToList(); //判断是否有对应的消耗记录 var consumeRecords = await _db.Queryable().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.BillingItemId)).AnyAsync(); if (consumeRecords) { throw NCCException.Oh("选中的开单记录中有已消耗的记录,不能删除"); } //判断是否有对应的退卡记录 var refundRecords = await _db.Queryable().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.BillingItemId)).AnyAsync(); if (refundRecords) { throw NCCException.Oh("选中的开单记录中有退卡记录,不能删除"); } //判断是否有对应的储扣记录 var deductRecords = await _db.Queryable().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.DeductId)).AnyAsync(); if (deductRecords) { throw NCCException.Oh("选中的开单记录中有储扣记录,不能删除"); } //删除开单记录表记录 await _db.Deleteable().Where(d => d.Id == id).ExecuteCommandAsync(); //清空子表数据 await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); //清空子表数据 await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); //清空子表数据 await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); //关闭事务 _db.CommitTran(); } catch (Exception) { //回滚事务 _db.RollbackTran(); throw NCCException.Oh(ErrorCode.COM1002); } } #endregion #region 获取扣款类型枚举内容 /// /// 获取扣款类型枚举内容 /// /// 扣款类型枚举列表 [HttpGet("deduct-types")] public List GetDeductTypes() { return Enum.GetValues() .Select(e => new EnumOutput { Value = (int)e, Name = e.ToString(), Description = e.GetDescription(), }) .ToList(); } #endregion #region 修改开单记录 /// /// 修改开单记录 /// /// /// 更新开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息 /// /// 示例请求: /// ```json /// { /// "id": "开单编号", /// "djmd": "单据门店", /// "jsj": "金三角", /// "kdrq": "2025-01-11", /// "lqKdPxmxList": [ /// { /// "px": "品项编号", /// "pxmc": "品项名称", /// "pxjg": 100.00, /// "projectNumber": 1, /// "sourceType": "购买", /// "lqKdJksyjList": [ /// { /// "jks": "健康师", /// "jksxm": "健康师姓名", /// "jksyj": "100" /// } /// ] /// } /// ] /// } /// ``` /// /// 参数说明: /// - id: 开单记录主键ID /// - input: 开单记录更新参数,包含品项明细和业绩信息 /// /// 开单记录主键ID /// 开单记录更新参数 /// 无返回值 /// 更新成功 /// 参数错误或数据验证失败 /// 服务器内部错误 [HttpPut("UpdateForNoDelete/{id}")] public async Task UpdateForNoDelete(string id, [FromBody] LqKdKdjlbUpInput input) { var entity = input.Adapt(); entity.Id = id; // 确保ID正确设置 try { //检查开单记录是否可以操作 var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(id); if (!canCancel) { throw NCCException.Oh(errorMessage); } //开启事务 _db.BeginTran(); // 更新开单记录主表 await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).IgnoreColumns(x => x.CreateTime).ExecuteCommandAsync(); //清空原有品项明细 await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync(); //清空原有健康师业绩 await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync(); //清空原有科技部老师业绩 await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync(); //清空原有扣款信息 await _db.Deleteable().Where(x => x.BillingId == id).ExecuteCommandAsync(); //循环品相信息 // 收集所有需要插入的实体,然后批量插入 var allPxmxEntities = new List(); var allJksyjEntities = new List(); var allKjbsyjEntities = new List(); var allDeductEntities = new List(); // 处理扣款信息列表 foreach (var item in input.lqKdKdjlbDeductList) { var lqKdDeductEntity = new LqKdDeductinfoEntity { Id = YitIdHelper.NextId().ToString(), BillingId = id, DeductId = item.DeductId, DeductType = item.DeductType, Amount = item.Amount, ProjectNumber = item.ProjectNumber, UnitPrice = item.UnitPrice, ItemName = item.ItemName, ItemId = item.ItemId, IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效 CreateTime = DateTime.Now, // 设置创建时间 }; allDeductEntities.Add(lqKdDeductEntity); } // 处理品项明细列表 foreach (var item in input.lqKdPxmxList) { // 创建品项明细实体 var lqKdPxmxEntity = new LqKdPxmxEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = id, Yjsj = input.kdrq, CreateTIme = DateTime.Now, MemberId = entity.Kdhy, IsEnabled = StatusEnum.有效.GetHashCode(), ProjectNumber = item.projectNumber, TotalPrice = (decimal)(item.pxjg * item.projectNumber), Px = item.px, Pxmc = item.pxmc, Pxjg = item.pxjg, SourceType = item.sourceType, ActualPrice = item.actualPrice, Remark = item.remark, IsEffective = StatusEnum.有效.GetHashCode(), ActivityId = input.activityId, }; allPxmxEntities.Add(lqKdPxmxEntity); // 收集该品项关联的健康师业绩 if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any()) { //把jksxm保存到HealthInstructorNames foreach (var ijks_tem in item.lqKdJksyjList) { allJksyjEntities.Add(new LqKdJksyjEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = id, Jks = ijks_tem.jks, Jksxm = ijks_tem.jksxm, Jkszh = ijks_tem.jkszh, Jksyj = ijks_tem.jksyj, Yjsj = input.kdrq, Jsj_id = ijks_tem.jsj_id, Kdpxid = lqKdPxmxEntity.Id, IsEffective = StatusEnum.有效.GetHashCode(), ActivityId = input.activityId, }); } } // 收集该品项关联的科技部老师业绩 if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any()) { foreach (var ikjbs_tem in item.lqKdKjbsyjList) { allKjbsyjEntities.Add(new LqKdKjbsyjEntity { Id = YitIdHelper.NextId().ToString(), Glkdbh = id, Kjbls = ikjbs_tem.kjbls, Kjblsxm = ikjbs_tem.kjblsxm, Kjblszh = ikjbs_tem.kjblszh, Kjblsyj = ikjbs_tem.kjblsyj, Yjsj = input.kdrq, Kdpxid = lqKdPxmxEntity.Id, IsEffective = StatusEnum.有效.GetHashCode(), ActivityId = input.activityId, } ); } } } //通过会员id查询会员信息 var memberInfo = await _db.Queryable().Where(u => u.Id == entity.Kdhy).FirstAsync(); //通过开单记录表查询这个会员开单金额 var kdAmount = await _db.Queryable().Where(u => u.Kdhy == entity.Kdhy).SumAsync(u => u.Sfyj); //如果开单金额小于500,为散客,如果大于500,为会员 if (kdAmount < 500) { memberInfo.Khlx = MemberTypeEnum.散客.GetHashCode().ToString(); } else { memberInfo.Khlx = MemberTypeEnum.会员.GetHashCode().ToString(); } await _db.Updateable(memberInfo).ExecuteCommandAsync(); // 批量插入扣款信息 if (allDeductEntities.Any()) { await _db.Insertable(allDeductEntities).ExecuteCommandAsync(); } // 批量插入品项明细 if (allPxmxEntities.Any()) { await _db.Insertable(allPxmxEntities).ExecuteCommandAsync(); } // 批量插入健康师业绩 if (allJksyjEntities.Any()) { await _db.Insertable(allJksyjEntities).ExecuteCommandAsync(); } // 批量插入科技部老师业绩 if (allKjbsyjEntities.Any()) { await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync(); } //关闭事务 _db.CommitTran(); } catch (Exception) { //回滚事务 _db.RollbackTran(); throw NCCException.Oh(ErrorCode.COM1001); } } #endregion #region 作废开单记录 /// /// 作废开单记录 /// /// 作废开单记录输入 /// 无返回值 /// /// 作废指定的开单记录,包括相关的品项明细、业绩记录等 /// /// 示例请求: /// ```json /// { /// "id": "123456789", /// "remarks": "客户要求作废此订单" /// } /// ``` /// /// 参数说明: /// - id: 开单记录主键ID(必填) /// - remarks: 作废备注说明 /// /// 作废成功 /// 参数错误,开单记录ID不能为空 /// 开单记录不存在 /// 服务器内部错误 [HttpPut("Cancel")] public async Task Cancel(CancelBillingInput input) { if (string.IsNullOrEmpty(input.Id)) { throw NCCException.Oh("开单记录ID不能为空"); } try { //开启事务 _db.BeginTran(); // 检查开单记录是否可以作废 var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(input.Id); if (!canCancel) { throw NCCException.Oh(errorMessage); } // 查询开单记录 var entity = await _db.Queryable().FirstAsync(p => p.Id == input.Id); if (entity == null) { throw NCCException.Oh("开单记录不存在"); } // 标记开单记录为无效,并添加作废备注 entity.IsEffective = StatusEnum.无效.GetHashCode(); entity.CancelRefRemarks = input.Remarks; entity.UpdateTime = DateTime.Now; await _db.Updateable(entity).ExecuteCommandAsync(); // 标记对应开单明细表为无效 await _db.Updateable().SetColumns(it => new LqKdPxmxEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync(); // 标记健康师业绩为无效 await _db.Updateable().SetColumns(it => new LqKdJksyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync(); // 标记科技部老师业绩为无效 await _db.Updateable().SetColumns(it => new LqKdKjbsyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync(); // 标记开单_储扣详细表为无效 await _db.Updateable().SetColumns(it => new LqKdDeductinfoEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.BillingId == input.Id).ExecuteCommandAsync(); //关闭事务 _db.CommitTran(); } catch (Exception ex) { //回滚事务 _db.RollbackTran(); throw NCCException.Oh($"作废开单记录失败: {ex.Message}"); } } #endregion #region 历史会员权益数据导入 /// /// 导入历史会员权益数据到开单记录表和开单品项明细表 /// /// /// 将历史会员权益表的数据导入到开单记录表和开单品项明细表中 /// /// 导入规则: /// 1. 逐条处理历史会员权益数据,每条记录生成一个开单记录和一个品项明细 /// 2. 开单时间和创建时间统一设置为 2025-06-01 00:00:00 /// 3. 使用YitIdHelper生成所有ID /// 4. 直接使用F_StoreId作为门店ID /// /// 字段映射: /// - F_MemberId/会员编号 -> kdhy (开单会员,优先使用会员ID) /// - 会员名称 -> kdhyc (开单会员名称) /// - 会员电话 -> kdhysjh (开单会员手机号) /// - F_StoreId -> djmd (单据门店ID) /// - 整单业绩 -> zdyj (整单业绩) /// - 实付业绩 -> sfyj (实付业绩) /// - 欠款 -> qk (欠款) /// - 来源 -> khly (客户来源) /// - 品项名称 -> pxmc (品项名称) /// - 品项价格 -> pxjg (品项价格) /// - 品项次数 -> F_ProjectNumber (项目次数) /// - F_MemberId/会员编号 -> F_MemberId (品项明细会员ID,优先使用会员ID) /// /// 导入结果统计 /// 导入成功 /// 服务器内部错误 [HttpPost("import-history-member-rights")] public async Task ImportHistoryMemberRights() { try { _db.BeginTran(); // 设置固定的时间 var fixedDateTime = new DateTime(2025, 6, 1, 0, 0, 0); // 1. 查询历史会员权益数据 var historyData = await _db.Ado.SqlQueryAsync(@" SELECT 会员编号, 会员名称, 会员电话, 开单门店, 开单时间, 整单业绩, 实付业绩, 欠款, 来源, 品项名称, 品项价格, 品项次数, F_StoreId, F_ProjectId, F_MemberId FROM `历史会员权益`"); int successCount = 0; int errorCount = 0; var errorMessages = new List(); // 2. 批量处理数据,每批1000条 const int batchSize = 1000; var totalBatches = (int)Math.Ceiling((double)historyData.Count / batchSize); for (int batchIndex = 0; batchIndex < totalBatches; batchIndex++) { var batchData = historyData.Skip(batchIndex * batchSize).Take(batchSize).ToList(); var kdjlbEntities = new List(); var pxmxEntities = new List(); foreach (var item in batchData) { try { // 3. 生成开单记录ID string kdjlbId = YitIdHelper.NextId().ToString(); // 4. 创建开单记录 var kdjlbEntity = new LqKdKdjlbEntity { Id = kdjlbId, Djmd = (string)item.F_StoreId, // 直接使用F_StoreId Kdhy = (string)item.F_MemberId, Kdhyc = (string)item.会员名称, Jsj = "暂无", Kdhysjh = (string)item.会员电话, Kdrq = fixedDateTime, Zdyj = Convert.ToDecimal(item.整单业绩 ?? 0), Sfyj = Convert.ToDecimal(item.实付业绩 ?? 0), Qk = Convert.ToDecimal(item.欠款 ?? 0), CreateTime = fixedDateTime, UpdateTime = fixedDateTime, Sfskdd = "否", IsEffective = 1, Bz = "导入历史会员权益数据" }; kdjlbEntities.Add(kdjlbEntity); // 5. 创建品项明细 string pxmxId = YitIdHelper.NextId().ToString(); var pxmxEntity = new LqKdPxmxEntity { Id = pxmxId, Glkdbh = kdjlbId, Px = (string)item.F_ProjectId, Pxmc = (string)item.品项名称, Pxjg = Convert.ToDecimal(item.品项价格 ?? 0) / Convert.ToDecimal(item.品项次数 ?? 0), ProjectNumber = Convert.ToDecimal(item.品项次数 ?? 0), SourceType = (string)item.来源, MemberId = (string)item.F_MemberId, Yjsj = fixedDateTime, CreateTIme = fixedDateTime, IsEffective = 1, IsEnabled = 0, TotalPrice = Convert.ToDecimal(item.品项价格 ?? 0), ActualPrice = Convert.ToDecimal(item.品项价格 ?? 0) }; pxmxEntities.Add(pxmxEntity); successCount++; } catch (Exception ex) { errorCount++; errorMessages.Add($"处理记录 {(string)item.会员编号}-{(string)item.品项名称} 时出错: {ex.Message}"); } } // 6. 批量插入开单记录 if (kdjlbEntities.Any()) { await _db.Insertable(kdjlbEntities).ExecuteCommandAsync(); } // 7. 批量插入品项明细 if (pxmxEntities.Any()) { await _db.Insertable(pxmxEntities).ExecuteCommandAsync(); } // 8. 显示进度 if (batchIndex % 10 == 0) { Console.WriteLine($"已处理 {batchIndex + 1}/{totalBatches} 批次,成功 {successCount} 条,失败 {errorCount} 条"); } } _db.CommitTran(); return new { success = true, message = "历史会员权益数据导入完成", totalRecords = historyData.Count, successCount = successCount, errorCount = errorCount, errorMessages = errorMessages.Take(10).ToList(), // 只返回前10个错误信息 importTime = DateTime.Now }; } catch (Exception ex) { _db.RollbackTran(); throw NCCException.Oh($"导入历史会员权益数据失败: {ex.Message}"); } } #endregion #region 根据门店获取该门店的欠款记录 /// /// 根据门店获取该门店的欠款记录 /// /// 查询参数 /// 分页的欠款记录 /// /// 获取指定门店的欠款记录,考虑已缴欠款情况,支持分页 /// /// 示例请求: /// ```json /// { /// "storeId": "门店ID", /// "currentPage": 1, /// "pageSize": 10, /// "sidx": "Kdrq", /// "sort": "desc" /// } /// ``` /// /// 参数说明: /// - storeId: 门店ID,必填 /// - currentPage: 当前页码 /// - pageSize: 每页大小 /// - sidx: 排序字段,默认按开单日期 /// - sort: 排序方式,默认降序 /// /// 返回说明: /// - 只返回仍有欠款的记录(总欠款 > 已缴欠款) /// - 包含开单编号、客户信息、欠款金额、已缴金额等 /// /// 成功返回分页的欠款记录列表 /// 门店ID不能为空 /// 服务器错误 [HttpGet("GetDebtRecordByStoreId")] public async Task GetDebtRecordByStoreId([FromQuery] DebtRecordQueryInput input) { try { if (string.IsNullOrEmpty(input.StoreId)) { throw NCCException.Oh("门店ID不能为空"); } var sidx = string.IsNullOrEmpty(input.sidx) ? "Kdrq" : input.sidx; var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; // 获取该门店的欠款记录,考虑已缴欠款情况 // 只返回仍有欠款的记录:总欠款 > 已缴欠款 var debtRecords = await _db.Queryable() .Where(p => p.Djmd == input.StoreId && p.Qk > p.PaidDebt && p.IsEffective == StatusEnum.有效.GetHashCode()) .Select(it => new LqKdKdjlbListOutput { id = it.Id, djmd = it.Djmd, jsj = it.Jsj, kdrq = it.Kdrq, gjlx = it.Gjlx, hgjg = it.Hgjg, zdyj = it.Zdyj, sfyj = it.Sfyj, qk = it.Qk, ckfs = it.Ckfs, PaidDebt = it.PaidDebt, fkfs = it.Fkfs, fkyy = it.Fkyy, fkpd = it.Fkpd, khly = it.Khly, tjr = it.Tjr, deductAmount = it.DeductAmount, sfskdd = it.Sfskdd, jj = it.Jj, bz = it.Bz, kdhy = it.Kdhy, kdhyc = SqlFunc.Subqueryable().Where(x => x.Id == it.Kdhy).Select(x => x.Khmc), kdhysjh = SqlFunc.Subqueryable().Where(x => x.Id == it.Kdhy).Select(x => x.Sjh), IsEffective = it.IsEffective, CreateUser = it.CreateUser, CreateUserName = SqlFunc.Subqueryable().Where(x => x.Id == it.CreateUser).Select(x => x.RealName), }) .MergeTable() .OrderBy(sidx + " " + sort) .ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(debtRecords); } catch (Exception ex) { throw NCCException.Oh($"获取门店欠款记录失败: {ex.Message}"); } } #endregion #region 获取某个会员的开单记录 /// /// 获取某个会员的开单记录 /// /// /// 根据会员ID查询该会员的所有开单记录,支持分页和时间筛选 /// /// 示例请求: /// ```json /// { /// "memberId": "会员ID", /// "storeId": "门店ID", /// "startTime": "2025-01-01", /// "endTime": "2025-12-31", /// "currentPage": 1, /// "pageSize": 20 /// } /// ``` /// /// 参数说明: /// - memberId: 会员ID(必填) /// - storeId: 门店ID(可选) /// - startTime: 开始时间(可选) /// - endTime: 结束时间(可选) /// - currentPage: 当前页码 /// - pageSize: 每页大小 /// /// 查询参数 /// 分页的开单记录列表 /// 成功返回会员开单记录列表 /// 参数错误 /// 服务器错误 [HttpGet("GetBillingRecordByMemberId")] public async Task GetBillingRecordByMemberId([FromQuery] BillingRecordQueryInput input) { try { var sidx = input.sidx == null ? "kdrq" : input.sidx; var data = await _db.Queryable() .Where(p => p.Kdhy == input.MemberId) .WhereIF(input.StartTime.HasValue, p => p.Kdrq >= input.StartTime.Value) .WhereIF(input.EndTime.HasValue, p => p.Kdrq <= input.EndTime.Value) .Select(it => new LqKdKdjlbListOutput { id = it.Id, djmd = it.Djmd, jsj = it.Jsj, kdrq = it.Kdrq, gjlx = it.Gjlx, hgjg = it.Hgjg, zdyj = it.Zdyj, sfyj = it.Sfyj, qk = it.Qk, ckfs = it.Ckfs, fkfs = it.Fkfs, fkyy = it.Fkyy, fkpd = it.Fkpd, khly = it.Khly, tjr = it.Tjr, deductAmount = it.DeductAmount, sfskdd = it.Sfskdd, jj = it.Jj, bz = it.Bz, kdhy = it.Kdhy, kdhyc = SqlFunc.Subqueryable().Where(x => x.Id == it.Kdhy).Select(x => x.Khmc), kdhysjh = SqlFunc.Subqueryable().Where(x => x.Id == it.Kdhy).Select(x => x.Sjh), IsEffective = it.IsEffective, CreateUser = it.CreateUser, CreateUserName = SqlFunc.Subqueryable().Where(x => x.Id == it.CreateUser).Select(x => x.RealName), }) .MergeTable() .OrderBy(sidx + " " + input.sort) .ToPagedListAsync(input.currentPage, input.pageSize); // 获取当前页的开单记录ID列表 var billingIds = data.list.Select(x => x.id).ToList(); // 批量查询品项明细 var itemDetails = new List(); if (billingIds.Any()) { itemDetails = await _db.Queryable() .Where(x => billingIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()) .Select(x => new LqKdPxmxInfoOutput { id = x.Id, glkdbh = x.Glkdbh, px = x.Px, pxmc = x.Pxmc, pxjg = x.Pxjg, projectNumber = x.ProjectNumber, isEnabled = x.IsEnabled, sourceType = x.SourceType, memberId = x.MemberId, createTime = x.CreateTIme, totalPrice = x.TotalPrice, actualPrice = x.ActualPrice, remark = x.Remark, isEffective = x.IsEffective }) .ToListAsync(); } // 按开单ID分组品项明细 var itemDetailsGrouped = itemDetails.GroupBy(x => x.glkdbh) .ToDictionary(g => g.Key, g => g.ToList()); // 为每个开单记录分配品项明细 foreach (var item in data.list) { item.ItemDetails = itemDetailsGrouped.ContainsKey(item.id) ? itemDetailsGrouped[item.id] : new List(); } return PageResult.SqlSugarPageResult(data); } catch (Exception ex) { throw NCCException.Oh($"获取会员开单记录失败: {ex.Message}"); } } #endregion #region 私有方法 /// /// 检查开单记录是否可以操作 /// /// 开单记录ID /// 是否可以作废 private async Task<(bool canCancel, string errorMessage)> CheckBillingCanCancelAsync(string billingId) { try { // 查询开单记录 var entity = await _db.Queryable().FirstAsync(p => p.Id == billingId); if (entity == null) { return (false, "开单记录不存在"); } // 检查是否已经作废 if (entity.IsEffective == StatusEnum.无效.GetHashCode()) { return (false, "该开单记录已经作废"); } // 判断是否有对应的补缴记录 var qkbjList = await _db.Queryable().Where(p => p.SupplementBillingId == billingId).ToListAsync(); if (qkbjList.Any()) { return (false, "该开单记录有对应的补缴记录,不能进行操作"); } // 查询开单记录下的品项明细ID列表 var pxmxIdList = await _db.Queryable().Where(p => p.Glkdbh == billingId).Select(p => p.Id).Distinct().ToListAsync(); // 判断是否有对应的消耗记录 var xhPxmxList = await _db.Queryable().Where(p => pxmxIdList.Contains(p.BillingItemId)).ToListAsync(); if (xhPxmxList.Any()) { return (false, "该开单记录有对应的消耗记录,不能进行操作"); } // 判断是否有退卡记录 var hytkMxList = await _db.Queryable().Where(p => pxmxIdList.Contains(p.BillingItemId)).ToListAsync(); if (hytkMxList.Any()) { return (false, "该开单记录有对应的退卡记录,不能进行操作"); } // 判断是否已经有储扣记录 var deductInfoList = await _db.Queryable().Where(p => pxmxIdList.Contains(p.DeductId)).ToListAsync(); if (deductInfoList.Any()) { return (false, "该开单记录有对应的储扣记录,不能进行操作"); } return (true, string.Empty); } catch (Exception ex) { return (false, $"检查开单记录状态失败: {ex.Message}"); } } #endregion } }