Commit 9ec80591acd5b0dd7317dc85419f0869e2c792bb

Authored by “wangming”
1 parent 887ceb49

优化库存使用批量创建接口:库存不足时直接抛出异常;优化批量查询性能;添加实体类描述显示到Swagger;更新项目配置以生成XML注释文件

netcore/src/Infrastructure/NCC/SpecificationDocument/Builders/SpecificationDocumentBuilder.cs
@@ -152,6 +152,9 @@ namespace NCC.SpecificationDocument @@ -152,6 +152,9 @@ namespace NCC.SpecificationDocument
152 //使得 Swagger 能够正确地显示 Enum 的对应关系 152 //使得 Swagger 能够正确地显示 Enum 的对应关系
153 if (_specificationDocumentSettings.EnableEnumSchemaFilter == true) swaggerGenOptions.SchemaFilter<EnumSchemaFilter>(); 153 if (_specificationDocumentSettings.EnableEnumSchemaFilter == true) swaggerGenOptions.SchemaFilter<EnumSchemaFilter>();
154 154
  155 + // 使得 Swagger 能够显示实体类的描述(从 XML 注释中读取)
  156 + swaggerGenOptions.SchemaFilter<EntityDescriptionSchemaFilter>();
  157 +
155 // 支持控制器排序操作 158 // 支持控制器排序操作
156 if (_specificationDocumentSettings.EnableTagsOrderDocumentFilter == true) swaggerGenOptions.DocumentFilter<TagsOrderDocumentFilter>(); 159 if (_specificationDocumentSettings.EnableTagsOrderDocumentFilter == true) swaggerGenOptions.DocumentFilter<TagsOrderDocumentFilter>();
157 160
netcore/src/Infrastructure/NCC/SpecificationDocument/Filters/EntityDescriptionSchemaFilter.cs 0 → 100644
  1 +using NCC.Dependency;
  2 +using NCC.ConfigurableOptions;
  3 +using Microsoft.OpenApi.Models;
  4 +using Swashbuckle.AspNetCore.SwaggerGen;
  5 +using System;
  6 +using System.Collections.Generic;
  7 +using System.IO;
  8 +using System.Linq;
  9 +using System.Xml.Linq;
  10 +
  11 +namespace NCC.SpecificationDocument
  12 +{
  13 + /// <summary>
  14 + /// 修正 规范化文档实体类描述提示
  15 + /// </summary>
  16 + [SuppressSniffer]
  17 + public class EntityDescriptionSchemaFilter : ISchemaFilter
  18 + {
  19 + /// <summary>
  20 + /// 实现过滤器方法
  21 + /// </summary>
  22 + /// <param name="schema">OpenAPI Schema</param>
  23 + /// <param name="context">Schema 过滤器上下文</param>
  24 + public void Apply(OpenApiSchema schema, SchemaFilterContext context)
  25 + {
  26 + var type = context.Type;
  27 +
  28 + // 只处理项目程序集中的类型
  29 + if (!App.Assemblies.Contains(type.Assembly))
  30 + return;
  31 +
  32 + // 尝试从 XML 注释中获取类型描述
  33 + try
  34 + {
  35 + // 直接使用程序集名称查找 XML 文件
  36 + var assemblyName = type.Assembly.GetName().Name;
  37 + var xmlFileName = $"{assemblyName}.xml";
  38 + var xmlFilePath = Path.Combine(AppContext.BaseDirectory, xmlFileName);
  39 +
  40 + if (File.Exists(xmlFilePath))
  41 + {
  42 + try
  43 + {
  44 + var xmlDoc = XDocument.Load(xmlFilePath);
  45 + var memberName = $"T:{type.FullName}";
  46 + var memberElement = xmlDoc.Descendants("member")
  47 + .FirstOrDefault(m => m.Attribute("name")?.Value == memberName);
  48 +
  49 + if (memberElement != null)
  50 + {
  51 + var summaryElement = memberElement.Element("summary");
  52 + if (summaryElement != null)
  53 + {
  54 + var description = summaryElement.Value.Trim();
  55 + if (!string.IsNullOrEmpty(description))
  56 + {
  57 + schema.Description = description;
  58 + return;
  59 + }
  60 + }
  61 + }
  62 + }
  63 + catch
  64 + {
  65 + // 忽略 XML 解析错误
  66 + }
  67 + }
  68 +
  69 + // 如果直接查找失败,尝试从配置的 XML 注释列表中查找
  70 + var specificationDocumentSettings = App.GetOptions<SpecificationDocumentSettingsOptions>();
  71 + var xmlComments = specificationDocumentSettings?.XmlComments;
  72 + if (xmlComments != null && xmlComments.Any())
  73 + {
  74 + foreach (var xmlComment in xmlComments)
  75 + {
  76 + var assemblyXmlName = xmlComment.EndsWith(".xml") ? xmlComment : $"{xmlComment}.xml";
  77 + var assemblyXmlPath = Path.Combine(AppContext.BaseDirectory, assemblyXmlName);
  78 +
  79 + if (File.Exists(assemblyXmlPath) && assemblyXmlPath != xmlFilePath)
  80 + {
  81 + try
  82 + {
  83 + var xmlDoc = XDocument.Load(assemblyXmlPath);
  84 + var memberName = $"T:{type.FullName}";
  85 + var memberElement = xmlDoc.Descendants("member")
  86 + .FirstOrDefault(m => m.Attribute("name")?.Value == memberName);
  87 +
  88 + if (memberElement != null)
  89 + {
  90 + var summaryElement = memberElement.Element("summary");
  91 + if (summaryElement != null)
  92 + {
  93 + var description = summaryElement.Value.Trim();
  94 + if (!string.IsNullOrEmpty(description))
  95 + {
  96 + schema.Description = description;
  97 + break;
  98 + }
  99 + }
  100 + }
  101 + }
  102 + catch
  103 + {
  104 + // 忽略 XML 解析错误
  105 + }
  106 + }
  107 + }
  108 + }
  109 + }
  110 + catch
  111 + {
  112 + // 忽略配置获取错误
  113 + }
  114 + }
  115 + }
  116 +}
  117 +
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
@@ -66,14 +66,24 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb @@ -66,14 +66,24 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
66 public decimal consumeAmount { get; set; } 66 public decimal consumeAmount { get; set; }
67 67
68 /// <summary> 68 /// <summary>
69 - /// 人头 - 统计该健康师在指定时间周期内服务的唯一客户数量(按客户去重 69 + /// 有效人头 - 统计该健康师在指定时间周期内服务的唯一客户数量(按客户去重,F_HasBilling=1,支持小数
70 /// </summary> 70 /// </summary>
71 - public int headCount { get; set; } 71 + public decimal headCount { get; set; }
72 72
73 /// <summary> 73 /// <summary>
74 - /// 人次 - 统计该健康师在指定时间周期内服务的客户人次(按客户+日期去重,同一客户不同天算多次 74 + /// 有效人次 - 统计该健康师在指定时间周期内服务的客户人次(按客户+日期去重,F_HasBilling=1,同一客户不同天算多次,支持小数
75 /// </summary> 75 /// </summary>
76 - public int personCount { get; set; } 76 + public decimal personCount { get; set; }
  77 +
  78 + /// <summary>
  79 + /// 无效人头 - 统计该健康师在指定时间周期内服务的唯一客户数量(按客户去重,F_HasBilling=0,支持小数)
  80 + /// </summary>
  81 + public decimal invalidHeadCount { get; set; }
  82 +
  83 + /// <summary>
  84 + /// 无效人次 - 统计该健康师在指定时间周期内服务的客户人次(按客户+日期去重,F_HasBilling=0,同一客户不同天算多次,支持小数)
  85 + /// </summary>
  86 + public decimal invalidPersonCount { get; set; }
77 87
78 /// <summary> 88 /// <summary>
79 /// 消耗项目数 - 统计该健康师在指定时间周期内消耗的项目总次数 89 /// 消耗项目数 - 统计该健康师在指定时间周期内消耗的项目总次数
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqProduct/LqProductCrInput.cs
1 using System; 1 using System;
2 using System.ComponentModel.DataAnnotations; 2 using System.ComponentModel.DataAnnotations;
  3 +using NCC.Dependency;
