using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
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_hytk_jksyj;
using NCC.Extend.Entitys.lq_hytk_kjbsyj;
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;
using NCC.Extend.Entitys.lq_package_info;
using NCC.Extend.Entitys.lq_mdxx;
using NCC.Extend.Entitys.lq_card_transfer_log;
using NCC.Extend.Entitys.Dto.LqDailyReport;
using NCC.Extend.Entitys.lq_yyjl;
using NCC.Extend.Entitys.lq_hzf;
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;
private readonly ILogger _logger;
private readonly LqDailyReportService _dailyReportService;
///
/// 初始化一个类型的新实例
///
public LqKdKdjlbService(
ISqlSugarRepository lqKdKdjlbRepository,
ISqlSugarRepository lqKdJksyjRepository,
ISqlSugarRepository lqKdKjbsyjRepository,
ISqlSugarRepository lqKdPxmxRepository,
IUserManager userManager,
WeChatBotService weChatBotService,
LqKdKdjlbStringGenerator stringGenerator,
ILogger logger,
LqDailyReportService dailyReportService
)
{
_lqKdKdjlbRepository = lqKdKdjlbRepository;
_db = _lqKdKdjlbRepository.Context;
_lqKdJksyjRepository = lqKdJksyjRepository;
_lqKdKjbsyjRepository = lqKdKjbsyjRepository;
_lqKdPxmxRepository = lqKdPxmxRepository;
_userManager = userManager;
_weChatBotService = weChatBotService;
_stringGenerator = stringGenerator;
_logger = logger;
_dailyReportService = dailyReportService;
}
#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();
output.upgradeLifeBeauty = entity.UpgradeLifeBeauty;
output.upgradeTechBeauty = entity.UpgradeTechBeauty;
output.upgradeMedicalBeauty = entity.UpgradeMedicalBeauty;
output.fileUrl = entity.F_FIleUrl;
if (!string.IsNullOrEmpty(entity.AppointmentId))
{
output.appointmentTime = await _db.Queryable().Where(x => x.Id == entity.AppointmentId).Select(x => x.Yysj).FirstAsync();
}
if (output.activityId != null)
{
output.activityName = await _db.Queryable().Where(x => x.Id == entity.ActivityId).Select(x => x.ActivityName).FirstAsync();
}
//获取会员信息
var MemberInfo = await _db.Queryable().FirstAsync(p => p.Id == entity.Kdhy);
output.kdhyc = MemberInfo.Khmc;
output.kdhysjh = MemberInfo.Sjh;
if (!string.IsNullOrEmpty(entity.ActivityId))
{
output.activityName = await _db.Queryable().Where(x => x.Id == entity.ActivityId).Select(x => x.ActivityName).FirstAsync();
}
// 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;
// 根据是否传入健康师ID或科技部老师ID,动态构建查询
ISugarQueryable baseQuery = null;
if (!string.IsNullOrEmpty(input.jksId) && !string.IsNullOrEmpty(input.kjblsId))
{
// 同时传入健康师ID和科技部老师ID,需要同时关联两个业绩表
baseQuery = _db.Queryable(
(jksyj, kjbsyj, kdjlb) => jksyj.Glkdbh == kdjlb.Id && kjbsyj.Glkdbh == kdjlb.Id)
.Where((jksyj, kjbsyj, kdjlb) => jksyj.Jkszh == input.jksId && jksyj.IsEffective == StatusEnum.有效.GetHashCode())
.Where((jksyj, kjbsyj, kdjlb) => kjbsyj.Kjbls == input.kjblsId && kjbsyj.IsEffective == StatusEnum.有效.GetHashCode())
.Select((jksyj, kjbsyj, kdjlb) => kdjlb)
.Distinct()
.MergeTable();
}
else if (!string.IsNullOrEmpty(input.jksId))
{
// 只传入健康师ID,关联健康师业绩表
baseQuery = _db.Queryable(
(jksyj, kdjlb) => jksyj.Glkdbh == kdjlb.Id)
.Where((jksyj, kdjlb) => jksyj.Jkszh == input.jksId && jksyj.IsEffective == StatusEnum.有效.GetHashCode())
.Select((jksyj, kdjlb) => kdjlb)
.Distinct()
.MergeTable();
}
else if (!string.IsNullOrEmpty(input.kjblsId))
{
// 只传入科技部老师ID,关联科技部老师业绩表
baseQuery = _db.Queryable(
(kjbsyj, kdjlb) => kjbsyj.Glkdbh == kdjlb.Id)
.Where((kjbsyj, kdjlb) => kjbsyj.Kjbls == input.kjblsId && kjbsyj.IsEffective == StatusEnum.有效.GetHashCode())
.Select((kjbsyj, kdjlb) => kdjlb)
.Distinct()
.MergeTable();
}
else
{
// 没有传入健康师ID或科技部老师ID,使用原来的查询逻辑
baseQuery = _db.Queryable();
}
var data = await baseQuery
.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(input.zdyj != null, p => p.Zdyj > input.zdyj)
.WhereIF(input.sfyj != null, p => p.Sfyj > input.sfyj)
.WhereIF(input.qk != null, p => p.Qk > 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))
.WhereIF(input.isEffective != 0, p => p.IsEffective == input.isEffective)
.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,
paidDebt = it.PaidDebt,
supplementBillingId = it.SupplementBillingId,
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),
activityId = it.ActivityId,
activityName = SqlFunc.Subqueryable().Where(x => x.Id == it.ActivityId).Select(x => x.ActivityName),
appointmentId = it.AppointmentId,
appointmentTime = it.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable().Where(x => x.Id == it.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)),
upgradeMedicalBeauty = it.UpgradeMedicalBeauty,
upgradeTechBeauty = it.UpgradeTechBeauty,
upgradeLifeBeauty = it.UpgradeLifeBeauty,
})
.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());
// 批量查询健康师业绩(性能优化:一次性查询所有开单的健康师业绩)
var jksyjList = new List();
if (billingIds.Any())
{
jksyjList = await _db.Queryable().Where(x => billingIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()).Select(x => new LqKdJksyjInfoOutput
{
id = x.Id,
glkdbh = x.Glkdbh,
kdpxid = x.Kdpxid,
jks = x.Jks,
jksxm = x.Jksxm,
jkszh = x.Jkszh,
jksyj = x.Jksyj,
yjsj = x.Yjsj,
jsj_id = x.Jsj_id,
isEffective = x.IsEffective
}).ToListAsync();
}
// 批量查询科技部老师业绩(性能优化:一次性查询所有开单的科技部老师业绩)
var kjbsyjList = new List();
if (billingIds.Any())
{
kjbsyjList = await _db.Queryable().Where(x => billingIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()).Select(x => new LqKdKjbsyjInfoOutput
{
id = x.Id,
glkdbh = x.Glkdbh,
kdpxid = x.Kdpxid,
kjbls = x.Kjbls,
kjblsxm = x.Kjblsxm,
kjblszh = x.Kjblszh,
kjblsyj = x.Kjblsyj,
yjsj = x.Yjsj,
isEffective = x.IsEffective
})
.ToListAsync();
}
// 批量查询储扣扣款信息(性能优化:一次性查询所有开单的储扣扣款信息)
var deductinfoList = new List();
if (billingIds.Any())
{
deductinfoList = await _db.Queryable().Where(x => billingIds.Contains(x.BillingId) && x.IsEffective == StatusEnum.有效.GetHashCode())
.Select(x => new LqKdDeductinfoInfoOutput
{
id = x.Id,
deductType = x.DeductType,
deductId = x.DeductId,
billingId = x.BillingId,
amount = x.Amount,
isEffective = x.IsEffective,
unitPrice = x.UnitPrice,
itemName = x.ItemName,
itemId = x.ItemId,
createTime = x.CreateTime,
projectNumber = x.ProjectNumber,
})
.ToListAsync();
}
// 按开单ID分组储扣扣款信息
var deductinfoGrouped = deductinfoList.GroupBy(x => x.billingId).ToDictionary(g => g.Key, g => g.ToList());
// 按开单ID分组健康师业绩
var jksyjGrouped = jksyjList.GroupBy(x => x.glkdbh).ToDictionary(g => g.Key, g => g.ToList());
// 按开单ID分组科技部老师业绩
var kjbsyjGrouped = kjbsyjList.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();
item.lqKdJksyjList = jksyjGrouped.ContainsKey(item.id) ? jksyjGrouped[item.id] : new List();
item.lqKdKjbsyjList = kjbsyjGrouped.ContainsKey(item.id) ? kjbsyjGrouped[item.id] : new List();
item.lqKdDeductList = deductinfoGrouped.ContainsKey(item.id) ? deductinfoGrouped[item.id] : new List();
}
return PageResult.SqlSugarPageResult(data);
}
#endregion
#region 根据健康师ID获取开单列表
///
/// 根据健康师ID获取开单列表
///
///
/// 根据健康师ID查询该健康师参与的所有开单记录,支持时间周期查询和分页
///
/// 示例请求:
/// GET /api/Extend/LqKdKdjlb/GetListByJksId?jksId=健康师ID&startTime=2025-01-01&endTime=2025-01-31¤tPage=1&pageSize=10
///
/// 参数说明:
/// - jksId: 健康师ID(必填)
/// - startTime: 开始时间(可选,格式:yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss)
/// - endTime: 结束时间(可选,格式:yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss)
/// - djmd: 门店ID(可选)
/// - isEffective: 是否有效(可选,0=全部,1=有效,2=无效,默认1)
/// - currentPage: 当前页码(必填)
/// - pageSize: 每页数量(必填)
///
/// 返回说明:
/// - 返回格式与GetList接口相同,包含开单基本信息及品项明细列表
///
/// 查询参数
/// 开单列表(分页)
/// 查询成功
/// 参数错误
/// 服务器内部错误
[HttpGet("GetListByJksId")]
public async Task GetListByJksId([FromQuery] LqKdKdjlbListByJksQueryInput input)
{
try
{
if (string.IsNullOrEmpty(input.jksId))
{
throw NCCException.Oh("健康师ID不能为空");
}
var sidx = input.sidx == null ? "kdrq" : input.sidx;
var sort = string.IsNullOrEmpty(input.sort) ? "DESC" : input.sort;
// 解析时间范围
DateTime? startDate = null;
DateTime? endDate = null;
if (!string.IsNullOrEmpty(input.startTime))
{
startDate = Ext.GetDateTime(input.startTime);
}
if (!string.IsNullOrEmpty(input.endTime))
{
endDate = Ext.GetDateTime(input.endTime);
// 如果只传了日期,则设置为当天的23:59:59
if (endDate.HasValue && !input.endTime.Contains(":"))
{
endDate = new DateTime(endDate.Value.Year, endDate.Value.Month, endDate.Value.Day, 23, 59, 59);
}
}
// 通过健康师业绩表关联查询开单记录
var data = await _db.Queryable((jksyj, kdjlb) => jksyj.Glkdbh == kdjlb.Id)
.Where((jksyj, kdjlb) => jksyj.Jkszh == input.jksId)
.WhereIF(input.isEffective != 0, (jksyj, kdjlb) => jksyj.IsEffective == input.isEffective && kdjlb.IsEffective == input.isEffective)
.WhereIF(input.isEffective == 0, (jksyj, kdjlb) => jksyj.IsEffective == StatusEnum.有效.GetHashCode() && kdjlb.IsEffective == StatusEnum.有效.GetHashCode())
.WhereIF(startDate.HasValue, (jksyj, kdjlb) => kdjlb.Kdrq >= startDate.Value)
.WhereIF(endDate.HasValue, (jksyj, kdjlb) => kdjlb.Kdrq <= endDate.Value)
.WhereIF(!string.IsNullOrEmpty(input.djmd), (jksyj, kdjlb) => kdjlb.Djmd == input.djmd)
.Select((jksyj, kdjlb) => new LqKdKdjlbListOutput
{
id = kdjlb.Id,
djmd = kdjlb.Djmd,
jsj = kdjlb.Jsj,
kdrq = kdjlb.Kdrq,
gjlx = kdjlb.Gjlx,
hgjg = kdjlb.Hgjg,
zdyj = kdjlb.Zdyj,
sfyj = kdjlb.Sfyj,
qk = kdjlb.Qk,
ckfs = kdjlb.Ckfs,
fkfs = kdjlb.Fkfs,
fkyy = kdjlb.Fkyy,
fkpd = kdjlb.Fkpd,
khly = kdjlb.Khly,
tjr = kdjlb.Tjr,
deductAmount = kdjlb.DeductAmount,
paidDebt = kdjlb.PaidDebt,
supplementBillingId = kdjlb.SupplementBillingId,
sfskdd = kdjlb.Sfskdd,
jj = kdjlb.Jj,
bz = kdjlb.Bz,
kdhy = kdjlb.Kdhy,
kdhyc = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.Kdhy).Select(x => x.Khmc),
kdhysjh = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.Kdhy).Select(x => x.Sjh),
isEffective = kdjlb.IsEffective,
createUser = kdjlb.CreateUser,
createUserName = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.CreateUser).Select(x => x.RealName),
activityId = kdjlb.ActivityId,
activityName = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.ActivityId).Select(x => x.ActivityName),
appointmentId = kdjlb.AppointmentId,
appointmentTime = kdjlb.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)),
})
.MergeTable()
.Distinct() // 去重,因为一个开单可能对应多个健康师业绩记录
.OrderBy($"{sidx} {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)
{
_logger.LogError(ex, $"根据健康师ID获取开单列表失败 - 健康师ID: {input?.jksId}, 开始时间: {input?.startTime}, 结束时间: {input?.endTime}");
throw NCCException.Oh($"根据健康师ID获取开单列表失败: {ex.Message}");
}
}
#endregion
#region 根据科技部老师ID获取开单列表
///
/// 根据科技部老师ID获取开单列表
///
///
/// 根据科技部老师ID查询该老师参与的所有开单记录,支持时间周期查询和分页
///
/// 示例请求:
/// GET /api/Extend/LqKdKdjlb/GetListByKjbId?kjblsId=科技部老师ID&startTime=2025-01-01&endTime=2025-01-31¤tPage=1&pageSize=10
///
/// 参数说明:
/// - kjblsId: 科技部老师ID(必填)
/// - startTime: 开始时间(可选,格式:yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss)
/// - endTime: 结束时间(可选,格式:yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss)
/// - djmd: 门店ID(可选)
/// - isEffective: 是否有效(可选,0=全部,1=有效,2=无效,默认1)
/// - currentPage: 当前页码(必填)
/// - pageSize: 每页数量(必填)
///
/// 返回说明:
/// - 返回格式与GetList接口相同,包含开单基本信息及品项明细列表
///
/// 查询参数
/// 开单列表(分页)
/// 查询成功
/// 参数错误
/// 服务器内部错误
[HttpGet("GetListByKjbId")]
public async Task GetListByKjbId([FromQuery] LqKdKdjlbListByKjbQueryInput input)
{
try
{
if (string.IsNullOrEmpty(input.kjblsId))
{
throw NCCException.Oh("科技部老师ID不能为空");
}
var sidx = input.sidx == null ? "kdrq" : input.sidx;
var sort = string.IsNullOrEmpty(input.sort) ? "DESC" : input.sort;
// 解析时间范围
DateTime? startDate = null;
DateTime? endDate = null;
if (!string.IsNullOrEmpty(input.startTime))
{
startDate = Ext.GetDateTime(input.startTime);
}
if (!string.IsNullOrEmpty(input.endTime))
{
endDate = Ext.GetDateTime(input.endTime);
// 如果只传了日期,则设置为当天的23:59:59
if (endDate.HasValue && !input.endTime.Contains(":"))
{
endDate = new DateTime(endDate.Value.Year, endDate.Value.Month, endDate.Value.Day, 23, 59, 59);
}
}
// 通过科技部老师业绩表关联查询开单记录
var data = await _db.Queryable(
(kjbsyj, kdjlb) => kjbsyj.Glkdbh == kdjlb.Id)
.Where((kjbsyj, kdjlb) => kjbsyj.Kjbls == input.kjblsId)
.WhereIF(input.isEffective != 0, (kjbsyj, kdjlb) => kjbsyj.IsEffective == input.isEffective && kdjlb.IsEffective == input.isEffective)
.WhereIF(input.isEffective == 0, (kjbsyj, kdjlb) => kjbsyj.IsEffective == StatusEnum.有效.GetHashCode() && kdjlb.IsEffective == StatusEnum.有效.GetHashCode())
.WhereIF(startDate.HasValue, (kjbsyj, kdjlb) => kdjlb.Kdrq >= startDate.Value)
.WhereIF(endDate.HasValue, (kjbsyj, kdjlb) => kdjlb.Kdrq <= endDate.Value)
.WhereIF(!string.IsNullOrEmpty(input.djmd), (kjbsyj, kdjlb) => kdjlb.Djmd == input.djmd)
.Select((kjbsyj, kdjlb) => new LqKdKdjlbListOutput
{
id = kdjlb.Id,
djmd = kdjlb.Djmd,
jsj = kdjlb.Jsj,
kdrq = kdjlb.Kdrq,
gjlx = kdjlb.Gjlx,
hgjg = kdjlb.Hgjg,
zdyj = kdjlb.Zdyj,
sfyj = kdjlb.Sfyj,
qk = kdjlb.Qk,
ckfs = kdjlb.Ckfs,
fkfs = kdjlb.Fkfs,
fkyy = kdjlb.Fkyy,
fkpd = kdjlb.Fkpd,
khly = kdjlb.Khly,
tjr = kdjlb.Tjr,
deductAmount = kdjlb.DeductAmount,
paidDebt = kdjlb.PaidDebt,
supplementBillingId = kdjlb.SupplementBillingId,
sfskdd = kdjlb.Sfskdd,
jj = kdjlb.Jj,
bz = kdjlb.Bz,
kdhy = kdjlb.Kdhy,
kdhyc = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.Kdhy).Select(x => x.Khmc),
kdhysjh = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.Kdhy).Select(x => x.Sjh),
isEffective = kdjlb.IsEffective,
createUser = kdjlb.CreateUser,
createUserName = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.CreateUser).Select(x => x.RealName),
activityId = kdjlb.ActivityId,
activityName = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.ActivityId).Select(x => x.ActivityName),
appointmentId = kdjlb.AppointmentId,
appointmentTime = kdjlb.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)),
})
.MergeTable()
.Distinct() // 去重,因为一个开单可能对应多个科技部老师业绩记录
.OrderBy($"{sidx} {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)
{
_logger.LogError(ex, $"根据科技部老师ID获取开单列表失败 - 科技部老师ID: {input?.kjblsId}, 开始时间: {input?.startTime}, 结束时间: {input?.endTime}");
throw NCCException.Oh($"根据科技部老师ID获取开单列表失败: {ex.Message}");
}
}
#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.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;
supplementBillingEntity.PaidDebt += input.supplementAmount > supplementAmount ? supplementAmount : input.supplementAmount;
await _db.Updateable(supplementBillingEntity).ExecuteCommandAsync();
}
//新增开单记录表记录
entity.CreateUser = userInfo.userId;
//计算储扣总金额
entity.DeductAmount = input.lqKdKdjlbDeductList.Sum(x => x.Amount ?? 0);
//判断升单类型:需要同时满足两个条件
//1. 当前开单包含对应类型的品项
//2. 该会员之前有开单记录(升单的前提)
//批量查询当前开单所有品项的分类(性能优化)
var itemIds = input.lqKdPxmxList?.Where(x => !string.IsNullOrEmpty(x.px)).Select(x => x.px).Distinct().ToList() ?? new List();
var itemCategoryDict = new Dictionary();
if (itemIds.Any())
{
var itemCategories = await _db.Queryable()
.Where(x => itemIds.Contains(x.Id) && x.IsEffective == StatusEnum.有效.GetHashCode())
.Select(x => new { x.Id, x.Qt2 })
.ToListAsync();
itemCategoryDict = itemCategories.ToDictionary(k => k.Id, v => v.Qt2 ?? "");
}
//判断当前开单是否包含医美品项
var hasMedicalItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
!string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美");
var MedicalItemInCurrentBillingAmount = input.lqKdPxmxList.Where(x =>
!string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美").Sum(x => x.actualPrice);
//判断当前开单是否包含科美品项
var hasTechItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
!string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "科美");
//判断当前开单是否包含生美品项
var hasLifeItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
!string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "生美");
//判断该会员之前是否有医美类型的开单记录(升医美的前提)
var hasPreviousMedicalBilling = await _db.Queryable()
.Where(pxmx => pxmx.MemberId == entity.Kdhy
&& pxmx.IsEffective == StatusEnum.有效.GetHashCode()
&& pxmx.ItemCategory == "医美"
&& pxmx.ActualPrice > 0
&& pxmx.Px != "61")
.AnyAsync();
//判断该会员之前是否有科美类型的开单记录(升科美的前提)
var hasPreviousTechBilling = await _db.Queryable()
.Where(pxmx => pxmx.MemberId == entity.Kdhy
&& pxmx.IsEffective == StatusEnum.有效.GetHashCode()
&& pxmx.ItemCategory == "科美"
&& pxmx.ActualPrice > 0
&& pxmx.Px != "61")
.AnyAsync();
//判断该会员之前是否有生美类型的开单记录(升生美的前提)
var hasPreviousLifeBilling = await _db.Queryable()
.Where(pxmx => pxmx.MemberId == entity.Kdhy
&& pxmx.IsEffective == StatusEnum.有效.GetHashCode()
&& pxmx.ItemCategory == "生美"
&& pxmx.ActualPrice > 0
&& pxmx.Px != "61")
.AnyAsync();
//判断升医美:当前开单包含医美品项 + 之前有医美类型的开单记录 + 实付业绩>=1000
if (hasMedicalItemInCurrentBilling && hasPreviousMedicalBilling && MedicalItemInCurrentBillingAmount >= 1000)
{
entity.UpgradeMedicalBeauty = "是";
}
else
{
entity.UpgradeMedicalBeauty = "否";
}
//判断升科美:当前开单包含科美品项 + 之前有科美类型的开单记录
if (hasTechItemInCurrentBilling && hasPreviousTechBilling)
{
entity.UpgradeTechBeauty = "是";
}
else
{
entity.UpgradeTechBeauty = "否";
}
//判断升生美:当前开单包含生美品项 + 之前有生美类型的开单记录
if (hasLifeItemInCurrentBilling && hasPreviousLifeBilling)
{
entity.UpgradeLifeBeauty = "是";
}
else
{
entity.UpgradeLifeBeauty = "否";
}
//保存开单记录
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, // 设置创建时间
ItemCategory = await _db.Queryable().Where(x => x.Id == item.ItemId).Select(x => x.Qt2).FirstAsync(),
};
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,
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(),
};
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,
ItemCategory = lqKdPxmxEntity.ItemCategory,
ItemId = lqKdPxmxEntity.Px,
ItemName = lqKdPxmxEntity.Pxmc,
StoreId = entity.Djmd,
PerformanceType = lqKdPxmxEntity.PerformanceType,
BeautyType = lqKdPxmxEntity.BeautyType,
});
}
}
// 收集该品项关联的科技部老师业绩
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,
ItemCategory = lqKdPxmxEntity.ItemCategory,
ItemId = lqKdPxmxEntity.Px,
ItemName = lqKdPxmxEntity.Pxmc,
StoreId = entity.Djmd,
PerformanceType = lqKdPxmxEntity.PerformanceType,
BeautyType = lqKdPxmxEntity.BeautyType,
}
);
}
}
}
//通过会员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
{
//判断如果开单金额大于0,则发送开单记录字符串到企业微信
if (newEntity.Sfyj > 0)
{
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}");
}
// 播报事业部开单统计数据
try
{
//判断如果开单金额大于0,则播报事业部开单统计数据
if (newEntity.Sfyj > 0 && newEntity.Kdrq.HasValue)
{
var billingDate = newEntity.Kdrq.Value.ToString("yyyy-MM-dd");
var statisticsInput = new BusinessUnitBillingStatisticsInput
{
Date = billingDate
};
var statisticsText = await _dailyReportService.GetBusinessUnitBillingStatisticsText(statisticsInput);
if (!string.IsNullOrEmpty(statisticsText) && !statisticsText.Contains("暂无开单数据"))
{
// 发送到企业微信群
try
{
var sendResult = await _weChatBotService.SendTextMessage(statisticsText);
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 修改开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
///
/// 修改开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
///
///
/// 参数说明:
/// - 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();
//查询是否有对应的补缴开单ID
if (!string.IsNullOrEmpty(entity.SupplementBillingId))
{
//查询补缴开单ID
var supplementBillingEntity = await _db.Queryable().FirstAsync(p => p.Id == entity.SupplementBillingId);//900,900
if (supplementBillingEntity == null || supplementBillingEntity.IsEffective == StatusEnum.无效.GetHashCode())
{
throw NCCException.Oh("补缴开单记录不存在或已作废");
}
//查询当前开单已经补缴金额
var OldSupplementAmount = await _db.Queryable().Where(p => p.Id == id).SumAsync(p => p.SupplementAmount);//900,0
supplementBillingEntity.PaidDebt = supplementBillingEntity.PaidDebt - OldSupplementAmount + input.supplementAmount;
await _db.Updateable(supplementBillingEntity).ExecuteCommandAsync();
}
//批量查询当前开单所有品项的分类(性能优化)
var itemIds = input.lqKdPxmxList?.Where(x => !string.IsNullOrEmpty(x.px)).Select(x => x.px).Distinct().ToList() ?? new List();
var itemCategoryDict = new Dictionary();
if (itemIds.Any())
{
var itemCategories = await _db.Queryable()
.Where(x => itemIds.Contains(x.Id) && x.IsEffective == StatusEnum.有效.GetHashCode())
.Select(x => new { x.Id, x.Qt2 })
.ToListAsync();
itemCategoryDict = itemCategories.ToDictionary(k => k.Id, v => v.Qt2 ?? "");
}
//判断当前开单是否包含医美品项
var hasMedicalItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美");
var MedicalItemInCurrentBillingAmount = input.lqKdPxmxList.Where(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美").Sum(x => x.actualPrice);
//判断当前开单是否包含科美品项
var hasTechItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "科美");
//判断当前开单是否包含生美品项
var hasLifeItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "生美");
//获取该会员之前开单品项里面是否有医美项目
var isMedicalProject = hasMedicalItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "医美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
if (isMedicalProject && MedicalItemInCurrentBillingAmount >= 1000)
{
entity.UpgradeLifeBeauty = "是";
}
else
{
entity.UpgradeLifeBeauty = "否";
}
//获取该会员之前开单品项里面是否有科美项目
var isTechProject = hasTechItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "科美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
if (isTechProject)
{
entity.UpgradeTechBeauty = "是";
}
else
{
entity.UpgradeTechBeauty = "否";
}
//获取该会员之前开单品项里面是否有生美项目
var isLifeProject = hasLifeItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "生美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
if (isLifeProject)
{
entity.UpgradeMedicalBeauty = "是";
}
else
{
entity.UpgradeMedicalBeauty = "否";
}
//计算储扣总金额
entity.DeductAmount = input.lqKdKdjlbDeductList.Sum(x => x.Amount ?? 0);
entity.UpdateTime = DateTime.Now;
// 更新开单记录主表
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, // 设置创建时间
ItemCategory = await _db.Queryable().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync()
};
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,
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(),
};
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,
ItemCategory = lqKdPxmxEntity.ItemCategory,
ItemId = lqKdPxmxEntity.Px,
StoreId = entity.Djmd,
ItemName = lqKdPxmxEntity.Pxmc,
PerformanceType = lqKdPxmxEntity.PerformanceType,
BeautyType = lqKdPxmxEntity.BeautyType,
});
}
}
// 收集该品项关联的科技部老师业绩
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,
ItemCategory = lqKdPxmxEntity.ItemCategory,
ItemId = lqKdPxmxEntity.Px,
StoreId = entity.Djmd,
ItemName = lqKdPxmxEntity.Pxmc,
PerformanceType = lqKdPxmxEntity.PerformanceType,
BeautyType = lqKdPxmxEntity.BeautyType,
});
}
}
}
//通过会员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 ex)
{
//回滚事务
_db.RollbackTran();
throw NCCException.Oh($"创建开单记录失败: {ex.Message}");
}
}
#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(input.zdyj != null, p => p.Zdyj > input.zdyj)
.WhereIF(input.sfyj != null, p => p.Sfyj > input.sfyj)
.WhereIF(input.qk != null, p => p.Qk > 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(input.isEffective != 0, p => p.IsEffective == input.isEffective)
.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,
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();
//清空扣款信息
await _db.Deleteable().In(u => u.BillingId, ids).ExecuteCommandAsync();
//关闭事务
_db.CommitTran();
}
catch (Exception ex)
{
//回滚事务
_db.RollbackTran();
throw NCCException.Oh($"批量删除开单记录表失败: {ex.Message}");
}
}
}
#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();
//清空扣款信息
await _db.Deleteable().Where(u => u.BillingId == id).ExecuteCommandAsync();
//关闭事务
_db.CommitTran();
}
catch (Exception ex)
{
//回滚事务
_db.RollbackTran();
throw NCCException.Oh($"删除开单记录表失败: {ex.Message}");
}
}
#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 获取状态枚举内容(所有的状态通用)
///
/// 获取状态枚举内容
///
/// 状态枚举列表
[HttpGet("status-enum")]
public List GetStatusEnum()
{
return Enum.GetValues()
.Select(e => new EnumOutput
{
Value = (int)e,
Name = e.ToString(),
Description = e.GetDescription(),
})
.ToList();
}
#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();
//如果存在补缴开单ID,则将补缴开单ID的IsEffective设置为无效
if (!string.IsNullOrEmpty(entity.SupplementBillingId))
{
var supplementBillingEntity = await _db.Queryable().FirstAsync(p => p.Id == entity.SupplementBillingId);
if (supplementBillingEntity != null && supplementBillingEntity.IsEffective == StatusEnum.有效.GetHashCode())
{
supplementBillingEntity.PaidDebt = supplementBillingEntity.PaidDebt - entity.SupplementAmount;
await _db.Updateable(supplementBillingEntity).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),
appointmentId = it.AppointmentId,
})
.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获取会员的开单品项列表
///
/// 根据会员id获取会员的开单品项列表
///
/// 查询参数
/// 会员的开单品项列表
[HttpPost("GetBillingItemsByMemberId")]
public async Task GetBillingItemsByMemberId([FromBody] BillingItemsByMemberIdQueryInput input)
{
try
{
var query = _db.Queryable()
.Where(p => p.MemberId == input.MemberId)
.WhereIF(!string.IsNullOrEmpty(input.ItemCategory), p => p.ItemCategory == input.ItemCategory)
.WhereIF(!string.IsNullOrEmpty(input.BillingId), p => p.Glkdbh == input.BillingId)
.WhereIF(input.StartTime.HasValue, p => p.Yjsj >= input.StartTime.Value)
.WhereIF(input.EndTime.HasValue, p => p.Yjsj <= input.EndTime.Value)
.WhereIF(input.MinActualPrice.HasValue, p => p.ActualPrice >= input.MinActualPrice.Value)
.WhereIF(input.MaxActualPrice.HasValue, p => p.ActualPrice <= input.MaxActualPrice.Value)
.OrderBy(p => p.Yjsj, OrderByType.Desc);
var list = await query.ToPageListAsync(input.currentPage, input.pageSize);
var totalCount = await query.CountAsync();
return new
{
list = list,
pagination = new
{
pageIndex = input.currentPage,
pageSize = input.pageSize,
totalCount = totalCount
}
};
}
catch (Exception ex)
{
throw NCCException.Oh($"获取会员开单品项列表失败: {ex.Message}");
}
}
#endregion
#region 根据开单id获取当前开单欠款信息
///
/// 根据开单id获取当前开单欠款信息
///
/// 开单记录ID
/// 当前开单欠款信息
[HttpGet("GetBillingDebtInfo/{billingId}")]
public async Task GetBillingDebtInfoByBillingId(string billingId)
{
//返回 整单金额、实付金额、欠款金额、已缴欠款金额
var entity = await _db.Queryable().FirstAsync(p => p.Id == billingId);
if (entity == null)
{
throw NCCException.Oh("开单记录不存在");
}
return new
{
zdyj = entity.Zdyj,
sfyj = entity.Sfyj,
qk = entity.Qk,
PaidDebt = entity.PaidDebt,
};
}
#endregion
#region 修改开单金额
///
/// 修改开单金额
///
/// 修改开单金额输入
/// 修改结果
///
/// 修改开单记录表的金额以及品项明细表里面的金额
///
/// 示例请求:
/// ```json
/// {
/// "billingId": "123456789",
/// "zdyj": 1000.00,
/// "sfyj": 800.00,
/// "deductAmount": 200.00,
/// "qk": 0.00,
/// "itemDetails": [
/// {
/// "itemDetailId": "item001",
/// "pxjg": 500.00,
/// "totalPrice": 500.00,
/// "actualPrice": 400.00
/// }
/// ],
/// "remark": "金额调整"
/// }
/// ```
///
/// 参数说明:
/// - billingId: 开单记录ID(必填)
/// - zdyj: 整单业绩
/// - sfyj: 实付业绩
/// - deductAmount: 储扣总金额
/// - qk: 欠款
/// - itemDetails: 品项明细金额列表
/// - remark: 修改备注
///
/// 成功修改开单金额
/// 请求参数错误
/// 开单记录不存在
/// 服务器内部错误
[HttpPut("UpdateBillingAmount")]
public async Task UpdateBillingAmount(LqKdKdjlbUpdateAmountInput input)
{
try
{
// 验证输入参数
if (string.IsNullOrEmpty(input.BillingId))
{
throw NCCException.Oh("开单记录ID不能为空");
}
// 检查开单记录是否存在且有效
var billingEntity = await _db.Queryable().Where(w => w.Id == input.BillingId && w.IsEffective == StatusEnum.有效.GetHashCode()).FirstAsync();
if (billingEntity == null)
{
throw NCCException.Oh("开单记录不存在或已作废");
}
// 验证品项明细
if (input.ItemDetails != null && input.ItemDetails.Any())
{
var itemDetailIds = input.ItemDetails.Select(x => x.ItemDetailId).ToList();
var existingItems = await _db.Queryable().Where(w => w.Glkdbh == input.BillingId && itemDetailIds.Contains(w.Id)).ToListAsync();
if (existingItems.Count != input.ItemDetails.Count)
{
throw NCCException.Oh("部分品项明细不存在或不属于该开单记录");
}
}
// 开始事务
_db.BeginTran();
try
{
// 更新开单记录金额
var updateResult = await _db.Updateable().SetColumns(it => new LqKdKdjlbEntity
{
Zdyj = input.Zdyj,
Sfyj = input.Sfyj,
DeductAmount = input.DeductAmount,
Qk = input.Qk,
UpdateTime = DateTime.Now
}).Where(w => w.Id == input.BillingId).ExecuteCommandAsync();
if (updateResult <= 0)
{
throw NCCException.Oh("更新开单记录金额失败");
}
// 更新品项明细金额
if (input.ItemDetails != null && input.ItemDetails.Any())
{
foreach (var itemDetail in input.ItemDetails)
{
var itemUpdateResult = await _db.Updateable().SetColumns(it => new LqKdPxmxEntity
{
Pxjg = itemDetail.Pxjg,
TotalPrice = itemDetail.TotalPrice,
ActualPrice = itemDetail.ActualPrice
}).Where(w => w.Id == itemDetail.ItemDetailId && w.Glkdbh == input.BillingId).ExecuteCommandAsync();
if (itemUpdateResult <= 0)
{
throw NCCException.Oh($"更新品项明细金额失败,品项ID: {itemDetail.ItemDetailId}");
}
}
}
// 提交事务
_db.CommitTran();
return "修改开单金额成功";
}
catch (Exception ex)
{
// 回滚事务
_db.RollbackTran();
throw NCCException.Oh($"修改开单金额失败: {ex.Message}");
}
}
catch (Exception ex)
{
throw NCCException.Oh($"修改开单金额时发生错误: {ex.Message}");
}
}
#endregion
#region 获取门店某个时间段的开单记录汇总信息(台账)
///
/// 获取门店某个时间段的开单记录汇总信息(台账)
///
/// 查询参数
/// 开单记录汇总信息
[HttpGet("GetBillingRecordSummaryByStoreId")]
public async Task GetBillingRecordSummaryByStoreId([FromQuery] BillingRecordSummaryQueryInput input)
{
try
{
// 验证参数
if (string.IsNullOrEmpty(input.StoreId))
{
throw NCCException.Oh("门店ID不能为空");
}
// 如果开始时间和结束时间为空,就默认是当月
if (input.StartTime == null || input.EndTime == null)
{
input.StartTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
input.EndTime = DateTime.Now.AddDays(DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month));
}
// 构建开单记录查询条件
var billingQuery = _db.Queryable()
.Where(w => w.Djmd == input.StoreId && w.Kdrq >= input.StartTime && w.Kdrq <= input.EndTime && w.IsEffective == StatusEnum.有效.GetHashCode());
// 客户筛选
if (!string.IsNullOrEmpty(input.MemberId))
{
billingQuery = billingQuery.Where(w => w.Kdhy == input.MemberId);
}
// 查询开单记录
var billingRecords = await billingQuery
.Select(it => new
{
id = it.Id,
djmd = it.Djmd,
jsj = it.Jsj,
kdrq = it.Kdrq,
gjlx = it.Gjlx,
zdyj = it.Zdyj,
sfyj = it.Sfyj,
qk = it.Qk,
hgjg = it.Hgjg,
hgjgName = SqlFunc.Subqueryable().Where(x => x.Id == it.Hgjg).Select(x => x.Hzmc),
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),
fkfs = it.Fkfs, // 付款方式
fkyy = it.Fkyy, // 付款医院ID
fkyyName = SqlFunc.Subqueryable().Where(x => x.Id == it.Fkyy).Select(x => x.Hzmc), // 付款医院名称
activityId = it.ActivityId, // 营销活动ID
activityName = SqlFunc.Subqueryable().Where(x => x.Id == it.ActivityId).Select(x => x.ActivityName), // 营销活动名称
khly = it.Khly, // 客户来源
bz = it.Bz, // 备注
createTime = it.CreateTime
})
.ToListAsync();
if (!billingRecords.Any())
{
return new
{
success = true,
data = new
{
billingSummary = new
{
totalCount = 0,
totalAmount = 0,
totalPaidAmount = 0,
totalDebt = 0,
billingRecords = new List