diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchCreateInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchCreateInput.cs
new file mode 100644
index 0000000..7c1b5b6
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchCreateInput.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqInventoryUsage
+{
+ ///
+ /// 库存使用记录批量创建输入
+ ///
+ public class LqInventoryUsageBatchCreateInput
+ {
+ ///
+ /// 使用记录列表
+ ///
+ [Required(ErrorMessage = "使用记录列表不能为空")]
+ [MinLength(1, ErrorMessage = "至少需要添加一条使用记录")]
+ [Display(Name = "使用记录列表")]
+ public List UsageItems { get; set; }
+
+ ///
+ /// 使用批次ID(可选,不传则自动生成)
+ ///
+ [StringLength(50, ErrorMessage = "批次ID长度不能超过50个字符")]
+ [Display(Name = "使用批次ID")]
+ public string BatchId { get; set; }
+ }
+
+ ///
+ /// 库存使用记录项输入
+ ///
+ public class LqInventoryUsageItemInput
+ {
+ ///
+ /// 产品ID
+ ///
+ [Required(ErrorMessage = "产品ID不能为空")]
+ [StringLength(50, ErrorMessage = "产品ID长度不能超过50个字符")]
+ [Display(Name = "产品ID")]
+ public string ProductId { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ [Required(ErrorMessage = "门店ID不能为空")]
+ [StringLength(50, ErrorMessage = "门店ID长度不能超过50个字符")]
+ [Display(Name = "门店ID")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 使用时间
+ ///
+ [Required(ErrorMessage = "使用时间不能为空")]
+ [Display(Name = "使用时间")]
+ public DateTime UsageTime { get; set; }
+
+ ///
+ /// 使用数量
+ ///
+ [Required(ErrorMessage = "使用数量不能为空")]
+ [Range(1, int.MaxValue, ErrorMessage = "使用数量必须大于0")]
+ [Display(Name = "使用数量")]
+ public int UsageQuantity { get; set; }
+
+ ///
+ /// 关联消耗ID(可选)
+ ///
+ [StringLength(50, ErrorMessage = "关联消耗ID长度不能超过50个字符")]
+ [Display(Name = "关联消耗ID")]
+ public string RelatedConsumeId { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchCreateOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchCreateOutput.cs
new file mode 100644
index 0000000..def66e4
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchCreateOutput.cs
@@ -0,0 +1,58 @@
+using System.Collections.Generic;
+
+namespace NCC.Extend.Entitys.Dto.LqInventoryUsage
+{
+ ///
+ /// 库存使用记录批量创建输出
+ ///
+ public class LqInventoryUsageBatchCreateOutput
+ {
+ ///
+ /// 批次ID
+ ///
+ public string BatchId { get; set; }
+
+ ///
+ /// 成功创建的数量
+ ///
+ public int SuccessCount { get; set; }
+
+ ///
+ /// 失败的数量
+ ///
+ public int FailCount { get; set; }
+
+ ///
+ /// 创建成功的记录ID列表
+ ///
+ public List SuccessIds { get; set; }
+
+ ///
+ /// 失败详情列表
+ ///
+ public List FailItems { get; set; }
+ }
+
+ ///
+ /// 批量创建失败项
+ ///
+ public class BatchCreateFailItem
+ {
+ ///
+ /// 失败索引(对应输入列表的索引)
+ ///
+ public int Index { get; set; }
+
+ ///
+ /// 产品ID
+ ///
+ public string ProductId { get; set; }
+
+ ///
+ /// 失败原因
+ ///
+ public string Reason { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchInfoOutput.cs
new file mode 100644
index 0000000..b60804e
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchInfoOutput.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+
+namespace NCC.Extend.Entitys.Dto.LqInventoryUsage
+{
+ ///
+ /// 库存使用记录批次信息输出
+ ///
+ public class LqInventoryUsageBatchInfoOutput
+ {
+ ///
+ /// 批次ID
+ ///
+ public string BatchId { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ public DateTime CreateTime { get; set; }
+
+ ///
+ /// 创建人ID
+ ///
+ public string CreateUser { get; set; }
+
+ ///
+ /// 创建人姓名
+ ///
+ public string CreateUserName { get; set; }
+
+ ///
+ /// 记录总数
+ ///
+ public int TotalCount { get; set; }
+
+ ///
+ /// 有效记录数
+ ///
+ public int EffectiveCount { get; set; }
+
+ ///
+ /// 无效记录数
+ ///
+ public int IneffectiveCount { get; set; }
+
+ ///
+ /// 使用总数量
+ ///
+ public int TotalUsageQuantity { get; set; }
+
+ ///
+ /// 使用总金额
+ ///
+ public decimal TotalUsageAmount { get; set; }
+
+ ///
+ /// 使用记录列表
+ ///
+ public List UsageRecords { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs
index 3891c30..9f1b9e1 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs
@@ -96,5 +96,10 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsage
/// 使用总价值
///
public decimal usageTotalValue { get; set; }
+
+ ///
+ /// 使用批次ID(同一批次申请的使用记录使用相同的批次ID)
+ ///
+ public string usageBatchId { get; set; }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListQueryInput.cs
index 23e1ea3..b63961b 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListQueryInput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListQueryInput.cs
@@ -51,5 +51,10 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsage
/// 是否有效
///
public int? IsEffective { get; set; }
+
+ ///
+ /// 使用批次ID(用于查询同一批次的所有记录)
+ ///
+ public string UsageBatchId { get; set; }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs
index b5669e6..f9955be 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs
@@ -28,6 +28,16 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
public string EndBillingTime { get; set; }
///
+ /// 开始时间(兼容参数名)
+ ///
+ public string startTime { get; set; }
+
+ ///
+ /// 结束时间(兼容参数名)
+ ///
+ public string endTime { get; set; }
+
+ ///
/// 营销活动ID
///
public string ActivityId { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LaundryStatisticsInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LaundryStatisticsInput.cs
new file mode 100644
index 0000000..7fcec65
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LaundryStatisticsInput.cs
@@ -0,0 +1,36 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundryFlow
+{
+ ///
+ /// 清洗费用统计输入
+ ///
+ public class LaundryStatisticsInput
+ {
+ ///
+ /// 开始月份(格式:YYYYMM,如202411)
+ ///
+ [Display(Name = "开始月份")]
+ public string StartMonth { get; set; }
+
+ ///
+ /// 结束月份(格式:YYYYMM,如202412)
+ ///
+ [Display(Name = "结束月份")]
+ public string EndMonth { get; set; }
+
+ ///
+ /// 门店ID(可选,门店统计时使用)
+ ///
+ [Display(Name = "门店ID")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 产品类型(可选,产品统计时使用)
+ ///
+ [Display(Name = "产品类型")]
+ public string ProductType { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowDifferenceOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowDifferenceOutput.cs
new file mode 100644
index 0000000..a2f317d
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowDifferenceOutput.cs
@@ -0,0 +1,73 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundryFlow
+{
+ ///
+ /// 清洗流水差异输出(送出数量 > 送回数量)
+ ///
+ public class LqLaundryFlowDifferenceOutput
+ {
+ ///
+ /// 批次号
+ ///
+ [Display(Name = "批次号")]
+ public string batchNumber { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ [Display(Name = "门店ID")]
+ public string storeId { get; set; }
+
+ ///
+ /// 门店名称
+ ///
+ [Display(Name = "门店名称")]
+ public string storeName { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型")]
+ public string productType { get; set; }
+
+ ///
+ /// 送出数量
+ ///
+ [Display(Name = "送出数量")]
+ public int sendQuantity { get; set; }
+
+ ///
+ /// 送回数量
+ ///
+ [Display(Name = "送回数量")]
+ public int returnQuantity { get; set; }
+
+ ///
+ /// 差异数量
+ ///
+ [Display(Name = "差异数量")]
+ public int differenceQuantity { get; set; }
+
+ ///
+ /// 差异说明(备注)
+ ///
+ [Display(Name = "差异说明")]
+ public string differenceRemark { get; set; }
+
+ ///
+ /// 送出时间
+ ///
+ [Display(Name = "送出时间")]
+ public DateTime? sendTime { get; set; }
+
+ ///
+ /// 送回时间
+ ///
+ [Display(Name = "送回时间")]
+ public DateTime? returnTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowInfoOutput.cs
new file mode 100644
index 0000000..09071e6
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowInfoOutput.cs
@@ -0,0 +1,115 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundryFlow
+{
+ ///
+ /// 清洗流水详情输出
+ ///
+ public class LqLaundryFlowInfoOutput
+ {
+ ///
+ /// 主键ID
+ ///
+ [Display(Name = "ID")]
+ public string id { get; set; }
+
+ ///
+ /// 流水类型(0:送出 1:送回)
+ ///
+ [Display(Name = "流水类型")]
+ public int flowType { get; set; }
+
+ ///
+ /// 流水类型名称
+ ///
+ [Display(Name = "流水类型名称")]
+ public string flowTypeName { get; set; }
+
+ ///
+ /// 批次号
+ ///
+ [Display(Name = "批次号")]
+ public string batchNumber { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ [Display(Name = "门店ID")]
+ public string storeId { get; set; }
+
+ ///
+ /// 门店名称
+ ///
+ [Display(Name = "门店名称")]
+ public string storeName { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型")]
+ public string productType { get; set; }
+
+ ///
+ /// 清洗商ID
+ ///
+ [Display(Name = "清洗商ID")]
+ public string laundrySupplierId { get; set; }
+
+ ///
+ /// 清洗商名称
+ ///
+ [Display(Name = "清洗商名称")]
+ public string laundrySupplierName { get; set; }
+
+ ///
+ /// 数量
+ ///
+ [Display(Name = "数量")]
+ public int quantity { get; set; }
+
+ ///
+ /// 清洗单价
+ ///
+ [Display(Name = "清洗单价")]
+ public decimal laundryPrice { get; set; }
+
+ ///
+ /// 总费用
+ ///
+ [Display(Name = "总费用")]
+ public decimal totalPrice { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Display(Name = "备注")]
+ public string remark { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效")]
+ public int isEffective { get; set; }
+
+ ///
+ /// 创建人ID
+ ///
+ [Display(Name = "创建人ID")]
+ public string createUser { get; set; }
+
+ ///
+ /// 创建人姓名
+ ///
+ [Display(Name = "创建人姓名")]
+ public string createUserName { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [Display(Name = "创建时间")]
+ public DateTime createTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowListOutput.cs
new file mode 100644
index 0000000..e0176a3
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowListOutput.cs
@@ -0,0 +1,115 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundryFlow
+{
+ ///
+ /// 清洗流水列表输出
+ ///
+ public class LqLaundryFlowListOutput
+ {
+ ///
+ /// 主键ID
+ ///
+ [Display(Name = "ID")]
+ public string id { get; set; }
+
+ ///
+ /// 流水类型(0:送出 1:送回)
+ ///
+ [Display(Name = "流水类型")]
+ public int flowType { get; set; }
+
+ ///
+ /// 流水类型名称
+ ///
+ [Display(Name = "流水类型名称")]
+ public string flowTypeName { get; set; }
+
+ ///
+ /// 批次号
+ ///
+ [Display(Name = "批次号")]
+ public string batchNumber { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ [Display(Name = "门店ID")]
+ public string storeId { get; set; }
+
+ ///
+ /// 门店名称
+ ///
+ [Display(Name = "门店名称")]
+ public string storeName { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型")]
+ public string productType { get; set; }
+
+ ///
+ /// 清洗商ID
+ ///
+ [Display(Name = "清洗商ID")]
+ public string laundrySupplierId { get; set; }
+
+ ///
+ /// 清洗商名称
+ ///
+ [Display(Name = "清洗商名称")]
+ public string laundrySupplierName { get; set; }
+
+ ///
+ /// 数量
+ ///
+ [Display(Name = "数量")]
+ public int quantity { get; set; }
+
+ ///
+ /// 清洗单价
+ ///
+ [Display(Name = "清洗单价")]
+ public decimal laundryPrice { get; set; }
+
+ ///
+ /// 总费用
+ ///
+ [Display(Name = "总费用")]
+ public decimal totalPrice { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Display(Name = "备注")]
+ public string remark { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效")]
+ public int isEffective { get; set; }
+
+ ///
+ /// 创建人ID
+ ///
+ [Display(Name = "创建人ID")]
+ public string createUser { get; set; }
+
+ ///
+ /// 创建人姓名
+ ///
+ [Display(Name = "创建人姓名")]
+ public string createUserName { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [Display(Name = "创建时间")]
+ public DateTime createTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowListQueryInput.cs
new file mode 100644
index 0000000..116933a
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowListQueryInput.cs
@@ -0,0 +1,62 @@
+using NCC.Common.Filter;
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundryFlow
+{
+ ///
+ /// 清洗流水列表查询输入
+ ///
+ public class LqLaundryFlowListQueryInput : PageInputBase
+ {
+ ///
+ /// 流水类型(0:送出 1:送回)
+ ///
+ [Display(Name = "流水类型", Description = "根据流水类型筛选")]
+ public int? FlowType { get; set; }
+
+ ///
+ /// 批次号
+ ///
+ [Display(Name = "批次号", Description = "根据批次号筛选")]
+ public string BatchNumber { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ [Display(Name = "门店ID", Description = "根据门店ID筛选")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型", Description = "根据产品类型筛选")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 清洗商ID
+ ///
+ [Display(Name = "清洗商ID", Description = "根据清洗商ID筛选")]
+ public string LaundrySupplierId { get; set; }
+
+ ///
+ /// 开始时间
+ ///
+ [Display(Name = "开始时间", Description = "根据创建时间范围筛选")]
+ public DateTime? StartTime { get; set; }
+
+ ///
+ /// 结束时间
+ ///
+ [Display(Name = "结束时间", Description = "根据创建时间范围筛选")]
+ public DateTime? EndTime { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效", Description = "根据是否有效筛选")]
+ public int? IsEffective { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowReturnInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowReturnInput.cs
new file mode 100644
index 0000000..d3b642a
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowReturnInput.cs
@@ -0,0 +1,43 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundryFlow
+{
+ ///
+ /// 清洗流水送回输入
+ ///
+ public class LqLaundryFlowReturnInput
+ {
+ ///
+ /// 批次号(对应的送出记录的ID)
+ ///
+ [Required(ErrorMessage = "批次号不能为空")]
+ [StringLength(50, ErrorMessage = "批次号长度不能超过50个字符")]
+ [Display(Name = "批次号")]
+ public string BatchNumber { get; set; }
+
+ ///
+ /// 清洗商ID(可能和送出的清洗商不同)
+ ///
+ [Required(ErrorMessage = "清洗商ID不能为空")]
+ [StringLength(50, ErrorMessage = "清洗商ID长度不能超过50个字符")]
+ [Display(Name = "清洗商ID")]
+ public string LaundrySupplierId { get; set; }
+
+ ///
+ /// 送回数量
+ ///
+ [Required(ErrorMessage = "送回数量不能为空")]
+ [Range(0, int.MaxValue, ErrorMessage = "送回数量不能小于0")]
+ [Display(Name = "送回数量")]
+ public int Quantity { get; set; }
+
+ ///
+ /// 备注(可说明差异原因,如损坏、丢失等)
+ ///
+ [StringLength(1000, ErrorMessage = "备注长度不能超过1000个字符")]
+ [Display(Name = "备注")]
+ public string Remark { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowSendInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowSendInput.cs
new file mode 100644
index 0000000..d647b0c
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowSendInput.cs
@@ -0,0 +1,51 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundryFlow
+{
+ ///
+ /// 清洗流水送出输入
+ ///
+ public class LqLaundryFlowSendInput
+ {
+ ///
+ /// 门店ID
+ ///
+ [Required(ErrorMessage = "门店ID不能为空")]
+ [StringLength(50, ErrorMessage = "门店ID长度不能超过50个字符")]
+ [Display(Name = "门店ID")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 产品类型(枚举:毛巾、垫子等)
+ ///
+ [Required(ErrorMessage = "产品类型不能为空")]
+ [StringLength(50, ErrorMessage = "产品类型长度不能超过50个字符")]
+ [Display(Name = "产品类型")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 清洗商ID
+ ///
+ [Required(ErrorMessage = "清洗商ID不能为空")]
+ [StringLength(50, ErrorMessage = "清洗商ID长度不能超过50个字符")]
+ [Display(Name = "清洗商ID")]
+ public string LaundrySupplierId { get; set; }
+
+ ///
+ /// 送出数量
+ ///
+ [Required(ErrorMessage = "送出数量不能为空")]
+ [Range(1, int.MaxValue, ErrorMessage = "送出数量必须大于0")]
+ [Display(Name = "送出数量")]
+ public int Quantity { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [StringLength(1000, ErrorMessage = "备注长度不能超过1000个字符")]
+ [Display(Name = "备注")]
+ public string Remark { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryStatisticsOutput.cs
new file mode 100644
index 0000000..6866110
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryStatisticsOutput.cs
@@ -0,0 +1,49 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundryFlow
+{
+ ///
+ /// 清洗费用统计输出
+ ///
+ public class LqLaundryStatisticsOutput
+ {
+ ///
+ /// 门店ID(门店统计时使用)
+ ///
+ [Display(Name = "门店ID")]
+ public string storeId { get; set; }
+
+ ///
+ /// 门店名称(门店统计时使用)
+ ///
+ [Display(Name = "门店名称")]
+ public string storeName { get; set; }
+
+ ///
+ /// 产品类型(产品统计时使用)
+ ///
+ [Display(Name = "产品类型")]
+ public string productType { get; set; }
+
+ ///
+ /// 统计月份(格式:YYYYMM)
+ ///
+ [Display(Name = "统计月份")]
+ public string statisticsMonth { get; set; }
+
+ ///
+ /// 总费用
+ ///
+ [Display(Name = "总费用")]
+ public decimal totalPrice { get; set; }
+
+ ///
+ /// 清洗次数
+ ///
+ [Display(Name = "清洗次数")]
+ public int count { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierCrInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierCrInput.cs
new file mode 100644
index 0000000..6996fef
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierCrInput.cs
@@ -0,0 +1,43 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundrySupplier
+{
+ ///
+ /// 清洗商创建输入
+ ///
+ public class LqLaundrySupplierCrInput
+ {
+ ///
+ /// 清洗商名称
+ ///
+ [Required(ErrorMessage = "清洗商名称不能为空")]
+ [StringLength(200, ErrorMessage = "清洗商名称长度不能超过200个字符")]
+ [Display(Name = "清洗商名称")]
+ public string SupplierName { get; set; }
+
+ ///
+ /// 产品类型(枚举:毛巾、垫子等)
+ ///
+ [Required(ErrorMessage = "产品类型不能为空")]
+ [StringLength(50, ErrorMessage = "产品类型长度不能超过50个字符")]
+ [Display(Name = "产品类型")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 清洗价格
+ ///
+ [Required(ErrorMessage = "清洗价格不能为空")]
+ [Range(0, double.MaxValue, ErrorMessage = "清洗价格不能小于0")]
+ [Display(Name = "清洗价格")]
+ public decimal LaundryPrice { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [StringLength(1000, ErrorMessage = "备注长度不能超过1000个字符")]
+ [Display(Name = "备注")]
+ public string Remark { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierInfoOutput.cs
new file mode 100644
index 0000000..acfd131
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierInfoOutput.cs
@@ -0,0 +1,85 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundrySupplier
+{
+ ///
+ /// 清洗商详情输出
+ ///
+ public class LqLaundrySupplierInfoOutput
+ {
+ ///
+ /// 主键ID
+ ///
+ [Display(Name = "ID")]
+ public string id { get; set; }
+
+ ///
+ /// 清洗商名称
+ ///
+ [Display(Name = "清洗商名称")]
+ public string supplierName { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型")]
+ public string productType { get; set; }
+
+ ///
+ /// 清洗价格
+ ///
+ [Display(Name = "清洗价格")]
+ public decimal laundryPrice { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Display(Name = "备注")]
+ public string remark { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效")]
+ public int isEffective { get; set; }
+
+ ///
+ /// 创建人ID
+ ///
+ [Display(Name = "创建人ID")]
+ public string createUser { get; set; }
+
+ ///
+ /// 创建人姓名
+ ///
+ [Display(Name = "创建人姓名")]
+ public string createUserName { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [Display(Name = "创建时间")]
+ public DateTime createTime { get; set; }
+
+ ///
+ /// 更新人ID
+ ///
+ [Display(Name = "更新人ID")]
+ public string updateUser { get; set; }
+
+ ///
+ /// 更新人姓名
+ ///
+ [Display(Name = "更新人姓名")]
+ public string updateUserName { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ [Display(Name = "更新时间")]
+ public DateTime? updateTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierListOutput.cs
new file mode 100644
index 0000000..efc5efc
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierListOutput.cs
@@ -0,0 +1,85 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundrySupplier
+{
+ ///
+ /// 清洗商列表输出
+ ///
+ public class LqLaundrySupplierListOutput
+ {
+ ///
+ /// 主键ID
+ ///
+ [Display(Name = "ID")]
+ public string id { get; set; }
+
+ ///
+ /// 清洗商名称
+ ///
+ [Display(Name = "清洗商名称")]
+ public string supplierName { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型")]
+ public string productType { get; set; }
+
+ ///
+ /// 清洗价格
+ ///
+ [Display(Name = "清洗价格")]
+ public decimal laundryPrice { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Display(Name = "备注")]
+ public string remark { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效")]
+ public int isEffective { get; set; }
+
+ ///
+ /// 创建人ID
+ ///
+ [Display(Name = "创建人ID")]
+ public string createUser { get; set; }
+
+ ///
+ /// 创建人姓名
+ ///
+ [Display(Name = "创建人姓名")]
+ public string createUserName { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [Display(Name = "创建时间")]
+ public DateTime createTime { get; set; }
+
+ ///
+ /// 更新人ID
+ ///
+ [Display(Name = "更新人ID")]
+ public string updateUser { get; set; }
+
+ ///
+ /// 更新人姓名
+ ///
+ [Display(Name = "更新人姓名")]
+ public string updateUserName { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ [Display(Name = "更新时间")]
+ public DateTime? updateTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierListQueryInput.cs
new file mode 100644
index 0000000..c60d6f4
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierListQueryInput.cs
@@ -0,0 +1,31 @@
+using NCC.Common.Filter;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundrySupplier
+{
+ ///
+ /// 清洗商列表查询输入
+ ///
+ public class LqLaundrySupplierListQueryInput : PageInputBase
+ {
+ ///
+ /// 清洗商名称(模糊查询)
+ ///
+ [Display(Name = "清洗商名称", Description = "根据清洗商名称筛选")]
+ public string SupplierName { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型", Description = "根据产品类型筛选")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效", Description = "根据是否有效筛选")]
+ public int? IsEffective { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierUpInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierUpInput.cs
new file mode 100644
index 0000000..ba744c5
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundrySupplier/LqLaundrySupplierUpInput.cs
@@ -0,0 +1,50 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqLaundrySupplier
+{
+ ///
+ /// 清洗商更新输入
+ ///
+ public class LqLaundrySupplierUpInput
+ {
+ ///
+ /// 主键ID
+ ///
+ [Required(ErrorMessage = "ID不能为空")]
+ [Display(Name = "ID")]
+ public string Id { get; set; }
+
+ ///
+ /// 清洗商名称
+ ///
+ [Required(ErrorMessage = "清洗商名称不能为空")]
+ [StringLength(200, ErrorMessage = "清洗商名称长度不能超过200个字符")]
+ [Display(Name = "清洗商名称")]
+ public string SupplierName { get; set; }
+
+ ///
+ /// 产品类型(枚举:毛巾、垫子等)
+ ///
+ [Required(ErrorMessage = "产品类型不能为空")]
+ [StringLength(50, ErrorMessage = "产品类型长度不能超过50个字符")]
+ [Display(Name = "产品类型")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 清洗价格
+ ///
+ [Required(ErrorMessage = "清洗价格不能为空")]
+ [Range(0, double.MaxValue, ErrorMessage = "清洗价格不能小于0")]
+ [Display(Name = "清洗价格")]
+ public decimal LaundryPrice { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [StringLength(1000, ErrorMessage = "备注长度不能超过1000个字符")]
+ [Display(Name = "备注")]
+ public string Remark { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/GoddessCardMemberListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/GoddessCardMemberListQueryInput.cs
new file mode 100644
index 0000000..cc62942
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/GoddessCardMemberListQueryInput.cs
@@ -0,0 +1,19 @@
+namespace NCC.Extend.Entitys.Dto.LqStatistics
+{
+ ///
+ /// 只买了女神卡的会员查询输入
+ ///
+ public class GoddessCardMemberListQueryInput
+ {
+ ///
+ /// 当前页码(从1开始)
+ ///
+ public int PageIndex { get; set; } = 1;
+
+ ///
+ /// 每页数量
+ ///
+ public int PageSize { get; set; } = 20;
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/GoddessCardMemberOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/GoddessCardMemberOutput.cs
new file mode 100644
index 0000000..f5efe75
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/GoddessCardMemberOutput.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+
+namespace NCC.Extend.Entitys.Dto.LqStatistics
+{
+ ///
+ /// 只买了女神卡的会员输出
+ ///
+ public class GoddessCardMemberOutput
+ {
+ ///
+ /// 会员ID
+ ///
+ public string memberId { get; set; }
+
+ ///
+ /// 会员名称
+ ///
+ public string memberName { get; set; }
+
+ ///
+ /// 手机号
+ ///
+ public string phone { get; set; }
+
+ ///
+ /// 归属门店ID
+ ///
+ public string storeId { get; set; }
+
+ ///
+ /// 归属门店名称
+ ///
+ public string storeName { get; set; }
+
+ ///
+ /// 开单记录列表
+ ///
+ public List billingList { get; set; }
+ }
+
+ ///
+ /// 女神卡开单信息
+ ///
+ public class GoddessCardBillingInfo
+ {
+ ///
+ /// 开单编号
+ ///
+ public string billingId { get; set; }
+
+ ///
+ /// 开单日期
+ ///
+ public DateTime? billingDate { get; set; }
+
+ ///
+ /// 实付业绩
+ ///
+ public decimal actualPerformance { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ public string storeId { get; set; }
+
+ ///
+ /// 门店名称
+ ///
+ public string storeName { get; set; }
+
+ ///
+ /// 品项列表
+ ///
+ public List itemList { get; set; }
+ }
+
+ ///
+ /// 女神卡开单品项信息
+ ///
+ public class GoddessCardBillingItemInfo
+ {
+ ///
+ /// 品项明细ID
+ ///
+ public string itemId { get; set; }
+
+ ///
+ /// 品项编号
+ ///
+ public string itemCode { get; set; }
+
+ ///
+ /// 品项名称
+ ///
+ public string itemName { get; set; }
+
+ ///
+ /// 品项价格
+ ///
+ public decimal itemPrice { get; set; }
+
+ ///
+ /// 来源类型
+ ///
+ public string sourceType { get; set; }
+
+ ///
+ /// 项目次数
+ ///
+ public decimal projectNumber { get; set; }
+
+ ///
+ /// 总价
+ ///
+ public decimal totalPrice { get; set; }
+
+ ///
+ /// 实付金额
+ ///
+ public decimal actualPrice { get; set; }
+
+ ///
+ /// 业绩时间
+ ///
+ public DateTime? performanceTime { get; set; }
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs
index 36f3e5c..365d95f 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs
@@ -33,11 +33,6 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
public decimal OrderAchievement { get; set; }
///
- /// 开卡品项次数
- ///
- public int OrderItemCount { get; set; }
-
- ///
/// 耗卡品项次数
///
public int ConsumeItemCount { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryCrInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryCrInput.cs
new file mode 100644
index 0000000..11c9bec
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryCrInput.cs
@@ -0,0 +1,36 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqStoreConsumableInventory
+{
+ ///
+ /// 门店消耗品库存创建输入
+ ///
+ public class LqStoreConsumableInventoryCrInput
+ {
+ ///
+ /// 门店ID
+ ///
+ [Required(ErrorMessage = "门店ID不能为空")]
+ [StringLength(50, ErrorMessage = "门店ID长度不能超过50个字符")]
+ [Display(Name = "门店ID")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 产品类型(枚举:毛巾、垫子等)
+ ///
+ [Required(ErrorMessage = "产品类型不能为空")]
+ [StringLength(50, ErrorMessage = "产品类型长度不能超过50个字符")]
+ [Display(Name = "产品类型")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 库存数量
+ ///
+ [Required(ErrorMessage = "库存数量不能为空")]
+ [Range(0, int.MaxValue, ErrorMessage = "库存数量不能小于0")]
+ [Display(Name = "库存数量")]
+ public int Quantity { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryInfoOutput.cs
new file mode 100644
index 0000000..3866b07
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryInfoOutput.cs
@@ -0,0 +1,85 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqStoreConsumableInventory
+{
+ ///
+ /// 门店消耗品库存详情输出
+ ///
+ public class LqStoreConsumableInventoryInfoOutput
+ {
+ ///
+ /// 主键ID
+ ///
+ [Display(Name = "ID")]
+ public string id { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ [Display(Name = "门店ID")]
+ public string storeId { get; set; }
+
+ ///
+ /// 门店名称
+ ///
+ [Display(Name = "门店名称")]
+ public string storeName { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型")]
+ public string productType { get; set; }
+
+ ///
+ /// 库存数量
+ ///
+ [Display(Name = "库存数量")]
+ public int quantity { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效")]
+ public int isEffective { get; set; }
+
+ ///
+ /// 创建人ID
+ ///
+ [Display(Name = "创建人ID")]
+ public string createUser { get; set; }
+
+ ///
+ /// 创建人姓名
+ ///
+ [Display(Name = "创建人姓名")]
+ public string createUserName { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [Display(Name = "创建时间")]
+ public DateTime createTime { get; set; }
+
+ ///
+ /// 更新人ID
+ ///
+ [Display(Name = "更新人ID")]
+ public string updateUser { get; set; }
+
+ ///
+ /// 更新人姓名
+ ///
+ [Display(Name = "更新人姓名")]
+ public string updateUserName { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ [Display(Name = "更新时间")]
+ public DateTime? updateTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryListOutput.cs
new file mode 100644
index 0000000..18c0068
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryListOutput.cs
@@ -0,0 +1,85 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqStoreConsumableInventory
+{
+ ///
+ /// 门店消耗品库存列表输出
+ ///
+ public class LqStoreConsumableInventoryListOutput
+ {
+ ///
+ /// 主键ID
+ ///
+ [Display(Name = "ID")]
+ public string id { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ [Display(Name = "门店ID")]
+ public string storeId { get; set; }
+
+ ///
+ /// 门店名称
+ ///
+ [Display(Name = "门店名称")]
+ public string storeName { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型")]
+ public string productType { get; set; }
+
+ ///
+ /// 库存数量
+ ///
+ [Display(Name = "库存数量")]
+ public int quantity { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效")]
+ public int isEffective { get; set; }
+
+ ///
+ /// 创建人ID
+ ///
+ [Display(Name = "创建人ID")]
+ public string createUser { get; set; }
+
+ ///
+ /// 创建人姓名
+ ///
+ [Display(Name = "创建人姓名")]
+ public string createUserName { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [Display(Name = "创建时间")]
+ public DateTime createTime { get; set; }
+
+ ///
+ /// 更新人ID
+ ///
+ [Display(Name = "更新人ID")]
+ public string updateUser { get; set; }
+
+ ///
+ /// 更新人姓名
+ ///
+ [Display(Name = "更新人姓名")]
+ public string updateUserName { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ [Display(Name = "更新时间")]
+ public DateTime? updateTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryListQueryInput.cs
new file mode 100644
index 0000000..2bb8299
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryListQueryInput.cs
@@ -0,0 +1,31 @@
+using NCC.Common.Filter;
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqStoreConsumableInventory
+{
+ ///
+ /// 门店消耗品库存列表查询输入
+ ///
+ public class LqStoreConsumableInventoryListQueryInput : PageInputBase
+ {
+ ///
+ /// 门店ID
+ ///
+ [Display(Name = "门店ID", Description = "根据门店ID筛选")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 产品类型
+ ///
+ [Display(Name = "产品类型", Description = "根据产品类型筛选")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 是否有效
+ ///
+ [Display(Name = "是否有效", Description = "根据是否有效筛选")]
+ public int? IsEffective { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryUpInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryUpInput.cs
new file mode 100644
index 0000000..54fec19
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreConsumableInventory/LqStoreConsumableInventoryUpInput.cs
@@ -0,0 +1,43 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NCC.Extend.Entitys.Dto.LqStoreConsumableInventory
+{
+ ///
+ /// 门店消耗品库存更新输入
+ ///
+ public class LqStoreConsumableInventoryUpInput
+ {
+ ///
+ /// 主键ID
+ ///
+ [Required(ErrorMessage = "ID不能为空")]
+ [Display(Name = "ID")]
+ public string Id { get; set; }
+
+ ///
+ /// 门店ID
+ ///
+ [Required(ErrorMessage = "门店ID不能为空")]
+ [StringLength(50, ErrorMessage = "门店ID长度不能超过50个字符")]
+ [Display(Name = "门店ID")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 产品类型(枚举:毛巾、垫子等)
+ ///
+ [Required(ErrorMessage = "产品类型不能为空")]
+ [StringLength(50, ErrorMessage = "产品类型长度不能超过50个字符")]
+ [Display(Name = "产品类型")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 库存数量
+ ///
+ [Required(ErrorMessage = "库存数量不能为空")]
+ [Range(0, int.MaxValue, ErrorMessage = "库存数量不能小于0")]
+ [Display(Name = "库存数量")]
+ public int Quantity { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage/LqInventoryUsageEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage/LqInventoryUsageEntity.cs
index 745b1a9..66babf8 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage/LqInventoryUsageEntity.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage/LqInventoryUsageEntity.cs
@@ -48,6 +48,12 @@ namespace NCC.Extend.Entitys.lq_inventory_usage
public string RelatedConsumeId { get; set; }
///
+ /// 使用批次ID(同一批次申请的使用记录使用相同的批次ID)
+ ///
+ [SugarColumn(ColumnName = "F_UsageBatchId")]
+ public string UsageBatchId { get; set; }
+
+ ///
/// 创建人ID
///
[SugarColumn(ColumnName = "F_CreateUser")]
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_laundry_flow/LqLaundryFlowEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_laundry_flow/LqLaundryFlowEntity.cs
new file mode 100644
index 0000000..3fc4e2a
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_laundry_flow/LqLaundryFlowEntity.cs
@@ -0,0 +1,94 @@
+using System;
+using NCC.Common.Const;
+using SqlSugar;
+
+namespace NCC.Extend.Entitys.lq_laundry_flow
+{
+ ///
+ /// 清洗流水表
+ ///
+ [SugarTable("lq_laundry_flow")]
+ [Tenant(ClaimConst.TENANT_ID)]
+ public class LqLaundryFlowEntity
+ {
+ ///
+ /// 主键ID(送出时作为批次号)
+ ///
+ [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)]
+ public string Id { get; set; }
+
+ ///
+ /// 流水类型(0:送出 1:送回)
+ ///
+ [SugarColumn(ColumnName = "F_FlowType")]
+ public int FlowType { get; set; } = 0;
+
+ ///
+ /// 批次号(送出时=F_Id,送回时=对应的送出记录的F_Id)
+ ///
+ [SugarColumn(ColumnName = "F_BatchNumber")]
+ public string BatchNumber { get; set; }
+
+ ///
+ /// 门店ID(关联lq_mdxx.F_Id)
+ ///
+ [SugarColumn(ColumnName = "F_StoreId")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 产品类型(枚举:毛巾、垫子等)
+ ///
+ [SugarColumn(ColumnName = "F_ProductType")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 清洗商ID(关联lq_laundry_supplier.F_Id)
+ ///
+ [SugarColumn(ColumnName = "F_LaundrySupplierId")]
+ public string LaundrySupplierId { get; set; }
+
+ ///
+ /// 数量
+ ///
+ [SugarColumn(ColumnName = "F_Quantity")]
+ public int Quantity { get; set; } = 0;
+
+ ///
+ /// 清洗单价(记录历史价格)
+ ///
+ [SugarColumn(ColumnName = "F_LaundryPrice")]
+ public decimal LaundryPrice { get; set; } = 0;
+
+ ///
+ /// 总费用(数量 × 单价)
+ ///
+ [SugarColumn(ColumnName = "F_TotalPrice")]
+ public decimal TotalPrice { get; set; } = 0;
+
+ ///
+ /// 备注(可说明差异原因,如损坏、丢失等)
+ ///
+ [SugarColumn(ColumnName = "F_Remark")]
+ public string Remark { get; set; }
+
+ ///
+ /// 是否有效(1:有效 0:无效)
+ ///
+ [SugarColumn(ColumnName = "F_IsEffective")]
+ public int IsEffective { get; set; } = 1;
+
+ ///
+ /// 创建人ID
+ ///
+ [SugarColumn(ColumnName = "F_CreateUser")]
+ public string CreateUser { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [SugarColumn(ColumnName = "F_CreateTime")]
+ public DateTime CreateTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_laundry_supplier/LqLaundrySupplierEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_laundry_supplier/LqLaundrySupplierEntity.cs
new file mode 100644
index 0000000..ba1c15d
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_laundry_supplier/LqLaundrySupplierEntity.cs
@@ -0,0 +1,76 @@
+using System;
+using NCC.Common.Const;
+using SqlSugar;
+
+namespace NCC.Extend.Entitys.lq_laundry_supplier
+{
+ ///
+ /// 清洗商表
+ ///
+ [SugarTable("lq_laundry_supplier")]
+ [Tenant(ClaimConst.TENANT_ID)]
+ public class LqLaundrySupplierEntity
+ {
+ ///
+ /// 主键ID
+ ///
+ [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)]
+ public string Id { get; set; }
+
+ ///
+ /// 清洗商名称
+ ///
+ [SugarColumn(ColumnName = "F_SupplierName")]
+ public string SupplierName { get; set; }
+
+ ///
+ /// 产品类型(枚举:毛巾、垫子等)
+ ///
+ [SugarColumn(ColumnName = "F_ProductType")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 清洗价格(当前价格)
+ ///
+ [SugarColumn(ColumnName = "F_LaundryPrice")]
+ public decimal LaundryPrice { get; set; } = 0;
+
+ ///
+ /// 备注
+ ///
+ [SugarColumn(ColumnName = "F_Remark")]
+ public string Remark { get; set; }
+
+ ///
+ /// 是否有效(1:有效 0:无效)
+ ///
+ [SugarColumn(ColumnName = "F_IsEffective")]
+ public int IsEffective { get; set; } = 1;
+
+ ///
+ /// 创建人ID
+ ///
+ [SugarColumn(ColumnName = "F_CreateUser")]
+ public string CreateUser { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [SugarColumn(ColumnName = "F_CreateTime")]
+ public DateTime CreateTime { get; set; }
+
+ ///
+ /// 更新人ID
+ ///
+ [SugarColumn(ColumnName = "F_UpdateUser")]
+ public string UpdateUser { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ [SugarColumn(ColumnName = "F_UpdateTime")]
+ public DateTime? UpdateTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_store_consumable_inventory/LqStoreConsumableInventoryEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_store_consumable_inventory/LqStoreConsumableInventoryEntity.cs
new file mode 100644
index 0000000..32f43e0
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_store_consumable_inventory/LqStoreConsumableInventoryEntity.cs
@@ -0,0 +1,70 @@
+using System;
+using NCC.Common.Const;
+using SqlSugar;
+
+namespace NCC.Extend.Entitys.lq_store_consumable_inventory
+{
+ ///
+ /// 门店消耗品库存表
+ ///
+ [SugarTable("lq_store_consumable_inventory")]
+ [Tenant(ClaimConst.TENANT_ID)]
+ public class LqStoreConsumableInventoryEntity
+ {
+ ///
+ /// 主键ID
+ ///
+ [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)]
+ public string Id { get; set; }
+
+ ///
+ /// 门店ID(关联lq_mdxx.F_Id)
+ ///
+ [SugarColumn(ColumnName = "F_StoreId")]
+ public string StoreId { get; set; }
+
+ ///
+ /// 产品类型(枚举:毛巾、垫子等)
+ ///
+ [SugarColumn(ColumnName = "F_ProductType")]
+ public string ProductType { get; set; }
+
+ ///
+ /// 当前库存数量
+ ///
+ [SugarColumn(ColumnName = "F_Quantity")]
+ public int Quantity { get; set; } = 0;
+
+ ///
+ /// 是否有效(1:有效 0:无效)
+ ///
+ [SugarColumn(ColumnName = "F_IsEffective")]
+ public int IsEffective { get; set; } = 1;
+
+ ///
+ /// 创建人ID
+ ///
+ [SugarColumn(ColumnName = "F_CreateUser")]
+ public string CreateUser { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [SugarColumn(ColumnName = "F_CreateTime")]
+ public DateTime CreateTime { get; set; }
+
+ ///
+ /// 更新人ID
+ ///
+ [SugarColumn(ColumnName = "F_UpdateUser")]
+ public string UpdateUser { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ [SugarColumn(ColumnName = "F_UpdateTime")]
+ public DateTime? UpdateTime { get; set; }
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Enum/ConsumableProductTypeEnum.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Enum/ConsumableProductTypeEnum.cs
new file mode 100644
index 0000000..eabc34e
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Enum/ConsumableProductTypeEnum.cs
@@ -0,0 +1,47 @@
+using System.ComponentModel;
+
+namespace NCC.Extend.Entitys.Enum
+{
+ ///
+ /// 消耗品产品类型枚举
+ ///
+ public enum ConsumableProductTypeEnum
+ {
+ ///
+ /// 毛巾
+ ///
+ [Description("毛巾")]
+ 毛巾 = 1,
+
+ ///
+ /// 垫子
+ ///
+ [Description("垫子")]
+ 垫子 = 2,
+
+ ///
+ /// 浴巾
+ ///
+ [Description("浴巾")]
+ 浴巾 = 3,
+
+ ///
+ /// 床单
+ ///
+ [Description("床单")]
+ 床单 = 4,
+
+ ///
+ /// 枕套
+ ///
+ [Description("枕套")]
+ 枕套 = 5,
+
+ ///
+ /// 其他
+ ///
+ [Description("其他")]
+ 其他 = 99
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqLaundryFlow/ILqLaundryFlowService.cs b/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqLaundryFlow/ILqLaundryFlowService.cs
new file mode 100644
index 0000000..6edc903
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqLaundryFlow/ILqLaundryFlowService.cs
@@ -0,0 +1,11 @@
+namespace NCC.Extend.Interfaces.LqLaundryFlow
+{
+ ///
+ /// 清洗流水服务接口
+ ///
+ public interface ILqLaundryFlowService
+ {
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqLaundrySupplier/ILqLaundrySupplierService.cs b/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqLaundrySupplier/ILqLaundrySupplierService.cs
new file mode 100644
index 0000000..a7bdfb4
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqLaundrySupplier/ILqLaundrySupplierService.cs
@@ -0,0 +1,11 @@
+namespace NCC.Extend.Interfaces.LqLaundrySupplier
+{
+ ///
+ /// 清洗商服务接口
+ ///
+ public interface ILqLaundrySupplierService
+ {
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqStoreConsumableInventory/ILqStoreConsumableInventoryService.cs b/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqStoreConsumableInventory/ILqStoreConsumableInventoryService.cs
new file mode 100644
index 0000000..97aca8d
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqStoreConsumableInventory/ILqStoreConsumableInventoryService.cs
@@ -0,0 +1,11 @@
+namespace NCC.Extend.Interfaces.LqStoreConsumableInventory
+{
+ ///
+ /// 门店消耗品库存服务接口
+ ///
+ public interface ILqStoreConsumableInventoryService
+ {
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
index 557b462..c23dda0 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
@@ -265,8 +265,8 @@ namespace NCC.Extend
FROM lq_kd_kdjlb billing
WHERE billing.djmd = store.F_Id
AND billing.F_IsEffective = 1
- AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
- AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
+ AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd 00:00:00}'
+ AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd 23:59:59}'
), 0) as BillingPerformance,
-- 退款业绩总和(退卡业绩)
COALESCE((
@@ -274,8 +274,8 @@ namespace NCC.Extend
FROM lq_hytk_hytk refund
WHERE refund.md = store.F_Id
AND refund.F_IsEffective = 1
- AND DATE(refund.tksj) >= '{startDate:yyyy-MM-dd}'
- AND DATE(refund.tksj) <= '{endDate:yyyy-MM-dd}'
+ AND DATE(refund.tksj) >= '{startDate:yyyy-MM-dd 00:00:00}'
+ AND DATE(refund.tksj) <= '{endDate:yyyy-MM-dd 23:59:59}'
), 0) as RefundPerformance
FROM lq_mdxx store
LEFT JOIN lq_md_target target ON target.F_StoreId = store.F_Id AND target.F_Month = '{month}'
@@ -1762,8 +1762,6 @@ namespace NCC.Extend
}
#endregion
-
-
#region 获取储值扣减金额统计
///
/// 获取储值扣减金额统计
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
index 3cfaf01..fdcdfd3 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
@@ -117,6 +118,173 @@ namespace NCC.Extend
}
#endregion
+ #region 批量添加库存使用记录
+ ///
+ /// 批量添加库存使用记录
+ ///
+ ///
+ /// 一次性添加多条库存使用记录,同一批次的所有记录使用相同的批次ID
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "batchId": "可选,不传则自动生成",
+ /// "usageItems": [
+ /// {
+ /// "productId": "产品ID",
+ /// "storeId": "门店ID",
+ /// "usageTime": "2024-01-01T10:00:00",
+ /// "usageQuantity": 10,
+ /// "relatedConsumeId": "关联消耗ID(可选)"
+ /// }
+ /// ]
+ /// }
+ /// ```
+ ///
+ /// 参数说明:
+ /// - batchId: 批次ID,可选。如果不传,系统会自动生成一个唯一的批次ID
+ /// - usageItems: 使用记录列表,至少需要一条记录
+ /// - productId: 产品ID(必填)
+ /// - storeId: 门店ID(必填)
+ /// - usageTime: 使用时间(必填)
+ /// - usageQuantity: 使用数量(必填,必须大于0)
+ /// - relatedConsumeId: 关联消耗ID(可选)
+ ///
+ /// 批量创建输入
+ /// 批量创建结果,包含批次ID和成功/失败信息
+ /// 批量创建成功,返回批次ID和创建结果
+ /// 输入参数错误或库存不足
+ /// 服务器错误
+ [HttpPost("BatchCreate")]
+ public async Task BatchCreateAsync([FromBody] LqInventoryUsageBatchCreateInput input)
+ {
+ try
+ {
+ if (input == null || input.UsageItems == null || !input.UsageItems.Any())
+ {
+ throw NCCException.Oh("使用记录列表不能为空");
+ }
+
+ // 生成批次ID(如果未提供)
+ var batchId = string.IsNullOrWhiteSpace(input.BatchId)
+ ? YitIdHelper.NextId().ToString()
+ : input.BatchId;
+
+ var successIds = new List();
+ var failItems = new List();
+
+ _db.Ado.BeginTran();
+
+ try
+ {
+ // 按产品ID分组,批量验证库存
+ var productGroups = input.UsageItems
+ .Select((item, index) => new { Item = item, Index = index })
+ .GroupBy(x => x.Item.ProductId)
+ .ToList();
+
+ // 计算每个产品的总需求并检查库存
+ foreach (var productGroup in productGroups)
+ {
+ var productId = productGroup.Key;
+ var totalRequired = productGroup.Sum(x => x.Item.UsageQuantity);
+
+ // 计算该产品的总库存数量
+ var totalInventory = await _db.Queryable()
+ .Where(x => x.ProductId == productId && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .SumAsync(x => (int?)x.Quantity) ?? 0;
+
+ // 计算该产品的已使用数量
+ var totalUsage = await _db.Queryable()
+ .Where(x => x.ProductId == productId && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .SumAsync(x => (int?)x.UsageQuantity) ?? 0;
+
+ // 计算可用库存
+ var availableInventory = totalInventory - totalUsage;
+
+ // 检查库存是否足够
+ if (availableInventory < totalRequired)
+ {
+ var failIndices = productGroup.Select(x => x.Index).ToList();
+
+ foreach (var index in failIndices)
+ {
+ failItems.Add(new BatchCreateFailItem
+ {
+ Index = index,
+ ProductId = productId,
+ Reason = $"库存不足,当前可用库存:{availableInventory},需要数量:{totalRequired}"
+ });
+ }
+ }
+ }
+
+ // 创建成功的使用记录
+ var entitiesToInsert = new List();
+ for (int i = 0; i < input.UsageItems.Count; i++)
+ {
+ var item = input.UsageItems[i];
+
+ // 跳过失败项
+ if (failItems.Any(x => x.Index == i))
+ {
+ continue;
+ }
+
+ var usageEntity = new LqInventoryUsageEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ ProductId = item.ProductId,
+ StoreId = item.StoreId,
+ UsageTime = item.UsageTime,
+ UsageQuantity = item.UsageQuantity,
+ RelatedConsumeId = item.RelatedConsumeId,
+ UsageBatchId = batchId,
+ CreateUser = _userManager.UserId,
+ CreateTime = DateTime.Now,
+ IsEffective = StatusEnum.有效.GetHashCode()
+ };
+
+ entitiesToInsert.Add(usageEntity);
+ successIds.Add(usageEntity.Id);
+ }
+
+ // 批量插入
+ if (entitiesToInsert.Any())
+ {
+ var insertCount = await _db.Insertable(entitiesToInsert).ExecuteCommandAsync();
+ if (insertCount != entitiesToInsert.Count)
+ {
+ throw NCCException.Oh($"批量插入失败,预期插入{entitiesToInsert.Count}条,实际插入{insertCount}条");
+ }
+ }
+
+ _db.Ado.CommitTran();
+
+ return new LqInventoryUsageBatchCreateOutput
+ {
+ BatchId = batchId,
+ SuccessCount = successIds.Count,
+ FailCount = failItems.Count,
+ SuccessIds = successIds,
+ FailItems = failItems
+ };
+ }
+ catch
+ {
+ _db.Ado.RollbackTran();
+ throw;
+ }
+ }
+ catch (Exception ex)
+ {
+ _db.Ado.RollbackTran();
+ _logger.LogError(ex, "批量添加库存使用记录失败");
+ throw NCCException.Oh($"批量添加失败:{ex.Message}");
+ }
+ }
+ #endregion
+
#region 作废库存使用记录
///
/// 作废库存使用记录
@@ -185,6 +353,7 @@ namespace NCC.Extend
.WhereIF(input.UsageStartTime.HasValue, (usage, product) => usage.UsageTime >= input.UsageStartTime.Value)
.WhereIF(input.UsageEndTime.HasValue, (usage, product) => usage.UsageTime <= input.UsageEndTime.Value)
.WhereIF(!string.IsNullOrWhiteSpace(input.RelatedConsumeId), (usage, product) => usage.RelatedConsumeId == input.RelatedConsumeId)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.UsageBatchId), (usage, product) => usage.UsageBatchId == input.UsageBatchId)
.WhereIF(input.IsEffective.HasValue, (usage, product) => usage.IsEffective == input.IsEffective.Value)
.Select((usage, product) => new LqInventoryUsageListOutput
{
@@ -198,6 +367,7 @@ namespace NCC.Extend
usageTime = usage.UsageTime,
usageQuantity = usage.UsageQuantity,
relatedConsumeId = usage.RelatedConsumeId,
+ usageBatchId = usage.UsageBatchId,
createUser = usage.CreateUser,
createUserName = "",
createTime = usage.CreateTime,
@@ -258,6 +428,129 @@ namespace NCC.Extend
}
#endregion
+ #region 根据批次号获取批次信息
+ ///
+ /// 根据批次号获取批次信息
+ ///
+ ///
+ /// 根据批次ID查询该批次的所有使用记录信息,包括批次基本信息和详细的使用记录列表
+ ///
+ /// 返回数据结构:
+ /// - 批次基本信息:批次ID、创建时间、创建人、统计信息等
+ /// - 使用记录列表:该批次的所有使用记录详情
+ ///
+ /// 参数说明:
+ /// - batchId: 批次ID(必填)
+ ///
+ /// 批次ID
+ /// 批次信息,包含该批次的所有使用记录
+ /// 查询成功,返回批次信息和使用记录列表
+ /// 批次ID不能为空
+ /// 批次不存在
+ /// 服务器错误
+ [HttpGet("GetBatchInfo")]
+ public async Task GetBatchInfoAsync([FromQuery] string batchId)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(batchId))
+ {
+ throw NCCException.Oh("批次ID不能为空");
+ }
+
+ // 查询该批次的所有使用记录
+ var usageRecords = await _db.Queryable(
+ (usage, product) => usage.ProductId == product.Id)
+ .LeftJoin((usage, product, store) => usage.StoreId == store.Id)
+ .Where((usage, product, store) => usage.UsageBatchId == batchId)
+ .Select((usage, product, store) => new LqInventoryUsageListOutput
+ {
+ id = usage.Id,
+ productId = usage.ProductId,
+ productName = product.ProductName,
+ productCategory = product.ProductCategory,
+ productPrice = product.Price,
+ storeId = usage.StoreId,
+ storeName = store.Dm,
+ usageTime = usage.UsageTime,
+ usageQuantity = usage.UsageQuantity,
+ relatedConsumeId = usage.RelatedConsumeId,
+ usageBatchId = usage.UsageBatchId,
+ createUser = usage.CreateUser,
+ createUserName = "",
+ createTime = usage.CreateTime,
+ updateUser = usage.UpdateUser,
+ updateUserName = "",
+ updateTime = usage.UpdateTime,
+ isEffective = usage.IsEffective
+ })
+ .MergeTable()
+ .OrderBy("createTime")
+ .ToListAsync();
+
+ if (!usageRecords.Any())
+ {
+ throw NCCException.Oh("批次不存在或该批次下没有使用记录");
+ }
+
+ // 补充用户信息
+ var userIds = usageRecords.SelectMany(x => new[] { x.createUser, x.updateUser })
+ .Where(x => !string.IsNullOrEmpty(x))
+ .Distinct()
+ .ToList();
+
+ if (userIds.Any())
+ {
+ var userList = await _db.Queryable()
+ .Where(x => userIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.RealName })
+ .ToListAsync();
+ var userDict = userList.ToDictionary(k => k.Id, v => v.RealName);
+
+ foreach (var item in usageRecords)
+ {
+ if (!string.IsNullOrEmpty(item.createUser) && userDict.ContainsKey(item.createUser))
+ item.createUserName = userDict[item.createUser];
+ if (!string.IsNullOrEmpty(item.updateUser) && userDict.ContainsKey(item.updateUser))
+ item.updateUserName = userDict[item.updateUser];
+ }
+ }
+
+ // 计算使用总金额
+ foreach (var record in usageRecords)
+ {
+ record.usageTotalValue = record.usageQuantity * record.productPrice;
+ }
+
+ // 获取批次基本信息(使用第一条记录的创建信息)
+ var firstRecord = usageRecords.OrderBy(x => x.createTime).First();
+ var effectiveRecords = usageRecords.Where(x => x.isEffective == StatusEnum.有效.GetHashCode()).ToList();
+ var ineffectiveRecords = usageRecords.Where(x => x.isEffective == StatusEnum.无效.GetHashCode()).ToList();
+
+ var batchInfo = new LqInventoryUsageBatchInfoOutput
+ {
+ BatchId = batchId,
+ CreateTime = firstRecord.createTime,
+ CreateUser = firstRecord.createUser,
+ CreateUserName = firstRecord.createUserName,
+ TotalCount = usageRecords.Count,
+ EffectiveCount = effectiveRecords.Count,
+ IneffectiveCount = ineffectiveRecords.Count,
+ TotalUsageQuantity = effectiveRecords.Sum(x => x.usageQuantity),
+ TotalUsageAmount = effectiveRecords.Sum(x => x.usageTotalValue),
+ UsageRecords = usageRecords
+ };
+
+ return batchInfo;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取批次信息失败");
+ throw NCCException.Oh($"获取批次信息失败:{ex.Message}");
+ }
+ }
+ #endregion
+
#region 统计时间周期内每个产品的使用数量
///
/// 统计时间周期内每个产品的使用数量
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
index eab8a42..cc72fb8 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
@@ -3099,22 +3099,22 @@ namespace NCC.Extend.LqKdKdjlb
// 查询购买数量
var purchasedCount = await _db.Queryable()
.Where(x => x.Id == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode())
- .SumAsync(x => x.ProjectNumber);
+ .SumAsync(x => (decimal?)x.ProjectNumber) ?? 0m;
// 查询消费数量
var consumedCount = await _db.Queryable()
.Where(x => x.BillingItemId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode())
- .SumAsync(x => x.OriginalProjectNumber);
+ .SumAsync(x => x.OriginalProjectNumber) ?? 0m;
// 查询退卡数量
var refundedCount = await _db.Queryable()
.Where(x => x.BillingItemId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode())
- .SumAsync(x => x.ProjectNumber);
+ .SumAsync(x => (decimal?)x.ProjectNumber) ?? 0m;
// 查询储扣数量
var deductCount = await _db.Queryable()
.Where(x => x.DeductId == billingItemId && x.IsEffective == StatusEnum.有效.GetHashCode())
- .SumAsync(x => x.ProjectNumber) ?? 0;
+ .SumAsync(x => (decimal?)x.ProjectNumber) ?? 0m;
// 计算剩余数量
var remainingCount = (int)(purchasedCount - consumedCount - refundedCount - deductCount);
@@ -3460,15 +3460,34 @@ namespace NCC.Extend.LqKdKdjlb
var sidx = input.sidx == null ? "yjsj" : input.sidx;
var sort = string.IsNullOrEmpty(input.sort) ? "DESC" : input.sort;
- // 处理开单时间范围
- List queryBillingTime = null;
+ // 处理开单时间范围(兼容 StartBillingTime/EndBillingTime 和 startTime/endTime 两种参数名)
DateTime? startBillingTime = null;
DateTime? endBillingTime = null;
- if (!string.IsNullOrEmpty(input.StartBillingTime) && !string.IsNullOrEmpty(input.EndBillingTime))
+
+ // 优先使用 StartBillingTime/EndBillingTime,如果没有则使用 startTime/endTime
+ string startTimeStr = !string.IsNullOrEmpty(input.StartBillingTime) ? input.StartBillingTime : input.startTime;
+ string endTimeStr = !string.IsNullOrEmpty(input.EndBillingTime) ? input.EndBillingTime : input.endTime;
+
+ if (!string.IsNullOrEmpty(startTimeStr) && !string.IsNullOrEmpty(endTimeStr))
{
- queryBillingTime = new List { input.StartBillingTime, input.EndBillingTime };
- startBillingTime = Ext.GetDateTime(queryBillingTime.First());
- endBillingTime = Ext.GetDateTime(queryBillingTime.Last());
+ // 尝试解析日期字符串(支持多种格式)
+ if (DateTime.TryParse(startTimeStr, out DateTime startDate))
+ {
+ startBillingTime = startDate;
+ }
+ else
+ {
+ throw NCCException.Oh($"开始时间格式错误:{startTimeStr}");
+ }
+
+ if (DateTime.TryParse(endTimeStr, out DateTime endDate))
+ {
+ endBillingTime = endDate;
+ }
+ else
+ {
+ throw NCCException.Oh($"结束时间格式错误:{endTimeStr}");
+ }
}
// 优化查询:先分页查询主表,再批量查询关联数据,避免子查询性能问题
@@ -3477,18 +3496,18 @@ namespace NCC.Extend.LqKdKdjlb
.Where(pxmx => pxmx.IsEffective == StatusEnum.有效.GetHashCode())
.WhereIF(!string.IsNullOrEmpty(input.Id), pxmx => pxmx.Id == input.Id)
.WhereIF(!string.IsNullOrEmpty(input.BillingId), pxmx => pxmx.Glkdbh == input.BillingId)
- .WhereIF(queryBillingTime != null && startBillingTime.HasValue, pxmx => pxmx.Yjsj >= new DateTime(startBillingTime.Value.Year, startBillingTime.Value.Month, startBillingTime.Value.Day, 0, 0, 0))
- .WhereIF(queryBillingTime != null && endBillingTime.HasValue, pxmx => pxmx.Yjsj <= new DateTime(endBillingTime.Value.Year, endBillingTime.Value.Month, endBillingTime.Value.Day, 23, 59, 59))
+ .WhereIF(startBillingTime.HasValue, pxmx => pxmx.Yjsj >= new DateTime(startBillingTime.Value.Year, startBillingTime.Value.Month, startBillingTime.Value.Day, 0, 0, 0))
+ .WhereIF(endBillingTime.HasValue, pxmx => pxmx.Yjsj <= new DateTime(endBillingTime.Value.Year, endBillingTime.Value.Month, endBillingTime.Value.Day, 23, 59, 59))
.WhereIF(!string.IsNullOrEmpty(input.ActivityId), pxmx => pxmx.ActivityId == input.ActivityId)
.WhereIF(!string.IsNullOrEmpty(input.MemberId), pxmx => pxmx.MemberId == input.MemberId)
.WhereIF(!string.IsNullOrEmpty(input.ItemId), pxmx => pxmx.Px == input.ItemId)
.WhereIF(!string.IsNullOrEmpty(input.ItemName), pxmx => pxmx.Pxmc != null && pxmx.Pxmc.Contains(input.ItemName))
- .WhereIF(!string.IsNullOrEmpty(input.SourceType), pxmx => pxmx.SourceType == input.SourceType);
+ .WhereIF(!string.IsNullOrEmpty(input.SourceType), pxmx => pxmx.SourceType == input.SourceType)
+ .WhereIF(!string.IsNullOrEmpty(input.ItemType), pxmx => pxmx.ItemCategory == input.ItemType);
// 2. 通过 EXISTS 子查询筛选关联字段(在分页前筛选,确保分页准确)
baseQuery = baseQuery.WhereIF(!string.IsNullOrEmpty(input.MemberName), pxmx => SqlFunc.Subqueryable().Where(x => x.Id == pxmx.MemberId && x.Khmc != null && x.Khmc.Contains(input.MemberName)).Any())
.WhereIF(!string.IsNullOrEmpty(input.MemberPhone), pxmx => SqlFunc.Subqueryable().Where(x => x.Id == pxmx.MemberId && x.Sjh == input.MemberPhone).Any())
- .WhereIF(!string.IsNullOrEmpty(input.ItemType), pxmx => SqlFunc.Subqueryable().Where(x => x.Id == pxmx.Px && x.Fl4 == input.ItemType).Any())
.WhereIF(!string.IsNullOrEmpty(input.StoreId), pxmx => SqlFunc.Subqueryable().Where(x => x.Id == pxmx.Glkdbh && x.Djmd == input.StoreId).Any());
// 3. 先分页查询主表数据(查询实体类,提高性能)
@@ -3498,7 +3517,6 @@ namespace NCC.Extend.LqKdKdjlb
var itemIds = pagedData.list.Select(x => x.Id).ToList();
var memberIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.MemberId)).Select(x => x.MemberId).Distinct().ToList();
var activityIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.ActivityId)).Select(x => x.ActivityId).Distinct().ToList();
- var projectIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.Px)).Select(x => x.Px).Distinct().ToList();
var billingIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.Glkdbh)).Select(x => x.Glkdbh).Distinct().ToList();
// 批量查询会员信息
@@ -3517,14 +3535,6 @@ namespace NCC.Extend.LqKdKdjlb
activityDict = activities.ToDictionary(x => x.Id, x => x.ActivityName ?? "");
}
- // 批量查询项目资料
- var projectDict = new Dictionary();
- if (projectIds.Any())
- {
- var projects = await _db.Queryable().Where(x => projectIds.Contains(x.Id)).Select(x => new { x.Id, x.Qt2 }).ToListAsync();
- projectDict = projects.ToDictionary(x => x.Id, x => x.Qt2 ?? "");
- }
-
// 批量查询开单记录,获取门店ID
var billingStoreDict = new Dictionary();
if (billingIds.Any())
@@ -3552,7 +3562,7 @@ namespace NCC.Extend.LqKdKdjlb
memberName = pxmx.MemberId != null && memberDict.ContainsKey(pxmx.MemberId) ? memberDict[pxmx.MemberId].Name : "",
memberPhone = pxmx.MemberId != null && memberDict.ContainsKey(pxmx.MemberId) ? memberDict[pxmx.MemberId].Phone : "",
itemName = pxmx.Pxmc,
- itemType = pxmx.Px != null && projectDict.ContainsKey(pxmx.Px) ? projectDict[pxmx.Px] : "",
+ itemType = pxmx.ItemCategory,
actualPrice = pxmx.ActualPrice,
projectNumber = pxmx.ProjectNumber,
sourceType = pxmx.SourceType,
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqLaundryFlowService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqLaundryFlowService.cs
new file mode 100644
index 0000000..4455261
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqLaundryFlowService.cs
@@ -0,0 +1,660 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using NCC.Common.Core.Manager;
+using NCC.Common.Enum;
+using NCC.Common.Filter;
+using NCC.Dependency;
+using NCC.DynamicApiController;
+using NCC.Extend.Entitys.Dto.LqLaundryFlow;
+using NCC.Extend.Entitys.Enum;
+using NCC.Extend.Entitys.lq_laundry_flow;
+using NCC.Extend.Entitys.lq_laundry_supplier;
+using NCC.Extend.Entitys.lq_mdxx;
+using NCC.Extend.Interfaces.LqLaundryFlow;
+using NCC.FriendlyException;
+using NCC.System.Entitys.Permission;
+using SqlSugar;
+using Yitter.IdGenerator;
+
+namespace NCC.Extend
+{
+ ///
+ /// 清洗流水服务
+ ///
+ [ApiDescriptionSettings(Tag = "绿纤清洗流水管理", Name = "LqLaundryFlow", Order = 200)]
+ [Route("api/Extend/LqLaundryFlow")]
+ public class LqLaundryFlowService : IDynamicApiController, ITransient, ILqLaundryFlowService
+ {
+ private readonly IUserManager _userManager;
+ private readonly ILogger _logger;
+ private readonly ISqlSugarClient _db;
+
+ ///
+ /// 构造函数
+ ///
+ public LqLaundryFlowService(IUserManager userManager, ILogger logger, ISqlSugarClient db)
+ {
+ _userManager = userManager;
+ _logger = logger;
+ _db = db;
+ }
+
+ #region 创建送出记录
+ ///
+ /// 创建送出记录
+ ///
+ ///
+ /// 门店选择清洗商和产品,填写送出数量,创建送出记录
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "storeId": "门店ID",
+ /// "productType": "毛巾",
+ /// "laundrySupplierId": "清洗商ID",
+ /// "quantity": 100,
+ /// "remark": "备注"
+ /// }
+ /// ```
+ ///
+ /// 送出输入
+ /// 创建结果(包含批次号)
+ /// 创建成功
+ /// 参数错误或清洗商不存在
+ /// 服务器错误
+ [HttpPost("Send")]
+ public async Task SendAsync([FromBody] LqLaundryFlowSendInput input)
+ {
+ try
+ {
+ // 验证门店是否存在
+ var store = await _db.Queryable()
+ .Where(x => x.Id == input.StoreId)
+ .FirstAsync();
+
+ if (store == null)
+ {
+ throw NCCException.Oh("门店不存在");
+ }
+
+ // 验证清洗商是否存在
+ var supplier = await _db.Queryable()
+ .Where(x => x.Id == input.LaundrySupplierId && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (supplier == null)
+ {
+ throw NCCException.Oh("清洗商不存在或已失效");
+ }
+
+ // 验证产品类型是否匹配
+ if (supplier.ProductType != input.ProductType)
+ {
+ throw NCCException.Oh($"清洗商【{supplier.SupplierName}】不支持清洗产品类型【{input.ProductType}】");
+ }
+
+ // 生成批次号(使用ID)
+ var batchId = YitIdHelper.NextId().ToString();
+
+ // 创建送出记录
+ var entity = new LqLaundryFlowEntity
+ {
+ Id = batchId,
+ FlowType = 0, // 送出
+ BatchNumber = batchId, // 批次号等于ID
+ StoreId = input.StoreId,
+ ProductType = input.ProductType,
+ LaundrySupplierId = input.LaundrySupplierId,
+ Quantity = input.Quantity,
+ LaundryPrice = supplier.LaundryPrice, // 记录历史价格
+ TotalPrice = 0, // 送出时总费用为0
+ Remark = input.Remark,
+ IsEffective = StatusEnum.有效.GetHashCode(),
+ CreateUser = _userManager.UserId,
+ CreateTime = DateTime.Now
+ };
+
+ var isOk = await _db.Insertable(entity).ExecuteCommandAsync();
+ if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
+
+ return new { batchNumber = batchId, message = "送出记录创建成功" };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "创建送出记录失败");
+ throw NCCException.Oh($"创建失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 创建送回记录
+ ///
+ /// 创建送回记录
+ ///
+ ///
+ /// 清洗完毕后,填写送回清洗商、送回数量,创建送回记录
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "batchNumber": "批次号(对应的送出记录的ID)",
+ /// "laundrySupplierId": "清洗商ID",
+ /// "quantity": 95,
+ /// "remark": "5条损坏"
+ /// }
+ /// ```
+ ///
+ /// 送回输入
+ /// 创建结果
+ /// 创建成功
+ /// 批次号不存在或参数错误
+ /// 服务器错误
+ [HttpPost("Return")]
+ public async Task ReturnAsync([FromBody] LqLaundryFlowReturnInput input)
+ {
+ try
+ {
+ // 验证对应的送出记录是否存在
+ var sendRecord = await _db.Queryable()
+ .Where(x => x.BatchNumber == input.BatchNumber && x.FlowType == 0 && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (sendRecord == null)
+ {
+ throw NCCException.Oh("对应的送出记录不存在或已失效");
+ }
+
+ // 检查是否已经存在送回记录
+ var existingReturn = await _db.Queryable()
+ .Where(x => x.BatchNumber == input.BatchNumber && x.FlowType == 1 && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (existingReturn != null)
+ {
+ throw NCCException.Oh("该批次已存在送回记录");
+ }
+
+ // 验证清洗商是否存在
+ var supplier = await _db.Queryable()
+ .Where(x => x.Id == input.LaundrySupplierId && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (supplier == null)
+ {
+ throw NCCException.Oh("清洗商不存在或已失效");
+ }
+
+ // 验证产品类型是否匹配
+ if (supplier.ProductType != sendRecord.ProductType)
+ {
+ throw NCCException.Oh($"清洗商【{supplier.SupplierName}】不支持清洗产品类型【{sendRecord.ProductType}】");
+ }
+
+ // 计算总费用(送回数量 × 清洗单价)
+ var totalPrice = input.Quantity * supplier.LaundryPrice;
+
+ // 创建送回记录
+ var entity = new LqLaundryFlowEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ FlowType = 1, // 送回
+ BatchNumber = input.BatchNumber, // 使用送出记录的批次号
+ StoreId = sendRecord.StoreId,
+ ProductType = sendRecord.ProductType,
+ LaundrySupplierId = input.LaundrySupplierId,
+ Quantity = input.Quantity,
+ LaundryPrice = supplier.LaundryPrice, // 记录历史价格(可能已变化)
+ TotalPrice = totalPrice,
+ Remark = input.Remark,
+ IsEffective = StatusEnum.有效.GetHashCode(),
+ CreateUser = _userManager.UserId,
+ CreateTime = DateTime.Now
+ };
+
+ var isOk = await _db.Insertable(entity).ExecuteCommandAsync();
+ if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
+
+ return new { message = "送回记录创建成功", totalPrice = totalPrice };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "创建送回记录失败");
+ throw NCCException.Oh($"创建失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 获取清洗流水列表
+ ///
+ /// 获取清洗流水列表
+ ///
+ ///
+ /// 分页查询清洗流水列表,支持按流水类型、批次号、门店、产品类型、清洗商、时间范围筛选
+ ///
+ /// 查询输入
+ /// 清洗流水列表
+ /// 查询成功
+ /// 服务器错误
+ [HttpGet("GetList")]
+ public async Task GetListAsync([FromQuery] LqLaundryFlowListQueryInput input)
+ {
+ try
+ {
+ var sidx = string.IsNullOrEmpty(input.sidx) ? "createTime" : input.sidx;
+ var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort;
+
+ var data = await _db.Queryable(
+ (flow, store, supplier) => flow.StoreId == store.Id && flow.LaundrySupplierId == supplier.Id)
+ .WhereIF(input.FlowType.HasValue, (flow, store, supplier) => flow.FlowType == input.FlowType.Value)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.BatchNumber), (flow, store, supplier) => flow.BatchNumber == input.BatchNumber)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (flow, store, supplier) => flow.StoreId == input.StoreId)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.ProductType), (flow, store, supplier) => flow.ProductType == input.ProductType)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.LaundrySupplierId), (flow, store, supplier) => flow.LaundrySupplierId == input.LaundrySupplierId)
+ .WhereIF(input.StartTime.HasValue, (flow, store, supplier) => flow.CreateTime >= input.StartTime.Value)
+ .WhereIF(input.EndTime.HasValue, (flow, store, supplier) => flow.CreateTime <= input.EndTime.Value)
+ .WhereIF(input.IsEffective.HasValue, (flow, store, supplier) => flow.IsEffective == input.IsEffective.Value)
+ .Select((flow, store, supplier) => new LqLaundryFlowListOutput
+ {
+ id = flow.Id,
+ flowType = flow.FlowType,
+ flowTypeName = flow.FlowType == 0 ? "送出" : "送回",
+ batchNumber = flow.BatchNumber,
+ storeId = flow.StoreId,
+ storeName = store.Dm ?? "",
+ productType = flow.ProductType,
+ laundrySupplierId = flow.LaundrySupplierId,
+ laundrySupplierName = supplier.SupplierName ?? "",
+ quantity = flow.Quantity,
+ laundryPrice = flow.LaundryPrice,
+ totalPrice = flow.TotalPrice,
+ remark = flow.Remark,
+ isEffective = flow.IsEffective,
+ createUser = flow.CreateUser,
+ createUserName = "",
+ createTime = flow.CreateTime
+ })
+ .MergeTable()
+ .OrderBy(sidx + " " + sort)
+ .ToPagedListAsync(input.currentPage, input.pageSize);
+
+ // 补充用户名称信息
+ var userIds = data.list.Select(x => x.createUser)
+ .Where(x => !string.IsNullOrEmpty(x))
+ .Distinct()
+ .ToList();
+
+ if (userIds.Any())
+ {
+ var userList = await _db.Queryable()
+ .Where(x => userIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.RealName })
+ .ToListAsync();
+ var userDict = userList.ToDictionary(k => k.Id, v => v.RealName);
+
+ foreach (var item in data.list)
+ {
+ item.createUserName = userDict.ContainsKey(item.createUser) ? userDict[item.createUser] : "";
+ }
+ }
+
+ return PageResult.SqlSugarPageResult(data);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取清洗流水列表失败");
+ throw NCCException.Oh($"查询失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 获取清洗流水详情
+ ///
+ /// 获取清洗流水详情
+ ///
+ ///
+ /// 根据ID获取清洗流水的详细信息
+ ///
+ /// 流水ID
+ /// 流水详情
+ /// 查询成功
+ /// 记录不存在
+ /// 服务器错误
+ [HttpGet("{id}")]
+ public async Task GetInfoAsync(string id)
+ {
+ try
+ {
+ var entity = await _db.Queryable(
+ (flow, store, supplier) => flow.StoreId == store.Id && flow.LaundrySupplierId == supplier.Id)
+ .Where((flow, store, supplier) => flow.Id == id)
+ .Select((flow, store, supplier) => new LqLaundryFlowInfoOutput
+ {
+ id = flow.Id,
+ flowType = flow.FlowType,
+ flowTypeName = flow.FlowType == 0 ? "送出" : "送回",
+ batchNumber = flow.BatchNumber,
+ storeId = flow.StoreId,
+ storeName = store.Dm ?? "",
+ productType = flow.ProductType,
+ laundrySupplierId = flow.LaundrySupplierId,
+ laundrySupplierName = supplier.SupplierName ?? "",
+ quantity = flow.Quantity,
+ laundryPrice = flow.LaundryPrice,
+ totalPrice = flow.TotalPrice,
+ remark = flow.Remark,
+ isEffective = flow.IsEffective,
+ createUser = flow.CreateUser,
+ createUserName = "",
+ createTime = flow.CreateTime
+ })
+ .FirstAsync();
+
+ if (entity == null)
+ {
+ throw NCCException.Oh("流水记录不存在");
+ }
+
+ // 补充用户名称
+ if (!string.IsNullOrEmpty(entity.createUser))
+ {
+ var createUser = await _db.Queryable()
+ .Where(x => x.Id == entity.createUser)
+ .Select(x => x.RealName)
+ .FirstAsync();
+ entity.createUserName = createUser ?? "";
+ }
+
+ return entity;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取清洗流水详情失败");
+ throw NCCException.Oh($"查询失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 查询差异记录
+ ///
+ /// 查询差异记录(送出数量 > 送回数量)
+ ///
+ ///
+ /// 查询所有送出数量大于送回数量的记录,用于追踪差异来源
+ ///
+ /// 查询输入(支持分页)
+ /// 差异记录列表
+ /// 查询成功
+ /// 服务器错误
+ [HttpPost("GetDifferenceList")]
+ public async Task GetDifferenceListAsync([FromBody] LqLaundryFlowListQueryInput input)
+ {
+ try
+ {
+ // 查询所有送出记录
+ var sendRecords = await _db.Queryable(
+ (flow, store) => flow.StoreId == store.Id)
+ .Where((flow, store) => flow.FlowType == 0 && flow.IsEffective == StatusEnum.有效.GetHashCode())
+ .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (flow, store) => flow.StoreId == input.StoreId)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.ProductType), (flow, store) => flow.ProductType == input.ProductType)
+ .WhereIF(input.StartTime.HasValue, (flow, store) => flow.CreateTime >= input.StartTime.Value)
+ .WhereIF(input.EndTime.HasValue, (flow, store) => flow.CreateTime <= input.EndTime.Value)
+ .Select((flow, store) => new
+ {
+ flow.BatchNumber,
+ flow.StoreId,
+ StoreName = store.Dm ?? "",
+ flow.ProductType,
+ SendQuantity = flow.Quantity,
+ SendTime = flow.CreateTime
+ })
+ .ToListAsync();
+
+ if (!sendRecords.Any())
+ {
+ return new
+ {
+ list = new List(),
+ pagination = new
+ {
+ page = input.currentPage,
+ pageSize = input.pageSize,
+ total = 0
+ }
+ };
+ }
+
+ // 查询所有送回记录
+ var returnRecords = await _db.Queryable()
+ .Where(x => x.FlowType == 1 && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .Select(x => new
+ {
+ x.BatchNumber,
+ ReturnQuantity = x.Quantity,
+ ReturnTime = x.CreateTime,
+ x.Remark
+ })
+ .ToListAsync();
+
+ var returnDict = returnRecords
+ .GroupBy(x => x.BatchNumber)
+ .ToDictionary(g => g.Key, g => g.First());
+
+ // 计算差异
+ var differenceList = sendRecords
+ .Select(send => new LqLaundryFlowDifferenceOutput
+ {
+ batchNumber = send.BatchNumber,
+ storeId = send.StoreId,
+ storeName = send.StoreName,
+ productType = send.ProductType,
+ sendQuantity = send.SendQuantity,
+ returnQuantity = returnDict.ContainsKey(send.BatchNumber) ? returnDict[send.BatchNumber].ReturnQuantity : 0,
+ differenceQuantity = send.SendQuantity - (returnDict.ContainsKey(send.BatchNumber) ? returnDict[send.BatchNumber].ReturnQuantity : 0),
+ differenceRemark = returnDict.ContainsKey(send.BatchNumber) ? returnDict[send.BatchNumber].Remark : "",
+ sendTime = send.SendTime,
+ returnTime = returnDict.ContainsKey(send.BatchNumber) ? returnDict[send.BatchNumber].ReturnTime : (DateTime?)null
+ })
+ .Where(x => x.differenceQuantity > 0)
+ .ToList();
+
+ // 手动分页
+ var totalCount = differenceList.Count;
+ var pagedList = differenceList
+ .Skip((input.currentPage - 1) * input.pageSize)
+ .Take(input.pageSize)
+ .ToList();
+
+ return new
+ {
+ list = pagedList,
+ pagination = new
+ {
+ page = input.currentPage,
+ pageSize = input.pageSize,
+ total = totalCount
+ }
+ };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "查询差异记录失败");
+ throw NCCException.Oh($"查询失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 门店每月清洗费用统计
+ ///
+ /// 门店每月清洗费用统计
+ ///
+ ///
+ /// 统计每个门店每月的清洗费用(只统计送回记录)
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "startMonth": "202411",
+ /// "endMonth": "202412",
+ /// "storeId": "门店ID(可选)"
+ /// }
+ /// ```
+ ///
+ /// 统计输入
+ /// 统计结果
+ /// 统计成功
+ /// 服务器错误
+ [HttpPost("GetStoreMonthlyStatistics")]
+ public async Task> GetStoreMonthlyStatisticsAsync([FromBody] LaundryStatisticsInput input)
+ {
+ try
+ {
+ // 构建月份过滤条件
+ var startDate = (DateTime?)null;
+ var endDate = (DateTime?)null;
+
+ if (!string.IsNullOrWhiteSpace(input.StartMonth) && input.StartMonth.Length == 6)
+ {
+ var year = int.Parse(input.StartMonth.Substring(0, 4));
+ var month = int.Parse(input.StartMonth.Substring(4, 2));
+ startDate = new DateTime(year, month, 1);
+ }
+
+ if (!string.IsNullOrWhiteSpace(input.EndMonth) && input.EndMonth.Length == 6)
+ {
+ var year = int.Parse(input.EndMonth.Substring(0, 4));
+ var month = int.Parse(input.EndMonth.Substring(4, 2));
+ endDate = new DateTime(year, month, DateTime.DaysInMonth(year, month), 23, 59, 59);
+ }
+
+ var query = _db.Queryable(
+ (flow, store) => flow.StoreId == store.Id)
+ .Where((flow, store) => flow.FlowType == 1 && flow.IsEffective == StatusEnum.有效.GetHashCode())
+ .WhereIF(startDate.HasValue, (flow, store) => flow.CreateTime >= startDate.Value)
+ .WhereIF(endDate.HasValue, (flow, store) => flow.CreateTime <= endDate.Value)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (flow, store) => flow.StoreId == input.StoreId);
+
+ var allRecords = await query
+ .Select((flow, store) => new
+ {
+ flow.StoreId,
+ StoreName = store.Dm ?? "",
+ flow.CreateTime,
+ flow.TotalPrice,
+ flow.Id
+ })
+ .ToListAsync();
+
+ // 在内存中分组统计
+ var result = allRecords
+ .GroupBy(x => new { x.StoreId, x.StoreName, Month = x.CreateTime.ToString("yyyyMM") })
+ .Select(g => new LqLaundryStatisticsOutput
+ {
+ storeId = g.Key.StoreId,
+ storeName = g.Key.StoreName,
+ statisticsMonth = g.Key.Month,
+ totalPrice = g.Sum(x => x.TotalPrice),
+ count = g.Count()
+ })
+ .OrderBy(x => x.storeId)
+ .ThenBy(x => x.statisticsMonth)
+ .ToList();
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "门店每月清洗费用统计失败");
+ throw NCCException.Oh($"统计失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 产品每月清洗费用统计
+ ///
+ /// 产品每月清洗费用统计
+ ///
+ ///
+ /// 统计每个产品每月的清洗费用(只统计送回记录)
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "startMonth": "202411",
+ /// "endMonth": "202412",
+ /// "productType": "毛巾(可选)"
+ /// }
+ /// ```
+ ///
+ /// 统计输入
+ /// 统计结果
+ /// 统计成功
+ /// 服务器错误
+ [HttpPost("GetProductMonthlyStatistics")]
+ public async Task> GetProductMonthlyStatisticsAsync([FromBody] LaundryStatisticsInput input)
+ {
+ try
+ {
+ // 构建月份过滤条件
+ var startDate = (DateTime?)null;
+ var endDate = (DateTime?)null;
+
+ if (!string.IsNullOrWhiteSpace(input.StartMonth) && input.StartMonth.Length == 6)
+ {
+ var year = int.Parse(input.StartMonth.Substring(0, 4));
+ var month = int.Parse(input.StartMonth.Substring(4, 2));
+ startDate = new DateTime(year, month, 1);
+ }
+
+ if (!string.IsNullOrWhiteSpace(input.EndMonth) && input.EndMonth.Length == 6)
+ {
+ var year = int.Parse(input.EndMonth.Substring(0, 4));
+ var month = int.Parse(input.EndMonth.Substring(4, 2));
+ endDate = new DateTime(year, month, DateTime.DaysInMonth(year, month), 23, 59, 59);
+ }
+
+ var query = _db.Queryable()
+ .Where(x => x.FlowType == 1 && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .WhereIF(startDate.HasValue, x => x.CreateTime >= startDate.Value)
+ .WhereIF(endDate.HasValue, x => x.CreateTime <= endDate.Value)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.ProductType), x => x.ProductType == input.ProductType);
+
+ var allRecords = await query
+ .Select(x => new
+ {
+ x.ProductType,
+ x.CreateTime,
+ x.TotalPrice,
+ x.Id
+ })
+ .ToListAsync();
+
+ // 在内存中分组统计
+ var result = allRecords
+ .GroupBy(x => new { x.ProductType, Month = x.CreateTime.ToString("yyyyMM") })
+ .Select(g => new LqLaundryStatisticsOutput
+ {
+ productType = g.Key.ProductType,
+ statisticsMonth = g.Key.Month,
+ totalPrice = g.Sum(x => x.TotalPrice),
+ count = g.Count()
+ })
+ .OrderBy(x => x.productType)
+ .ThenBy(x => x.statisticsMonth)
+ .ToList();
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "产品每月清洗费用统计失败");
+ throw NCCException.Oh($"统计失败:{ex.Message}");
+ }
+ }
+ #endregion
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqLaundrySupplierService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqLaundrySupplierService.cs
new file mode 100644
index 0000000..213546a
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqLaundrySupplierService.cs
@@ -0,0 +1,353 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using NCC.Common.Core.Manager;
+using NCC.Common.Enum;
+using NCC.Common.Filter;
+using NCC.Dependency;
+using NCC.DynamicApiController;
+using NCC.Extend.Entitys.Dto.LqLaundrySupplier;
+using NCC.Extend.Entitys.Enum;
+using NCC.Extend.Entitys.lq_laundry_supplier;
+using NCC.Extend.Interfaces.LqLaundrySupplier;
+using NCC.FriendlyException;
+using NCC.System.Entitys.Permission;
+using SqlSugar;
+using Yitter.IdGenerator;
+
+namespace NCC.Extend
+{
+ ///
+ /// 清洗商服务
+ ///
+ [ApiDescriptionSettings(Tag = "绿纤清洗商管理", Name = "LqLaundrySupplier", Order = 200)]
+ [Route("api/Extend/LqLaundrySupplier")]
+ public class LqLaundrySupplierService : IDynamicApiController, ITransient, ILqLaundrySupplierService
+ {
+ private readonly IUserManager _userManager;
+ private readonly ILogger _logger;
+ private readonly ISqlSugarClient _db;
+
+ ///
+ /// 构造函数
+ ///
+ public LqLaundrySupplierService(IUserManager userManager, ILogger logger, ISqlSugarClient db)
+ {
+ _userManager = userManager;
+ _logger = logger;
+ _db = db;
+ }
+
+ #region 创建清洗商
+ ///
+ /// 创建清洗商
+ ///
+ ///
+ /// 创建清洗商记录,一个清洗商对应一个产品类型一条记录
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "supplierName": "清洗商A",
+ /// "productType": "毛巾",
+ /// "laundryPrice": 5.00,
+ /// "remark": "备注信息"
+ /// }
+ /// ```
+ ///
+ /// 创建输入
+ /// 创建结果
+ /// 创建成功
+ /// 清洗商已存在或参数错误
+ /// 服务器错误
+ [HttpPost("Create")]
+ public async Task CreateAsync([FromBody] LqLaundrySupplierCrInput input)
+ {
+ try
+ {
+ // 检查是否已存在相同清洗商和产品类型的记录
+ var existing = await _db.Queryable()
+ .Where(x => x.SupplierName == input.SupplierName
+ && x.ProductType == input.ProductType
+ && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (existing != null)
+ {
+ throw NCCException.Oh($"清洗商【{input.SupplierName}】已存在{input.ProductType}的记录");
+ }
+
+ // 创建清洗商记录
+ var entity = new LqLaundrySupplierEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ SupplierName = input.SupplierName,
+ ProductType = input.ProductType,
+ LaundryPrice = input.LaundryPrice,
+ Remark = input.Remark,
+ IsEffective = StatusEnum.有效.GetHashCode(),
+ CreateUser = _userManager.UserId,
+ CreateTime = DateTime.Now
+ };
+
+ var isOk = await _db.Insertable(entity).ExecuteCommandAsync();
+ if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "创建清洗商失败");
+ throw NCCException.Oh($"创建失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 更新清洗商
+ ///
+ /// 更新清洗商
+ ///
+ ///
+ /// 更新清洗商信息,包括价格(更新价格不影响历史流水记录的价格)
+ ///
+ /// 更新输入
+ /// 更新结果
+ /// 更新成功
+ /// 记录不存在或参数错误
+ /// 服务器错误
+ [HttpPut("Update")]
+ public async Task UpdateAsync([FromBody] LqLaundrySupplierUpInput input)
+ {
+ try
+ {
+ var existing = await _db.Queryable()
+ .Where(x => x.Id == input.Id && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (existing == null)
+ {
+ throw NCCException.Oh("清洗商记录不存在或已失效");
+ }
+
+ // 如果清洗商名称或产品类型改变,检查是否冲突
+ if (existing.SupplierName != input.SupplierName || existing.ProductType != input.ProductType)
+ {
+ var conflict = await _db.Queryable()
+ .Where(x => x.Id != input.Id
+ && x.SupplierName == input.SupplierName
+ && x.ProductType == input.ProductType
+ && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (conflict != null)
+ {
+ throw NCCException.Oh($"清洗商【{input.SupplierName}】已存在{input.ProductType}的记录");
+ }
+ }
+
+ // 更新记录
+ existing.SupplierName = input.SupplierName;
+ existing.ProductType = input.ProductType;
+ existing.LaundryPrice = input.LaundryPrice;
+ existing.Remark = input.Remark;
+ existing.UpdateUser = _userManager.UserId;
+ existing.UpdateTime = DateTime.Now;
+
+ var isOk = await _db.Updateable(existing).ExecuteCommandAsync();
+ if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "更新清洗商失败");
+ throw NCCException.Oh($"更新失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 获取清洗商列表
+ ///
+ /// 获取清洗商列表
+ ///
+ ///
+ /// 分页查询清洗商列表,支持按清洗商名称、产品类型筛选
+ ///
+ /// 查询输入
+ /// 清洗商列表
+ /// 查询成功
+ /// 服务器错误
+ [HttpGet("GetList")]
+ public async Task GetListAsync([FromQuery] LqLaundrySupplierListQueryInput input)
+ {
+ try
+ {
+ var sidx = string.IsNullOrEmpty(input.sidx) ? "createTime" : input.sidx;
+ var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort;
+
+ var data = await _db.Queryable()
+ .WhereIF(!string.IsNullOrWhiteSpace(input.SupplierName), x => x.SupplierName.Contains(input.SupplierName))
+ .WhereIF(!string.IsNullOrWhiteSpace(input.ProductType), x => x.ProductType == input.ProductType)
+ .WhereIF(input.IsEffective.HasValue, x => x.IsEffective == input.IsEffective.Value)
+ .Select(x => new LqLaundrySupplierListOutput
+ {
+ id = x.Id,
+ supplierName = x.SupplierName,
+ productType = x.ProductType,
+ laundryPrice = x.LaundryPrice,
+ remark = x.Remark,
+ isEffective = x.IsEffective,
+ createUser = x.CreateUser,
+ createUserName = "",
+ createTime = x.CreateTime,
+ updateUser = x.UpdateUser,
+ updateUserName = "",
+ updateTime = x.UpdateTime
+ })
+ .MergeTable()
+ .OrderBy(sidx + " " + sort)
+ .ToPagedListAsync(input.currentPage, input.pageSize);
+
+ // 补充用户名称信息
+ var userIds = data.list.SelectMany(x => new[] { x.createUser, x.updateUser })
+ .Where(x => !string.IsNullOrEmpty(x))
+ .Distinct()
+ .ToList();
+
+ if (userIds.Any())
+ {
+ var userList = await _db.Queryable()
+ .Where(x => userIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.RealName })
+ .ToListAsync();
+ var userDict = userList.ToDictionary(k => k.Id, v => v.RealName);
+
+ foreach (var item in data.list)
+ {
+ item.createUserName = userDict.ContainsKey(item.createUser) ? userDict[item.createUser] : "";
+ item.updateUserName = !string.IsNullOrEmpty(item.updateUser) && userDict.ContainsKey(item.updateUser) ? userDict[item.updateUser] : "";
+ }
+ }
+
+ return PageResult.SqlSugarPageResult(data);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取清洗商列表失败");
+ throw NCCException.Oh($"查询失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 获取清洗商详情
+ ///
+ /// 获取清洗商详情
+ ///
+ ///
+ /// 根据ID获取清洗商的详细信息
+ ///
+ /// 清洗商ID
+ /// 清洗商详情
+ /// 查询成功
+ /// 记录不存在
+ /// 服务器错误
+ [HttpGet("{id}")]
+ public async Task GetInfoAsync(string id)
+ {
+ try
+ {
+ var entity = await _db.Queryable()
+ .Where(x => x.Id == id)
+ .Select(x => new LqLaundrySupplierInfoOutput
+ {
+ id = x.Id,
+ supplierName = x.SupplierName,
+ productType = x.ProductType,
+ laundryPrice = x.LaundryPrice,
+ remark = x.Remark,
+ isEffective = x.IsEffective,
+ createUser = x.CreateUser,
+ createUserName = "",
+ createTime = x.CreateTime,
+ updateUser = x.UpdateUser,
+ updateUserName = "",
+ updateTime = x.UpdateTime
+ })
+ .FirstAsync();
+
+ if (entity == null)
+ {
+ throw NCCException.Oh("清洗商记录不存在");
+ }
+
+ // 补充用户名称
+ if (!string.IsNullOrEmpty(entity.createUser))
+ {
+ var createUser = await _db.Queryable()
+ .Where(x => x.Id == entity.createUser)
+ .Select(x => x.RealName)
+ .FirstAsync();
+ entity.createUserName = createUser ?? "";
+ }
+
+ if (!string.IsNullOrEmpty(entity.updateUser))
+ {
+ var updateUser = await _db.Queryable()
+ .Where(x => x.Id == entity.updateUser)
+ .Select(x => x.RealName)
+ .FirstAsync();
+ entity.updateUserName = updateUser ?? "";
+ }
+
+ return entity;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取清洗商详情失败");
+ throw NCCException.Oh($"查询失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 删除/作废清洗商
+ ///
+ /// 删除/作废清洗商
+ ///
+ ///
+ /// 将清洗商记录标记为无效
+ ///
+ /// 清洗商ID
+ /// 操作结果
+ /// 操作成功
+ /// 记录不存在
+ /// 服务器错误
+ [HttpDelete("{id}")]
+ public async Task DeleteAsync(string id)
+ {
+ try
+ {
+ var entity = await _db.Queryable()
+ .Where(x => x.Id == id)
+ .FirstAsync();
+
+ if (entity == null)
+ {
+ throw NCCException.Oh("清洗商记录不存在");
+ }
+
+ entity.IsEffective = StatusEnum.无效.GetHashCode();
+ entity.UpdateUser = _userManager.UserId;
+ entity.UpdateTime = DateTime.Now;
+
+ var isOk = await _db.Updateable(entity).ExecuteCommandAsync();
+ if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "删除清洗商失败");
+ throw NCCException.Oh($"删除失败:{ex.Message}");
+ }
+ }
+ #endregion
+ }
+}
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
index d2b781a..77942db 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -26,6 +26,8 @@ using NCC.Extend.Entitys.lq_kd_jksyj;
using NCC.Extend.Entitys.lq_kd_kdjlb;
using NCC.Extend.Entitys.lq_kd_kjbsyj;
using NCC.Extend.Entitys.lq_mdxx;
+using NCC.Extend.Entitys.lq_md_target;
+using NCC.Extend.Entitys.lq_hytk_hytk;
using NCC.Extend.Entitys.lq_md_xdbhsj;
using NCC.Extend.Entitys.lq_xh_kjbsyj;
using NCC.Extend.Entitys.lq_xh_hyhk;
@@ -50,6 +52,7 @@ using NCC.System.Entitys.Permission;
using SqlSugar;
using Yitter.IdGenerator;
using NCC.Extend.Entitys.lq_kd_pxmx;
+using NCC.Extend.Entitys.lq_khxx;
namespace NCC.Extend.LqStatistics
{
@@ -886,52 +889,18 @@ namespace NCC.Extend.LqStatistics
var now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);
-
// 获取当前月份(YYYYMM格式)
var currentMonth = now.ToString("yyyyMM");
-
// 1. 获取本月的门店目标业绩总和(从门店目标表获取 F_StoreTarget 字段,按月份过滤)
- var targetPerformanceSql = @"
- SELECT
- COALESCE(SUM(F_StoreTarget), 0) AS TotalTargetPerformance
- FROM lq_md_target
- WHERE F_Month = @month";
-
- var targetParameters = new Dictionary
- {
- { "@month", currentMonth }
- };
-
- var targetResult = await _db.Ado.SqlQueryAsync(targetPerformanceSql, targetParameters);
- var targetPerformance = 0m;
- if (targetResult != null && targetResult.Any())
- {
- targetPerformance = Convert.ToDecimal(targetResult.FirstOrDefault()?.TotalTargetPerformance ?? 0);
- }
-
+ var targetPerformance = await _db.Queryable().Where(x => x.Month == currentMonth).SumAsync(x => (decimal?)x.StoreTarget) ?? 0m;
// 2. 从开单记录表获取本月实付金额的总和(sfyj 字段)
- var actualPerformanceSql = @"
- SELECT
- COALESCE(SUM(sfyj), 0) AS TotalActualPerformance
- FROM lq_kd_kdjlb
- WHERE F_IsEffective = 1
- AND kdrq >= @startDate
- AND kdrq <= @endDate";
-
- var parameters = new Dictionary
- {
- { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") },
- { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") }
- };
-
- var actualResult = await _db.Ado.SqlQueryAsync(actualPerformanceSql, parameters);
- var actualPerformance = 0m;
- if (actualResult != null && actualResult.Any())
- {
- actualPerformance = Convert.ToDecimal(actualResult.FirstOrDefault()?.TotalActualPerformance ?? 0);
- }
-
- // 3. 计算完成率
+ var endDateTime = endDate.Date.AddDays(1).AddSeconds(-1); // 结束日期的23:59:59
+ var billingPerformance = await _db.Queryable().Where(x => x.IsEffective == 1).Where(x => x.Kdrq.HasValue && x.Kdrq.Value >= startDate && x.Kdrq.Value <= endDateTime).SumAsync(x => (decimal?)x.Sfyj) ?? 0m;
+ // 3. 从退卡记录表获取本月退卡金额的总和(F_ActualRefundAmount 字段)
+ var refundPerformance = await _db.Queryable().Where(x => x.IsEffective == 1).Where(x => x.Tksj.HasValue && x.Tksj.Value.Date >= startDate.Date && x.Tksj.Value.Date <= endDate.Date).SumAsync(x => x.ActualRefundAmount) ?? 0m;
+ // 4. 计算实际开单业绩(开单业绩 - 退卡金额)
+ var actualPerformance = billingPerformance - refundPerformance;
+ // 5. 计算完成率(使用实际开单业绩)
var completionRate = 0m;
if (targetPerformance > 0)
{
@@ -941,6 +910,8 @@ namespace NCC.Extend.LqStatistics
return new
{
TargetPerformance = targetPerformance,
+ BillingPerformance = billingPerformance,
+ RefundPerformance = refundPerformance,
ActualPerformance = actualPerformance,
CompletionRate = decimal.Round(completionRate, 2),
Month = now.ToString("yyyy年MM月"),
@@ -952,6 +923,8 @@ namespace NCC.Extend.LqStatistics
return new
{
TargetPerformance = 0m,
+ BillingPerformance = 0m,
+ RefundPerformance = 0m,
ActualPerformance = 0m,
CompletionRate = 0m,
Month = DateTime.Now.ToString("yyyy年MM月"),
@@ -3679,7 +3652,6 @@ namespace NCC.Extend.LqStatistics
/// - ConsumeProjectCount: 消耗项目数
/// - ConsumeAchievement: 消耗业绩
/// - OrderAchievement: 开单业绩(开卡业绩)
- /// - OrderItemCount: 开单品项次数
/// - ConsumeItemCount: 耗卡品项次数
/// - ConsumeLaborCost: 消耗手工费(耗卡手工费)
///
@@ -3689,92 +3661,45 @@ namespace NCC.Extend.LqStatistics
/// 参数错误
/// 服务器内部错误
[HttpPost("GetTechTeacherStatistics")]
- [AllowAnonymous]
public async Task> GetTechTeacherStatistics(TechTeacherStatisticsInput input)
{
+ TechTeacherSimpleStatisticsOutput result = new TechTeacherSimpleStatisticsOutput
+ {
+ DepartmentName = "科技部",
+ TeacherName = "",
+ OrderAchievement = 0m,
+ ConsumeAchievement = 0m,
+ ConsumeItemCount = 0,
+ ConsumeProjectCount = 0,
+ ConsumeLaborCost = 0m,
+ RefundAchievement = 0m,
+ };
try
{
// 1. 验证必须传入科技老师ID
if (string.IsNullOrEmpty(input.TeacherId))
{
- return new List
- {
- new TechTeacherSimpleStatisticsOutput
- {
- DepartmentName = "科技部",
- TeacherName = "",
- OrderAchievement = 0m,
- OrderItemCount = 0,
- ConsumeAchievement = 0m,
- ConsumeItemCount = 0,
- ConsumeProjectCount = 0,
- ConsumeLaborCost = 0m,
- RefundAchievement = 0m,
- }
- };
+ return new List { result };
}
// 2. 获取科技老师信息
- var teacher = await _db.Queryable()
- .Where(x => x.Gw == "科技老师" && x.Id == input.TeacherId)
- .Select(x => new
- {
- TeacherId = x.Id,
- TeacherName = x.RealName,
- TeacherAccount = x.Account,
- })
- .FirstAsync();
-
- if (teacher == null)
+ var teacher = await _db.Queryable().Where(x => x.Gw == "科技老师" && x.Id == input.TeacherId)
+ .Select(x => new
{
- return new List
- {
- new TechTeacherSimpleStatisticsOutput
- {
- DepartmentName = "科技部",
- TeacherName = "",
- OrderAchievement = 0m,
- OrderItemCount = 0,
- ConsumeAchievement = 0m,
- ConsumeItemCount = 0,
- ConsumeProjectCount = 0,
- ConsumeLaborCost = 0m,
- RefundAchievement = 0m,
- }
- };
- }
-
- // 3. 查询开单业绩(从 lq_kd_kjbsyj 关联 lq_kd_kdjlb 和 lq_kd_pxmx)
- var orderQuery = _db.Queryable(
- (kjbsyj, kdjlb, pxmx) => kjbsyj.Glkdbh == kdjlb.Id && kjbsyj.Kdpxid == pxmx.Id)
- .Where((kjbsyj, kdjlb, pxmx) => kjbsyj.IsEffective == StatusEnum.有效.GetHashCode())
- .Where((kjbsyj, kdjlb, pxmx) => kdjlb.IsEffective == StatusEnum.有效.GetHashCode())
- .Where((kjbsyj, kdjlb, pxmx) => kjbsyj.Kjblszh == teacher.TeacherAccount);
-
- // 日期过滤
- if (input.StartDate.HasValue)
- {
- orderQuery = orderQuery.Where((kjbsyj, kdjlb, pxmx) => kjbsyj.Yjsj >= input.StartDate.Value);
- }
+ TeacherId = x.Id,
+ TeacherName = x.RealName,
+ TeacherAccount = x.Account,
+ }).FirstAsync();
- if (input.EndDate.HasValue)
+ if (teacher == null)
{
- orderQuery = orderQuery.Where((kjbsyj, kdjlb, pxmx) => kjbsyj.Yjsj <= input.EndDate.Value);
+ return new List { result };
}
-
- var orderStats = await orderQuery
- .Select((kjbsyj, kdjlb, pxmx) => new
- {
- OrderAchievement = SqlFunc.ToDecimal(kjbsyj.Kjblsyj),
- OrderItemCount = SqlFunc.ToInt32(pxmx.ProjectNumber),
- })
- .ToListAsync();
+ // 3. 查询开单业绩(从 lq_kd_kjbsyj 表)
+ var OrderAchievement = await _db.Queryable().Where(x => x.Kjbls == teacher.TeacherId && x.IsEffective == StatusEnum.有效.GetHashCode() && x.Yjsj >= input.StartDate.Value && x.Yjsj <= input.EndDate.Value).SumAsync(x => x.Kjblsyj);
// 4. 查询耗卡业绩(从 lq_xh_kjbsyj 关联 lq_xh_hyhk 和 lq_xh_pxmx)
- // 注意:lq_xh_kjbsyj.kjbls 字段存储的是健康师id,不是科技部老师id
- // 科技部老师信息在 kjblszh(账号)和 kjblsxm(姓名)字段
- var consumeQuery = _db.Queryable(
- (kjbsyj, hyhk, pxmx) => kjbsyj.Glkdbh == hyhk.Id && kjbsyj.Hkpxid == pxmx.Id)
+ var consumeQuery = _db.Queryable((kjbsyj, hyhk, pxmx) => kjbsyj.Glkdbh == hyhk.Id && kjbsyj.Hkpxid == pxmx.Id)
.Where((kjbsyj, hyhk, pxmx) => kjbsyj.IsEffective == StatusEnum.有效.GetHashCode())
.Where((kjbsyj, hyhk, pxmx) => hyhk.IsEffective == StatusEnum.有效.GetHashCode())
.Where((kjbsyj, hyhk, pxmx) => kjbsyj.Kjblszh == teacher.TeacherAccount);
@@ -3799,69 +3724,230 @@ namespace NCC.Extend.LqStatistics
ConsumeLaborCost = kjbsyj.LaborCost,
})
.ToListAsync();
-
// 5. 查询退卡业绩(从 lq_hytk_kjbsyj 表)
- var refundQuery = _db.Queryable()
- .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
- .Where(x => x.Kjblszh == teacher.TeacherAccount);
+ var RefundAchievement = await _db.Queryable().Where(x => x.Kjbls == teacher.TeacherId && x.IsEffective == StatusEnum.有效.GetHashCode() && x.Tksj >= input.StartDate.Value && x.Tksj <= input.EndDate.Value).SumAsync(x => x.Kjblsyj);
+ // 6. 统计并返回结果
+ result.TeacherName = teacher.TeacherName;
+ result.OrderAchievement = OrderAchievement.ToDecimal();
+ result.ConsumeAchievement = consumeStats.Sum(x => x.ConsumeAchievement ?? 0m);
+ result.ConsumeItemCount = consumeStats.Sum(x => x.ConsumeItemCount);
+ result.ConsumeProjectCount = consumeStats.Sum(x => x.ConsumeProjectCount);
+ result.ConsumeLaborCost = consumeStats.Sum(x => x.ConsumeLaborCost ?? 0m);
+ result.RefundAchievement = RefundAchievement.ToDecimal();
+ return new List { result };
+ }
+ catch (Exception)
+ {
+ return new List { result };
+ }
+ }
- // 日期过滤
- if (input.StartDate.HasValue)
- {
- refundQuery = refundQuery.Where(x => x.Tksj >= input.StartDate.Value);
- }
+ #endregion
- if (input.EndDate.HasValue)
+ #region 只买了女神卡的会员统计
+ ///
+ /// 获取只买了女神卡的会员及其开单数据(分页)
+ ///
+ ///
+ /// 统计所有只购买了女神卡(品项编号为61)的会员,并返回这些会员的开单记录
+ ///
+ /// 判断逻辑:
+ /// 1. 会员必须有购买女神卡的记录
+ /// 2. 该会员的所有有效开单记录中,所有品项都必须是女神卡(px = "61")
+ ///
+ /// 返回说明:
+ /// - list: 会员列表
+ /// - memberId: 会员ID
+ /// - memberName: 会员名称
+ /// - phone: 手机号
+ /// - storeId: 归属门店ID
+ /// - storeName: 归属门店名称
+ /// - billingList: 开单记录列表
+ /// - billingId: 开单编号
+ /// - billingDate: 开单日期
+ /// - actualPerformance: 实付业绩
+ /// - storeId: 门店ID
+ /// - storeName: 门店名称
+ /// - itemList: 品项列表
+ /// - itemId: 品项明细ID
+ /// - itemCode: 品项编号(如61)
+ /// - itemName: 品项名称(如女神卡)
+ /// - itemPrice: 品项价格
+ /// - sourceType: 来源类型
+ /// - projectNumber: 项目次数
+ /// - totalPrice: 总价
+ /// - actualPrice: 实付金额
+ /// - performanceTime: 业绩时间
+ /// - pagination: 分页信息
+ /// - pageIndex: 当前页码
+ /// - pageSize: 每页数量
+ /// - total: 总记录数
+ ///
+ /// 查询参数
+ /// 只买了女神卡的会员列表(分页)
+ /// 成功返回会员列表
+ /// 服务器内部错误
+ [HttpPost("GetGoddessCardMembers")]
+ public async Task GetGoddessCardMembers([FromBody] GoddessCardMemberListQueryInput input)
+ {
+ try
+ {
+ // 使用SQL一次性查询出符合条件的会员ID
+ // 逻辑:会员有购买女神卡的记录,且该会员的所有开单记录的所有品项都是女神卡
+ var sql = @"
+ SELECT DISTINCT kd1.Kdhy AS MemberId
+ FROM lq_kd_pxmx pxmx1
+ INNER JOIN lq_kd_kdjlb kd1 ON pxmx1.glkdbh = kd1.F_Id
+ WHERE pxmx1.px = '61'
+ AND pxmx1.F_IsEffective = 1
+ AND kd1.F_IsEffective = 1
+ AND kd1.Kdhy IS NOT NULL
+ AND kd1.Kdhy != ''
+ AND NOT EXISTS (
+ -- 排除那些有非女神卡品项的会员
+ SELECT 1
+ FROM lq_kd_pxmx pxmx2
+ INNER JOIN lq_kd_kdjlb kd2 ON pxmx2.glkdbh = kd2.F_Id
+ WHERE kd2.Kdhy = kd1.Kdhy
+ AND pxmx2.F_IsEffective = 1
+ AND kd2.F_IsEffective = 1
+ AND pxmx2.px != '61'
+ )";
+
+ var validMemberIds = await _db.Ado.SqlQueryAsync(sql);
+
+ if (!validMemberIds.Any())
{
- refundQuery = refundQuery.Where(x => x.Tksj <= input.EndDate.Value);
+ return new
+ {
+ list = new List(),
+ pagination = new
+ {
+ pageIndex = input.PageIndex,
+ pageSize = input.PageSize,
+ total = 0
+ }
+ };
}
- var refundStats = await refundQuery
+ // 分页处理
+ var totalCount = validMemberIds.Count;
+ var pagedMemberIds = validMemberIds
+ .Skip((input.PageIndex - 1) * input.PageSize)
+ .Take(input.PageSize)
+ .ToList();
+
+ // 获取会员基本信息
+ var members = await _db.Queryable()
+ .Where(x => pagedMemberIds.Contains(x.Id))
.Select(x => new
{
- RefundAchievement = x.Kjblsyj,
+ x.Id,
+ x.Khmc,
+ x.Sjh,
+ x.Gsmd
})
.ToListAsync();
- // 6. 统计并返回结果
- var result = new TechTeacherSimpleStatisticsOutput
- {
- DepartmentName = "科技部",
- TeacherName = teacher.TeacherName,
- OrderAchievement = orderStats.Sum(x => x.OrderAchievement != null && decimal.TryParse(x.OrderAchievement.ToString(), out var val) ? val : 0m),
- OrderItemCount = orderStats.Sum(x => x.OrderItemCount),
- ConsumeAchievement = consumeStats.Sum(x => x.ConsumeAchievement ?? 0m),
- ConsumeItemCount = consumeStats.Sum(x => x.ConsumeItemCount),
- ConsumeProjectCount = consumeStats.Sum(x => x.ConsumeProjectCount),
- ConsumeLaborCost = consumeStats.Sum(x => x.ConsumeLaborCost ?? 0m),
- RefundAchievement = refundStats.Sum(x => x.RefundAchievement ?? 0m),
- };
+ // 获取门店信息
+ var storeIds = members.Where(x => !string.IsNullOrEmpty(x.Gsmd)).Select(x => x.Gsmd).Distinct().ToList();
+ var stores = new Dictionary();
+ if (storeIds.Any())
+ {
+ var storeList = await _db.Queryable()
+ .Where(x => storeIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.Dm })
+ .ToListAsync();
+ stores = storeList.ToDictionary(x => x.Id, x => x.Dm ?? "");
+ }
- return new List { result };
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "获取科技部老师业绩统计时发生错误");
- // 发生错误时返回全0的结果,而不是抛出异常
- return new List
+ // 获取所有开单记录
+ var billings = await _db.Queryable().Where(x => pagedMemberIds.Contains(x.Kdhy) && x.IsEffective == StatusEnum.有效.GetHashCode()).ToListAsync();
+
+ // 获取开单记录的门店信息
+ var billingStoreIds = billings.Where(x => !string.IsNullOrEmpty(x.Djmd)).Select(x => x.Djmd).Distinct().ToList();
+ var billingStores = new Dictionary();
+ if (billingStoreIds.Any())
+ {
+ var billingStoreList = await _db.Queryable()
+ .Where(x => billingStoreIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.Dm })
+ .ToListAsync();
+ billingStores = billingStoreList.ToDictionary(x => x.Id, x => x.Dm ?? "");
+ }
+
+ // 获取所有开单的品项明细
+ var billingIds = billings.Select(x => x.Id).ToList();
+ var billingItems = await _db.Queryable()
+ .Where(x => billingIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .ToListAsync();
+
+ // 按开单编号分组品项
+ var itemsByBilling = billingItems
+ .GroupBy(x => x.Glkdbh)
+ .ToDictionary(g => g.Key, g => g.ToList());
+
+ // 组装返回数据
+ var result = members.Select(member =>
+ {
+ var memberBillings = billings.Where(x => x.Kdhy == member.Id).ToList();
+
+ return new GoddessCardMemberOutput
+ {
+ memberId = member.Id,
+ memberName = member.Khmc ?? "",
+ phone = member.Sjh ?? "",
+ storeId = member.Gsmd ?? "",
+ storeName = !string.IsNullOrEmpty(member.Gsmd) && stores.ContainsKey(member.Gsmd) ? stores[member.Gsmd] : "",
+ billingList = memberBillings.Select(b =>
+ {
+ var hasItems = itemsByBilling.ContainsKey(b.Id);
+ var items = hasItems ? itemsByBilling[b.Id] : new List();
+
+ return new GoddessCardBillingInfo
+ {
+ billingId = b.Id,
+ billingDate = b.Kdrq,
+ actualPerformance = b.Sfyj,
+ storeId = b.Djmd ?? "",
+ storeName = !string.IsNullOrEmpty(b.Djmd) && billingStores.ContainsKey(b.Djmd) ? billingStores[b.Djmd] : "",
+ itemList = items.Select(item => new GoddessCardBillingItemInfo
+ {
+ itemId = item.Id ?? "",
+ itemCode = item.Px ?? "",
+ itemName = item.Pxmc ?? "",
+ itemPrice = item.Pxjg,
+ sourceType = item.SourceType ?? "",
+ projectNumber = item.ProjectNumber,
+ totalPrice = item.TotalPrice,
+ actualPrice = item.ActualPrice,
+ performanceTime = item.Yjsj
+ }).ToList()
+ };
+ }).ToList()
+ };
+ }).ToList();
+
+ return new
{
- new TechTeacherSimpleStatisticsOutput
+ list = result,
+ pagination = new
{
- DepartmentName = "科技部",
- TeacherName = "",
- OrderAchievement = 0m,
- OrderItemCount = 0,
- ConsumeAchievement = 0m,
- ConsumeItemCount = 0,
- ConsumeProjectCount = 0,
- ConsumeLaborCost = 0m,
- RefundAchievement = 0m,
+ pageIndex = input.PageIndex,
+ pageSize = input.PageSize,
+ total = totalCount
}
};
}
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取只买了女神卡的会员数据时发生错误");
+ throw NCCException.Oh($"获取数据失败:{ex.Message}");
+ }
}
-
#endregion
+
+
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqStoreConsumableInventoryService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqStoreConsumableInventoryService.cs
new file mode 100644
index 0000000..367f998
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqStoreConsumableInventoryService.cs
@@ -0,0 +1,373 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using NCC.Common.Core.Manager;
+using NCC.Common.Enum;
+using NCC.Common.Filter;
+using NCC.Dependency;
+using NCC.DynamicApiController;
+using NCC.Extend.Entitys.Dto.LqStoreConsumableInventory;
+using NCC.Extend.Entitys.Enum;
+using NCC.Extend.Entitys.lq_mdxx;
+using NCC.Extend.Entitys.lq_store_consumable_inventory;
+using NCC.Extend.Interfaces.LqStoreConsumableInventory;
+using NCC.FriendlyException;
+using NCC.System.Entitys.Permission;
+using SqlSugar;
+using Yitter.IdGenerator;
+
+namespace NCC.Extend
+{
+ ///
+ /// 门店消耗品库存服务
+ ///
+ [ApiDescriptionSettings(Tag = "绿纤门店消耗品库存管理", Name = "LqStoreConsumableInventory", Order = 200)]
+ [Route("api/Extend/LqStoreConsumableInventory")]
+ public class LqStoreConsumableInventoryService : IDynamicApiController, ITransient, ILqStoreConsumableInventoryService
+ {
+ private readonly IUserManager _userManager;
+ private readonly ILogger _logger;
+ private readonly ISqlSugarClient _db;
+
+ ///
+ /// 构造函数
+ ///
+ public LqStoreConsumableInventoryService(IUserManager userManager, ILogger logger, ISqlSugarClient db)
+ {
+ _userManager = userManager;
+ _logger = logger;
+ _db = db;
+ }
+
+ #region 创建门店消耗品库存
+ ///
+ /// 创建门店消耗品库存
+ ///
+ ///
+ /// 为指定门店创建消耗品库存记录
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "storeId": "门店ID",
+ /// "productType": "毛巾",
+ /// "quantity": 100
+ /// }
+ /// ```
+ ///
+ /// 创建输入
+ /// 创建结果
+ /// 创建成功
+ /// 门店不存在或参数错误
+ /// 服务器错误
+ [HttpPost("Create")]
+ public async Task CreateAsync([FromBody] LqStoreConsumableInventoryCrInput input)
+ {
+ try
+ {
+ // 验证门店是否存在
+ var store = await _db.Queryable()
+ .Where(x => x.Id == input.StoreId)
+ .FirstAsync();
+
+ if (store == null)
+ {
+ throw NCCException.Oh("门店不存在");
+ }
+
+ // 检查是否已存在相同门店和产品类型的记录
+ var existing = await _db.Queryable()
+ .Where(x => x.StoreId == input.StoreId
+ && x.ProductType == input.ProductType
+ && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (existing != null)
+ {
+ throw NCCException.Oh($"该门店已存在{input.ProductType}的库存记录,请使用更新功能");
+ }
+
+ // 创建库存记录
+ var entity = new LqStoreConsumableInventoryEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ StoreId = input.StoreId,
+ ProductType = input.ProductType,
+ Quantity = input.Quantity,
+ IsEffective = StatusEnum.有效.GetHashCode(),
+ CreateUser = _userManager.UserId,
+ CreateTime = DateTime.Now
+ };
+
+ var isOk = await _db.Insertable(entity).ExecuteCommandAsync();
+ if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "创建门店消耗品库存失败");
+ throw NCCException.Oh($"创建失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 更新门店消耗品库存
+ ///
+ /// 更新门店消耗品库存
+ ///
+ ///
+ /// 更新指定门店的消耗品库存记录
+ ///
+ /// 更新输入
+ /// 更新结果
+ /// 更新成功
+ /// 记录不存在或参数错误
+ /// 服务器错误
+ [HttpPut("Update")]
+ public async Task UpdateAsync([FromBody] LqStoreConsumableInventoryUpInput input)
+ {
+ try
+ {
+ var existing = await _db.Queryable()
+ .Where(x => x.Id == input.Id && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (existing == null)
+ {
+ throw NCCException.Oh("库存记录不存在或已失效");
+ }
+
+ // 验证门店是否存在
+ var store = await _db.Queryable()
+ .Where(x => x.Id == input.StoreId)
+ .FirstAsync();
+
+ if (store == null)
+ {
+ throw NCCException.Oh("门店不存在");
+ }
+
+ // 如果门店或产品类型改变,检查是否冲突
+ if (existing.StoreId != input.StoreId || existing.ProductType != input.ProductType)
+ {
+ var conflict = await _db.Queryable()
+ .Where(x => x.Id != input.Id
+ && x.StoreId == input.StoreId
+ && x.ProductType == input.ProductType
+ && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .FirstAsync();
+
+ if (conflict != null)
+ {
+ throw NCCException.Oh($"该门店已存在{input.ProductType}的库存记录");
+ }
+ }
+
+ // 更新记录
+ existing.StoreId = input.StoreId;
+ existing.ProductType = input.ProductType;
+ existing.Quantity = input.Quantity;
+ existing.UpdateUser = _userManager.UserId;
+ existing.UpdateTime = DateTime.Now;
+
+ var isOk = await _db.Updateable(existing).ExecuteCommandAsync();
+ if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "更新门店消耗品库存失败");
+ throw NCCException.Oh($"更新失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 获取门店消耗品库存列表
+ ///
+ /// 获取门店消耗品库存列表
+ ///
+ ///
+ /// 分页查询门店消耗品库存列表,支持按门店、产品类型筛选
+ ///
+ /// 查询输入
+ /// 库存列表
+ /// 查询成功
+ /// 服务器错误
+ [HttpGet("GetList")]
+ public async Task GetListAsync([FromQuery] LqStoreConsumableInventoryListQueryInput input)
+ {
+ try
+ {
+ var sidx = string.IsNullOrEmpty(input.sidx) ? "createTime" : input.sidx;
+ var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort;
+
+ var data = await _db.Queryable(
+ (inv, store) => inv.StoreId == store.Id)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (inv, store) => inv.StoreId == input.StoreId)
+ .WhereIF(!string.IsNullOrWhiteSpace(input.ProductType), (inv, store) => inv.ProductType == input.ProductType)
+ .WhereIF(input.IsEffective.HasValue, (inv, store) => inv.IsEffective == input.IsEffective.Value)
+ .Select((inv, store) => new LqStoreConsumableInventoryListOutput
+ {
+ id = inv.Id,
+ storeId = inv.StoreId,
+ storeName = store.Dm ?? "",
+ productType = inv.ProductType,
+ quantity = inv.Quantity,
+ isEffective = inv.IsEffective,
+ createUser = inv.CreateUser,
+ createUserName = "",
+ createTime = inv.CreateTime,
+ updateUser = inv.UpdateUser,
+ updateUserName = "",
+ updateTime = inv.UpdateTime
+ })
+ .MergeTable()
+ .OrderBy(sidx + " " + sort)
+ .ToPagedListAsync(input.currentPage, input.pageSize);
+
+ // 补充用户名称信息
+ var userIds = data.list.SelectMany(x => new[] { x.createUser, x.updateUser })
+ .Where(x => !string.IsNullOrEmpty(x))
+ .Distinct()
+ .ToList();
+
+ if (userIds.Any())
+ {
+ var userList = await _db.Queryable()
+ .Where(x => userIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.RealName })
+ .ToListAsync();
+ var userDict = userList.ToDictionary(k => k.Id, v => v.RealName);
+
+ foreach (var item in data.list)
+ {
+ item.createUserName = userDict.ContainsKey(item.createUser) ? userDict[item.createUser] : "";
+ item.updateUserName = !string.IsNullOrEmpty(item.updateUser) && userDict.ContainsKey(item.updateUser) ? userDict[item.updateUser] : "";
+ }
+ }
+
+ return PageResult.SqlSugarPageResult(data);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取门店消耗品库存列表失败");
+ throw NCCException.Oh($"查询失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 获取门店消耗品库存详情
+ ///
+ /// 获取门店消耗品库存详情
+ ///
+ ///
+ /// 根据ID获取门店消耗品库存的详细信息
+ ///
+ /// 库存ID
+ /// 库存详情
+ /// 查询成功
+ /// 记录不存在
+ /// 服务器错误
+ [HttpGet("{id}")]
+ public async Task GetInfoAsync(string id)
+ {
+ try
+ {
+ var entity = await _db.Queryable(
+ (inv, store) => inv.StoreId == store.Id)
+ .Where((inv, store) => inv.Id == id)
+ .Select((inv, store) => new LqStoreConsumableInventoryInfoOutput
+ {
+ id = inv.Id,
+ storeId = inv.StoreId,
+ storeName = store.Dm ?? "",
+ productType = inv.ProductType,
+ quantity = inv.Quantity,
+ isEffective = inv.IsEffective,
+ createUser = inv.CreateUser,
+ createUserName = "",
+ createTime = inv.CreateTime,
+ updateUser = inv.UpdateUser,
+ updateUserName = "",
+ updateTime = inv.UpdateTime
+ })
+ .FirstAsync();
+
+ if (entity == null)
+ {
+ throw NCCException.Oh("库存记录不存在");
+ }
+
+ // 补充用户名称
+ if (!string.IsNullOrEmpty(entity.createUser))
+ {
+ var createUser = await _db.Queryable()
+ .Where(x => x.Id == entity.createUser)
+ .Select(x => x.RealName)
+ .FirstAsync();
+ entity.createUserName = createUser ?? "";
+ }
+
+ if (!string.IsNullOrEmpty(entity.updateUser))
+ {
+ var updateUser = await _db.Queryable()
+ .Where(x => x.Id == entity.updateUser)
+ .Select(x => x.RealName)
+ .FirstAsync();
+ entity.updateUserName = updateUser ?? "";
+ }
+
+ return entity;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取门店消耗品库存详情失败");
+ throw NCCException.Oh($"查询失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 删除/作废门店消耗品库存
+ ///
+ /// 删除/作废门店消耗品库存
+ ///
+ ///
+ /// 将库存记录标记为无效
+ ///
+ /// 库存ID
+ /// 操作结果
+ /// 操作成功
+ /// 记录不存在
+ /// 服务器错误
+ [HttpDelete("{id}")]
+ public async Task DeleteAsync(string id)
+ {
+ try
+ {
+ var entity = await _db.Queryable()
+ .Where(x => x.Id == id)
+ .FirstAsync();
+
+ if (entity == null)
+ {
+ throw NCCException.Oh("库存记录不存在");
+ }
+
+ entity.IsEffective = StatusEnum.无效.GetHashCode();
+ entity.UpdateUser = _userManager.UserId;
+ entity.UpdateTime = DateTime.Now;
+
+ var isOk = await _db.Updateable(entity).ExecuteCommandAsync();
+ if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "删除门店消耗品库存失败");
+ throw NCCException.Oh($"删除失败:{ex.Message}");
+ }
+ }
+ #endregion
+ }
+}
+
+
diff --git a/sql/创建清洗管理相关表.sql b/sql/创建清洗管理相关表.sql
new file mode 100644
index 0000000..e90f1ec
--- /dev/null
+++ b/sql/创建清洗管理相关表.sql
@@ -0,0 +1,72 @@
+-- ============================================
+-- 创建清洗管理相关表
+-- ============================================
+-- 说明:包含门店消耗品库存表、清洗商表、清洗流水表
+-- ============================================
+
+-- ============================================
+-- 1. 创建门店消耗品库存表(lq_store_consumable_inventory)
+-- ============================================
+CREATE TABLE IF NOT EXISTS `lq_store_consumable_inventory` (
+ `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID',
+ `F_StoreId` VARCHAR(50) NULL COMMENT '门店ID(关联lq_mdxx.F_Id)',
+ `F_ProductType` VARCHAR(50) NULL COMMENT '产品类型(枚举:毛巾、垫子等)',
+ `F_Quantity` INT NULL DEFAULT 0 COMMENT '当前库存数量',
+ `F_IsEffective` INT NULL DEFAULT 1 COMMENT '是否有效(1:有效 0:无效)',
+ `F_CreateUser` VARCHAR(50) NULL COMMENT '创建人ID',
+ `F_CreateTime` DATETIME NULL COMMENT '创建时间',
+ `F_UpdateUser` VARCHAR(50) NULL COMMENT '更新人ID',
+ `F_UpdateTime` DATETIME NULL COMMENT '更新时间',
+ PRIMARY KEY (`F_Id`),
+ INDEX `idx_store_id` (`F_StoreId`) COMMENT '门店ID索引',
+ INDEX `idx_product_type` (`F_ProductType`) COMMENT '产品类型索引',
+ INDEX `idx_store_product` (`F_StoreId`, `F_ProductType`) COMMENT '门店+产品类型联合索引'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='门店消耗品库存表';
+
+-- ============================================
+-- 2. 创建清洗商表(lq_laundry_supplier)
+-- ============================================
+CREATE TABLE IF NOT EXISTS `lq_laundry_supplier` (
+ `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID',
+ `F_SupplierName` VARCHAR(200) NULL COMMENT '清洗商名称',
+ `F_ProductType` VARCHAR(50) NULL COMMENT '产品类型(枚举:毛巾、垫子等)',
+ `F_LaundryPrice` DECIMAL(18,2) NULL DEFAULT 0 COMMENT '清洗价格(当前价格)',
+ `F_Remark` VARCHAR(1000) NULL COMMENT '备注',
+ `F_IsEffective` INT NULL DEFAULT 1 COMMENT '是否有效(1:有效 0:无效)',
+ `F_CreateUser` VARCHAR(50) NULL COMMENT '创建人ID',
+ `F_CreateTime` DATETIME NULL COMMENT '创建时间',
+ `F_UpdateUser` VARCHAR(50) NULL COMMENT '更新人ID',
+ `F_UpdateTime` DATETIME NULL COMMENT '更新时间',
+ PRIMARY KEY (`F_Id`),
+ INDEX `idx_supplier_name` (`F_SupplierName`) COMMENT '清洗商名称索引',
+ INDEX `idx_product_type` (`F_ProductType`) COMMENT '产品类型索引',
+ INDEX `idx_supplier_product` (`F_SupplierName`, `F_ProductType`) COMMENT '清洗商+产品类型联合索引'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='清洗商表';
+
+-- ============================================
+-- 3. 创建清洗流水表(lq_laundry_flow)
+-- ============================================
+CREATE TABLE IF NOT EXISTS `lq_laundry_flow` (
+ `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID(送出时作为批次号)',
+ `F_FlowType` INT NULL DEFAULT 0 COMMENT '流水类型(0:送出 1:送回)',
+ `F_BatchNumber` VARCHAR(50) NULL COMMENT '批次号(送出时=F_Id,送回时=对应的送出记录的F_Id)',
+ `F_StoreId` VARCHAR(50) NULL COMMENT '门店ID(关联lq_mdxx.F_Id)',
+ `F_ProductType` VARCHAR(50) NULL COMMENT '产品类型(枚举:毛巾、垫子等)',
+ `F_LaundrySupplierId` VARCHAR(50) NULL COMMENT '清洗商ID(关联lq_laundry_supplier.F_Id)',
+ `F_Quantity` INT NULL DEFAULT 0 COMMENT '数量',
+ `F_LaundryPrice` DECIMAL(18,2) NULL DEFAULT 0 COMMENT '清洗单价(记录历史价格)',
+ `F_TotalPrice` DECIMAL(18,2) NULL DEFAULT 0 COMMENT '总费用(数量 × 单价)',
+ `F_Remark` VARCHAR(1000) NULL COMMENT '备注(可说明差异原因,如损坏、丢失等)',
+ `F_IsEffective` INT NULL DEFAULT 1 COMMENT '是否有效(1:有效 0:无效)',
+ `F_CreateUser` VARCHAR(50) NULL COMMENT '创建人ID',
+ `F_CreateTime` DATETIME NULL COMMENT '创建时间',
+ PRIMARY KEY (`F_Id`),
+ INDEX `idx_flow_type` (`F_FlowType`) COMMENT '流水类型索引',
+ INDEX `idx_batch_number` (`F_BatchNumber`) COMMENT '批次号索引',
+ INDEX `idx_store_id` (`F_StoreId`) COMMENT '门店ID索引',
+ INDEX `idx_product_type` (`F_ProductType`) COMMENT '产品类型索引',
+ INDEX `idx_supplier_id` (`F_LaundrySupplierId`) COMMENT '清洗商ID索引',
+ INDEX `idx_create_time` (`F_CreateTime`) COMMENT '创建时间索引',
+ INDEX `idx_store_month` (`F_StoreId`, `F_CreateTime`) COMMENT '门店+时间联合索引(用于月度统计)'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='清洗流水表';
+