3 4
4 namespace NCC.Extend.Entitys.Dto.LqProduct 5 namespace NCC.Extend.Entitys.Dto.LqProduct
5 { 6 {
netcore/src/Modularity/Extend/NCC.Extend.Entitys/NCC.Extend.Entitys.csproj
@@ -2,9 +2,12 @@ @@ -2,9 +2,12 @@
2 <PropertyGroup> 2 <PropertyGroup>
3 <TargetFramework>net6.0</TargetFramework> 3 <TargetFramework>net6.0</TargetFramework>
4 </PropertyGroup> 4 </PropertyGroup>
5 - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> 5 + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  6 + <DocumentationFile>bin\Debug\$(AssemblyName).xml</DocumentationFile>
  7 + </PropertyGroup>
  8 + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
6 <OutputPath /> 9 <OutputPath />
7 - <DocumentationFile>bin\Release\NCC.Extend.Entitys.xml</DocumentationFile> 10 + <DocumentationFile>bin\Release\$(AssemblyName).xml</DocumentationFile>
8 </PropertyGroup> 11 </PropertyGroup>
9 <ItemGroup> 12 <ItemGroup>
10 <ProjectReference Include="..\..\..\Infrastructure\NCC.Expand.Thirdparty\NCC.Expand.Thirdparty.csproj" /> 13 <ProjectReference Include="..\..\..\Infrastructure\NCC.Expand.Thirdparty\NCC.Expand.Thirdparty.csproj" />
netcore/src/Modularity/Extend/NCC.Extend.Interfaces/NCC.Extend.Interfaces.csproj
@@ -3,6 +3,12 @@ @@ -3,6 +3,12 @@
3 <PropertyGroup> 3 <PropertyGroup>
4 <TargetFramework>net6.0</TargetFramework> 4 <TargetFramework>net6.0</TargetFramework>
5 </PropertyGroup> 5 </PropertyGroup>
  6 + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  7 + <DocumentationFile>bin\Debug\$(AssemblyName).xml</DocumentationFile>
  8 + </PropertyGroup>
  9 + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  10 + <DocumentationFile>bin\Release\$(AssemblyName).xml</DocumentationFile>
  11 + </PropertyGroup>
6 12
7 <ItemGroup> 13 <ItemGroup>
8 <ProjectReference Include="..\NCC.Extend.Entitys\NCC.Extend.Entitys.csproj" /> 14 <ProjectReference Include="..\NCC.Extend.Entitys\NCC.Extend.Entitys.csproj" />
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryService.cs
@@ -399,9 +399,7 @@ namespace NCC.Extend @@ -399,9 +399,7 @@ namespace NCC.Extend
399 throw NCCException.Oh("库存ID不能为空"); 399 throw NCCException.Oh("库存ID不能为空");
400 } 400 }
401 401
402 - var inventory = await _db.Queryable<LqInventoryEntity>()  
403 - .Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode())  
404 - .FirstAsync(); 402 + var inventory = await _db.Queryable<LqInventoryEntity>().Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode()).FirstAsync();
405 403
406 if (inventory == null) 404 if (inventory == null)
407 { 405 {
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
@@ -166,71 +166,59 @@ namespace NCC.Extend @@ -166,71 +166,59 @@ namespace NCC.Extend
166 } 166 }
167 167
168 // 生成批次ID(如果未提供) 168 // 生成批次ID(如果未提供)
169 - var batchId = string.IsNullOrWhiteSpace(input.BatchId)  
170 - ? YitIdHelper.NextId().ToString()  
171 - : input.BatchId;  
172 - 169 + var batchId = string.IsNullOrWhiteSpace(input.BatchId) ? YitIdHelper.NextId().ToString() : input.BatchId;
173 var successIds = new List<string>(); 170 var successIds = new List<string>();
174 - var failItems = new List<BatchCreateFailItem>();  
175 171
176 - _db.Ado.BeginTran(); 172 + // 按产品ID分组,批量验证库存(在事务外先检查,避免不必要的回滚)
  173 + var productGroups = input.UsageItems.Select((item, index) => new { Item = item, Index = index }).GroupBy(x => x.Item.ProductId).ToList();
177 174
178 - try  
179 - {  
180 - // 按产品ID分组,批量验证库存  
181 - var productGroups = input.UsageItems  
182 - .Select((item, index) => new { Item = item, Index = index })  
183 - .GroupBy(x => x.Item.ProductId)  
184 - .ToList();  
185 -  
186 - // 计算每个产品的总需求并检查库存  
187 - foreach (var productGroup in productGroups)  
188 - {  
189 - var productId = productGroup.Key;  
190 - var totalRequired = productGroup.Sum(x => x.Item.UsageQuantity); 175 + // 获取所有需要检查的产品ID
  176 + var productIds = productGroups.Select(x => x.Key).Distinct().ToList();
191 177
192 - // 计算该产品的总库存数量  
193 - var totalInventory = await _db.Queryable<LqInventoryEntity>()  
194 - .Where(x => x.ProductId == productId && x.IsEffective == StatusEnum.有效.GetHashCode())  
195 - .SumAsync(x => (int?)x.Quantity) ?? 0; 178 + // 批量查询所有产品的库存信息(总库存)
  179 + var inventoryList = await _db.Queryable<LqInventoryEntity>().Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode()).GroupBy(x => x.ProductId).Select(x => new { ProductId = x.ProductId, TotalInventory = SqlFunc.AggregateSum(x.Quantity) }).ToListAsync();
  180 + var inventoryMap = inventoryList.ToDictionary(x => x.ProductId, x => Convert.ToInt32(x.TotalInventory));
196 181
197 - // 计算该产品的已使用数量  
198 - var totalUsage = await _db.Queryable<LqInventoryUsageEntity>()  
199 - .Where(x => x.ProductId == productId && x.IsEffective == StatusEnum.有效.GetHashCode())  
200 - .SumAsync(x => (int?)x.UsageQuantity) ?? 0; 182 + // 批量查询所有产品的已使用数量
  183 + var usageList = await _db.Queryable<LqInventoryUsageEntity>().Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode()).GroupBy(x => x.ProductId).Select(x => new { ProductId = x.ProductId, TotalUsage = SqlFunc.AggregateSum(x.UsageQuantity) }).ToListAsync();
  184 + var usageMap = usageList.ToDictionary(x => x.ProductId, x => Convert.ToInt32(x.TotalUsage));
201 185
202 - // 计算可用库存  
203 - var availableInventory = totalInventory - totalUsage; 186 + // 批量查询所有产品的名称
  187 + var productDict = await _db.Queryable<LqProductEntity>()
  188 + .Where(x => productIds.Contains(x.Id))
  189 + .Select(x => new { x.Id, x.ProductName })
  190 + .ToListAsync();
  191 + var productNameMap = productDict.ToDictionary(x => x.Id, x => x.ProductName ?? "未知产品");
204 192
205 - // 检查库存是否足够  
206 - if (availableInventory < totalRequired)  
207 - {  
208 - var failIndices = productGroup.Select(x => x.Index).ToList();  
209 -  
210 - foreach (var index in failIndices)  
211 - {  
212 - failItems.Add(new BatchCreateFailItem  
213 - {  
214 - Index = index,  
215 - ProductId = productId,  
216 - Reason = $"库存不足,当前可用库存:{availableInventory},需要数量:{totalRequired}"  
217 - });  
218 - }  
219 - } 193 + // 检查每个产品的库存
  194 + foreach (var productGroup in productGroups)
  195 + {
  196 + var productId = productGroup.Key;
  197 + var totalRequired = productGroup.Sum(x => x.Item.UsageQuantity);
  198 +
  199 + // 从字典中获取库存信息
  200 + var totalInventory = inventoryMap.GetValueOrDefault(productId, 0);
  201 + var totalUsage = usageMap.GetValueOrDefault(productId, 0);
  202 + var availableInventory = totalInventory - totalUsage;
  203 +
  204 + // 检查库存是否足够,如果不足则直接抛出异常
  205 + if (availableInventory < totalRequired)
  206 + {
  207 + var productName = productNameMap.GetValueOrDefault(productId, "未知产品");
  208 + throw NCCException.Oh($"产品【{productName}】(ID: {productId}) 库存不足,当前可用库存:{availableInventory},需要数量:{totalRequired}");
220 } 209 }
  210 + }
  211 +
  212 + _db.Ado.BeginTran();
