// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 // // SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: // 1.请不要删除和修改根目录下的LICENSE文件。 // 2.请不要删除和修改SimpleAdmin源码头部的版权声明。 // 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin // 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。 // 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。 // 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。 using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using Minio.DataModel; using NewLife.Reflection; using SimpleAdmin.Application.Entity; using SimpleAdmin.Application.Services.Prescription; using Yitter.IdGenerator; public class PrescriptionService : DbRepository, IPrescriptionService { //public async Task> GetPrescriptionList(PrescriptionQueryInput input) //{ // var query = Context.Queryable() // .LeftJoin((p, pe) => p.PersonId == pe.Id) // .LeftJoin((p, pe, emp) => pe.EmployeeId != null && pe.EmployeeId == emp.Id) // .WhereIF(!string.IsNullOrEmpty(input.PersonName), (p, pe, emp) => // input.PersonName.Contains(pe.Name) || pe.Name.Contains(input.PersonName) || // input.PersonName.Contains(emp.Name) || emp.Name.Contains(input.PersonName)) // .WhereIF(input.PersonType.HasValue && input.PersonType == PersonType.Employee, (p, pe) => // pe.EmployeeId == null) // .WhereIF(input.PersonType.HasValue && input.PersonType != PersonType.Employee, (p, pe) => // pe.EmployeeId != null); // try // { // // 获取处方基础信息(添加总成本属性) // var prescriptionPage = await query.OrderByDescending((p, pe, emp) => p.CreateTime) // .Select((p, pe, emp) => new PrescriptionDto // { // Id = p.Id, // PersonId = p.PersonId, // PersonName = pe.Name, // PersonType = pe.EmployeeId != null ? PersonType.Relative : PersonType.Employee, // VisitDate = p.VisitDate, // CreateTime = p.CreateTime.Value, // EmployeeName = emp.Name, // CanEdit = false, // TotalMedicineCost = 0 // 初始化总成本 // }).ToPagedListAsync(input.PageNum, input.PageSize); // // 设置可编辑状态 // foreach (var item in prescriptionPage.List) // { // if (item.CreateTime.Date == DateTime.Now.Date || UserManager.SuperAdmin) // { // item.CanEdit = true; // } // } // // 获取当前页所有处方的药材明细 // var pidList = prescriptionPage.List.Select(a => a.Id).ToList(); // var prescriptionHerbList = await Context.Queryable() // .Where(a => pidList.Contains(a.PrescriptionId)) // .ToListAsync(); // if (prescriptionHerbList.Any()) // { // // 1. 按【处方ID+药材ID】分组,统计每个处方中每种药材的总使用量 // var prescriptionMedicinalUsage = prescriptionHerbList // .GroupBy(h => new { h.PrescriptionId, h.MedicinalId }) // .Select(g => new // { // PrescriptionId = g.Key.PrescriptionId, // MedicinalId = g.Key.MedicinalId, // TotalUsedWeight = g.Sum(h => h.AmountG) // 同一处方中同一药材的总用量 // }) // .ToList(); // // 2. 收集所有涉及的药材ID,批量获取FIFO计算所需数据 // var medicinalIds = prescriptionMedicinalUsage.Select(u => u.MedicinalId).Distinct().ToList(); // var fifoData = await GetMedicinalFifoData(medicinalIds); // // 3. 按处方ID分组,计算每个处方的总成本 // var groupedByPrescription = prescriptionMedicinalUsage // .GroupBy(u => u.PrescriptionId) // .ToDictionary(g => g.Key, g => g.ToList()); // // 4. 遍历处方,计算总成本 // foreach (var prescription in prescriptionPage.List) // { // if (groupedByPrescription.TryGetValue(prescription.Id, out var usages)) // { // foreach (var usage in usages) // { // // 基于FIFO计算当前药材的使用成本 // var herbCost = CalculateHerbCostByFifo( // fifoData, // usage.MedicinalId, // usage.TotalUsedWeight // ); // // 累加到处方总成本 // prescription.TotalMedicineCost += herbCost; // } // } // } // } // foreach (var item in prescriptionPage.List) // { // //家属成本多十个点 // if (item.PersonType== PersonType.Relative) // { // item.TotalMedicineCost = Math.Round(item.TotalMedicineCost * (decimal)1.1, 2, MidpointRounding.AwayFromZero); // } // } // return prescriptionPage; // } // catch (Exception ex) // { // Console.WriteLine($"错误: {ex.Message}"); // throw; // } //} ///// ///// 获取药材FIFO计算所需的基础数据(入库记录) ///// //private async Task>> GetMedicinalFifoData(List medicinalIds) //{ // if (!medicinalIds.Any()) // return new Dictionary>(); // // 批量查询所有涉及药材的入库记录(按时间排序) // var addLogs = await Context.Queryable() // .Where(l => medicinalIds.Contains(l.MedicinalId)) // .OrderBy(l => l.CreateTime) // .Select(l => new MedicinalFifoRecord // { // MedicinalId = l.MedicinalId, // AddWeight = l.AddWeight, // UnitPrice = l.UnitPrice // }) // .ToListAsync(); // // 按药材ID分组 // return addLogs.GroupBy(l => l.MedicinalId) // .ToDictionary(g => g.Key, g => g.ToList()); //} ///// ///// 按FIFO规则计算单种药材的使用成本 ///// //private decimal CalculateHerbCostByFifo( // Dictionary> fifoData, // long medicinalId, // decimal usedWeight) //{ // if (usedWeight <= 0 || !fifoData.TryGetValue(medicinalId, out var records) || !records.Any()) // return 0; // decimal remainingToCalculate = usedWeight; // decimal totalCost = 0; // // 按入库顺序依次消耗 // foreach (var record in records) // { // if (remainingToCalculate <= 0) // break; // if (record.AddWeight <= remainingToCalculate) // { // // 当前批次全部消耗 // totalCost += record.AddWeight * record.UnitPrice; // remainingToCalculate -= record.AddWeight; // } // else // { // // 消耗当前批次的部分量 // totalCost += remainingToCalculate * record.UnitPrice; // remainingToCalculate = 0; // } // } // return Math.Round(totalCost, 2, MidpointRounding.AwayFromZero); //} public async Task> GetPrescriptionList(PrescriptionQueryInput input) { var query = Context.Queryable() .LeftJoin((p, pe) => p.PersonId == pe.Id) .LeftJoin((p, pe, emp) => pe.EmployeeId != null && pe.EmployeeId == emp.Id) .WhereIF(!string.IsNullOrEmpty(input.PersonName), (p, pe, emp) => input.PersonName.Contains(pe.Name) || pe.Name.Contains(input.PersonName) || input.PersonName.Contains(emp.Name) || emp.Name.Contains(input.PersonName)) .WhereIF(input.PersonType.HasValue && input.PersonType == PersonType.Employee, (p, pe) => pe.EmployeeId == null) .WhereIF(input.PersonType.HasValue && input.PersonType != PersonType.Employee, (p, pe) => pe.EmployeeId != null); try { // 获取处方基础信息 var prescriptionPage = await query.OrderByDescending((p, pe, emp) => p.CreateTime) .Select((p, pe, emp) => new PrescriptionDto { Id = p.Id, PersonId = p.PersonId, PersonName = pe.Name, PersonType = pe.EmployeeId != null ? PersonType.Relative : PersonType.Employee, VisitDate = p.VisitDate, CreateTime = p.CreateTime.Value, EmployeeName = emp.Name, CanEdit = false, TotalMedicineCost = 0 }).ToPagedListAsync(input.PageNum, input.PageSize); if (!prescriptionPage.List.Any()) return prescriptionPage; // 获取当前页所有处方的 Id var pidList = prescriptionPage.List.Select(a => a.Id).ToList(); // ------------------ “所有处方”按时间的消耗 ------------------ // 1) 取出所有处方 Id(按时间升序),用于按时间顺序模拟消耗 var allPrescriptionIds = await Context.Queryable() .OrderBy(p => p.CreateTime) .Select(p => p.Id) .ToListAsync(); // 2) 拉取所有处方对应的药材明细 var allPrescriptionHerbList = await Context.Queryable() .Where(ph => allPrescriptionIds.Contains(ph.PrescriptionId)) .ToListAsync(); // 3) 按【处方ID+药材ID】分组,统计每个处方中每种药材的总使用量(全量) var allUsageGroups = allPrescriptionHerbList .GroupBy(h => new { h.PrescriptionId, h.MedicinalId }) .Select(g => new { PrescriptionId = g.Key.PrescriptionId, MedicinalId = g.Key.MedicinalId, TotalUsedWeight = g.Sum(h => h.AmountG) }) .ToList(); // 4) 收集所有涉及的药材 ID,批量获取 FIFO 入库数据 var allMedicinalIds = allUsageGroups.Select(u => u.MedicinalId).Distinct().ToList(); var fifoData = await GetMedicinalFifoData(allMedicinalIds); // 5) 为每个处方建立快速查找:PrescriptionId -> usages var groupedByPrescriptionAll = allUsageGroups .GroupBy(u => u.PrescriptionId) .ToDictionary(g => g.Key, g => g.ToList()); // 6) 初始化 FIFO 偏移(累计消耗量)字典偏移字典 var fifoOffsets = new Dictionary(); // 7) 模拟按时间顺序消耗所有处方,并把每张处方的成本记录到字典 var prescriptionCostDict = new Dictionary(); foreach (var pid in allPrescriptionIds) { decimal presCost = 0; if (groupedByPrescriptionAll.TryGetValue(pid, out var usages)) { foreach (var usage in usages) { var herbCost = CalculateHerbCostByFifoWithOffset( fifoData, fifoOffsets, usage.MedicinalId, usage.TotalUsedWeight ); presCost += herbCost; } } // 四舍五入到 4 位(在分处方层面保留更多精度,最后显示再 round 2 位) prescriptionCostDict[pid] = Math.Round(presCost, 4, MidpointRounding.AwayFromZero); } //decimal all = 0; // 8) 将计算结果映射回当前页的处方 foreach (var item in prescriptionPage.List) { if (prescriptionCostDict.TryGetValue(item.Id, out var cost)) { item.TotalMedicineCost = cost; } // 设置 CanEdit if (item.CreateTime.Date == DateTime.Now.Date || UserManager.SuperAdmin) { item.CanEdit = true; } //all += item.TotalMedicineCost; // 家属加价 10% if (item.PersonType == PersonType.Relative) { item.TotalMedicineCost = Math.Round(item.TotalMedicineCost * 1.1m, 2, MidpointRounding.AwayFromZero); } else { item.TotalMedicineCost = Math.Round(item.TotalMedicineCost, 2, MidpointRounding.AwayFromZero); } } //var all= prescriptionPage.List.Sum(a => a.TotalMedicineCost); //var cc = await GetAllPrescriptionTotalCost(); return prescriptionPage; } catch (Exception ex) { Console.WriteLine($"错误: {ex.Message}"); throw; } } /// /// 获取所有处方的总药材成本 /// /// public async Task GetAllPrescriptionTotalCost() { // 1) 获取所有处方的 ID(按创建时间升序),并明确类型为 long var allPrescriptionIds = await Context.Queryable() .OrderBy(p => p.CreateTime) .Select(p => p.Id) // 确保这里的 Id 类型是 long 或 long? .ToListAsync(); if (!allPrescriptionIds.Any()) return 0m; // 把 id 列表规范为 long 列表(处理可能的 nullable) var allIdsLong = allPrescriptionIds.Select(id => Convert.ToInt64(id)).ToList(); // 2) 获取所有处方的药材使用明细 var allPrescriptionHerbList = await Context.Queryable() .Where(ph => allIdsLong.Contains(Convert.ToInt64(ph.PrescriptionId))) .ToListAsync(); var allUsageGroups = allPrescriptionHerbList .GroupBy(h => new { PrescriptionId = Convert.ToInt64(h.PrescriptionId), h.MedicinalId }) .Select(g => new { PrescriptionId = g.Key.PrescriptionId, g.Key.MedicinalId, TotalUsedWeight = g.Sum(h => h.AmountG) }) .ToList(); // 3) 获取所有药材的 FIFO 入库记录 var allMedicinalIds = allUsageGroups.Select(u => u.MedicinalId).Distinct().ToList(); var fifoData = await GetMedicinalFifoData(allMedicinalIds); // 4) 按处方构建使用分组(key 为 long) var groupedByPrescriptionAll = allUsageGroups .GroupBy(u => u.PrescriptionId) .ToDictionary(g => g.Key, g => g.ToList()); // 5) FIFO 偏移量 var fifoOffsets = new Dictionary(); decimal totalSum = 0m; // 6) 获取每张处方的人员类型(IsRelative: bool),并把 key 明确为 long,避免类型不匹配 var personTmp = await Context.Queryable() .LeftJoin((p, pe) => p.PersonId == pe.Id) .Where(p => allIdsLong.Contains(Convert.ToInt64(p.Id))) .Select((p, pe) => new { Id = p.Id, IsRelative = pe.EmployeeId != null }) .ToListAsync(); // 明确转换 key 为 long(处理 nullable) var personInfoDict = personTmp .ToDictionary(x => Convert.ToInt64(x.Id), x => x.IsRelative); // 7) FIFO 成本计算(按时间顺序已由 allIdsLong 保持) foreach (var pidObj in allIdsLong) { long pid = pidObj; // 明确 pid 是 long decimal presCost = 0m; if (groupedByPrescriptionAll.TryGetValue(pid, out var usages)) { foreach (var usage in usages) { var cost = CalculateHerbCostByFifoWithOffset( fifoData, fifoOffsets, usage.MedicinalId, usage.TotalUsedWeight ); presCost += cost; } } // 处方级别保留 4 位小数 presCost = Math.Round(presCost, 4, MidpointRounding.AwayFromZero); // 家属加价 10%(若字典缺失,默认不是家属) var isRelative = personInfoDict.TryGetValue(pid, out var rel) && rel; if (isRelative) { presCost = Math.Round(presCost * 1.1m, 2, MidpointRounding.AwayFromZero); } else { presCost = Math.Round(presCost, 2, MidpointRounding.AwayFromZero); } totalSum += presCost; } return totalSum; } /// /// 获取药材FIFO计算所需的基础数据(入库记录) /// private async Task>> GetMedicinalFifoData(List medicinalIds) { if (!medicinalIds.Any()) return new Dictionary>(); // 批量查询所有涉及药材的入库记录(按时间排序) var addLogs = await Context.Queryable() .Where(l => medicinalIds.Contains(l.MedicinalId)) .OrderBy(l => l.CreateTime) .Select(l => new MedicinalFifoRecord { MedicinalId = l.MedicinalId, AddWeight = l.AddWeight, UnitPrice = l.UnitPrice }) .ToListAsync(); // 按药材ID分组 return addLogs.GroupBy(l => l.MedicinalId) .ToDictionary(g => g.Key, g => g.ToList()); } /// /// 按FIFO规则计算单种药材的使用成本(累计消耗) /// private decimal CalculateHerbCostByFifoWithOffset( Dictionary> fifoData, Dictionary fifoOffsets, long medicinalId, decimal usedWeight) { if (usedWeight <= 0 || !fifoData.TryGetValue(medicinalId, out var records) || !records.Any()) return 0; decimal alreadyUsed = fifoOffsets.GetValueOrDefault(medicinalId, 0); decimal remainingToUse = usedWeight; decimal totalCost = 0; foreach (var record in records) { if (remainingToUse <= 0) break; // 跳过已经消耗完的批次 if (alreadyUsed >= record.AddWeight) { alreadyUsed -= record.AddWeight; continue; } // 当前批次可用量 decimal available = record.AddWeight - alreadyUsed; decimal toConsume = Math.Min(available, remainingToUse); totalCost += toConsume * record.UnitPrice; remainingToUse -= toConsume; alreadyUsed = 0; } // 更新累计消耗量 fifoOffsets[medicinalId] = fifoOffsets.GetValueOrDefault(medicinalId, 0) + usedWeight; return Math.Round(totalCost, 4, MidpointRounding.AwayFromZero); } /// /// 获取处方详情 /// /// /// /// public async Task GetPrescriptionDetail(long id) { var prescription = await Context.Queryable() .Where(p => p.Id == id) .FirstAsync(); if (prescription == null) { throw Oops.Bah("处方不存在"); } var person = await Context.Queryable() .Where(pe => pe.Id == prescription.PersonId) .FirstAsync(); var herbs = await Context.Queryable((ph, mt) => ph.MedicinalId == mt.Id) .Where(ph => ph.PrescriptionId == id) .Select((ph, mt) => new PrescriptionHerbDto { MedicinalId = mt.Id, MedicinalName = mt.MedicinalName, AmountG = ph.AmountG, Note = ph.Note }) .ToListAsync(); // 判断是否是员工还是家属 PersonDto employee = null; if (person.EmployeeId.HasValue) { var empPerson = await Context.Queryable() .Where(pe => pe.Id == person.EmployeeId) .Select(pe => new PersonDto { Id = pe.Id, Name = pe.Name }) .FirstAsync(); employee = empPerson; } var RelationShipName = await GetRelationTypes(); return new PrescriptionDetailDto { Id = prescription.Id, Person = new PersonDetailDto { Id = person.Id, Name = person.Name, Birthday = person.Birthday, Weight = prescription.Weight, Height = person.Height, IsEmployee = !person.EmployeeId.HasValue, EmployeeId = person.EmployeeId, EmployeeName = employee?.Name, RelationShip = person.RelationShip, RelationShipName = RelationShipName.FirstOrDefault(t => t.Id == person.RelationShip)?.Name }, DiagnosisResult = prescription.DiagnosisResult, UserMedicinaAdvice = prescription.UserMedicinaAdvice, VisitDate = prescription.VisitDate, Herbs = herbs, CanEdit = prescription.CreateTime.Value.Date == DateTime.Now.Date }; } /// /// 添加编辑处方记录 /// /// /// public async Task SavePrescription(SavePrescriptionInput input) { // 基础参数验证 if (input == null) throw Oops.Bah("输入参数不能为空"); if (input.Herbs == null || !input.Herbs.Any()) throw Oops.Bah("处方药材不能为空"); await Context.Ado.BeginTranAsync(); try { // 用于在方法内记录新创建的员工 id(如果需要) long? createdEmployeeId = null; // ---------- 1. 处理前端在 person 中携带的“临时员工”(如果存在) ---------- if (input.Person == null) throw Oops.Bah("人员信息不能为空"); // 场景 A: 病人是家属(IsEmployee == false),但关联的员工是前端临时新增 if (!input.Person.IsEmployee) { // 如果前端明确标记关联员工为临时新增(EmployeeIsTemp),需要先创建员工 if (input.Person.EmployeeIsTemp) { if (string.IsNullOrWhiteSpace(input.Person.EmployeeName)) throw Oops.Bah("标记了临时关联员工,但未提供员工姓名"); // 创建新员工记录(作为 Person 表的一条记录,RelationShip=null, EmployeeId=null) var newEmp = new Person { Id = YitIdHelper.NextId(), Name = input.Person.EmployeeName?.Trim(), }; await Context.Insertable(newEmp).ExecuteCommandAsync(); createdEmployeeId = newEmp.Id; } else if (input.Person.EmployeeId.HasValue && input.Person.EmployeeId.Value > 0) { // 如果前端传的是已有员工 ID,校验该员工是否存在 var exists = await Context.Queryable() .AnyAsync(p => p.Id == input.Person.EmployeeId.Value); if (!exists) throw Oops.Bah($"关联的员工 ID={input.Person.EmployeeId.Value} 不存在"); } else if (!string.IsNullOrWhiteSpace(input.Person.EmployeeName)) { var newEmp = new Person { Id = YitIdHelper.NextId(), Name = input.Person.EmployeeName.Trim(), }; await Context.Insertable(newEmp).ExecuteCommandAsync(); createdEmployeeId = newEmp.Id; } else { // 没有绑定员工信息(既不是 temp,也没有 employeeId),则无法继续 if (input.Person.Id == null || input.Person.Id <= 0) throw Oops.Bah("家属未关联员工,请选择或新增关联员工"); } } else { // 病人是公司员工(IsEmployee == true) // 如果没有 Id,但前端同时将该员工标记为临时新增,或者传了 Name 而无 Id,则创建员工 if ((!input.Person.Id.HasValue || input.Person.Id.Value <= 0) && (!string.IsNullOrWhiteSpace(input.Person.Name) || input.Person.EmployeeIsTemp)) { // 如果前端对员工也标记了 EmployeeIsTemp 且提供了 EmployeeName,则优先使用 EmployeeName(兼容性) string empNameForCreate = !string.IsNullOrWhiteSpace(input.Person.Name) ? input.Person.Name.Trim() : input.Person.EmployeeName?.Trim(); if (string.IsNullOrWhiteSpace(empNameForCreate)) throw Oops.Bah("新增员工时姓名不能为空"); var newEmp = new Person { Id = YitIdHelper.NextId(), Name = empNameForCreate, Birthday = input.Person.Birthday, Height = input.Person.Height, }; await Context.Insertable(newEmp).ExecuteCommandAsync(); // 将新创建的员工 id 作为主 person id(病人本身) input.Person.Id = newEmp.Id; } else if (input.Person.Id.HasValue && input.Person.Id.Value > 0) { // 校验存在性 var exists = await Context.Queryable().AnyAsync(p => p.Id == input.Person.Id.Value); if (!exists) throw Oops.Bah($"指定的人员 ID={input.Person.Id.Value} 不存在"); } } // ---------- 2. 确定主 personId(如果是临时创建的员工, input.Person.Id 已在上一步设置) ---------- long personId; if (input.Person.Id.HasValue && input.Person.Id.Value > 0) { personId = input.Person.Id.Value; // 已存在的人员(或上一步创建的员工),我们仍然可以继续使用 Storageable 更新信息 } else { // 没有 Id,需新建主人员(可能是家属或员工) if (string.IsNullOrWhiteSpace(input.Person.Name)) throw Oops.Bah("新增人员信息不完整,姓名为必填项"); personId = YitIdHelper.NextId(); } // ---------- 3. 保存/更新主人员信息(使用 Storageable,兼容插入或更新) ---------- var mappedEmployeeId = input.Person.EmployeeId; // 如果之前创建了员工(createdEmployeeId),优先使用该 id 作为家属的 EmployeeId if (createdEmployeeId.HasValue) mappedEmployeeId = createdEmployeeId.Value; // 如果 person.IsEmployee == true,则 person.EmployeeId 应为 null(员工不需要关联员工) if (input.Person.IsEmployee) mappedEmployeeId = null; var person = new Person { Id = personId, Name = input.Person?.Name, Birthday = input.Person?.Birthday, Height = input.Person?.Height, RelationShip = input.Person?.RelationShip, EmployeeId = mappedEmployeeId }; await Context.Storageable(person).ExecuteCommandAsync(); // ---------- 4. 处方保存/编辑逻辑---------- Prescription prescription; if (input.Id.HasValue) // 编辑 { prescription = await Context.Queryable() .FirstAsync(p => p.Id == input.Id.Value); if (prescription == null) throw Oops.Bah("处方不存在"); if (prescription.CreateTime.Value.Date != DateTime.Now.Date && !UserManager.SuperAdmin) throw Oops.Bah("只能编辑当天的处方"); // 查询原有消耗记录并恢复库存 var oldHerbs = await Context.Queryable() .Where(ph => ph.PrescriptionId == input.Id.Value) .ToListAsync(); if (oldHerbs.Count > 0) { foreach (var g in oldHerbs.GroupBy(x => x.MedicinalId)) { // 恢复库存(加回已消耗) await Context.Updateable() .SetColumns(m => new Medicinal { Number = m.Number + g.Sum(x => x.AmountG) }) .Where(m => m.Id == g.Key) .ExecuteCommandAsync(); } } // 删除原有处方药材 await Context.Deleteable() .Where(ph => ph.PrescriptionId == input.Id.Value) .ExecuteCommandAsync(); // 更新处方 prescription.PersonId = personId; prescription.Weight = input.Person.Weight; prescription.DiagnosisResult = input.DiagnosisResult; prescription.UserMedicinaAdvice = input.UserMedicinaAdvice; prescription.VisitDate = input.VisitDate ?? DateTime.Now; await Context.Updateable(prescription).ExecuteCommandAsync(); } else // 新增 { var prescripId = YitIdHelper.NextId(); prescription = new Prescription { Id = prescripId, Weight = input.Person.Weight, PersonId = personId, DiagnosisResult = input.DiagnosisResult, UserMedicinaAdvice = input.UserMedicinaAdvice, VisitDate = input.VisitDate ?? DateTime.Now, }; await Context.Insertable(prescription).ExecuteCommandAsync(); input.Id = prescripId; } // ---------- 5. 校验库存 & 扣减 & 插入消耗记录 ---------- var herbIds = input.Herbs.Select(h => h.Id).ToList(); var medicinalList = await Context.Queryable() .Where(m => herbIds.Contains(m.Id)) .ToListAsync(); List HerbsId = new List(); foreach (var herb in input.Herbs) { var medicinal = medicinalList.FirstOrDefault(m => m.Id == herb.Id); if (medicinal == null) throw Oops.Bah($"药材[{herb.Id}]不存在"); if (herb.AmountG <= 0) throw Oops.Bah($"请填写正确的消耗量,药材[{medicinal.MedicinalName}]"); if (medicinal.Number < herb.AmountG) throw Oops.Bah($"药材[{medicinal.MedicinalName}]库存不足,当前库存: {medicinal.Number}g,需要: {herb.AmountG}g"); if (HerbsId.Contains(herb.Id)) throw Oops.Bah($"请勿重复添加同一个药材【{medicinal.MedicinalName}】"); HerbsId.Add(herb.Id); medicinal.Number -= herb.AmountG; } // 批量更新药材库存 await Context.Updateable(medicinalList).ExecuteCommandAsync(); // 批量插入处方药材 var prescriptionHerbs = input.Herbs.Select(h => new PrescriptionHerb { PrescriptionId = input.Id.Value, MedicinalId = h.Id, AmountG = h.AmountG, Note = h.Note }).ToList(); await Context.Insertable(prescriptionHerbs).ExecuteCommandAsync(); await Context.Ado.CommitTranAsync(); return input.Id.Value; } catch { await Context.Ado.RollbackTranAsync(); throw; } } /// /// 获取 人员下拉 /// /// /// /// public async Task> GetPersonList(PersonType? type, long? employeeId) { var query = Context.Queryable(); // 筛选员工或家属 if (type.HasValue) { if (type == PersonType.Employee) { query = query.Where(pe => pe.EmployeeId == null); } else if (type == PersonType.Relative && employeeId.HasValue) { query = query.Where(pe => pe.EmployeeId == employeeId); } } return await query.Select(pe => new PersonDto { Id = pe.Id, Name = pe.Name, Birthday = pe.Birthday, Height = pe.Height, RelationShip = pe.RelationShip }).ToListAsync(); } /// /// 获取关系下拉 /// /// public async Task> GetRelationTypes() { var sysmodel = await Context.Queryable().FirstAsync(a => a.DictValue == "Relation"); if (sysmodel != null) { return await Context.Queryable().Where(a => a.ParentId == sysmodel.Id).Select(pe => new RelationTypeDto { Id = int.Parse(pe.DictValue), Name = pe.DictLabel, }).ToListAsync(); } else { return new List { new RelationTypeDto { Id = 1, Name = "配偶" }, new RelationTypeDto { Id = 2, Name = "子女" }, new RelationTypeDto { Id = 3, Name = "父母" }, new RelationTypeDto { Id = 4, Name = "兄弟姐妹" }, new RelationTypeDto { Id = 5, Name = "其他" } }; } } }