diff --git a/SimpleAdmin.Application/Entity/Medicinal.cs b/SimpleAdmin.Application/Entity/Medicinal.cs
deleted file mode 100644
index c4512e2..0000000
--- a/SimpleAdmin.Application/Entity/Medicinal.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Entity
-{
- [SugarTable("WeiBingJu_Medicinal", TableDescription = "药材管理")]
- [Tenant(SqlSugarConst.DB_DEFAULT)]
- [CodeGen]
- public class Medicinal : DataEntityBase
- {
- ///
- /// 药材
- ///
- [SugarColumn(ColumnDescription = "药材")]
- public string MedicinalName { get; set; }
- ///
- /// 数量(g)
- ///
- [SugarColumn(ColumnDescription = "数量(g)")]
- public decimal Number { get; set; }
-
- }
-}
diff --git a/SimpleAdmin.Application/Entity/MedicinalAddLog.cs b/SimpleAdmin.Application/Entity/MedicinalAddLog.cs
deleted file mode 100644
index b7d979a..0000000
--- a/SimpleAdmin.Application/Entity/MedicinalAddLog.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Entity
-{
- [SugarTable("WeiBingJu_MedicinalAddLog", TableDescription = "药材添加记录")]
- [Tenant(SqlSugarConst.DB_DEFAULT)]
- [CodeGen]
- public class MedicinalAddLog : DataEntityBase
- {
- ///
- /// 药材关联id
- ///
- [SugarColumn(ColumnDescription = "药材关联id")]
- public long MedicinalId { get; set; }
- ///
- /// 添加重量(g)
- ///
- [SugarColumn(ColumnDescription = "添加重量(g)")]
- public decimal AddWeight { get; set; }
-
- ///
- /// 药材单价
- ///
- [SugarColumn(ColumnDescription = "药材单价")]
- public decimal UnitPrice { get; set; }
-
- ///
- /// 总价
- ///
- [SugarColumn(ColumnDescription = "总价")]
- public decimal TotalPrice { get; set; }
-
- }
-}
diff --git a/SimpleAdmin.Application/Entity/Person.cs b/SimpleAdmin.Application/Entity/Person.cs
deleted file mode 100644
index a9735c5..0000000
--- a/SimpleAdmin.Application/Entity/Person.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Entity
-{
- [SugarTable("WeiBingJu_Person", TableDescription = "人员表(员工和员工亲戚)")]
- [Tenant(SqlSugarConst.DB_DEFAULT)]
- [CodeGen]
- public class Person : DataEntityBase
- {
- [SugarColumn(Length = 128, ColumnDescription = "姓名")]
- public string Name { get; set; }
-
- ///
- /// 若非空,表示此 person 是某员工的家属
- ///
- [SugarColumn(ColumnDescription = "关联员工Id", IsNullable = true)]
- public long? EmployeeId { get; set; }
-
- [SugarColumn(ColumnDescription = "关系配置id", IsNullable = true)]
- public long? RelationShip { get; set; }
-
- [SugarColumn(ColumnDescription = "出生日期", IsNullable = true)]
- public string Birthday { get; set; }
-
- /// 单位 cm,保留两位小数
- [SugarColumn(ColumnDescription = "身高(cm)", IsNullable = true)]
- public decimal? Height { get; set; }
-
- [SugarColumn(ColumnDescription = "1领导,2普通员工,3其他", IsNullable = true)]
- public int? Type { get; set; }
- }
-}
diff --git a/SimpleAdmin.Application/Entity/Prescription.cs b/SimpleAdmin.Application/Entity/Prescription.cs
deleted file mode 100644
index 6e337a7..0000000
--- a/SimpleAdmin.Application/Entity/Prescription.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Entity
-{
- [SugarTable("WeiBingJu_Prescription", TableDescription = "处方管理")]
- [Tenant(SqlSugarConst.DB_DEFAULT)]
- [CodeGen]
- public class Prescription : DataEntityBase
- {
- [SugarColumn(ColumnDescription = "处方对象 PersonId")]
- public long PersonId { get; set; }
-
- [SugarColumn(ColumnDescription = "诊断结果", ColumnDataType = StaticConfig.CodeFirst_BigString)]
- public string DiagnosisResult { get; set; }
-
- [SugarColumn(ColumnDescription = "用药建议", ColumnDataType = StaticConfig.CodeFirst_BigString)]
- public string UserMedicinaAdvice { get; set; }
-
- [SugarColumn(ColumnDescription = "就诊时间")]
- public DateTime? VisitDate { get; set; }
-
-
- /// 单位 kg,保留两位小数
- [SugarColumn(ColumnDescription = "体重(kg)", IsNullable = true)]
- public decimal? Weight { get; set; }
- }
-
-}
-
diff --git a/SimpleAdmin.Application/Entity/PrescriptionHerb.cs b/SimpleAdmin.Application/Entity/PrescriptionHerb.cs
deleted file mode 100644
index 24ca4b7..0000000
--- a/SimpleAdmin.Application/Entity/PrescriptionHerb.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Entity
-{
- [SugarTable("WeiBingJu_PrescriptionHerb", TableDescription = "处方消耗药材表")]
- [Tenant(SqlSugarConst.DB_DEFAULT)]
- [CodeGen]
- public class PrescriptionHerb : DataEntityBase
- {
- [SugarColumn(ColumnDescription = "处方Id")]
- public long PrescriptionId { get; set; }
-
- [SugarColumn(ColumnDescription = "药材Id")]
- public long MedicinalId { get; set; }
-
- /// 消耗克数,单位 g
- [SugarColumn(ColumnDescription = "消耗(g)")]
- public decimal AmountG { get; set; }
-
- [SugarColumn(Length = 500, IsNullable = true, ColumnDescription = "用量说明/备注")]
- public string Note { get; set; }
- }
-}
diff --git a/SimpleAdmin.Application/GlobalUsings.cs b/SimpleAdmin.Application/GlobalUsings.cs
index 5f1ff20..d5406ed 100644
--- a/SimpleAdmin.Application/GlobalUsings.cs
+++ b/SimpleAdmin.Application/GlobalUsings.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
+// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
//
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。
@@ -23,7 +23,3 @@ global using SimpleAdmin.SqlSugar;
global using SimpleAdmin.System;
global using SqlSugar;
global using System.Reflection;
-global using SimpleAdmin.Application.Services.Medicinal;
-global using SimpleAdmin.Application.Services.Medicinal.Dto;
-global using SimpleAdmin.Application.Services.Prescription;
-global using SimpleAdmin.Application.Services.Prescription.Dto;
diff --git a/SimpleAdmin.Application/Services/Medicinal/Dto/MedicinalDto.cs b/SimpleAdmin.Application/Services/Medicinal/Dto/MedicinalDto.cs
deleted file mode 100644
index b11e594..0000000
--- a/SimpleAdmin.Application/Services/Medicinal/Dto/MedicinalDto.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Services.Medicinal.Dto
-{
- // 药材列表DTO
- public class MedicinalDto
- {
- public long Id { get; set; }
- public string MedicinalName { get; set; }
- public decimal Stock { get; set; }
- public decimal UsedCost { get; set; }
- public decimal RemainingValue { get; set; }
- public decimal TotalUsedWeight { get; set; }
- }
-
- // 药材消耗记录DTO
- public class MedicinalConsumptionRecordDto
- {
- public decimal AmountG { get; set; }
- public string PersonName { get; set; }
- public bool IsRelative { get; set; }
- public string EmployeeName { get; set; }
- public DateTime? UseTime { get; set; }
- }
-
- // 药材添加记录DTO
- public class MedicinalAddLogDto
- {
- public long Id { get; set; }
- public decimal AddWeight { get; set; }
- public decimal UnitPrice { get; set; }
- public DateTime AddTime { get; set; }
- public string AddUserName { get; set; }
- public decimal TotalPrice { get; set; }
-
- public bool CanEdit { get; set; }
- }
-
- // 添加药材输入DTO
- public class AddMedicinalInput
- {
- public long? Id { get; set; }
- public decimal AddWeight { get; set; }
- public decimal TotalPrice { get; set; }
- ///
- /// 未找到下拉框的时候传这个值代表添加新的药材
- ///
- public string MedicinalName { get; set; }
- }
-
- // 修改药材添加记录输入DTO
- public class UpdateMedicinalAddLogInput
- {
- public long Id { get; set; }
- public decimal AddWeight { get; set; }
- public decimal TotalPrice { get; set; }
- }
-
- // 药材类型DTO
- public class MedicinalTypeDto
- {
- public long Id { get; set; }
- public string Name { get; set; }
- }
-
- // 添加药材类型输入DTO
- public class AddMedicinalTypeInput
- {
- [Required(ErrorMessage = "药材名称不能为空")]
- public string Name { get; set; }
- }
-
- public class DeleteMedicinalLog
- {
- public long Id { get; set; }
- }
-
-}
diff --git a/SimpleAdmin.Application/Services/Medicinal/IMedicinalService.cs b/SimpleAdmin.Application/Services/Medicinal/IMedicinalService.cs
deleted file mode 100644
index 44325fb..0000000
--- a/SimpleAdmin.Application/Services/Medicinal/IMedicinalService.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Dynamic.Core;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Services.Medicinal
-{
- public interface IMedicinalService : ITransient
- {
- ///
- /// 获取药材列表,带筛选
- ///
- /// 药材名称筛选
- /// 页码
- /// 页大小
- ///
- Task> GetMedicinalList(string medicinalName, int page, int size);
-
- ///
- /// 获取药材消耗记录
- ///
- /// 药材ID
- ///
- Task> GetConsumptionRecords(long medicinalId, int page, int size);
- ///
- /// 获取药材下拉
- ///
- ///
- Task> GetMedicinalDictList();
-
- ///
- /// 获取药材添加记录
- ///
- /// 药材ID
- ///
- Task> GetAdditionRecords(long medicinalId, int page, int size);
-
- ///
- /// 添加药材
- ///
- /// 添加信息
- ///
- Task AddMedicinal(AddMedicinalInput input);
-
- ///
- /// 修改药材添加记录(仅当天可修改)
- ///
- /// 记录ID
- /// 修改信息
- ///
- Task UpdateAdditionRecord(UpdateMedicinalAddLogInput input);
-
- ///
- /// 删除药材添加记录(仅当天可删除)
- ///
- /// 记录ID
- ///
- Task DeleteAdditionRecord(long id);
- }
-}
diff --git a/SimpleAdmin.Application/Services/Medicinal/MedicinalService.cs b/SimpleAdmin.Application/Services/Medicinal/MedicinalService.cs
deleted file mode 100644
index f939c05..0000000
--- a/SimpleAdmin.Application/Services/Medicinal/MedicinalService.cs
+++ /dev/null
@@ -1,408 +0,0 @@
-// 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.Linq.Dynamic.Core;
-using SimpleAdmin.Application.Entity;
-using Yitter.IdGenerator;
-
-public class MedicinalService : DbRepository, IMedicinalService
-{
- private readonly ISysUserService _sysUserService;
- private readonly IDictService _dictService;
- public MedicinalService(ISysUserService sysUserService, IDictService dictService)
- {
- _sysUserService = sysUserService;
- _dictService = dictService;
- }
- public async Task> GetMedicinalList(string medicinalName, int page, int size)
- {
- // 1. 获取数据范围(原有逻辑不变)
- //var dataScope = await _sysUserService.GetLoginUserApiDataScope();
- //dataScope?.Remove(UserManager.OrgId);
-
- // 2. 分页查询药材基本信息
- var query = this.AsQueryable()
- //.WhereIF(dataScope.Any(), m => dataScope.Contains(m.Id))
- .WhereIF(!string.IsNullOrEmpty(medicinalName), m => m.MedicinalName.Contains(medicinalName))
- .OrderBy(p => p.Number)
- .Select(m => new MedicinalDto
- {
- Id = m.Id,
- MedicinalName = m.MedicinalName,
- Stock = m.Number, // 当前库存
- });
-
- var pageInfo = await query.ToPagedListAsync(page, size);
- if (!pageInfo.List.Any()) return pageInfo;
-
- // 3. 批量计算:成本、剩余价值、总使用量
- var medicinalIds = pageInfo.List.Select(dto => dto.Id).ToList();
- var costAndUsageDict = await BatchCalculateCostAndValue(medicinalIds);
- //decimal all = 0;
- foreach (var item in pageInfo.List)
- {
- if (costAndUsageDict.TryGetValue(item.Id, out var metrics))
- {
- //all += metrics.usedCost;
- item.UsedCost = metrics.usedCost; // 使用成本
- item.RemainingValue = metrics.remainingValue; // 剩余价值
- item.TotalUsedWeight = metrics.totalUsedWeight; // 新增:总使用量
- }
- else
- {
- // 无数据时默认值
- item.UsedCost = 0;
- item.RemainingValue = 0;
- item.TotalUsedWeight = 0;
- }
- }
-
- return pageInfo;
- }
-
- ///
- /// 获取药材下拉
- ///
- ///
- public async Task> GetMedicinalDictList()
- {
- ////获取数据范围
- //var dataScope = await _sysUserService.GetLoginUserApiDataScope();
- //dataScope?.Remove(UserManager.OrgId);
- //药材查询
- return await this.AsQueryable()
- //.WhereIF(dataScope.Any(), m => dataScope.Contains(m.Id)) //机构ID查询
- .OrderBy(p => p.Number)
- .Select(m => new MedicinalDto
- {
- Id = m.Id,
- MedicinalName = m.MedicinalName,
- Stock = m.Number,
- }).ToListAsync();
- }
- ///
- /// 获取药材消耗记录
- ///
- ///
- ///
- ///
- public async Task> GetConsumptionRecords(long medicinalId, int page, int size)
- {
- return await Context.Queryable()
- // 先关联处方
- .LeftJoin((ph, p) => ph.PrescriptionId == p.Id)
- // 再关联就诊人
- .LeftJoin((ph, p, pe) => p.PersonId == pe.Id)
- // 再关联员工
- .LeftJoin((ph, p, pe, emp) => pe.EmployeeId != null && pe.EmployeeId == emp.Id)
- .Where((ph, p, pe, emp) => ph.MedicinalId == medicinalId)
- .OrderByDescending((ph, p, pe, emp) => p.CreateTime)
- .Select((ph, p, pe, emp) => new MedicinalConsumptionRecordDto
- {
- AmountG = ph.AmountG,
- PersonName = pe.Name,
- IsRelative = pe.EmployeeId != null,
- EmployeeName = emp == null ? null : emp.Name,
- UseTime = p.VisitDate
- })
- .ToPagedListAsync(page, size);
- }
-
- ///
- /// 获取药材添加记录
- ///
- ///
- ///
- public async Task> GetAdditionRecords(long medicinalId, int page, int size)
- {
- var returnList = await Context.Queryable()
- .Where(log => log.MedicinalId == medicinalId)
- .OrderByDescending(dto => dto.CreateTime)
- .Select(log => new MedicinalAddLogDto
- {
- Id = log.Id,
- AddWeight = log.AddWeight,
- UnitPrice = log.UnitPrice,
- AddTime = log.CreateTime.Value,
- AddUserName = log.CreateUser,
- TotalPrice = log.TotalPrice,
- CanEdit = false
- })
- .ToPagedListAsync(page, size);
-
- foreach (var item in returnList.List)
- {
- if (item.AddTime.Date == DateTime.Now.Date || UserManager.SuperAdmin)
- {
- item.CanEdit = true;
- }
- }
- return returnList;
- }
- ///
- /// 添加药材
- ///
- ///
- ///
- public async Task AddMedicinal(AddMedicinalInput input)
- {
- if (input.TotalPrice <= 0 || input.AddWeight <= 0) throw Oops.Bah("请填写正确的金额!");
- // 开启事务
- await Context.Ado.BeginTranAsync();
- try
- {
- // 检查药材是否已存在
- var existing = await Context.Queryable()
- .Where(m => m.Id == input.Id)
- .FirstAsync();
-
- if (existing != null)
- {
- // 更新库存
- existing.Number += input.AddWeight;
- await Context.Updateable(existing).ExecuteCommandAsync();
- }
- else
- {
- var medicinalModel = await Context.Queryable()
- .Where(m => m.MedicinalName == input.MedicinalName)
- .FirstAsync();
- if (medicinalModel != null) throw Oops.Bah("已存在该药材,请在下拉框选择!");
- input.Id = YitIdHelper.NextId();
- // 新增药材记录
- await Context.Insertable(new Medicinal
- {
- Id = input.Id.Value,
- Number = input.AddWeight,
- MedicinalName = input.MedicinalName,
- }).ExecuteCommandAsync();
- }
- decimal unitPrice = 0m;
- if (input.AddWeight > 0)
- {
- unitPrice = Decimal.Round(input.TotalPrice / input.AddWeight, 5, MidpointRounding.AwayFromZero);
- }
- // 添加记录
- await Context.Insertable(new MedicinalAddLog
- {
- MedicinalId = input.Id.Value,
- AddWeight = input.AddWeight,
- UnitPrice = unitPrice,
- TotalPrice = input.TotalPrice
- }).ExecuteCommandAsync();
-
- await Context.Ado.CommitTranAsync();
- }
- catch
- {
- await Context.Ado.RollbackTranAsync();
- throw;
- }
- return true;
- }
- ///
- /// 修改药材添加记录
- ///
- ///
- ///
- ///
- public async Task UpdateAdditionRecord(UpdateMedicinalAddLogInput input)
- {
- if(input.TotalPrice<=0 || input.AddWeight<=0) throw Oops.Bah("请填写正确的金额!");
- var log = await Context.Queryable().Where(l => l.Id == input.Id).FirstAsync();
- if (log == null)
- {
- throw Oops.Bah("记录不存在!");
- }
-
- // 检查是否是当天记录,超级管理员随时都可以操作
- if (log.CreateTime.Value.Date != DateTime.Now.Date && !UserManager.SuperAdmin)
- {
- throw Oops.Bah("只能修改当天的记录!");
- }
-
- // 计算重量变化
- var weightDiff = input.AddWeight - log.AddWeight;
-
- // 开启事务
- await Context.Ado.BeginTranAsync();
-
- try
- {
- // 更新记录
- log.AddWeight = input.AddWeight;
- log.TotalPrice = input.TotalPrice;
- log.UnitPrice = Decimal.Round(input.TotalPrice / input.AddWeight, 5, MidpointRounding.AwayFromZero);
- await Context.Updateable(log).ExecuteCommandAsync();
-
- // 更新药材库存
- var medicinal = await Context.Queryable()
- .Where(m => m.Id == log.MedicinalId)
- .FirstAsync();
-
- if (medicinal != null)
- {
- medicinal.Number += weightDiff;
- if(medicinal.Number<0) throw Oops.Bah("无法修改,库存不足!");
- await Context.Updateable(medicinal).ExecuteCommandAsync();
- }
-
- await Context.Ado.CommitTranAsync();
- }
- catch
- {
- await Context.Ado.RollbackTranAsync();
- throw;
- }
-
- return true;
- }
- ///
- /// 删除药材添加记录
- ///
- ///
- ///
- ///
- public async Task DeleteAdditionRecord(long id)
- {
- var log = await Context.Queryable().Where(l => l.Id == id).FirstAsync();
- if (log == null)
- {
- throw Oops.Bah("记录不存在!");
- }
-
- // 检查是否是当天记录,超级管理员随时都可以操作
- if (log.CreateTime.Value.Date != DateTime.Now.Date && !UserManager.SuperAdmin)
- {
- throw Oops.Bah("只能删除当天的记录!");
- }
-
- // 开启事务
- await Context.Ado.BeginTranAsync();
-
- try
- {
- // 删除记录
- log.IsDelete = true;
- await Context.Updateable(log).ExecuteCommandAsync();
-
-
- // 更新药材库存
- var medicinal = await Context.Queryable()
- .Where(m => m.Id == log.MedicinalId)
- .FirstAsync();
-
- if (medicinal != null)
- {
- medicinal.Number -= log.AddWeight;
- if(medicinal.Number<0) throw Oops.Bah("无法删除,库存不足!");
- await Context.Updateable(medicinal).ExecuteCommandAsync();
- }
-
- await Context.Ado.CommitTranAsync();
- }
- catch
- {
- await Context.Ado.RollbackTranAsync();
- throw;
- }
-
- return true;
- }
-
- ///
- /// 批量计算:使用成本、剩余价值、总使用量(先进先出法)
- ///
- /// 需要计算的药材ID列表
- private async Task> BatchCalculateCostAndValue(List medicinalIds)
- {
- var result = new Dictionary();
- if (!medicinalIds.Any()) return result;
-
- // 1. 批量查询所有药材的【当前库存】
- var medicinals = await Context.Queryable()
- .Where(m => medicinalIds.Contains(m.Id))
- .Select(m => new { m.Id, m.Number }) // Number = 当前库存
- .ToListAsync();
-
- // 2. 批量查询所有药材的【入库记录】
- var allAddLogs = await Context.Queryable()
- .Where(l => medicinalIds.Contains(l.MedicinalId))
- .OrderBy(l => l.CreateTime) // 先进先出:按入库时间排序
- .Select(l => new {
- l.MedicinalId,
- l.AddWeight, // 入库量
- l.UnitPrice, // 单价(用于计算成本)
- l.TotalPrice // 总成本(用于计算成本)
- })
- .ToListAsync();
-
- // 3. 按【药材ID】分组入库记录
- var groupedLogs = allAddLogs
- .GroupBy(l => l.MedicinalId)
- .ToDictionary(g => g.Key, g => g.ToList());
-
- // 4. 遍历每个药材,内存中计算所有指标
- foreach (var med in medicinals)
- {
- // 初始化默认值(无入库记录时)
- decimal totalIn = 0; // 总入库量(AddWeight 总和)
- decimal totalCost = 0; // 总入库成本
- decimal totalUsedWeight = 0; // 总使用量
- decimal usedCost = 0; // 使用成本
- decimal remainingValue = 0;// 剩余价值
-
- // 若有入库记录,计算总入库量、总成本
- if (groupedLogs.TryGetValue(med.Id, out var addLogs) && addLogs.Any())
- {
- totalIn = addLogs.Sum(l => l.AddWeight); // 总入库量 = 所有 AddWeight 求和
- totalCost = addLogs.Sum(l => l.TotalPrice); // 总入库成本
- }
-
- // 计算总使用量:总入库量 - 当前库存(若结果为负,说明数据异常,按0处理)
- totalUsedWeight = Math.Max(0, totalIn - med.Number);
-
- // 若有使用量,先进先出法计算使用成本
- if (totalUsedWeight > 0 && groupedLogs.TryGetValue(med.Id, out var validLogs))
- {
- decimal remainingToCalculate = totalUsedWeight;
- foreach (var log in validLogs)
- {
- if (remainingToCalculate <= 0) break;
-
- // 当前入库记录的量 <= 剩余未计算的使用量:全部计入成本
- if (log.AddWeight <= remainingToCalculate)
- {
- usedCost += log.AddWeight * log.UnitPrice;
- remainingToCalculate -= log.AddWeight;
- }
- // 当前入库记录的量 > 剩余未计算的使用量:仅计算剩余部分
- else
- {
- usedCost += remainingToCalculate * log.UnitPrice;
- remainingToCalculate = 0;
- }
- }
- }
-
- // 剩余价值 = 总入库成本 - 使用成本(若为负,按0处理)
- remainingValue = Math.Max(0, totalCost - usedCost);
-
- // 存入结果字典
- result[med.Id] = (usedCost, remainingValue, totalUsedWeight);
- }
-
- return result;
- }
-}
diff --git a/SimpleAdmin.Application/Services/Prescription/Dto/PrescriptionDtos.cs b/SimpleAdmin.Application/Services/Prescription/Dto/PrescriptionDtos.cs
deleted file mode 100644
index b0c00d7..0000000
--- a/SimpleAdmin.Application/Services/Prescription/Dto/PrescriptionDtos.cs
+++ /dev/null
@@ -1,166 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Services.Prescription.Dto
-{
- // 人员类型枚举
- public enum PersonType
- {
- [Description("员工")]
- Employee = 1,
- [Description("家属")]
- Relative = 2
- }
-
- // 处方列表DTO
- public class PrescriptionDto
- {
- public long Id { get; set; }
- public long PersonId { get; set; }
- public string PersonName { get; set; }
- public PersonType PersonType { get; set; }
- ///
- /// 关联的员工名称
- ///
- public string EmployeeName { get; set; }
- public DateTime? VisitDate { get; set; }
- public bool CanEdit { get; set; }
-
- public DateTime CreateTime { get; set; }
-
- public decimal TotalMedicineCost { get; set; }
- public int? Type { get; set; }
-
- }
- // 辅助类:存储FIFO计算所需的入库记录信息
- public class MedicinalFifoRecord
- {
- public long MedicinalId { get; set; }
- public decimal AddWeight { get; set; }
- public decimal UnitPrice { get; set; }
- }
- // 处方查询输入DTO
- public class PrescriptionQueryInput: BasePageInput
- {
- public DateTime? VisitDate { get; set; }
- public long? MedicinalId { get; set; }
- public PersonType? PersonType { get; set; }
- public long? EmployeeId { get; set; }
-
- public string PersonName { get; set; }
- }
-
- // 处方详情DTO
- public class PrescriptionDetailDto
- {
- public long Id { get; set; }
- public PersonDetailDto Person { get; set; }
- public string DiagnosisResult { get; set; }
- public string UserMedicinaAdvice { get; set; }
- public DateTime? VisitDate { get; set; }
- public List Herbs { get; set; }
- public bool CanEdit { get; set; }
- }
-
- // 人员详情DTO
- public class PersonDetailDto
- {
- public long? Id { get; set; }
- public string Name { get; set; }
- public string Birthday { get; set; }
- public decimal? Weight { get; set; }
- public decimal? Height { get; set; }
- public bool IsEmployee { get; set; }
- public long? EmployeeId { get; set; }
- public string EmployeeName { get; set; }
- public long? RelationShip { get; set; }
- public string RelationShipName { get; set; }
- public int? Type { get; set; }
- }
-
- // 处方药材DTO
- public class PrescriptionHerbDto
- {
- public long MedicinalId { get; set; }
- public string MedicinalName { get; set; }
- public decimal AmountG { get; set; }
- public string Note { get; set; }
- }
-
- // DTOs(替换/合并到你的 DTO 定义处)
- public class SavePrescriptionInput
- {
- public long? Id { get; set; }
- public PersonInputDto Person { get; set; }
- public string DiagnosisResult { get; set; }
- public string UserMedicinaAdvice { get; set; }
- public DateTime? VisitDate { get; set; }
- public List Herbs { get; set; }
- }
-
- // 人员输入 DTO(新增字段以支持前端发送临时员工信息)
- public class PersonInputDto
- {
- public long? Id { get; set; } // 可能的已存在人员ID
- public string TempId { get; set; } // 可选:前端临时 id(兼容旧流程)
- public string Name { get; set; }
- public string Birthday { get; set; }
- public decimal? Weight { get; set; }
- public decimal? Height { get; set; }
- public bool IsEmployee { get; set; }
- public long? RelationShip { get; set; } // 亲属关系 id(若为家属)
- public long? EmployeeId { get; set; } // 关联员工 id(如果是家属)
- ///
- /// 当前关联的员工是否是前端临时新增(需要后端创建)
- ///
- public bool EmployeeIsTemp { get; set; }
- ///
- /// 当 EmployeeIsTemp 为 true 或者前端直接传了员工名字时,后端可用此字段创建员工
- ///
- public string EmployeeName { get; set; }
- public int? Type { get; set; }
- }
- // 处方药材输入DTO
- public class PrescriptionHerbInputDto
- {
- public long Id { get; set; }
- public decimal AmountG { get; set; }
- public string Note { get; set; }
- }
-
- // 人员DTO
- public class PersonDto
- {
- public long Id { get; set; }
- public string Name { get; set; }
- public string Birthday { get; set; }
-
- public decimal? Height { get; set; }
-
- public long? RelationShip { get; set; }
- public int? Type { get; set; }
- }
-
- // 关系类型DTO
- public class RelationTypeDto
- {
- public long Id { get; set; }
- public string Name { get; set; }
- }
-
-}
diff --git a/SimpleAdmin.Application/Services/Prescription/IPrescriptionService.cs b/SimpleAdmin.Application/Services/Prescription/IPrescriptionService.cs
deleted file mode 100644
index 0bce9c1..0000000
--- a/SimpleAdmin.Application/Services/Prescription/IPrescriptionService.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Dynamic.Core;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SimpleAdmin.Application.Services.Prescription
-{
- public interface IPrescriptionService : ITransient
- {
- ///
- /// 获取处方列表,带筛选
- ///
- /// 筛选条件
- ///
- Task> GetPrescriptionList(PrescriptionQueryInput input);
-
- ///
- /// 获取所有处方的成本
- ///
- ///
- Task GetAllPrescriptionTotalCost();
-
- ///
- /// 获取处方详情
- ///
- /// 处方ID
- ///
- Task GetPrescriptionDetail(long id);
-
- ///
- /// 保存处方(新增或编辑)
- ///
- /// 处方信息
- ///
- Task SavePrescription(SavePrescriptionInput input);
-
- ///
- /// 获取人员列表(员工和家属)
- ///
- /// 人员类型
- /// 员工ID(用于获取家属)
- ///
- Task> GetPersonList(PersonType? type, long? employeeId);
-
-
- ///
- /// 获取所有关系类型(写死在代码中)
- ///
- ///
- Task> GetRelationTypes();
- }
-}
diff --git a/SimpleAdmin.Application/Services/Prescription/PrescriptionService.cs b/SimpleAdmin.Application/Services/Prescription/PrescriptionService.cs
deleted file mode 100644
index 2506eea..0000000
--- a/SimpleAdmin.Application/Services/Prescription/PrescriptionService.cs
+++ /dev/null
@@ -1,892 +0,0 @@
-// 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,
- Type = pe.Type
- }).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.Type == 2)
- {
- 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,
- Type = pe.Type
- })
- .ToListAsync();
-
- // 明确转换 key 为 long(处理 nullable)
- var personInfoDict = personTmp
- .ToDictionary(x => Convert.ToInt64(x.Id), x => new { x.IsRelative, x.Type });
-
- // 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%
- personInfoDict.TryGetValue(pid, out var personInfo);
- var isRelative = personInfo?.IsRelative ?? false;
- var isOrdinary = personInfo?.Type == 2;
- if (isRelative || isOrdinary)
- {
- 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,
- Type = person.Type
- },
- 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 dbPerson = await Context.Queryable().FirstAsync(p => p.Id == personId);
- if (dbPerson != null)
- {
- dbPerson.Name = input.Person?.Name ?? dbPerson.Name;
- dbPerson.Birthday = input.Person?.Birthday ?? dbPerson.Birthday;
- dbPerson.Height = input.Person?.Height ?? dbPerson.Height;
- dbPerson.RelationShip = input.Person?.RelationShip ?? dbPerson.RelationShip;
- dbPerson.EmployeeId = mappedEmployeeId;
- if (input.Person != null && input.Person.IsEmployee)
- {
- dbPerson.Type = input.Person?.Type;
- }
- else
- {
- dbPerson.Type = null;
- }
- await Context.Updateable(dbPerson).ExecuteCommandAsync();
- }
- else
- {
- var person = new Person
- {
- Id = personId,
- Name = input.Person?.Name,
- Birthday = input.Person?.Birthday,
- Height = input.Person?.Height,
- RelationShip = input.Person?.RelationShip,
- EmployeeId = mappedEmployeeId,
- Type = input.Person.IsEmployee ? input.Person?.Type : null
- };
- await Context.Insertable(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,
- Type = pe.Type
- }).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 = "其他" }
- };
- }
- }
-}
diff --git a/SimpleAdmin.Application/Services/Statistics/Dto/StatisticsDto.cs b/SimpleAdmin.Application/Services/Statistics/Dto/StatisticsDto.cs
deleted file mode 100644
index 93c7653..0000000
--- a/SimpleAdmin.Application/Services/Statistics/Dto/StatisticsDto.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
-//
-// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
-// 1.请不要删除和修改根目录下的LICENSE文件。
-// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
-// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
-
-namespace SimpleAdmin.Application.Services.Statistics
-{
- ///
- /// 年度统计数据 DTO(对应前端 YearlyStatisticsDto)
- ///
- public class YearlyStatisticsDto
- {
- /// 统计年份(如 "2025")
- public string Year { get; set; }
-
- /// 处方总花费(元)
- public decimal TotalCost { get; set; }
-
- /// 处方总数量(张)
- public int TotalQty { get; set; }
-
- /// 员工处方花费(元)
- public decimal StaffCost { get; set; }
-
- /// 家属处方花费(元)
- public decimal FamilyCost { get; set; }
-
- /// 员工处方数量(张)
- public int StaffQty { get; set; }
-
- /// 家属处方数量(张)
- public int FamilyQty { get; set; }
-
- /// 药材耗材花费(元)—— 当年所有入库药材总价
- public decimal MaterialCost { get; set; }
-
- /// 服务人数(当年有过处方记录的不重复人员数)
- public int ServiceCount { get; set; }
-
- // ---------- 同比趋势(百分比,正数为增长,负数为下降) ----------
-
- /// 处方总花费同比趋势 (%)
- public decimal TotalCostTrend { get; set; }
-
- /// 处方总数量同比趋势 (%)
- public decimal TotalQtyTrend { get; set; }
-
- /// 药材花费同比趋势 (%)
- public decimal MaterialCostTrend { get; set; }
-
- /// 服务人数同比趋势 (%)
- public decimal ServiceCountTrend { get; set; }
- }
-
- ///
- /// 月度明细数据 DTO(对应前端 MonthlyDetailDto)
- ///
- public class MonthlyDetailDto
- {
- /// 月份显示(如 "1月")
- public string Month { get; set; }
-
- /// 员工处方花费(元)
- public decimal StaffCost { get; set; }
-
- /// 家属处方花费(元)
- public decimal FamilyCost { get; set; }
-
- /// 员工处方数量(张)
- public int StaffQty { get; set; }
-
- /// 家属处方数量(张)
- public int FamilyQty { get; set; }
-
- /// 药材耗材花费(元)—— 当月入库总价
- public decimal MaterialCost { get; set; }
- }
-
- ///
- /// 全量总览 DTO(不区分年份,含剩余药材价值)
- ///
- public class TotalOverviewDto
- {
- /// 处方总花费(元,含所有年份)
- public decimal TotalCost { get; set; }
-
- /// 处方总数量(张)
- public int TotalQty { get; set; }
-
- /// 员工处方花费(元)
- public decimal StaffCost { get; set; }
-
- /// 家属处方花费(元)
- public decimal FamilyCost { get; set; }
-
- /// 员工处方数量(张)
- public int StaffQty { get; set; }
-
- /// 家属处方数量(张)
- public int FamilyQty { get; set; }
-
- /// 药材采购总花费(元,历史所有入库总额)
- public decimal MaterialCost { get; set; }
-
- /// 累计服务人数(不重复人员)
- public int ServiceCount { get; set; }
-
- /// 当前剩余药材库存价值(元,按最新入库单价估算)
- public decimal RemainingMedicinalValue { get; set; }
- }
-}
diff --git a/SimpleAdmin.Application/Services/Statistics/IStatisticsService.cs b/SimpleAdmin.Application/Services/Statistics/IStatisticsService.cs
deleted file mode 100644
index 0c3a3ed..0000000
--- a/SimpleAdmin.Application/Services/Statistics/IStatisticsService.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
-//
-// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
-// 1.请不要删除和修改根目录下的LICENSE文件。
-// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
-// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
-
-namespace SimpleAdmin.Application.Services.Statistics
-{
- ///
- /// 统计分析 Service 接口
- ///
- public interface IStatisticsService : ITransient
- {
- ///
- /// 获取全量总览数据(不区分年份,含剩余药材库存价值)
- ///
- Task GetTotalOverview();
-
- ///
- /// 获取指定年份的年度汇总统计数据(含同比趋势)
- ///
- /// 年份,如 2025
- Task GetYearlyStatistics(int year);
-
- ///
- /// 获取指定年份的逐月明细数据(返回 12 条,无数据月份补零)
- ///
- /// 年份,如 2025
- Task> GetMonthlyDetails(int year);
- }
-}
diff --git a/SimpleAdmin.Application/Services/Statistics/StatisticsService.cs b/SimpleAdmin.Application/Services/Statistics/StatisticsService.cs
deleted file mode 100644
index 4391105..0000000
--- a/SimpleAdmin.Application/Services/Statistics/StatisticsService.cs
+++ /dev/null
@@ -1,545 +0,0 @@
-// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
-//
-// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
-// 1.请不要删除和修改根目录下的LICENSE文件。
-// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
-// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
-
-using SimpleAdmin.Application.Entity;
-using SimpleAdmin.Application.Services.Statistics;
-
-///
-/// 统计分析 Service 实现
-///
-public class StatisticsService : DbRepository, IStatisticsService
-{
- // ─────────────────────────────────────────────────────────────
- // 公开接口
- // ─────────────────────────────────────────────────────────────
-
- ///
- public async Task GetTotalOverview()
- {
- // 1) 所有处方(按创建时间升序,FIFO 需要)
- var prescriptionInfos = await Context.Queryable()
- .LeftJoin((p, pe) => p.PersonId == pe.Id)
- .OrderBy((p, pe) => p.CreateTime)
- .Select((p, pe) => new
- {
- p.Id,
- p.PersonId,
- IsRelative = pe.EmployeeId != null,
- Type = pe.Type
- })
- .ToListAsync();
-
- decimal staffCost = 0, familyCost = 0;
- int staffQty = 0, familyQty = 0;
-
- if (prescriptionInfos.Any())
- {
- var allIds = prescriptionInfos.Select(x => x.Id).ToList();
-
- var allHerbs = await Context.Queryable()
- .Where(ph => allIds.Contains(ph.PrescriptionId))
- .ToListAsync();
-
- var usageGroups = allHerbs
- .GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
- .Select(g => new
- {
- g.Key.PrescriptionId,
- g.Key.MedicinalId,
- TotalUsedWeight = g.Sum(h => h.AmountG)
- })
- .ToList();
-
- var allMedicinalIds = usageGroups.Select(u => u.MedicinalId).Distinct().ToList();
- var fifoData = await GetFifoData(allMedicinalIds);
- var fifoOffsets = new Dictionary();
-
- var usageByPres = usageGroups
- .GroupBy(u => u.PrescriptionId)
- .ToDictionary(g => g.Key, g => g.ToList());
-
- foreach (var pInfo in prescriptionInfos)
- {
- decimal cost = 0;
- if (usageByPres.TryGetValue(pInfo.Id, out var usages))
- foreach (var u in usages)
- cost += CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
-
- cost = Math.Round(cost, 4, MidpointRounding.AwayFromZero);
-
- if (pInfo.IsRelative)
- {
- familyCost += Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
- familyQty++;
- }
- else if (pInfo.Type == 2)
- {
- staffCost += Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
- staffQty++;
- }
- else
- {
- staffCost += Math.Round(cost, 2, MidpointRounding.AwayFromZero);
- staffQty++;
- }
- }
- }
-
- // 2) 药材采购总花费(历史所有入库总额)
- var materialCost = await Context.Queryable()
- .SumAsync(l => l.TotalPrice);
-
- // 3) 累计服务人数(不重复人员)
- var serviceCount = prescriptionInfos.Select(x => x.PersonId).Distinct().Count();
-
- // 4) 剩余药材库存价值:每种药材当前库存 × 最新入库单价
- var medicinals = await Context.Queryable()
- .Where(m => m.Number > 0)
- .Select(m => new { m.Id, m.Number })
- .ToListAsync();
-
- decimal remainingValue = 0;
- if (medicinals.Any())
- {
- var medIds = medicinals.Select(m => m.Id).ToList();
-
- // 取每种药材最新的一条入库记录单价
- var latestPrices = await Context.Queryable()
- .Where(l => medIds.Contains(l.MedicinalId))
- .GroupBy(l => l.MedicinalId)
- .Select(l => new
- {
- MedicinalId = l.MedicinalId,
- LatestPrice = SqlFunc.AggregateMax(l.UnitPrice)
- })
- .ToListAsync();
-
- var priceDict = latestPrices.ToDictionary(x => x.MedicinalId, x => x.LatestPrice);
-
- foreach (var med in medicinals)
- {
- if (priceDict.TryGetValue(med.Id, out var price))
- remainingValue += Math.Round(med.Number * price, 2, MidpointRounding.AwayFromZero);
- }
- }
-
- return new TotalOverviewDto
- {
- TotalCost = Math.Round(staffCost + familyCost, 2),
- TotalQty = staffQty + familyQty,
- StaffCost = Math.Round(staffCost, 2),
- FamilyCost = Math.Round(familyCost, 2),
- StaffQty = staffQty,
- FamilyQty = familyQty,
- MaterialCost = Math.Round(materialCost, 2),
- ServiceCount = serviceCount,
- RemainingMedicinalValue = Math.Round(remainingValue, 2),
- };
- }
-
- ///
- public async Task GetYearlyStatistics(int year)
- {
- var currentYearData = await BuildYearData(year);
- var lastYearData = await BuildYearData(year - 1);
-
- return new YearlyStatisticsDto
- {
- Year = year.ToString(),
- TotalCost = currentYearData.TotalCost,
- TotalQty = currentYearData.TotalQty,
- StaffCost = currentYearData.StaffCost,
- FamilyCost = currentYearData.FamilyCost,
- StaffQty = currentYearData.StaffQty,
- FamilyQty = currentYearData.FamilyQty,
- MaterialCost = currentYearData.MaterialCost,
- ServiceCount = currentYearData.ServiceCount,
-
- TotalCostTrend = CalcTrend(lastYearData.TotalCost, currentYearData.TotalCost),
- TotalQtyTrend = CalcTrend(lastYearData.TotalQty, currentYearData.TotalQty),
- MaterialCostTrend = CalcTrend(lastYearData.MaterialCost, currentYearData.MaterialCost),
- ServiceCountTrend = CalcTrend(lastYearData.ServiceCount, currentYearData.ServiceCount),
- };
- }
-
- ///
- public async Task> GetMonthlyDetails(int year)
- {
- var start = new DateTime(year, 1, 1);
- var end = new DateTime(year, 12, 31, 23, 59, 59);
-
- // 1) 当年所有处方(按时间升序,供 FIFO 用)
- var prescriptionInfos = await Context.Queryable()
- .LeftJoin((p, pe) => p.PersonId == pe.Id)
- .Where((p, pe) => p.VisitDate >= start && p.VisitDate <= end)
- .OrderBy((p, pe) => p.CreateTime)
- .Select((p, pe) => new
- {
- p.Id,
- p.VisitDate,
- p.CreateTime,
- IsRelative = pe.EmployeeId != null,
- Type = pe.Type
- })
- .ToListAsync();
-
- if (!prescriptionInfos.Any())
- return BuildEmptyMonthList();
-
- var allPrescriptionIds = prescriptionInfos.Select(x => x.Id).ToList();
-
- // 2) 批量拉取处方药材明细
- var allHerbs = await Context.Queryable()
- .Where(ph => allPrescriptionIds.Contains(ph.PrescriptionId))
- .ToListAsync();
-
- // 3) 按 处方ID+药材ID 分组,得到每张处方消耗每种药材的总量
- var usageGroups = allHerbs
- .GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
- .Select(g => new
- {
- g.Key.PrescriptionId,
- g.Key.MedicinalId,
- TotalUsedWeight = g.Sum(h => h.AmountG)
- })
- .ToList();
-
- // 4) FIFO 入库数据
- var allMedicinalIds = usageGroups.Select(u => u.MedicinalId).Distinct().ToList();
- var fifoData = await GetFifoData(allMedicinalIds);
-
- // 5) 需要在"所有历史处方"维度跑 FIFO,才能保证成本正确(与 PrescriptionService 逻辑一致)
- // 先把当年之前的"历史"也加进来跑偏移
- var historyIds = await Context.Queryable()
- .Where(p => p.VisitDate < start)
- .OrderBy(p => p.CreateTime)
- .Select(p => p.Id)
- .ToListAsync();
-
- var fifoOffsets = new Dictionary();
-
- // 先消耗历史处方(建立偏移量),不需要计入结果
- if (historyIds.Any())
- {
- var historyHerbs = await Context.Queryable()
- .Where(ph => historyIds.Contains(ph.PrescriptionId))
- .ToListAsync();
-
- var historyUsage = historyHerbs
- .GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
- .Select(g => new
- {
- g.Key.PrescriptionId,
- g.Key.MedicinalId,
- TotalUsedWeight = g.Sum(h => h.AmountG)
- })
- .ToList();
-
- var historyByPres = historyUsage
- .GroupBy(u => u.PrescriptionId)
- .ToDictionary(g => g.Key, g => g.ToList());
-
- foreach (var hid in historyIds)
- {
- if (!historyByPres.TryGetValue(hid, out var usages)) continue;
- foreach (var u in usages)
- CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
- }
- }
-
- // 6) 计算当年每张处方的成本,按月汇总
- var usageByPres = usageGroups
- .GroupBy(u => u.PrescriptionId)
- .ToDictionary(g => g.Key, g => g.ToList());
-
- // 月度桶:1–12
- var monthlyStaffCost = new decimal[13];
- var monthlyFamilyCost = new decimal[13];
- var monthlyStaffQty = new int[13];
- var monthlyFamilyQty = new int[13];
-
- var presInfoDict = prescriptionInfos.ToDictionary(x => x.Id);
-
- foreach (var pInfo in prescriptionInfos)
- {
- int month = pInfo.VisitDate?.Month ?? pInfo.CreateTime?.Month ?? 0;
- if (month < 1 || month > 12) continue;
-
- decimal cost = 0m;
- if (usageByPres.TryGetValue(pInfo.Id, out var usages))
- {
- foreach (var u in usages)
- cost += CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
- }
- cost = Math.Round(cost, 4, MidpointRounding.AwayFromZero);
-
- if (pInfo.IsRelative)
- {
- cost = Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
- monthlyFamilyCost[month] += cost;
- monthlyFamilyQty[month]++;
- }
- else if (pInfo.Type == 2)
- {
- cost = Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
- monthlyStaffCost[month] += cost;
- monthlyStaffQty[month]++;
- }
- else
- {
- cost = Math.Round(cost, 2, MidpointRounding.AwayFromZero);
- monthlyStaffCost[month] += cost;
- monthlyStaffQty[month]++;
- }
- }
-
- // 7) 药材花费:按入库记录的月份汇总当年花费
- var materialLogs = await Context.Queryable()
- .Where(l => l.CreateTime >= start && l.CreateTime <= end)
- .Select(l => new { l.CreateTime, l.TotalPrice })
- .ToListAsync();
-
- var monthlyMaterialCost = new decimal[13];
- foreach (var log in materialLogs)
- {
- int month = log.CreateTime?.Month ?? 0;
- if (month >= 1 && month <= 12)
- monthlyMaterialCost[month] += log.TotalPrice;
- }
-
- // 8) 组装返回结果
- var result = new List(12);
- for (int m = 1; m <= 12; m++)
- {
- result.Add(new MonthlyDetailDto
- {
- Month = $"{m}月",
- StaffCost = Math.Round(monthlyStaffCost[m], 2),
- FamilyCost = Math.Round(monthlyFamilyCost[m], 2),
- StaffQty = monthlyStaffQty[m],
- FamilyQty = monthlyFamilyQty[m],
- MaterialCost = Math.Round(monthlyMaterialCost[m], 2),
- });
- }
- return result;
- }
-
- // ─────────────────────────────────────────────────────────────
- // 私有辅助方法
- // ─────────────────────────────────────────────────────────────
-
- ///
- /// 计算指定年份的聚合数据(用于年度统计与同比计算)
- ///
- private async Task<(decimal TotalCost, int TotalQty, decimal StaffCost, decimal FamilyCost,
- int StaffQty, int FamilyQty, decimal MaterialCost, int ServiceCount)> BuildYearData(int year)
- {
- var start = new DateTime(year, 1, 1);
- var end = new DateTime(year, 12, 31, 23, 59, 59);
-
- // 当年处方(按时间升序,FIFO 需要)
- var prescriptionInfos = await Context.Queryable()
- .LeftJoin((p, pe) => p.PersonId == pe.Id)
- .Where((p, pe) => p.VisitDate >= start && p.VisitDate <= end)
- .OrderBy((p, pe) => p.CreateTime)
- .Select((p, pe) => new
- {
- p.Id,
- p.PersonId,
- IsRelative = pe.EmployeeId != null,
- Type = pe.Type
- })
- .ToListAsync();
-
- if (!prescriptionInfos.Any())
- return (0, 0, 0, 0, 0, 0, 0, 0);
-
- var allPrescriptionIds = prescriptionInfos.Select(x => x.Id).ToList();
-
- var allHerbs = await Context.Queryable()
- .Where(ph => allPrescriptionIds.Contains(ph.PrescriptionId))
- .ToListAsync();
-
- var usageGroups = allHerbs
- .GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
- .Select(g => new
- {
- g.Key.PrescriptionId,
- g.Key.MedicinalId,
- TotalUsedWeight = g.Sum(h => h.AmountG)
- })
- .ToList();
-
- var allMedicinalIds = usageGroups.Select(u => u.MedicinalId).Distinct().ToList();
- var fifoData = await GetFifoData(allMedicinalIds);
-
- // 历史偏移量(当年之前的处方)
- var historyIds = await Context.Queryable()
- .Where(p => p.VisitDate < start)
- .OrderBy(p => p.CreateTime)
- .Select(p => p.Id)
- .ToListAsync();
-
- var fifoOffsets = new Dictionary();
- if (historyIds.Any())
- {
- var historyHerbs = await Context.Queryable()
- .Where(ph => historyIds.Contains(ph.PrescriptionId))
- .ToListAsync();
-
- var historyUsage = historyHerbs
- .GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
- .Select(g => new
- {
- g.Key.PrescriptionId,
- g.Key.MedicinalId,
- TotalUsedWeight = g.Sum(h => h.AmountG)
- })
- .GroupBy(u => u.PrescriptionId)
- .ToDictionary(g => g.Key, g => g.ToList());
-
- foreach (var hid in historyIds)
- {
- if (!historyUsage.TryGetValue(hid, out var usages)) continue;
- foreach (var u in usages)
- CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
- }
- }
-
- // 按处方计算成本
- var usageByPres = usageGroups
- .GroupBy(u => u.PrescriptionId)
- .ToDictionary(g => g.Key, g => g.ToList());
-
- decimal staffCost = 0, familyCost = 0;
- int staffQty = 0, familyQty = 0;
-
- foreach (var pInfo in prescriptionInfos)
- {
- decimal cost = 0;
- if (usageByPres.TryGetValue(pInfo.Id, out var usages))
- foreach (var u in usages)
- cost += CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
-
- cost = Math.Round(cost, 4, MidpointRounding.AwayFromZero);
-
- if (pInfo.IsRelative)
- {
- familyCost += Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
- familyQty++;
- }
- else if (pInfo.Type == 2)
- {
- staffCost += Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
- staffQty++;
- }
- else
- {
- staffCost += Math.Round(cost, 2, MidpointRounding.AwayFromZero);
- staffQty++;
- }
- }
-
- // 药材花费:当年所有入库记录的总额
- var materialCost = await Context.Queryable()
- .Where(l => l.CreateTime >= start && l.CreateTime <= end)
- .SumAsync(l => l.TotalPrice);
-
- // 服务人数:当年有处方的不重复人员
- var serviceCount = prescriptionInfos.Select(x => x.PersonId).Distinct().Count();
-
- return (
- Math.Round(staffCost + familyCost, 2),
- staffQty + familyQty,
- Math.Round(staffCost, 2),
- Math.Round(familyCost, 2),
- staffQty,
- familyQty,
- Math.Round(materialCost, 2),
- serviceCount
- );
- }
-
- ///
- /// 读取 FIFO 入库数据(按时间升序)
- ///
- private async Task>> GetFifoData(List medicinalIds)
- {
- if (!medicinalIds.Any())
- return new Dictionary>();
-
- var logs = 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();
-
- return logs.GroupBy(l => l.MedicinalId)
- .ToDictionary(g => g.Key, g => g.ToList());
- }
-
- ///
- /// 按 FIFO 规则(含累计偏移)计算单次消耗成本,并推进偏移量
- ///
- private static decimal CalculateFifoCost(
- 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);
- }
-
- /// 计算同比增涨率(%),基期为 0 时返回 0
- private static decimal CalcTrend(T baseVal, T currentVal) where T : struct, IConvertible
- {
- decimal b = Convert.ToDecimal(baseVal);
- decimal c = Convert.ToDecimal(currentVal);
- if (b == 0) return 0;
- return Math.Round((c - b) / b * 100, 1, MidpointRounding.AwayFromZero);
- }
-
- /// 返回全零的 12 个月明细列表
- private static List BuildEmptyMonthList()
- {
- var list = new List(12);
- for (int m = 1; m <= 12; m++)
- list.Add(new MonthlyDetailDto { Month = $"{m}月" });
- return list;
- }
-}
diff --git a/SimpleAdmin.Application/SimpleAdmin.Application.xml b/SimpleAdmin.Application/SimpleAdmin.Application.xml
index 939e70e..a803db0 100644
--- a/SimpleAdmin.Application/SimpleAdmin.Application.xml
+++ b/SimpleAdmin.Application/SimpleAdmin.Application.xml
@@ -54,288 +54,11 @@
排序码
-
-
- 药材
-
-
-
-
- 数量(g)
-
-
-
-
- 药材关联id
-
-
-
-
- 添加重量(g)
-
-
-
-
- 药材单价
-
-
-
-
- 总价
-
-
-
-
- 若非空,表示此 person 是某员工的家属
-
-
-
- 单位 cm,保留两位小数
-
-
- 单位 kg,保留两位小数
-
-
- 消耗克数,单位 g
-
系统配置
-
-
- 未找到下拉框的时候传这个值代表添加新的药材
-
-
-
-
- 获取药材列表,带筛选
-
- 药材名称筛选
- 页码
- 页大小
-
-
-
-
- 获取药材消耗记录
-
- 药材ID
-
-
-
-
- 获取药材下拉
-
-
-
-
-
- 获取药材添加记录
-
- 药材ID
-
-
-
-
- 添加药材
-
- 添加信息
-
-
-
-
- 修改药材添加记录(仅当天可修改)
-
- 记录ID
- 修改信息
-
-
-
-
- 删除药材添加记录(仅当天可删除)
-
- 记录ID
-
-
-
-
- 关联的员工名称
-
-
-
-
- 当前关联的员工是否是前端临时新增(需要后端创建)
-
-
-
-
- 当 EmployeeIsTemp 为 true 或者前端直接传了员工名字时,后端可用此字段创建员工
-
-
-
-
- 获取处方列表,带筛选
-
- 筛选条件
-
-
-
-
- 获取所有处方的成本
-
-
-
-
-
- 获取处方详情
-
- 处方ID
-
-
-
-
- 保存处方(新增或编辑)
-
- 处方信息
-
-
-
-
- 获取人员列表(员工和家属)
-
- 人员类型
- 员工ID(用于获取家属)
-
-
-
-
- 获取所有关系类型(写死在代码中)
-
-
-
-
-
- 年度统计数据 DTO(对应前端 YearlyStatisticsDto)
-
-
-
- 统计年份(如 "2025")
-
-
- 处方总花费(元)
-
-
- 处方总数量(张)
-
-
- 员工处方花费(元)
-
-
- 家属处方花费(元)
-
-
- 员工处方数量(张)
-
-
- 家属处方数量(张)
-
-
- 药材耗材花费(元)—— 当年所有入库药材总价
-
-
- 服务人数(当年有过处方记录的不重复人员数)
-
-
- 处方总花费同比趋势 (%)
-
-
- 处方总数量同比趋势 (%)
-
-
- 药材花费同比趋势 (%)
-
-
- 服务人数同比趋势 (%)
-
-
-
- 月度明细数据 DTO(对应前端 MonthlyDetailDto)
-
-
-
- 月份显示(如 "1月")
-
-
- 员工处方花费(元)
-
-
- 家属处方花费(元)
-
-
- 员工处方数量(张)
-
-
- 家属处方数量(张)
-
-
- 药材耗材花费(元)—— 当月入库总价
-
-
-
- 全量总览 DTO(不区分年份,含剩余药材价值)
-
-
-
- 处方总花费(元,含所有年份)
-
-
- 处方总数量(张)
-
-
- 员工处方花费(元)
-
-
- 家属处方花费(元)
-
-
- 员工处方数量(张)
-
-
- 家属处方数量(张)
-
-
- 药材采购总花费(元,历史所有入库总额)
-
-
- 累计服务人数(不重复人员)
-
-
- 当前剩余药材库存价值(元,按最新入库单价估算)
-
-
-
- 统计分析 Service 接口
-
-
-
-
- 获取全量总览数据(不区分年份,含剩余药材库存价值)
-
-
-
-
- 获取指定年份的年度汇总统计数据(含同比趋势)
-
- 年份,如 2025
-
-
-
- 获取指定年份的逐月明细数据(返回 12 条,无数据月份补零)
-
- 年份,如 2025
-
机构服务
@@ -878,128 +601,5 @@
-
-
- 获取药材下拉
-
-
-
-
-
-
- 获取药材添加记录
-
-
-
-
-
-
- 添加药材
-
-
-
-
-
-
- 修改药材添加记录
-
-
-
-
-
-
-
- 删除药材添加记录
-
-
-
-
-
-
-
- 批量计算:使用成本、剩余价值、总使用量(先进先出法)
-
- 需要计算的药材ID列表
-
-
-
- 获取所有处方的总药材成本
-
-
-
-
-
- 获取药材FIFO计算所需的基础数据(入库记录)
-
-
-
-
- 按FIFO规则计算单种药材的使用成本(累计消耗)
-
-
-
-
- 获取处方详情
-
-
-
-
-
-
-
- 添加编辑处方记录
-
-
-
-
-
-
- 获取 人员下拉
-
-
-
-
-
-
-
- 获取关系下拉
-
-
-
-
-
- 统计分析 Service 实现
-
-
-
-
-
-
-
-
-
-
-
-
-
- 计算指定年份的聚合数据(用于年度统计与同比计算)
-
-
-
-
- 读取 FIFO 入库数据(按时间升序)
-
-
-
-
- 按 FIFO 规则(含累计偏移)计算单次消耗成本,并推进偏移量
-
-
-
- 计算同比增涨率(%),基期为 0 时返回 0
-
-
- 返回全零的 12 个月明细列表
-
diff --git a/SimpleAdmin.SqlSugar/SqlSugar.Development.json b/SimpleAdmin.SqlSugar/SqlSugar.Development.json
index a945d35..65efa72 100644
--- a/SimpleAdmin.SqlSugar/SqlSugar.Development.json
+++ b/SimpleAdmin.SqlSugar/SqlSugar.Development.json
@@ -5,9 +5,9 @@
"ConnectionStrings": [
{
"ConfigId": "SimpleAdmin", //租户ID
- "ConnectionString": "AllowLoadLocalInfile=true;Server=116.168.197.2;User ID=admin;Password=so^867z$4S;Port=16306;Database=WeiBingJu;CharSet=utf8mb4;pooling=true;SslMode=None;min pool size=1;Connection Timeout=30;Pooling=false", //连接字符串
- "DbType": "MySql", //数据库类型 WeiBingJu
- "IsAutoCloseConnection": true //是否自动释放 weibingjutest
+ "ConnectionString": "AllowLoadLocalInfile=true;Server=116.168.197.2;User ID=admin;Password=so^867z$4S;Port=16306;Database=OnboardX;CharSet=utf8mb4;pooling=true;SslMode=None;min pool size=1;Connection Timeout=30;Pooling=false", //连接字符串
+ "DbType": "MySql", //数据库类型
+ "IsAutoCloseConnection": true //是否自动释放
}
// {
// "ConfigId": "SimpleAdmin", //租户ID
diff --git a/SimpleAdmin.SqlSugar/SqlSugar.Production.json b/SimpleAdmin.SqlSugar/SqlSugar.Production.json
index 94418c9..85d62ac 100644
--- a/SimpleAdmin.SqlSugar/SqlSugar.Production.json
+++ b/SimpleAdmin.SqlSugar/SqlSugar.Production.json
@@ -5,7 +5,7 @@
"ConnectionStrings": [
{
"ConfigId": "SimpleAdmin", //租户ID
- "ConnectionString": "AllowLoadLocalInfile=true;Server=116.168.197.2;User ID=admin;Password=so^867z$4S;Port=16306;Database=WeiBingJu;CharSet=utf8mb4;pooling=true;SslMode=None;min pool size=1;Connection Timeout=30;Pooling=false", //连接字符串
+ "ConnectionString": "AllowLoadLocalInfile=true;Server=116.168.197.2;User ID=admin;Password=so^867z$4S;Port=16306;Database=OnboardX;CharSet=utf8mb4;pooling=true;SslMode=None;min pool size=1;Connection Timeout=30;Pooling=false", //连接字符串
"DbType": "MySql", //数据库类型
"IsAutoCloseConnection": true //是否自动释放
}
diff --git a/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/MedicinalController.cs b/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/MedicinalController.cs
deleted file mode 100644
index 6033c42..0000000
--- a/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/MedicinalController.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-
-namespace SimpleAdmin.Web.Core;
-
-///
-/// 药材管理
-///
-[ApiDescriptionSettings("Application", Tag = "药材管理")]
-[Route("biz/weibingju/medicinal")]
-[RolePermission]
-public class MedicinalController : IDynamicApiController
-{
- private readonly IMedicinalService _medicinalService;
-
- public MedicinalController(IMedicinalService medicinalService)
- {
- _medicinalService = medicinalService;
- }
-
- ///
- /// 获取药材列表
- ///
- [HttpGet("list")]
- [DisplayName("获取药材列表")]
- public async Task GetList([FromQuery] string medicinalName, [FromQuery] int pageNum = 1, [FromQuery] int pageSize = 10)
- {
- return await _medicinalService.GetMedicinalList(medicinalName, pageNum, pageSize);
- }
-
- ///
- /// 获取药材消耗记录
- ///
- [HttpGet("consumption-records")]
- [DisplayName("获取药材消耗记录")]
- public async Task GetConsumptionRecords([FromQuery] long id, [FromQuery] int pageNum = 1, [FromQuery] int pageSize = 10)
- {
- return await _medicinalService.GetConsumptionRecords(id, pageNum, pageSize);
- }
-
- ///
- /// 获取药材下拉
- ///
- [HttpGet("getmedicinaldictlist")]
- [DisplayName("获取药材下拉")]
- public async Task GetMedicinalDictList()
- {
- return await _medicinalService.GetMedicinalDictList();
- }
-
- ///
- /// 获取药材添加记录
- ///
- [HttpGet("addition-records")]
- [DisplayName("获取药材添加记录")]
- public async Task GetAdditionRecords(long id, [FromQuery] int pageNum = 1, [FromQuery] int pageSize = 10)
- {
- return await _medicinalService.GetAdditionRecords(id, pageNum, pageSize);
- }
-
- ///
- /// 添加药材
- ///
- [HttpPost("add")]
- [DisplayName("添加药材")]
- public async Task AddMedicinal([FromBody] AddMedicinalInput input)
- {
- return await _medicinalService.AddMedicinal(input);
- }
-
- ///
- /// 修改药材添加记录
- ///
- [HttpPost("addition-records")]
- [DisplayName("修改药材添加记录")]
- public async Task UpdateAdditionRecord([FromBody] UpdateMedicinalAddLogInput input)
- {
- return await _medicinalService.UpdateAdditionRecord(input);
- }
-
- ///
- /// 删除药材添加记录
- ///
- [HttpPost("addition-delete")]
- [DisplayName("删除药材添加记录")]
- public async Task DeleteAdditionRecord(DeleteMedicinalLog input)
- {
- return await _medicinalService.DeleteAdditionRecord(input.Id);
- }
-}
diff --git a/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/PrescriptionController.cs b/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/PrescriptionController.cs
deleted file mode 100644
index 6b86a28..0000000
--- a/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/PrescriptionController.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
-//
-// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
-// 1.请不要删除和修改根目录下的LICENSE文件。
-// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
-// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
-// 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
-// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
-// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
-
-namespace SimpleAdmin.Web.Core;
-
-///
-/// 药材管理
-///
-[ApiDescriptionSettings("Application", Tag = "处方管理")]
-[Route("biz/weibingju/prescription")]
-[RolePermission]
-public class PrescriptionController : IDynamicApiController
-{
- private readonly IPrescriptionService _service;
-
- public PrescriptionController(IPrescriptionService service)
- {
- _service = service;
- }
-
- ///
- /// 获取处方列表
- ///
- [HttpGet("list")]
- [DisplayName("获取处方列表")]
- public async Task GetList([FromQuery] PrescriptionQueryInput input)
- {
- return await _service.GetPrescriptionList(input);
- }
-
- ///
- /// 获取处方总列表使用成本
- ///
- [HttpGet("gettotalcost")]
- [DisplayName("获取处方总列表使用成本")]
- public async Task GetAllPrescriptionTotalCost()
- {
- return await _service.GetAllPrescriptionTotalCost();
- }
-
- ///
- /// 获取处方详情
- ///
- [HttpGet("getprescriptiondetile")]
- [DisplayName("获取处方详情")]
- public async Task GetDetail(long id)
- {
- return await _service.GetPrescriptionDetail(id);
- }
-
- ///
- /// 保存处方(新增或编辑)
- ///
- [HttpPost("add")]
- [DisplayName("保存处方(新增或编辑)")]
- public async Task SavePrescription([FromBody] SavePrescriptionInput input)
- {
- return await _service.SavePrescription(input);
- }
-
- ///
- /// 获取人员列表
- ///
- [HttpGet("persons")]
- [DisplayName("获取人员列表")]
- public async Task GetPersonList([FromQuery] PersonType? type, [FromQuery] long? employeeId)
- {
- return await _service.GetPersonList(type, employeeId);
- }
-
- ///
- /// 获取关系类型
- ///
- [HttpGet("relation-types")]
- [DisplayName("获取关系类型")]
- public async Task GetRelationTypes()
- {
- return await _service.GetRelationTypes();
- }
-}
diff --git a/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/StatisticsController.cs b/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/StatisticsController.cs
deleted file mode 100644
index 2bfd80f..0000000
--- a/SimpleAdmin.Web.Core/Controllers/Application/WeiBingJu/StatisticsController.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
-//
-// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
-// 1.请不要删除和修改根目录下的LICENSE文件。
-// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
-// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
-
-using MoYu.FriendlyException;
-using SimpleAdmin.Application.Services.Statistics;
-
-namespace SimpleAdmin.Web.Core;
-
-///
-/// 统计分析
-///
-[ApiDescriptionSettings("Application", Tag = "统计分析")]
-[Route("biz/weibingju/statistics")]
-[RolePermission]
-public class StatisticsController : IDynamicApiController
-{
- private readonly IStatisticsService _service;
-
- public StatisticsController(IStatisticsService service)
- {
- _service = service;
- }
-
- ///
- /// 获取全量总览数据(不区分年份,含剩余药材库存价值)
- ///
- [HttpGet("overview")]
- [DisplayName("获取全量总览数据")]
- public async Task GetTotalOverview()
- {
- return await _service.GetTotalOverview();
- }
-
- ///
- /// 获取年度统计数据(含同比趋势)
- ///
- [HttpGet("yearly")]
- [DisplayName("获取年度统计数据")]
- public async Task GetYearlyStatistics([FromQuery] string year)
- {
- if (!int.TryParse(year, out int yearInt) || yearInt < 2000 || yearInt > 2100)
- throw Oops.Bah("年份参数不合法");
-
- return await _service.GetYearlyStatistics(yearInt);
- }
-
- ///
- /// 获取月度明细数据(返回 12 个月,无数据月份自动补零)
- ///
- [HttpGet("monthly")]
- [DisplayName("获取月度明细数据")]
- public async Task GetMonthlyDetails([FromQuery] string year)
- {
- if (!int.TryParse(year, out int yearInt) || yearInt < 2000 || yearInt > 2100)
- throw Oops.Bah("年份参数不合法");
-
- return await _service.GetMonthlyDetails(yearInt);
- }
-}
diff --git a/SimpleAdmin.Web.Core/GlobalUsing.cs b/SimpleAdmin.Web.Core/GlobalUsing.cs
index 4ef2f44..aba71bf 100644
--- a/SimpleAdmin.Web.Core/GlobalUsing.cs
+++ b/SimpleAdmin.Web.Core/GlobalUsing.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
+// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
//
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。
@@ -40,7 +40,3 @@ global using Microsoft.AspNetCore.Hosting;
global using IP2Region.Net.Abstractions;
global using IP2Region.Net.XDB;
global using NewLife;
-global using SimpleAdmin.Application.Services.Medicinal;
-global using SimpleAdmin.Application.Services.Medicinal.Dto;
-global using SimpleAdmin.Application.Services.Prescription;
-global using SimpleAdmin.Application.Services.Prescription.Dto;
diff --git a/SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml b/SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml
index d5f8800..9804993 100644
--- a/SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml
+++ b/SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml
@@ -345,101 +345,6 @@
-
-
- 药材管理
-
-
-
-
- 获取药材列表
-
-
-
-
- 获取药材消耗记录
-
-
-
-
- 获取药材下拉
-
-
-
-
- 获取药材添加记录
-
-
-
-
- 添加药材
-
-
-
-
- 修改药材添加记录
-
-
-
-
- 删除药材添加记录
-
-
-
-
- 药材管理
-
-
-
-
- 获取处方列表
-
-
-
-
- 获取处方总列表使用成本
-
-
-
-
- 获取处方详情
-
-
-
-
- 保存处方(新增或编辑)
-
-
-
-
- 获取人员列表
-
-
-
-
- 获取关系类型
-
-
-
-
- 统计分析
-
-
-
-
- 获取全量总览数据(不区分年份,含剩余药材库存价值)
-
-
-
-
- 获取年度统计数据(含同比趋势)
-
-
-
-
- 获取月度明细数据(返回 12 个月,无数据月份自动补零)
-
-
B端登录控制器