221 213
222 - // 创建成功的使用记录 214 + try
  215 + {
  216 + // 创建使用记录
223 var entitiesToInsert = new List<LqInventoryUsageEntity>(); 217 var entitiesToInsert = new List<LqInventoryUsageEntity>();
224 for (int i = 0; i < input.UsageItems.Count; i++) 218 for (int i = 0; i < input.UsageItems.Count; i++)
225 { 219 {
226 var item = input.UsageItems[i]; 220 var item = input.UsageItems[i];
227 221
228 - // 跳过失败项  
229 - if (failItems.Any(x => x.Index == i))  
230 - {  
231 - continue;  
232 - }  
233 -  
234 var usageEntity = new LqInventoryUsageEntity 222 var usageEntity = new LqInventoryUsageEntity
235 { 223 {
236 Id = YitIdHelper.NextId().ToString(), 224 Id = YitIdHelper.NextId().ToString(),
@@ -265,9 +253,9 @@ namespace NCC.Extend @@ -265,9 +253,9 @@ namespace NCC.Extend
265 { 253 {
266 BatchId = batchId, 254 BatchId = batchId,
267 SuccessCount = successIds.Count, 255 SuccessCount = successIds.Count,
268 - FailCount = failItems.Count, 256 + FailCount = 0,
269 SuccessIds = successIds, 257 SuccessIds = successIds,
270 - FailItems = failItems 258 + FailItems = new List<BatchCreateFailItem>()
271 }; 259 };
272 } 260 }
273 catch 261 catch
@@ -346,35 +334,34 @@ namespace NCC.Extend @@ -346,35 +334,34 @@ namespace NCC.Extend
346 var sidx = input.sidx == null ? "id" : input.sidx; 334 var sidx = input.sidx == null ? "id" : input.sidx;
347 335
348 // 查询使用记录信息,关联产品表 336 // 查询使用记录信息,关联产品表
349 - var data = await _db.Queryable<LqInventoryUsageEntity, LqProductEntity>(  
350 - (usage, product) => usage.ProductId == product.Id)  
351 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (usage, product) => usage.ProductId == input.ProductId)  
352 - .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (usage, product) => usage.StoreId == input.StoreId)  
353 - .WhereIF(input.UsageStartTime.HasValue, (usage, product) => usage.UsageTime >= input.UsageStartTime.Value)  
354 - .WhereIF(input.UsageEndTime.HasValue, (usage, product) => usage.UsageTime <= input.UsageEndTime.Value)  
355 - .WhereIF(!string.IsNullOrWhiteSpace(input.RelatedConsumeId), (usage, product) => usage.RelatedConsumeId == input.RelatedConsumeId)  
356 - .WhereIF(!string.IsNullOrWhiteSpace(input.UsageBatchId), (usage, product) => usage.UsageBatchId == input.UsageBatchId)  
357 - .WhereIF(input.IsEffective.HasValue, (usage, product) => usage.IsEffective == input.IsEffective.Value)  
358 - .Select((usage, product) => new LqInventoryUsageListOutput 337 + var data = await _db.Queryable<LqInventoryUsageEntity, LqProductEntity>((u, product) => u.ProductId == product.Id)
  338 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (u, product) => u.ProductId == input.ProductId)
  339 + .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (u, product) => u.StoreId == input.StoreId)
  340 + .WhereIF(input.UsageStartTime.HasValue, (u, product) => u.UsageTime >= input.UsageStartTime.Value)
  341 + .WhereIF(input.UsageEndTime.HasValue, (u, product) => u.UsageTime <= input.UsageEndTime.Value)
  342 + .WhereIF(!string.IsNullOrWhiteSpace(input.RelatedConsumeId), (u, product) => u.RelatedConsumeId == input.RelatedConsumeId)
  343 + .WhereIF(!string.IsNullOrWhiteSpace(input.UsageBatchId), (u, product) => u.UsageBatchId == input.UsageBatchId)
  344 + .WhereIF(input.IsEffective.HasValue, (u, product) => u.IsEffective == input.IsEffective.Value)
  345 + .Select((u, product) => new LqInventoryUsageListOutput
359 { 346 {
360 - id = usage.Id,  
361 - productId = usage.ProductId, 347 + id = u.Id,
  348 + productId = u.ProductId,
362 productName = product.ProductName, 349 productName = product.ProductName,
363 productCategory = product.ProductCategory, 350 productCategory = product.ProductCategory,
364 productPrice = product.Price, 351 productPrice = product.Price,
365 - storeId = usage.StoreId,  
366 - storeName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(u => u.Id == usage.StoreId).Select(u => u.Dm),  
367 - usageTime = usage.UsageTime,  
368 - usageQuantity = usage.UsageQuantity,  
369 - relatedConsumeId = usage.RelatedConsumeId,  
370 - usageBatchId = usage.UsageBatchId,  
371 - createUser = usage.CreateUser, 352 + storeId = u.StoreId,
  353 + storeName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(store => store.Id == u.StoreId).Select(store => store.Dm),
  354 + usageTime = u.UsageTime,
  355 + usageQuantity = u.UsageQuantity,
  356 + relatedConsumeId = u.RelatedConsumeId,
  357 + usageBatchId = u.UsageBatchId,
  358 + createUser = u.CreateUser,
372 createUserName = "", 359 createUserName = "",
373 - createTime = usage.CreateTime,  
374 - updateUser = usage.UpdateUser, 360 + createTime = u.CreateTime,
  361 + updateUser = u.UpdateUser,
375 updateUserName = "", 362 updateUserName = "",
376 - updateTime = usage.UpdateTime,  
377 - isEffective = usage.IsEffective 363 + updateTime = u.UpdateTime,
  364 + isEffective = u.IsEffective
378 }) 365 })
379 .MergeTable() 366 .MergeTable()
380 .OrderBy(sidx + " " + input.sort) 367 .OrderBy(sidx + " " + input.sort)
@@ -460,29 +447,29 @@ namespace NCC.Extend @@ -460,29 +447,29 @@ namespace NCC.Extend
460 447
461 // 查询该批次的所有使用记录 448 // 查询该批次的所有使用记录
462 var usageRecords = await _db.Queryable<LqInventoryUsageEntity, LqProductEntity>( 449 var usageRecords = await _db.Queryable<LqInventoryUsageEntity, LqProductEntity>(
463 - (usage, product) => usage.ProductId == product.Id)  
464 - .LeftJoin<LqMdxxEntity>((usage, product, store) => usage.StoreId == store.Id)  
465 - .Where((usage, product, store) => usage.UsageBatchId == batchId)  
466 - .Select((usage, product, store) => new LqInventoryUsageListOutput 450 + (u, product) => u.ProductId == product.Id)
  451 + .LeftJoin<LqMdxxEntity>((u, product, store) => u.StoreId == store.Id)
  452 + .Where((u, product, store) => u.UsageBatchId == batchId)
  453 + .Select((u, product, store) => new LqInventoryUsageListOutput
467 { 454 {
468 - id = usage.Id,  
469 - productId = usage.ProductId, 455 + id = u.Id,
  456 + productId = u.ProductId,
470 productName = product.ProductName, 457 productName = product.ProductName,
471 productCategory = product.ProductCategory, 458 productCategory = product.ProductCategory,
472 productPrice = product.Price, 459 productPrice = product.Price,
473 - storeId = usage.StoreId, 460 + storeId = u.StoreId,
474 storeName = store.Dm, 461 storeName = store.Dm,
475 - usageTime = usage.UsageTime,  
476 - usageQuantity = usage.UsageQuantity,  
477 - relatedConsumeId = usage.RelatedConsumeId,  
478 - usageBatchId = usage.UsageBatchId,  
479 - createUser = usage.CreateUser, 462 + usageTime = u.UsageTime,
  463 + usageQuantity = u.UsageQuantity,
  464 + relatedConsumeId = u.RelatedConsumeId,
  465 + usageBatchId = u.UsageBatchId,
  466 + createUser = u.CreateUser,
480 createUserName = "", 467 createUserName = "",
481 - createTime = usage.CreateTime,  
482 - updateUser = usage.UpdateUser, 468 + createTime = u.CreateTime,
  469 + updateUser = u.UpdateUser,
483 updateUserName = "", 470 updateUserName = "",
484 - updateTime = usage.UpdateTime,  
485 - isEffective = usage.IsEffective 471 + updateTime = u.UpdateTime,
  472 + isEffective = u.IsEffective
486 }) 473 })
487 .MergeTable() 474 .MergeTable()
488 .OrderBy("createTime") 475 .OrderBy("createTime")
@@ -563,23 +550,23 @@ namespace NCC.Extend @@ -563,23 +550,23 @@ namespace NCC.Extend
563 try 550 try
564 { 551 {
565 var data = await _db.Queryable<LqInventoryUsageEntity>() 552 var data = await _db.Queryable<LqInventoryUsageEntity>()
566 - .LeftJoin<LqProductEntity>((usage, product) => usage.ProductId == product.Id)  
567 - .Where((usage, product) => usage.UsageTime >= input.StartTime && usage.UsageTime <= input.EndTime)  
568 - .Where((usage, product) => usage.IsEffective == StatusEnum.有效.GetHashCode())  
569 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (usage, product) => usage.ProductId == input.ProductId)  
570 - .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (usage, product) => usage.StoreId == input.StoreId)  
571 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), (usage, product) => product.ProductCategory == input.ProductCategory)  
572 - .GroupBy((usage, product) => new { usage.ProductId, product.ProductName, product.ProductCategory, product.Price })  
573 - .Select((usage, product) => new ProductUsageStatisticsOutput 553 + .LeftJoin<LqProductEntity>((u, product) => u.ProductId == product.Id)
  554 + .Where((u, product) => u.UsageTime >= input.StartTime && u.UsageTime <= input.EndTime)
  555 + .Where((u, product) => u.IsEffective == StatusEnum.有效.GetHashCode())
  556 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (u, product) => u.ProductId == input.ProductId)
  557 + .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (u, product) => u.StoreId == input.StoreId)
  558 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), (u, product) => product.ProductCategory == input.ProductCategory)
  559 + .GroupBy((u, product) => new { u.ProductId, product.ProductName, product.ProductCategory, product.Price })
  560 + .Select((u, product) => new ProductUsageStatisticsOutput
574 { 561 {
575 - ProductId = usage.ProductId, 562 + ProductId = u.ProductId,
576 ProductName = product.ProductName, 563 ProductName = product.ProductName,
577 ProductCategory = product.ProductCategory, 564 ProductCategory = product.ProductCategory,
578 ProductPrice = product.Price, 565 ProductPrice = product.Price,
579 - TotalUsageQuantity = SqlFunc.AggregateSum(usage.UsageQuantity),  
580 - TotalUsageAmount = SqlFunc.AggregateSum(usage.UsageQuantity * product.Price),  
581 - UsageCount = SqlFunc.AggregateCount(usage.Id),  
582 - AverageUsageQuantity = SqlFunc.AggregateAvg(usage.UsageQuantity) 566 + TotalUsageQuantity = SqlFunc.AggregateSum(u.UsageQuantity),
  567 + TotalUsageAmount = SqlFunc.AggregateSum(u.UsageQuantity * product.Price),
  568 + UsageCount = SqlFunc.AggregateCount(u.Id),
  569 + AverageUsageQuantity = SqlFunc.AggregateAvg(u.UsageQuantity)
583 }) 570 })
584 .ToListAsync(); 571 .ToListAsync();
585 572
@@ -611,22 +598,22 @@ namespace NCC.Extend @@ -611,22 +598,22 @@ namespace NCC.Extend
611 try 598 try
612 { 599 {
613 var data = await _db.Queryable<LqInventoryUsageEntity>() 600 var data = await _db.Queryable<LqInventoryUsageEntity>()
614 - .LeftJoin<LqProductEntity>((usage, product) => usage.ProductId == product.Id)  
615 - .LeftJoin<LqMdxxEntity>((usage, product, store) => usage.StoreId == store.Id)  
616 - .Where((usage, product, store) => usage.UsageTime >= input.StartTime && usage.UsageTime <= input.EndTime)  
617 - .Where((usage, product, store) => usage.IsEffective == StatusEnum.有效.GetHashCode())  
618 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (usage, product, store) => usage.ProductId == input.ProductId)  
619 - .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (usage, product, store) => usage.StoreId == input.StoreId)  
620 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), (usage, product, store) => product.ProductCategory == input.ProductCategory)  
621 - .GroupBy((usage, product, store) => new { usage.StoreId, store.Dm })  
622 - .Select((usage, product, store) => new StoreUsageStatisticsOutput 601 + .LeftJoin<LqProductEntity>((u, product) => u.ProductId == product.Id)
  602 + .LeftJoin<LqMdxxEntity>((u, product, store) => u.StoreId == store.Id)
  603 + .Where((u, product, store) => u.UsageTime >= input.StartTime && u.UsageTime <= input.EndTime)
  604 + .Where((u, product, store) => u.IsEffective == StatusEnum.有效.GetHashCode())
  605 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (u, product, store) => u.ProductId == input.ProductId)
  606 + .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (u, product, store) => u.StoreId == input.StoreId)
  607 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), (u, product, store) => product.ProductCategory == input.ProductCategory)
  608 + .GroupBy((u, product, store) => new { u.StoreId, store.Dm })
  609 + .Select((u, product, store) => new StoreUsageStatisticsOutput
623 { 610 {
624 - StoreId = usage.StoreId, 611 + StoreId = u.StoreId,
625 StoreName = store.Dm, 612 StoreName = store.Dm,
626 - TotalUsageQuantity = SqlFunc.AggregateSum(usage.UsageQuantity),  
627 - TotalUsageAmount = SqlFunc.AggregateSum(usage.UsageQuantity * product.Price),  
628 - UsageCount = SqlFunc.AggregateCount(usage.Id),  
629 - ProductVarietyCount = SqlFunc.AggregateCount(usage.ProductId) 613 + TotalUsageQuantity = SqlFunc.AggregateSum(u.UsageQuantity),
  614 + TotalUsageAmount = SqlFunc.AggregateSum(u.UsageQuantity * product.Price),
  615 + UsageCount = SqlFunc.AggregateCount(u.Id),
  616 + ProductVarietyCount = SqlFunc.AggregateCount(u.ProductId)
630 }) 617 })
631 .ToListAsync(); 618 .ToListAsync();
632 619
@@ -659,18 +646,18 @@ namespace NCC.Extend @@ -659,18 +646,18 @@ namespace NCC.Extend
659 { 646 {
660 // 先获取基础数据 647 // 先获取基础数据
661 var baseData = await _db.Queryable<LqInventoryUsageEntity>() 648 var baseData = await _db.Queryable<LqInventoryUsageEntity>()
662 - .LeftJoin<LqProductEntity>((usage, product) => usage.ProductId == product.Id)  
663 - .Where((usage, product) => usage.UsageTime >= input.StartTime && usage.UsageTime <= input.EndTime)  
664 - .Where((usage, product) => usage.IsEffective == StatusEnum.有效.GetHashCode())  
665 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (usage, product) => usage.ProductId == input.ProductId)  
666 - .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (usage, product) => usage.StoreId == input.StoreId)  
667 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), (usage, product) => product.ProductCategory == input.ProductCategory)  
668 - .Select((usage, product) => new 649 + .LeftJoin<LqProductEntity>((u, product) => u.ProductId == product.Id)
  650 + .Where((u, product) => u.UsageTime >= input.StartTime && u.UsageTime <= input.EndTime)
  651 + .Where((u, product) => u.IsEffective == StatusEnum.有效.GetHashCode())
  652 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (u, product) => u.ProductId == input.ProductId)
  653 + .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (u, product) => u.StoreId == input.StoreId)
  654 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), (u, product) => product.ProductCategory == input.ProductCategory)
  655 + .Select((u, product) => new
669 { 656 {
670 - UsageTime = usage.UsageTime,  
671 - UsageQuantity = usage.UsageQuantity,  
672 - UsageAmount = usage.UsageQuantity * product.Price,  
673 - ProductId = usage.ProductId 657 + UsageTime = u.UsageTime,
  658 + UsageQuantity = u.UsageQuantity,
  659 + UsageAmount = u.UsageQuantity * product.Price,
  660 + ProductId = u.ProductId
674 }) 661 })
675 .ToListAsync(); 662 .ToListAsync();
676 663
@@ -740,22 +727,22 @@ namespace NCC.Extend @@ -740,22 +727,22 @@ namespace NCC.Extend
740 try 727 try
741 { 728 {
742 var data = await _db.Queryable<LqInventoryUsageEntity>() 729 var data = await _db.Queryable<LqInventoryUsageEntity>()
743 - .LeftJoin<LqProductEntity>((usage, product) => usage.ProductId == product.Id)  
744 - .Where((usage, product) => usage.UsageTime >= input.StartTime && usage.UsageTime <= input.EndTime)  
745 - .Where((usage, product) => usage.IsEffective == StatusEnum.有效.GetHashCode())  
746 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (usage, product) => usage.ProductId == input.ProductId)  
747 - .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (usage, product) => usage.StoreId == input.StoreId)  
748 - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), (usage, product) => product.ProductCategory == input.ProductCategory)  
749 - .GroupBy((usage, product) => new { usage.ProductId, product.ProductName, product.ProductCategory })  
750 - .Select((usage, product) => new ProductUsageRankingOutput 730 + .LeftJoin<LqProductEntity>((u, product) => u.ProductId == product.Id)
  731 + .Where((u, product) => u.UsageTime >= input.StartTime && u.UsageTime <= input.EndTime)
  732 + .Where((u, product) => u.IsEffective == StatusEnum.有效.GetHashCode())
  733 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (u, product) => u.ProductId == input.ProductId)
  734 + .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (u, product) => u.StoreId == input.StoreId)
  735 + .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), (u, product) => product.ProductCategory == input.ProductCategory)
  736 + .GroupBy((u, product) => new { u.ProductId, product.ProductName, product.ProductCategory })
  737 + .Select((u, product) => new ProductUsageRankingOutput
751 { 738 {
752 - ProductId = usage.ProductId, 739 + ProductId = u.ProductId,
753 ProductName = product.ProductName, 740 ProductName = product.ProductName,
754 ProductCategory = product.ProductCategory, 741 ProductCategory = product.ProductCategory,
755 - TotalUsageQuantity = SqlFunc.AggregateSum(usage.UsageQuantity),  
756 - TotalUsageAmount = SqlFunc.AggregateSum(usage.UsageQuantity * product.Price),  
757 - UsageCount = SqlFunc.AggregateCount(usage.Id),  
758 - StoreCount = SqlFunc.AggregateCount(usage.StoreId) 742 + TotalUsageQuantity = SqlFunc.AggregateSum(u.UsageQuantity),
  743 + TotalUsageAmount = SqlFunc.AggregateSum(u.UsageQuantity * product.Price),
  744 + UsageCount = SqlFunc.AggregateCount(u.Id),
  745 + StoreCount = SqlFunc.AggregateCount(u.StoreId)
759 }) 746 })
760 .OrderBy("TotalUsageQuantity desc") 747 .OrderBy("TotalUsageQuantity desc")
761 .Take(input.RankingCount) 748 .Take(input.RankingCount)
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
@@ -2549,7 +2549,8 @@ namespace NCC.Extend.LqKdKdjlb @@ -2549,7 +2549,8 @@ namespace NCC.Extend.LqKdKdjlb
2549 totalPrice = it.TotalPrice, 2549 totalPrice = it.TotalPrice,
2550 actualPrice = it.ActualPrice, 2550 actualPrice = it.ActualPrice,
2551 projectNumber = it.ProjectNumber, 2551 projectNumber = it.ProjectNumber,
2552 - remark = it.Remark 2552 + remark = it.Remark,
  2553 + itemCategory = it.ItemCategory,
2553 }) 2554 })
2554 .ToListAsync(); 2555 .ToListAsync();
2555 2556
@@ -2596,7 +2597,8 @@ namespace NCC.Extend.LqKdKdjlb @@ -2596,7 +2597,8 @@ namespace NCC.Extend.LqKdKdjlb
2596 totalPrice = x.totalPrice, 2597 totalPrice = x.totalPrice,
2597 actualPrice = x.actualPrice, 2598 actualPrice = x.actualPrice,
2598 projectNumber = x.projectNumber, 2599 projectNumber = x.projectNumber,
2599 - remark = x.remark 2600 + remark = x.remark,
  2601 + itemCategory = x.itemCategory,
2600 }).ToList(), 2602 }).ToList(),
2601 2603
2602 giftedItems = itemDetails.Where(x => x.glkdbh == billing.id && x.sourceType == "赠送").Select(x => new 2604 giftedItems = itemDetails.Where(x => x.glkdbh == billing.id && x.sourceType == "赠送").Select(x => new
@@ -2608,7 +2610,8 @@ namespace NCC.Extend.LqKdKdjlb @@ -2608,7 +2610,8 @@ namespace NCC.Extend.LqKdKdjlb
2608 totalPrice = x.totalPrice, 2610 totalPrice = x.totalPrice,
2609 actualPrice = x.actualPrice, 2611 actualPrice = x.actualPrice,
2610 projectNumber = x.projectNumber, 2612 projectNumber = x.projectNumber,
2611 - remark = x.remark 2613 + remark = x.remark,
  2614 + itemCategory = x.itemCategory,
2612 }).ToList(), 2615 }).ToList(),
2613 2616
2614 experienceItems = itemDetails.Where(x => x.glkdbh == billing.id && x.sourceType == "体验").Select(x => new 2617 experienceItems = itemDetails.Where(x => x.glkdbh == billing.id && x.sourceType == "体验").Select(x => new
@@ -2620,7 +2623,8 @@ namespace NCC.Extend.LqKdKdjlb @@ -2620,7 +2623,8 @@ namespace NCC.Extend.LqKdKdjlb
2620 totalPrice = x.totalPrice, 2623 totalPrice = x.totalPrice,
2621 actualPrice = x.actualPrice, 2624 actualPrice = x.actualPrice,
2622 projectNumber = x.projectNumber, 2625 projectNumber = x.projectNumber,
2623 - remark = x.remark 2626 + remark = x.remark,
  2627 + itemCategory = x.itemCategory,
2624 }).ToList(), 2628 }).ToList(),
2625 2629
2626 // 金额信息 2630 // 金额信息
@@ -2670,7 +2674,6 @@ namespace NCC.Extend.LqKdKdjlb @@ -2670,7 +2674,6 @@ namespace NCC.Extend.LqKdKdjlb
2670 }, 2674 },
2671 message = "获取开单记录汇总信息成功" 2675 message = "获取开单记录汇总信息成功"
2672 }; 2676 };
2673 -  
2674 return result; 2677 return result;
2675 } 2678 }
2676 catch (Exception ex) 2679 catch (Exception ex)
@@ -3210,8 +3213,10 @@ namespace NCC.Extend.LqKdKdjlb @@ -3210,8 +3213,10 @@ namespace NCC.Extend.LqKdKdjlb
3210 3213
3211 -- 消耗相关统计 3214 -- 消耗相关统计
3212 COALESCE(consume_stats.ConsumeAmount, 0) as ConsumeAmount, 3215 COALESCE(consume_stats.ConsumeAmount, 0) as ConsumeAmount,
3213 - COALESCE(consume_stats.HeadCount, 0) as HeadCount,  
3214 - COALESCE(consume_stats.PersonCount, 0) as PersonCount, 3216 + CAST(COALESCE(headcount_stats.HeadCount, 0) AS DECIMAL(18,2)) as HeadCount,
  3217 + CAST(COALESCE(personcount_stats.PersonCount, 0) AS DECIMAL(18,2)) as PersonCount,
  3218 + CAST(COALESCE(invalid_headcount_stats.HeadCount, 0) AS DECIMAL(18,2)) as InvalidHeadCount,
  3219 + CAST(COALESCE(invalid_personcount_stats.PersonCount, 0) AS DECIMAL(18,2)) as InvalidPersonCount,
3215 CAST(COALESCE(consume_stats.ProjectCount, 0) AS DECIMAL(18,2)) as ProjectCount 3220 CAST(COALESCE(consume_stats.ProjectCount, 0) AS DECIMAL(18,2)) as ProjectCount
3216 3221
3217 FROM BASE_USER u 3222 FROM BASE_USER u
@@ -3274,8 +3279,6 @@ namespace NCC.Extend.LqKdKdjlb @@ -3274,8 +3279,6 @@ namespace NCC.Extend.LqKdKdjlb
3274 SELECT 3279 SELECT
3275 jksyj.jkszh as EmployeeId, 3280 jksyj.jkszh as EmployeeId,
3276 SUM(jksyj.jksyj) as ConsumeAmount, 3281 SUM(jksyj.jksyj) as ConsumeAmount,
3277 - COUNT(DISTINCT hyhk.hy) as HeadCount,  
3278 - COUNT(DISTINCT CONCAT(jksyj.jkszh, '_', hyhk.hy, '_', DATE(hyhk.hksj))) as PersonCount,  
3279 CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount 3282 CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount
3280 FROM lq_xh_jksyj jksyj 3283 FROM lq_xh_jksyj jksyj
3281 INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id 3284 INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
@@ -3287,6 +3290,98 @@ namespace NCC.Extend.LqKdKdjlb @@ -3287,6 +3290,98 @@ namespace NCC.Extend.LqKdKdjlb
3287 GROUP BY jksyj.jkszh 3290 GROUP BY jksyj.jkszh
3288 ) consume_stats ON u.F_Id = consume_stats.EmployeeId 3291 ) consume_stats ON u.F_Id = consume_stats.EmployeeId
3289 3292
  3293 + -- 有效人头统计子查询(从人次记录表获取,按月份+客户+数量去重后累加数量,F_HasBilling=1)
  3294 + LEFT JOIN (
  3295 + SELECT
  3296 + F_PersonId as EmployeeId,
  3297 + CAST(COALESCE(SUM(F_Quantity), 0) AS DECIMAL(18,2)) as HeadCount
  3298 + FROM (
  3299 + SELECT
  3300 + F_PersonId,
  3301 + F_WorkMonth,
  3302 + F_MemberId,
  3303 + F_Quantity
  3304 + FROM lq_person_times_record
  3305 + WHERE F_PersonId IS NOT NULL
  3306 + AND F_IsEffective = 1
  3307 + AND F_PersonType = '健康师'
  3308 + AND F_HasBilling = 1
  3309 + AND F_WorkDate >= DATE(@startTime)
  3310 + AND F_WorkDate <= DATE(@endTime)
  3311 + GROUP BY F_PersonId, F_WorkMonth, F_MemberId, F_Quantity
  3312 + ) as distinct_headcount
  3313 + GROUP BY F_PersonId
  3314 + ) headcount_stats ON u.F_Id = headcount_stats.EmployeeId
  3315 +
  3316 + -- 有效人次统计子查询(从人次记录表获取,按日期+客户+数量去重后累加数量,F_HasBilling=1)
  3317 + LEFT JOIN (
  3318 + SELECT
  3319 + F_PersonId as EmployeeId,
  3320 + CAST(COALESCE(SUM(F_Quantity), 0) AS DECIMAL(18,2)) as PersonCount
  3321 + FROM (
  3322 + SELECT
  3323 + F_PersonId,
  3324 + F_WorkDate,
  3325 + F_MemberId,
  3326 + F_Quantity
  3327 + FROM lq_person_times_record
  3328 + WHERE F_PersonId IS NOT NULL
  3329 + AND F_IsEffective = 1
  3330 + AND F_PersonType = '健康师'
  3331 + AND F_HasBilling = 1
  3332 + AND F_WorkDate >= DATE(@startTime)
  3333 + AND F_WorkDate <= DATE(@endTime)
  3334 + GROUP BY F_PersonId, F_WorkDate, F_MemberId, F_Quantity
  3335 + ) as distinct_personcount
  3336 + GROUP BY F_PersonId
  3337 + ) personcount_stats ON u.F_Id = personcount_stats.EmployeeId
  3338 +
  3339 + -- 无效人头统计子查询(从人次记录表获取,按月份+客户+数量去重后累加数量,F_HasBilling=0)
  3340 + LEFT JOIN (
  3341 + SELECT
  3342 + F_PersonId as EmployeeId,
  3343 + CAST(COALESCE(SUM(F_Quantity), 0) AS DECIMAL(18,2)) as HeadCount
  3344 + FROM (
  3345 + SELECT
  3346 + F_PersonId,
  3347 + F_WorkMonth,
  3348 + F_MemberId,
  3349 + F_Quantity
  3350 + FROM lq_person_times_record
  3351 + WHERE F_PersonId IS NOT NULL
  3352 + AND F_IsEffective = 1
  3353 + AND F_PersonType = '健康师'
  3354 + AND F_HasBilling = 0
  3355 + AND F_WorkDate >= DATE(@startTime)
  3356 + AND F_WorkDate <= DATE(@endTime)
  3357 + GROUP BY F_PersonId, F_WorkMonth, F_MemberId, F_Quantity
  3358 + ) as distinct_headcount
  3359 + GROUP BY F_PersonId
  3360 + ) invalid_headcount_stats ON u.F_Id = invalid_headcount_stats.EmployeeId
  3361 +
  3362 + -- 无效人次统计子查询(从人次记录表获取,按日期+客户+数量去重后累加数量,F_HasBilling=0)
  3363 + LEFT JOIN (
  3364 + SELECT
  3365 + F_PersonId as EmployeeId,
  3366 + CAST(COALESCE(SUM(F_Quantity), 0) AS DECIMAL(18,2)) as PersonCount
  3367 + FROM (
  3368 + SELECT
  3369 + F_PersonId,
  3370 + F_WorkDate,
  3371 + F_MemberId,
  3372 + F_Quantity
  3373 + FROM lq_person_times_record
  3374 + WHERE F_PersonId IS NOT NULL
  3375 + AND F_IsEffective = 1
  3376 + AND F_PersonType = '健康师'
  3377 + AND F_HasBilling = 0
  3378 + AND F_WorkDate >= DATE(@startTime)
  3379 + AND F_WorkDate <= DATE(@endTime)
  3380 + GROUP BY F_PersonId, F_WorkDate, F_MemberId, F_Quantity
  3381 + ) as distinct_personcount
  3382 + GROUP BY F_PersonId
  3383 + ) invalid_personcount_stats ON u.F_Id = invalid_personcount_stats.EmployeeId
  3384 +
3290 WHERE u.F_GW = '健康师' 3385 WHERE u.F_GW = '健康师'
3291 "; 3386 ";
3292 3387
netcore/src/Modularity/Extend/NCC.Extend/LqProductService.cs
@@ -228,7 +228,7 @@ namespace NCC.Extend @@ -228,7 +228,7 @@ namespace NCC.Extend
228 price = x.Price, 228 price = x.Price,
229 productCategory = x.ProductCategory, 229 productCategory = x.ProductCategory,
230 departmentId = x.DepartmentId, 230 departmentId = x.DepartmentId,
231 - departmentName = "", 231 + departmentName = SqlFunc.Subqueryable<OrganizeEntity>().Where(y => y.Id == x.DepartmentId).Select(y => y.FullName),
232 standardUnit = x.StandardUnit, 232 standardUnit = x.StandardUnit,
233 onShelfStatus = x.OnShelfStatus, 233 onShelfStatus = x.OnShelfStatus,
234 statisticsCategory = x.StatisticsCategory, 234 statisticsCategory = x.StatisticsCategory,
netcore/src/Modularity/Extend/NCC.Extend/LqPurchaseRecordsService.cs
@@ -28,7 +28,7 @@ namespace NCC.Extend.LqPurchaseRecords @@ -28,7 +28,7 @@ namespace NCC.Extend.LqPurchaseRecords
28 /// <summary> 28 /// <summary>
29 /// 购买记录表服务 29 /// 购买记录表服务
30 /// </summary> 30 /// </summary>
31 - [ApiDescriptionSettings(Tag = "Extend",Name = "LqPurchaseRecords", Order = 200)] 31 + [ApiDescriptionSettings(Tag = "Extend", Name = "LqPurchaseRecords", Order = 200)]
32 [Route("api/Extend/[controller]")] 32 [Route("api/Extend/[controller]")]
33 public class LqPurchaseRecordsService : ILqPurchaseRecordsService, IDynamicApiController, ITransient 33 public class LqPurchaseRecordsService : ILqPurchaseRecordsService, IDynamicApiController, ITransient
34 { 34 {
@@ -43,7 +43,7 @@ namespace NCC.Extend.LqPurchaseRecords @@ -43,7 +43,7 @@ namespace NCC.Extend.LqPurchaseRecords
43 ISqlSugarRepository<LqPurchaseRecordsEntity> lqPurchaseRecordsRepository, 43 ISqlSugarRepository<LqPurchaseRecordsEntity> lqPurchaseRecordsRepository,
44 IUserManager userManager) 44 IUserManager userManager)
45 { 45 {
46 - _lqPurchaseRecordsRepository = lqPurchaseRecordsRepository; 46 + _lqPurchaseRecordsRepository = lqPurchaseRecordsRepository;
47 _db = _lqPurchaseRecordsRepository.Context; 47 _db = _lqPurchaseRecordsRepository.Context;
48 _userManager = userManager; 48 _userManager = userManager;
49 } 49 }
@@ -98,25 +98,25 @@ namespace NCC.Extend.LqPurchaseRecords @@ -98,25 +98,25 @@ namespace NCC.Extend.LqPurchaseRecords
98 .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0)) 98 .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0))
99 .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59)) 99 .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59))
100 .WhereIF(!string.IsNullOrEmpty(input.applicationId), p => p.ApplicationId.Contains(input.applicationId)) 100 .WhereIF(!string.IsNullOrEmpty(input.applicationId), p => p.ApplicationId.Contains(input.applicationId))
101 - .Select(it=> new LqPurchaseRecordsListOutput 101 + .Select(it => new LqPurchaseRecordsListOutput
102 { 102 {
103 id = it.Id, 103 id = it.Id,
104 - reimbursementCategoryId=it.ReimbursementCategoryId,  
105 - reimbursementCategoryName=it.ReimbursementCategoryName,  
106 - unitPrice=it.UnitPrice,  
107 - quantity=it.Quantity,  
108 - amount=it.Amount,  
109 - memo=it.Memo,  
110 - purchaseTime=it.PurchaseTime,  
111 - createTime=it.CreateTime,  
112 - createUser=it.CreateUser,  
113 - createUserStoreId=it.CreateUserStoreId,  
114 - approveStatus=it.ApproveStatus,  
115 - approveUser=it.ApproveUser,  
116 - approveTime=it.ApproveTime,  
117 - applicationId=it.ApplicationId,  
118 - }).MergeTable().OrderBy(sidx+" "+input.sort).ToPagedListAsync(input.currentPage, input.pageSize);  
119 - return PageResult<LqPurchaseRecordsListOutput>.SqlSugarPageResult(data); 104 + reimbursementCategoryId = it.ReimbursementCategoryId,
  105 + reimbursementCategoryName = it.ReimbursementCategoryName,
  106 + unitPrice = it.UnitPrice,
  107 + quantity = it.Quantity,
  108 + amount = it.Amount,
  109 + memo = it.Memo,
  110 + purchaseTime = it.PurchaseTime,
  111 + createTime = it.CreateTime,
  112 + createUser = it.CreateUser,
  113 + createUserStoreId = it.CreateUserStoreId,
  114 + approveStatus = it.ApproveStatus,
  115 + approveUser = it.ApproveUser,
  116 + approveTime = it.ApproveTime,
  117 + applicationId = it.ApplicationId,
  118 + }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
  119 + return PageResult<LqPurchaseRecordsListOutput>.SqlSugarPageResult(data);
120 } 120 }
121 121
122 /// <summary> 122 /// <summary>
@@ -171,25 +171,25 @@ namespace NCC.Extend.LqPurchaseRecords @@ -171,25 +171,25 @@ namespace NCC.Extend.LqPurchaseRecords
171 .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0)) 171 .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0))
172 .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59)) 172 .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59))
173 .WhereIF(!string.IsNullOrEmpty(input.applicationId), p => p.ApplicationId.Contains(input.applicationId)) 173 .WhereIF(!string.IsNullOrEmpty(input.applicationId), p => p.ApplicationId.Contains(input.applicationId))
174 - .Select(it=> new LqPurchaseRecordsListOutput 174 + .Select(it => new LqPurchaseRecordsListOutput
175 { 175 {
176 id = it.Id, 176 id = it.Id,
177 - reimbursementCategoryId=it.ReimbursementCategoryId,  
178 - reimbursementCategoryName=it.ReimbursementCategoryName,  
179 - unitPrice=it.UnitPrice,  
180 - quantity=it.Quantity,  
181 - amount=it.Amount,  
182 - memo=it.Memo,  
183 - purchaseTime=it.PurchaseTime,  
184 - createTime=it.CreateTime,  
185 - createUser=it.CreateUser,  
186 - createUserStoreId=it.CreateUserStoreId,  
187 - approveStatus=it.ApproveStatus,  
188 - approveUser=it.ApproveUser,  
189 - approveTime=it.ApproveTime,  
190 - applicationId=it.ApplicationId,  
191 - }).MergeTable().OrderBy(sidx+" "+input.sort).ToListAsync();  
192 - return data; 177 + reimbursementCategoryId = it.ReimbursementCategoryId,
  178 + reimbursementCategoryName = it.ReimbursementCategoryName,
  179 + unitPrice = it.UnitPrice,
  180 + quantity = it.Quantity,
  181 + amount = it.Amount,
  182 + memo = it.Memo,
  183 + purchaseTime = it.PurchaseTime,
  184 + createTime = it.CreateTime,
  185 + createUser = it.CreateUser,
  186 + createUserStoreId = it.CreateUserStoreId,
  187 + approveStatus = it.ApproveStatus,
  188 + approveUser = it.ApproveUser,
  189 + approveTime = it.ApproveTime,
  190 + applicationId = it.ApplicationId,
  191 + }).MergeTable().OrderBy(sidx + " " + input.sort).ToListAsync();
  192 + return data;
193 } 193 }
194 194
195 /// <summary> 195 /// <summary>
@@ -211,7 +211,7 @@ namespace NCC.Extend.LqPurchaseRecords @@ -211,7 +211,7 @@ namespace NCC.Extend.LqPurchaseRecords
211 { 211 {
212 exportData = await this.GetNoPagingList(input); 212 exportData = await this.GetNoPagingList(input);
213 } 213 }
214 - List<ParamsModel> paramList = "[{\"value\":\"记录编号\",\"field\":\"id\"},{\"value\":\"购买物品编号\",\"field\":\"reimbursementCategoryId\"},{\"value\":\"购买物品名称\",\"field\":\"reimbursementCategoryName\"},{\"value\":\"单价\",\"field\":\"unitPrice\"},{\"value\":\"数量\",\"field\":\"quantity\"},{\"value\":\"总金额\",\"field\":\"amount\"},{\"value\":\"备注说明\",\"field\":\"memo\"},{\"value\":\"购买时间\",\"field\":\"purchaseTime\"},{\"value\":\"创建时间\",\"field\":\"createTime\"},{\"value\":\"创建人\",\"field\":\"createUser\"},{\"value\":\"创建人门店\",\"field\":\"createUserStoreId\"},{\"value\":\"审批状态\",\"field\":\"approveStatus\"},{\"value\":\"审批人\",\"field\":\"approveUser\"},{\"value\":\"审批时间\",\"field\":\"approveTime\"},{\"value\":\"审批单编号\",\"field\":\"applicationId\"},]".ToList<ParamsModel>(); 214 + List<ParamsModel> paramList = "[{\"value\":\"记录编号\",\"field\":\"id\"},{\"value\":\"购买物品编号\",\"field\":\"reimbursementCategoryId\"},{\"value\":\"购买物品名称\",\"field\":\"reimbursementCategoryName\"},{\"value\":\"单价\",\"field\":\"unitPrice\"},{\"value\":\"数量\",\"field\":\"quantity\"},{\"value\":\"总金额\",\"field\":\"amount\"},{\"value\":\"备注说明\",\"field\":\"memo\"},{\"value\":\"购买时间\",\"field\":\"purchaseTime\"},{\"value\":\"创建时间\",\"field\":\"createTime\"},{\"value\":\"创建人\",\"field\":\"createUser\"},{\"value\":\"创建人门店\",\"field\":\"createUserStoreId\"},{\"value\":\"审批状态\",\"field\":\"approveStatus\"},{\"value\":\"审批人\",\"field\":\"approveUser\"},{\"value\":\"审批时间\",\"field\":\"approveTime\"},{\"value\":\"审批单编号\",\"field\":\"applicationId\"},]".ToList<ParamsModel>();
215 ExcelConfig excelconfig = new ExcelConfig(); 215 ExcelConfig excelconfig = new ExcelConfig();
216 excelconfig.FileName = "购买记录表.xls"; 216 excelconfig.FileName = "购买记录表.xls";
217 excelconfig.HeadFont = "微软雅黑"; 217 excelconfig.HeadFont = "微软雅黑";
@@ -254,7 +254,7 @@ namespace NCC.Extend.LqPurchaseRecords @@ -254,7 +254,7 @@ namespace NCC.Extend.LqPurchaseRecords
254 //开启事务 254 //开启事务
255 _db.BeginTran(); 255 _db.BeginTran();
256 //批量删除购买记录表 256 //批量删除购买记录表
257 - await _db.Deleteable<LqPurchaseRecordsEntity>().In(d => d.Id,ids).ExecuteCommandAsync(); 257 + await _db.Deleteable<LqPurchaseRecordsEntity>().In(d => d.Id, ids).ExecuteCommandAsync();
258 //关闭事务 258 //关闭事务
259 _db.CommitTran(); 259 _db.CommitTran();
260 } 260 }
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -3504,6 +3504,7 @@ namespace NCC.Extend.LqStatistics @@ -3504,6 +3504,7 @@ namespace NCC.Extend.LqStatistics
3504 AND F_PersonType = '健康师' 3504 AND F_PersonType = '健康师'
3505 AND F_WorkMonth = '{month}' 3505 AND F_WorkMonth = '{month}'
3506 AND F_IsEffective = 1 3506 AND F_IsEffective = 1
  3507 + AND F_HasBilling = 1
3507 GROUP BY F_PersonId, F_WorkMonth, F_MemberId, F_Quantity 3508 GROUP BY F_PersonId, F_WorkMonth, F_MemberId, F_Quantity
3508 ) as distinct_records"; 3509 ) as distinct_records";
3509 3510
@@ -3531,7 +3532,8 @@ namespace NCC.Extend.LqStatistics @@ -3531,7 +3532,8 @@ namespace NCC.Extend.LqStatistics
3531 WHERE F_PersonId = '{userId}' 3532 WHERE F_PersonId = '{userId}'
3532 AND F_PersonType = '健康师' 3533 AND F_PersonType = '健康师'
3533 AND F_WorkMonth = '{month}' 3534 AND F_WorkMonth = '{month}'
3534 - AND F_IsEffective = 1 3535 + AND F_IsEffective = 1
  3536 + AND F_HasBilling = 1
3535 GROUP BY F_PersonId, F_WorkDate, F_MemberId, F_Quantity 3537 GROUP BY F_PersonId, F_WorkDate, F_MemberId, F_Quantity
3536 ) as distinct_records"; 3538 ) as distinct_records";
3537 3539
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
@@ -1757,11 +1757,20 @@ namespace NCC.Extend.LqXhHyhk @@ -1757,11 +1757,20 @@ namespace NCC.Extend.LqXhHyhk
1757 var kjbsyjList = await _db.Queryable<LqXhKjbsyjEntity>().Where(x => consumeIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()).ToListAsync(); 1757 var kjbsyjList = await _db.Queryable<LqXhKjbsyjEntity>().Where(x => consumeIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()).ToListAsync();
1758 1758
1759 // 查询已存在的人次记录(按BusinessId去重,避免重复添加) 1759 // 查询已存在的人次记录(按BusinessId去重,避免重复添加)
1760 - var existingBusinessIds = await _db.Queryable<LqPersonTimesRecordEntity>()  
1761 - .Where(x => consumeIds.Contains(x.BusinessId) && x.BusinessType == "耗卡" && x.IsEffective == StatusEnum.有效.GetHashCode())  
1762 - .Select(x => x.BusinessId)  
1763 - .Distinct()  
1764 - .ToListAsync(); 1760 + var existingBusinessIds = await _db.Queryable<LqPersonTimesRecordEntity>().Where(x => consumeIds.Contains(x.BusinessId) && x.BusinessType == "耗卡" && x.IsEffective == StatusEnum.有效.GetHashCode()).Select(x => x.BusinessId).Distinct().ToListAsync();
  1761 +
  1762 + // 批量查询所有会员是否有开单记录(优化:避免在循环中执行N次查询)
  1763 + var memberIds = consumeList.Where(x => !string.IsNullOrEmpty(x.Hy)).Select(x => x.Hy).Distinct().ToList();
  1764 + var membersWithBilling = new HashSet<string>();
  1765 + if (memberIds.Any())
  1766 + {
  1767 + var billingMemberIds = await _db.Queryable<LqKdKdjlbEntity>()
  1768 + .Where(x => memberIds.Contains(x.Kdhy) && x.IsEffective == StatusEnum.有效.GetHashCode() && x.Sfyj > 0)
  1769 + .Select(x => x.Kdhy)
  1770 + .Distinct()
  1771 + .ToListAsync();
  1772 + membersWithBilling = new HashSet<string>(billingMemberIds);
  1773 + }
1765 1774
1766 // 3. 构建人次记录列表 1775 // 3. 构建人次记录列表
1767 var personTimesRecords = new List<LqPersonTimesRecordEntity>(); 1776 var personTimesRecords = new List<LqPersonTimesRecordEntity>();
@@ -1782,19 +1791,14 @@ namespace NCC.Extend.LqXhHyhk @@ -1782,19 +1791,14 @@ namespace NCC.Extend.LqXhHyhk
1782 } 1791 }
1783 var workDate = consume.Hksj.Value.Date; // 工作日期(用于人次统计) 1792 var workDate = consume.Hksj.Value.Date; // 工作日期(用于人次统计)
1784 var workMonth = consume.Hksj.Value.ToString("yyyyMM"); // 工作月份(用于人头统计) 1793 var workMonth = consume.Hksj.Value.ToString("yyyyMM"); // 工作月份(用于人头统计)
1785 - 1794 + //查看该会员是否在开单记录表中存在开单记录(从批量查询结果中查找)
  1795 + var billingRecord = !string.IsNullOrEmpty(consume.Hy) && membersWithBilling.Contains(consume.Hy);
1786 // 处理健康师业绩:去重后计算人次数量(剔除T区健康师) 1796 // 处理健康师业绩:去重后计算人次数量(剔除T区健康师)
1787 - var consumeJksyjList = jksyjList.Where(x => x.Glkdbh == consume.Id  
1788 - && !string.IsNullOrEmpty(x.Jks)  
1789 - && !string.IsNullOrEmpty(x.Jksxm)  
1790 - && (x.Jksxm == null || !x.Jksxm.Contains("T区"))).ToList(); 1797 + var consumeJksyjList = jksyjList.Where(x => x.Glkdbh == consume.Id && !string.IsNullOrEmpty(x.Jks) && !string.IsNullOrEmpty(x.Jksxm) && (x.Jksxm == null || !x.Jksxm.Contains("T区"))).ToList();
1791 if (consumeJksyjList.Any()) 1798 if (consumeJksyjList.Any())
1792 { 1799 {
1793 // 按健康师ID去重 1800 // 按健康师ID去重
1794 - var distinctJksyjList = consumeJksyjList  
1795 - .GroupBy(x => x.Jks)  
1796 - .Select(g => g.First())  
1797 - .ToList(); 1801 + var distinctJksyjList = consumeJksyjList.GroupBy(x => x.Jks).Select(g => g.First()).ToList();
1798 1802
1799 // 计算人次数量:1 / 健康师数量 1803 // 计算人次数量:1 / 健康师数量
1800 var jksQuantity = distinctJksyjList.Count > 0 ? 1.0m / distinctJksyjList.Count : 0; 1804 var jksQuantity = distinctJksyjList.Count > 0 ? 1.0m / distinctJksyjList.Count : 0;
@@ -1815,23 +1819,18 @@ namespace NCC.Extend.LqXhHyhk @@ -1815,23 +1819,18 @@ namespace NCC.Extend.LqXhHyhk
1815 WorkMonth = workMonth, 1819 WorkMonth = workMonth,
1816 Quantity = jksQuantity, 1820 Quantity = jksQuantity,
1817 CreateTime = DateTime.Now, 1821 CreateTime = DateTime.Now,
1818 - IsEffective = StatusEnum.有效.GetHashCode() 1822 + IsEffective = StatusEnum.有效.GetHashCode(),
  1823 + HasBilling = billingRecord ? 1 : 0
1819 }); 1824 });
1820 } 1825 }
1821 } 1826 }
1822 1827
1823 // 处理科技老师业绩:去重后计算人次数量(剔除T区科技老师) 1828 // 处理科技老师业绩:去重后计算人次数量(剔除T区科技老师)
1824 - var consumeKjbsyjList = kjbsyjList.Where(x => x.Glkdbh == consume.Id  
1825 - && !string.IsNullOrEmpty(x.Kjbls)  
1826 - && !string.IsNullOrEmpty(x.Kjblsxm)  
1827 - && (x.Kjblsxm == null || !x.Kjblsxm.Contains("T区"))).ToList(); 1829 + var consumeKjbsyjList = kjbsyjList.Where(x => x.Glkdbh == consume.Id && !string.IsNullOrEmpty(x.Kjbls) && !string.IsNullOrEmpty(x.Kjblsxm) && (x.Kjblsxm == null || !x.Kjblsxm.Contains("T区"))).ToList();
1828 if (consumeKjbsyjList.Any()) 1830 if (consumeKjbsyjList.Any())
1829 { 1831 {
1830 // 按科技老师ID去重 1832 // 按科技老师ID去重
1831 - var distinctKjbsyjList = consumeKjbsyjList  
1832 - .GroupBy(x => x.Kjbls)  
1833 - .Select(g => g.First())  
1834 - .ToList(); 1833 + var distinctKjbsyjList = consumeKjbsyjList.GroupBy(x => x.Kjbls).Select(g => g.First()).ToList();
1835 1834
1836 // 计算人次数量:1 / 科技老师数量 1835 // 计算人次数量:1 / 科技老师数量
1837 var kjbsQuantity = distinctKjbsyjList.Count > 0 ? 1.0m / distinctKjbsyjList.Count : 0; 1836 var kjbsQuantity = distinctKjbsyjList.Count > 0 ? 1.0m / distinctKjbsyjList.Count : 0;
@@ -1852,7 +1851,8 @@ namespace NCC.Extend.LqXhHyhk @@ -1852,7 +1851,8 @@ namespace NCC.Extend.LqXhHyhk
1852 WorkMonth = workMonth, 1851 WorkMonth = workMonth,
1853 Quantity = kjbsQuantity, 1852 Quantity = kjbsQuantity,
1854 CreateTime = DateTime.Now, 1853 CreateTime = DateTime.Now,
1855 - IsEffective = StatusEnum.有效.GetHashCode() 1854 + IsEffective = StatusEnum.有效.GetHashCode(),
  1855 + HasBilling = billingRecord ? 1 : 0
1856 }); 1856 });
1857 } 1857 }
1858 } 1858 }
@@ -1865,9 +1865,7 @@ namespace NCC.Extend.LqXhHyhk @@ -1865,9 +1865,7 @@ namespace NCC.Extend.LqXhHyhk
1865 // 如果没有指定耗卡ID,不删除任何记录(因为已经在构建记录列表时跳过了已存在的记录) 1865 // 如果没有指定耗卡ID,不删除任何记录(因为已经在构建记录列表时跳过了已存在的记录)
1866 if (!string.IsNullOrEmpty(consumeId)) 1866 if (!string.IsNullOrEmpty(consumeId))
1867 { 1867 {
1868 - await _db.Deleteable<LqPersonTimesRecordEntity>()  
1869 - .Where(x => x.BusinessId == consumeId && x.BusinessType == "耗卡")  
1870 - .ExecuteCommandAsync(); 1868 + await _db.Deleteable<LqPersonTimesRecordEntity>().Where(x => x.BusinessId == consumeId && x.BusinessType == "耗卡").ExecuteCommandAsync();
1871 } 1869 }
1872 1870
1873 // 批量插入新记录 1871 // 批量插入新记录
sql/分析健康师消耗项目数差异.sql 0 → 100644
  1 +-- ============================================
  2 +-- 分析健康师消耗项目数差异
  3 +-- 员工ID: 18566028067 (李芳)
  4 +-- ============================================
  5 +
  6 +-- 1. 接口统计逻辑(使用耗卡时间,时间范围:2025-11-01 到 2025-11-25 11:55:32)
  7 +SELECT
  8 + jksyj.jkszh as EmployeeId,
  9 + CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount_接口逻辑
  10 +FROM lq_xh_jksyj jksyj
  11 +INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  12 +WHERE jksyj.jkszh = '18566028067'
  13 + AND jksyj.F_IsEffective = 1
  14 + AND hyhk.F_IsEffective = 1
  15 + AND hyhk.hksj >= '2025-11-01 00:00:00'
  16 + AND hyhk.hksj <= '2025-11-25 11:55:32'
  17 +GROUP BY jksyj.jkszh;
  18 +
  19 +-- 2. 整个11月的统计(使用业绩时间)
  20 +SELECT
  21 + jksyj.jkszh as EmployeeId,
  22 + CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount_整个11月
  23 +FROM lq_xh_jksyj jksyj
  24 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  25 + AND jksyj.F_IsEffective = 1
  26 + AND DATE_FORMAT(jksyj.yjsj, '%Y%m') = '202511'
  27 +GROUP BY jksyj.jkszh;
  28 +
  29 +-- 3. 查看11月25日之后的记录(可能导致差异的原因)
  30 +SELECT
  31 + jksyj.jkszh as EmployeeId,
  32 + jksyj.F_kdpxNumber as 项目数,
  33 + jksyj.yjsj as 业绩时间,
  34 + hyhk.hksj as 耗卡时间,
  35 + hyhk.F_IsEffective as 耗卡记录是否有效,
  36 + jksyj.F_IsEffective as 业绩记录是否有效
  37 +FROM lq_xh_jksyj jksyj
  38 +LEFT JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  39 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  40 + AND jksyj.F_IsEffective = 1
  41 + AND (
  42 + (jksyj.yjsj >= '2025-11-25 11:55:32' AND jksyj.yjsj < '2025-12-01')
  43 + OR (hyhk.hksj >= '2025-11-25 11:55:32' AND hyhk.hksj < '2025-12-01')
  44 + )
  45 +ORDER BY jksyj.yjsj;
  46 +
  47 +-- 4. 查看耗卡记录无效但业绩记录有效的记录(可能导致差异的原因)
  48 +SELECT
  49 + jksyj.jkszh as EmployeeId,
  50 + jksyj.F_kdpxNumber as 项目数,
  51 + jksyj.yjsj as 业绩时间,
  52 + hyhk.hksj as 耗卡时间,
  53 + hyhk.F_IsEffective as 耗卡记录是否有效,
  54 + jksyj.F_IsEffective as 业绩记录是否有效
  55 +FROM lq_xh_jksyj jksyj
  56 +LEFT JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  57 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  58 + AND jksyj.F_IsEffective = 1
  59 + AND (hyhk.F_IsEffective != 1 OR hyhk.F_IsEffective IS NULL)
  60 + AND jksyj.yjsj >= '2025-11-01'
  61 + AND jksyj.yjsj < '2025-12-01'
  62 +ORDER BY jksyj.yjsj;
  63 +
  64 +-- 5. 查看耗卡时间和业绩时间不一致的记录(可能导致差异的原因)
  65 +SELECT
  66 + jksyj.jkszh as EmployeeId,
  67 + jksyj.F_kdpxNumber as 项目数,
  68 + jksyj.yjsj as 业绩时间,
  69 + hyhk.hksj as 耗卡时间,
  70 + DATEDIFF(jksyj.yjsj, hyhk.hksj) as 时间差_天,
  71 + CASE
  72 + WHEN hyhk.hksj >= '2025-11-01 00:00:00' AND hyhk.hksj <= '2025-11-25 11:55:32' THEN '在接口时间范围内'
  73 + ELSE '不在接口时间范围内'
  74 + END as 是否在接口时间范围
  75 +FROM lq_xh_jksyj jksyj
  76 +INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  77 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  78 + AND jksyj.F_IsEffective = 1
  79 + AND hyhk.F_IsEffective = 1
  80 + AND (
  81 + (jksyj.yjsj >= '2025-11-01' AND jksyj.yjsj < '2025-12-01')
  82 + OR (hyhk.hksj >= '2025-11-01' AND hyhk.hksj < '2025-12-01')
  83 + )
  84 + AND (
  85 + jksyj.yjsj < '2025-11-01'
  86 + OR jksyj.yjsj >= '2025-12-01'
  87 + OR hyhk.hksj < '2025-11-01'
  88 + OR hyhk.hksj >= '2025-12-01'
  89 + OR DATEDIFF(jksyj.yjsj, hyhk.hksj) != 0
  90 + )
  91 +ORDER BY jksyj.yjsj;
  92 +
  93 +
sql/查询员工11月25日之后的记录.sql 0 → 100644
  1 +-- ============================================
  2 +-- 查询员工在2025-11-25 11:55:32之后的记录
  3 +-- 员工ID: 18566028067 (李芳)
  4 +-- ============================================
  5 +
  6 +-- 1. 查询11月25日11:55:32之后的业绩记录(使用业绩时间)
  7 +SELECT
  8 + jksyj.jkszh as 健康师账号,
  9 + jksyj.jksxm as 健康师姓名,
  10 + jksyj.F_kdpxNumber as 项目数,
  11 + jksyj.yjsj as 业绩时间,
  12 + hyhk.hksj as 耗卡时间,
  13 + hyhk.F_IsEffective as 耗卡记录是否有效,
  14 + jksyj.F_IsEffective as 业绩记录是否有效,
  15 + hyhk.F_Id as 耗卡记录ID
  16 +FROM lq_xh_jksyj jksyj
  17 +LEFT JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  18 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  19 + AND jksyj.F_IsEffective = 1
  20 + AND jksyj.yjsj > '2025-11-25 11:55:32'
  21 + AND jksyj.yjsj < '2025-12-01'
  22 +ORDER BY jksyj.yjsj;
  23 +
  24 +-- 2. 查询11月25日11:55:32之后的耗卡记录(使用耗卡时间,这是接口统计使用的条件)
  25 +SELECT
  26 + jksyj.jkszh as 健康师账号,
  27 + jksyj.jksxm as 健康师姓名,
  28 + jksyj.F_kdpxNumber as 项目数,
  29 + jksyj.yjsj as 业绩时间,
  30 + hyhk.hksj as 耗卡时间,
  31 + hyhk.F_IsEffective as 耗卡记录是否有效,
  32 + jksyj.F_IsEffective as 业绩记录是否有效,
  33 + hyhk.F_Id as 耗卡记录ID
  34 +FROM lq_xh_jksyj jksyj
  35 +INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  36 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  37 + AND jksyj.F_IsEffective = 1
  38 + AND hyhk.F_IsEffective = 1
  39 + AND hyhk.hksj > '2025-11-25 11:55:32'
  40 + AND hyhk.hksj < '2025-12-01'
  41 +ORDER BY hyhk.hksj;
  42 +
  43 +-- 3. 统计11月25日11:55:32之后的项目数总和(使用耗卡时间,接口统计逻辑)
  44 +SELECT
  45 + jksyj.jkszh as 健康师账号,
  46 + CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as 项目数总和
  47 +FROM lq_xh_jksyj jksyj
  48 +INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  49 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  50 + AND jksyj.F_IsEffective = 1
  51 + AND hyhk.F_IsEffective = 1
  52 + AND hyhk.hksj > '2025-11-25 11:55:32'
  53 + AND hyhk.hksj < '2025-12-01'
  54 +GROUP BY jksyj.jkszh;
  55 +
  56 +
sql/查询员工消耗项目数.sql 0 → 100644
  1 +-- ============================================
  2 +-- 查询员工在指定月份的消耗项目数
  3 +-- ============================================
  4 +-- 员工ID: 18566028067
  5 +-- 查询月份: 2025年11月 (202511)
  6 +-- ============================================
  7 +
  8 +-- 方式1:从健康师业绩表统计(推荐,使用F_kdpxNumber字段,包含原始+加班+陪同项目数)
  9 +SELECT
  10 + jksyj.jks as 健康师ID,
  11 + jksyj.jksxm as 健康师姓名,
  12 + jksyj.jkszh as 健康师账号,
  13 + COALESCE(SUM(jksyj.F_kdpxNumber), 0) as 消耗项目总数,
  14 + COALESCE(SUM(COALESCE(jksyj.F_OriginalKdpxNumber, jksyj.F_kdpxNumber)), 0) as 原始项目数,
  15 + COALESCE(SUM(COALESCE(jksyj.F_OvertimeKdpxNumber, 0)), 0) as 加班项目数,
  16 + COALESCE(SUM(COALESCE(jksyj.F_AccompaniedProjectNumber, 0)), 0) as 陪同项目数
  17 +FROM lq_xh_jksyj jksyj
  18 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  19 + AND jksyj.F_IsEffective = 1
  20 + AND DATE_FORMAT(jksyj.yjsj, '%Y%m') = '202511'
  21 +GROUP BY jksyj.jks, jksyj.jksxm, jksyj.jkszh;
  22 +
  23 +-- 方式2:从品项明细表统计(备用方式,统计F_ProjectNumber字段)
  24 +SELECT
  25 + jksyj.jks as 健康师ID,
  26 + jksyj.jksxm as 健康师姓名,
  27 + jksyj.jkszh as 健康师账号,
  28 + COALESCE(SUM(pxmx.F_ProjectNumber), 0) as 消耗项目数
  29 +FROM lq_xh_jksyj jksyj
  30 +INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  31 +INNER JOIN lq_xh_pxmx pxmx ON pxmx.F_ConsumeInfoId = hyhk.F_Id
  32 +WHERE (jksyj.jks = '18566028067' OR jksyj.jkszh = '18566028067')
  33 + AND jksyj.F_IsEffective = 1
  34 + AND hyhk.F_IsEffective = 1
  35 + AND pxmx.F_IsEffective = 1
  36 + AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '202511'
  37 +GROUP BY jksyj.jks, jksyj.jksxm, jksyj.jkszh;
  38 +
  39 +
sql/添加人次记录表字段.sql
@@ -16,3 +16,5 @@ ADD COLUMN `F_HasBilling` INT(11) DEFAULT 0 COMMENT &#39;是否有开单(0-否,1-是 @@ -16,3 +16,5 @@ ADD COLUMN `F_HasBilling` INT(11) DEFAULT 0 COMMENT &#39;是否有开单(0-否,1-是
16 -- WHERE TABLE_NAME = 'lq_person_times_record' 16 -- WHERE TABLE_NAME = 'lq_person_times_record'
17 -- AND COLUMN_NAME = 'F_HasBilling'; 17 -- AND COLUMN_NAME = 'F_HasBilling';
18 18
  19 +
  20 +