Commit dc67d3d0a3ee964e9bfffccf825780357bb6ec33

Authored by “wangming”
1 parent 1fffa876

feat: 优化统计接口和会员信息接口

1. 线索池客户统计接口:新增归属门店、电话、拓客人员字段
2. 会员信息接口:确保列表和单个查询返回所有字段(包括isEffective)
3. 修改开单信息接口:重命名为UpdateBillingInfo,支持修改备注和简介
4. 金三角开卡业绩统计:改为实时统计,优化查询性能
5. 科技部开单业绩统计:改为实时统计,优化查询性能
6. 门店耗卡业绩统计:改为实时统计,优化查询性能
7. 部门消耗业绩统计:改为实时统计,优化查询性能,支持健康师和科技部老师两种类型
8. 女神卡会员列表接口:新增门店筛选功能
antis-ncc-admin/src/views/departmentConsumePerformanceStatistics/index.vue
@@ -41,9 +41,9 @@ @@ -41,9 +41,9 @@
41 41
42 <!-- 表格卡片 --> 42 <!-- 表格卡片 -->
43 <el-card class="table-card"> 43 <el-card class="table-card">
44 - <div slot="header" class="clearfix"> 44 + <!-- <div slot="header" class="clearfix">
45 <span><i class="el-icon-s-grid"></i> 个人消耗业绩列表</span> 45 <span><i class="el-icon-s-grid"></i> 个人消耗业绩列表</span>
46 - </div> 46 + </div> -->
47 <div class="table-container"> 47 <div class="table-container">
48 <el-table :data="tableData" v-loading="loading" element-loading-text="加载中..." :height="tableHeight" 48 <el-table :data="tableData" v-loading="loading" element-loading-text="加载中..." :height="tableHeight"
49 border stripe style="width: 100%"> 49 border stripe style="width: 100%">
antis-ncc-admin/src/views/goldTriangleStatistics/index.vue
@@ -36,13 +36,10 @@ @@ -36,13 +36,10 @@
36 36
37 <!-- 表格卡片 --> 37 <!-- 表格卡片 -->
38 <el-card class="table-card"> 38 <el-card class="table-card">
39 - <div slot="header" class="clearfix">  
40 - <span><i class="el-icon-s-grid"></i> 金三角开卡业绩列表</span>  
41 - </div>  
42 <div class="table-container"> 39 <div class="table-container">
43 <el-table :data="tableData" v-loading="loading" element-loading-text="加载中..." :height="tableHeight" 40 <el-table :data="tableData" v-loading="loading" element-loading-text="加载中..." :height="tableHeight"
44 border stripe style="width: 100%"> 41 border stripe style="width: 100%">
45 - <el-table-column prop="GoldTriangleName" label="金三角战队" width="150" fixed="left"></el-table-column> 42 + <el-table-column prop="GoldTriangleName" label="金三角战队" fixed="left"></el-table-column>
46 <el-table-column prop="StoreName" label="门店名称" width="150" fixed="left"></el-table-column> 43 <el-table-column prop="StoreName" label="门店名称" width="150" fixed="left"></el-table-column>
47 <el-table-column prop="OrderCount" label="订单数量" width="100" align="right"></el-table-column> 44 <el-table-column prop="OrderCount" label="订单数量" width="100" align="right"></el-table-column>
48 <el-table-column prop="TotalPerformance" label="总业绩金额" width="120" align="right"> 45 <el-table-column prop="TotalPerformance" label="总业绩金额" width="120" align="right">
antis-ncc-admin/src/views/storeConsumePerformanceStatistics/index.vue
@@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
37 <div class="table-container"> 37 <div class="table-container">
38 <el-table :data="tableData" v-loading="loading" element-loading-text="加载中..." :height="tableHeight" 38 <el-table :data="tableData" v-loading="loading" element-loading-text="加载中..." :height="tableHeight"
39 border stripe style="width: 100%"> 39 border stripe style="width: 100%">
40 - <el-table-column prop="StoreName" label="门店名称" width="200" fixed="left"></el-table-column> 40 + <el-table-column prop="StoreName" label="门店名称" fixed="left"></el-table-column>
41 <el-table-column prop="TotalPerformance" label="总业绩" width="120" align="right"> 41 <el-table-column prop="TotalPerformance" label="总业绩" width="120" align="right">
42 <template slot-scope="scope"> 42 <template slot-scope="scope">
43 {{ formatMoney(scope.row.TotalPerformance) }} 43 {{ formatMoney(scope.row.TotalPerformance) }}
antis-ncc-admin/src/views/techPerformanceStatistics/index.vue
@@ -36,14 +36,14 @@ @@ -36,14 +36,14 @@
36 36
37 <!-- 表格卡片 --> 37 <!-- 表格卡片 -->
38 <el-card class="table-card"> 38 <el-card class="table-card">
39 - <div slot="header" class="clearfix"> 39 + <!-- <div slot="header" class="clearfix">
40 <span><i class="el-icon-s-grid"></i> 科技部开单业绩列表</span> 40 <span><i class="el-icon-s-grid"></i> 科技部开单业绩列表</span>
41 - </div> 41 + </div> -->
42 <div class="table-container"> 42 <div class="table-container">
43 <el-table :data="tableData" v-loading="loading" element-loading-text="加载中..." :height="tableHeight" 43 <el-table :data="tableData" v-loading="loading" element-loading-text="加载中..." :height="tableHeight"
44 border stripe style="width: 100%"> 44 border stripe style="width: 100%">
45 - <el-table-column prop="TeacherName" label="老师姓名" width="120" fixed="left"></el-table-column>  
46 - <el-table-column prop="StoreName" label="门店名称" width="150" fixed="left"></el-table-column> 45 + <el-table-column prop="TeacherName" label="老师姓名" fixed="left"></el-table-column>
  46 + <!-- <el-table-column prop="StoreName" label="门店名称" width="150" fixed="left"></el-table-column> -->
47 <el-table-column prop="TotalPerformance" label="总业绩" width="100" align="right"> 47 <el-table-column prop="TotalPerformance" label="总业绩" width="100" align="right">
48 <template slot-scope="scope"> 48 <template slot-scope="scope">
49 {{ formatMoney(scope.row.TotalPerformance) }} 49 {{ formatMoney(scope.row.TotalPerformance) }}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbUpdateFileInput.cs
@@ -5,7 +5,7 @@ using NCC.Common.Model; @@ -5,7 +5,7 @@ using NCC.Common.Model;
5 namespace NCC.Extend.Entitys.Dto.LqKdKdjlb 5 namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
6 { 6 {
7 /// <summary> 7 /// <summary>
8 - /// 修改开单文件输入 8 + /// 修改开单信息输入(文件、备注、简介)
9 /// </summary> 9 /// </summary>
10 public class LqKdKdjlbUpdateFileInput 10 public class LqKdKdjlbUpdateFileInput
11 { 11 {
@@ -24,6 +24,16 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb @@ -24,6 +24,16 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
24 /// 方案其他 24 /// 方案其他
25 /// </summary> 25 /// </summary>
26 public string F_FIleUrl { get; set; } 26 public string F_FIleUrl { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 备注
  30 + /// </summary>
  31 + public string Bz { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 简介
  35 + /// </summary>
  36 + public string Jj { get; set; }
27 } 37 }
28 } 38 }
29 39
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxInfoOutput.cs
@@ -40,6 +40,11 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx @@ -40,6 +40,11 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
40 public string gsmd { get; set; } 40 public string gsmd { get; set; }
41 41
42 /// <summary> 42 /// <summary>
  43 + /// 归属门店名称
  44 + /// </summary>
  45 + public string gsmdName { get; set; }
  46 +
  47 + /// <summary>
43 /// 注册时间 48 /// 注册时间
44 /// </summary> 49 /// </summary>
45 public DateTime? zcsj { get; set; } 50 public DateTime? zcsj { get; set; }
@@ -230,5 +235,9 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx @@ -230,5 +235,9 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
230 /// </summary> 235 /// </summary>
231 public DateTime? consumeLevelUpdateTime { get; set; } 236 public DateTime? consumeLevelUpdateTime { get; set; }
232 237
  238 + /// <summary>
  239 + /// 是否有效
  240 + /// </summary>
  241 + public int isEffective { get; set; }
233 } 242 }
234 } 243 }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs
@@ -256,5 +256,10 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx @@ -256,5 +256,10 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
256 /// 消费等级更新时间 256 /// 消费等级更新时间
257 /// </summary> 257 /// </summary>
258 public DateTime? consumeLevelUpdateTime { get; set; } 258 public DateTime? consumeLevelUpdateTime { get; set; }
  259 +
  260 + /// <summary>
  261 + /// 是否有效
  262 + /// </summary>
  263 + public int isEffective { get; set; }
259 } 264 }
260 } 265 }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/GoddessCardMemberListQueryInput.cs
  1 +using System.Collections.Generic;
  2 +
1 namespace NCC.Extend.Entitys.Dto.LqStatistics 3 namespace NCC.Extend.Entitys.Dto.LqStatistics
2 { 4 {
3 /// <summary> 5 /// <summary>
@@ -14,6 +16,16 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics @@ -14,6 +16,16 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
14 /// 每页数量 16 /// 每页数量
15 /// </summary> 17 /// </summary>
16 public int PageSize { get; set; } = 20; 18 public int PageSize { get; set; } = 20;
  19 +
  20 + /// <summary>
  21 + /// 门店ID(单个门店筛选)
  22 + /// </summary>
  23 + public string StoreId { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 门店ID列表(支持多门店筛选)
  27 + /// </summary>
  28 + public List<string> StoreIds { get; set; }
17 } 29 }
18 } 30 }
19 31
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/LeadCustomerStatisticsListOutput.cs
@@ -23,6 +23,31 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics @@ -23,6 +23,31 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
23 public DateTime? ExpansionTime { get; set; } 23 public DateTime? ExpansionTime { get; set; }
24 24
25 /// <summary> 25 /// <summary>
  26 + /// 归属门店ID
  27 + /// </summary>
  28 + public string StoreId { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 归属门店名称
  32 + /// </summary>
  33 + public string StoreName { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 电话
  37 + /// </summary>
  38 + public string Phone { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 拓客人员ID
  42 + /// </summary>
  43 + public string ExpansionUserId { get; set; }
  44 +
  45 + /// <summary>
  46 + /// 拓客人员姓名
  47 + /// </summary>
  48 + public string ExpansionUserName { get; set; }
  49 +
  50 + /// <summary>
26 /// 是否邀约(是/否) 51 /// 是否邀约(是/否)
27 /// </summary> 52 /// </summary>
28 public string HasInvite { get; set; } 53 public string HasInvite { get; set; }
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
@@ -4080,13 +4080,13 @@ namespace NCC.Extend.LqKdKdjlb @@ -4080,13 +4080,13 @@ namespace NCC.Extend.LqKdKdjlb
4080 4080
4081 // 构建返回结果 4081 // 构建返回结果
4082 var result = PageResult<LqKdDeductinfoListOutput>.SqlSugarPageResult(data); 4082 var result = PageResult<LqKdDeductinfoListOutput>.SqlSugarPageResult(data);
4083 - 4083 +
4084 // 单独查询统计数据,避免复杂JOIN导致的问题 4084 // 单独查询统计数据,避免复杂JOIN导致的问题
4085 try 4085 try
4086 { 4086 {
4087 // 先获取符合条件的开单记录ID列表(用于门店、时间、关键词筛选) 4087 // 先获取符合条件的开单记录ID列表(用于门店、时间、关键词筛选)
4088 var billingIds = new List<string>(); 4088 var billingIds = new List<string>();
4089 - if (!string.IsNullOrEmpty(input.StoreId) || (input.StoreIds != null && input.StoreIds.Any()) || 4089 + if (!string.IsNullOrEmpty(input.StoreId) || (input.StoreIds != null && input.StoreIds.Any()) ||
4090 input.StartTime.HasValue || input.EndTime.HasValue || !string.IsNullOrEmpty(input.keyword)) 4090 input.StartTime.HasValue || input.EndTime.HasValue || !string.IsNullOrEmpty(input.keyword))
4091 { 4091 {
4092 var billingQuery = _db.Queryable<LqKdKdjlbEntity>() 4092 var billingQuery = _db.Queryable<LqKdKdjlbEntity>()
@@ -4103,7 +4103,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -4103,7 +4103,7 @@ namespace NCC.Extend.LqKdKdjlb
4103 .Any()); 4103 .Any());
4104 billingIds = await billingQuery.Select(billing => billing.Id).ToListAsync(); 4104 billingIds = await billingQuery.Select(billing => billing.Id).ToListAsync();
4105 } 4105 }
4106 - 4106 +
4107 // 构建统计查询(只查询储扣表) 4107 // 构建统计查询(只查询储扣表)
4108 var statisticsQuery = _db.Queryable<LqKdDeductinfoEntity>() 4108 var statisticsQuery = _db.Queryable<LqKdDeductinfoEntity>()
4109 .WhereIF(input.IsEffective.HasValue, deduct => deduct.IsEffective == input.IsEffective.Value) 4109 .WhereIF(input.IsEffective.HasValue, deduct => deduct.IsEffective == input.IsEffective.Value)
@@ -4121,17 +4121,17 @@ namespace NCC.Extend.LqKdKdjlb @@ -4121,17 +4121,17 @@ namespace NCC.Extend.LqKdKdjlb
4121 .WhereIF(input.EndCreateTime.HasValue, deduct => deduct.CreateTime <= input.EndCreateTime.Value) 4121 .WhereIF(input.EndCreateTime.HasValue, deduct => deduct.CreateTime <= input.EndCreateTime.Value)
4122 .WhereIF(!string.IsNullOrEmpty(input.ItemCategory), deduct => deduct.ItemCategory == input.ItemCategory) 4122 .WhereIF(!string.IsNullOrEmpty(input.ItemCategory), deduct => deduct.ItemCategory == input.ItemCategory)
4123 // 关键词筛选:检查品项名称 4123 // 关键词筛选:检查品项名称
4124 - .WhereIF(!string.IsNullOrEmpty(input.keyword) && !billingIds.Any(), deduct => 4124 + .WhereIF(!string.IsNullOrEmpty(input.keyword) && !billingIds.Any(), deduct =>
4125 deduct.ItemName != null && deduct.ItemName.Contains(input.keyword)) 4125 deduct.ItemName != null && deduct.ItemName.Contains(input.keyword))
4126 // 时间筛选:如果储扣记录有开单时间,也需要检查 4126 // 时间筛选:如果储扣记录有开单时间,也需要检查
4127 - .WhereIF(input.StartTime.HasValue && !billingIds.Any(), deduct => 4127 + .WhereIF(input.StartTime.HasValue && !billingIds.Any(), deduct =>
4128 deduct.BillingTime.HasValue && deduct.BillingTime >= input.StartTime.Value) 4128 deduct.BillingTime.HasValue && deduct.BillingTime >= input.StartTime.Value)
4129 - .WhereIF(input.EndTime.HasValue && !billingIds.Any(), deduct => 4129 + .WhereIF(input.EndTime.HasValue && !billingIds.Any(), deduct =>
4130 deduct.BillingTime.HasValue && deduct.BillingTime <= input.EndTime.Value); 4130 deduct.BillingTime.HasValue && deduct.BillingTime <= input.EndTime.Value);
4131 4131
4132 // 统计总记录数 4132 // 统计总记录数
4133 var totalCount = await statisticsQuery.CountAsync(); 4133 var totalCount = await statisticsQuery.CountAsync();
4134 - 4134 +
4135 // 统计总金额和总项目数 4135 // 统计总金额和总项目数
4136 var statisticsList = await statisticsQuery 4136 var statisticsList = await statisticsQuery
4137 .Select(deduct => new 4137 .Select(deduct => new
@@ -4140,10 +4140,10 @@ namespace NCC.Extend.LqKdKdjlb @@ -4140,10 +4140,10 @@ namespace NCC.Extend.LqKdKdjlb
4140 ProjectNumber = deduct.ProjectNumber ?? 0m 4140 ProjectNumber = deduct.ProjectNumber ?? 0m
4141 }) 4141 })
4142 .ToListAsync(); 4142 .ToListAsync();
4143 - 4143 +
4144 var totalAmount = statisticsList?.Sum(x => x.Amount) ?? 0m; 4144 var totalAmount = statisticsList?.Sum(x => x.Amount) ?? 0m;
4145 var totalProjectNumber = statisticsList?.Sum(x => x.ProjectNumber) ?? 0m; 4145 var totalProjectNumber = statisticsList?.Sum(x => x.ProjectNumber) ?? 0m;
4146 - 4146 +
4147 // 拼接统计信息到返回结果 4147 // 拼接统计信息到返回结果
4148 return new 4148 return new
4149 { 4149 {
@@ -4449,12 +4449,14 @@ namespace NCC.Extend.LqKdKdjlb @@ -4449,12 +4449,14 @@ namespace NCC.Extend.LqKdKdjlb
4449 throw NCCException.Oh($"清空欠款失败: {ex.Message}"); 4449 throw NCCException.Oh($"清空欠款失败: {ex.Message}");
4450 } 4450 }
4451 } 4451 }
  4452 + #endregion
4452 4453
  4454 + #region 修改开单信息(文件、备注、简介)
4453 /// <summary> 4455 /// <summary>
4454 - /// 修改开单文件 4456 + /// 修改开单信息(文件、备注、简介)
4455 /// </summary> 4457 /// </summary>
4456 /// <remarks> 4458 /// <remarks>
4457 - /// 根据开单记录ID修改上传文件(scwj)和方案其他(F_FIleUrl)字段 4459 + /// 根据开单记录ID修改上传文件(scwj)、方案其他(F_FIleUrl)、备注(Bz)和简介(Jj)字段
4458 /// 4460 ///
4459 /// 示例请求: 4461 /// 示例请求:
4460 /// ```json 4462 /// ```json
@@ -4466,7 +4468,9 @@ namespace NCC.Extend.LqKdKdjlb @@ -4466,7 +4468,9 @@ namespace NCC.Extend.LqKdKdjlb
4466 /// "url": "文件URL" 4468 /// "url": "文件URL"
4467 /// } 4469 /// }
4468 /// ], 4470 /// ],
4469 - /// "F_FIleUrl": "方案其他文件URL" 4471 + /// "F_FIleUrl": "方案其他文件URL",
  4472 + /// "Bz": "备注信息",
  4473 + /// "Jj": "简介信息"
4470 /// } 4474 /// }
4471 /// ``` 4475 /// ```
4472 /// 4476 ///
@@ -4474,14 +4478,16 @@ namespace NCC.Extend.LqKdKdjlb @@ -4474,14 +4478,16 @@ namespace NCC.Extend.LqKdKdjlb
4474 /// - id: 开单记录ID(必填) 4478 /// - id: 开单记录ID(必填)
4475 /// - scwj: 上传文件列表(可选,不传则不更新) 4479 /// - scwj: 上传文件列表(可选,不传则不更新)
4476 /// - F_FIleUrl: 方案其他文件URL(可选,不传则不更新) 4480 /// - F_FIleUrl: 方案其他文件URL(可选,不传则不更新)
  4481 + /// - Bz: 备注(可选,不传则不更新)
  4482 + /// - Jj: 简介(可选,不传则不更新)
4477 /// </remarks> 4483 /// </remarks>
4478 - /// <param name="input">更新文件输入参数</param> 4484 + /// <param name="input">更新信息输入参数</param>
4479 /// <returns>更新结果</returns> 4485 /// <returns>更新结果</returns>
4480 /// <response code="200">更新成功</response> 4486 /// <response code="200">更新成功</response>
4481 /// <response code="400">参数错误或开单记录不存在</response> 4487 /// <response code="400">参数错误或开单记录不存在</response>
4482 /// <response code="500">服务器错误</response> 4488 /// <response code="500">服务器错误</response>
4483 - [HttpPut("UpdateFile")]  
4484 - public async Task<dynamic> UpdateFile([FromBody] LqKdKdjlbUpdateFileInput input) 4489 + [HttpPut("UpdateBillingInfo")]
  4490 + public async Task<dynamic> UpdateBillingInfo([FromBody] LqKdKdjlbUpdateFileInput input)
4485 { 4491 {
4486 try 4492 try
4487 { 4493 {
@@ -4531,10 +4537,30 @@ namespace NCC.Extend.LqKdKdjlb @@ -4531,10 +4537,30 @@ namespace NCC.Extend.LqKdKdjlb
4531 updatedFields.Add("F_FIleUrl"); 4537 updatedFields.Add("F_FIleUrl");
4532 } 4538 }
4533 4539
  4540 + // 如果提供了Bz,则更新备注
  4541 + if (input.Bz != null)
  4542 + {
  4543 + updateBuilder = updateBuilder.SetColumns(it => new LqKdKdjlbEntity
  4544 + {
  4545 + Bz = input.Bz
  4546 + });
  4547 + updatedFields.Add("Bz");
  4548 + }
  4549 +
  4550 + // 如果提供了Jj,则更新简介
  4551 + if (input.Jj != null)
  4552 + {
  4553 + updateBuilder = updateBuilder.SetColumns(it => new LqKdKdjlbEntity
  4554 + {
  4555 + Jj = input.Jj
  4556 + });
  4557 + updatedFields.Add("Jj");
  4558 + }
  4559 +
4534 // 如果没有提供任何字段,则返回错误 4560 // 如果没有提供任何字段,则返回错误
4535 if (!updatedFields.Any()) 4561 if (!updatedFields.Any())
4536 { 4562 {
4537 - throw NCCException.Oh("至少需要提供一个要更新的字段(scwj或F_FIleUrl)"); 4563 + throw NCCException.Oh("至少需要提供一个要更新的字段(scwj、F_FIleUrl、Bz或Jj)");
4538 } 4564 }
4539 4565
4540 // 执行更新 4566 // 执行更新
@@ -4544,7 +4570,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -4544,7 +4570,7 @@ namespace NCC.Extend.LqKdKdjlb
4544 4570
4545 if (updateResult <= 0) 4571 if (updateResult <= 0)
4546 { 4572 {
4547 - throw NCCException.Oh("更新开单文件失败"); 4573 + throw NCCException.Oh("更新开单信息失败");
4548 } 4574 }
4549 4575
4550 return new 4576 return new
@@ -4560,8 +4586,8 @@ namespace NCC.Extend.LqKdKdjlb @@ -4560,8 +4586,8 @@ namespace NCC.Extend.LqKdKdjlb
4560 } 4586 }
4561 catch (Exception ex) 4587 catch (Exception ex)
4562 { 4588 {
4563 - _logger.LogError(ex, $"修改开单文件失败 - 开单ID: {input?.Id}");  
4564 - throw NCCException.Oh($"修改开单文件失败: {ex.Message}"); 4589 + _logger.LogError(ex, $"修改开单信息失败 - 开单ID: {input?.Id}");
  4590 + throw NCCException.Oh($"修改开单信息失败: {ex.Message}");
4565 } 4591 }
4566 } 4592 }
4567 #endregion 4593 #endregion
netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
@@ -75,6 +75,14 @@ namespace NCC.Extend.LqKhxx @@ -75,6 +75,14 @@ namespace NCC.Extend.LqKhxx
75 { 75 {
76 var entity = await _db.Queryable<LqKhxxEntity>().FirstAsync(p => p.Id == id); 76 var entity = await _db.Queryable<LqKhxxEntity>().FirstAsync(p => p.Id == id);
77 var output = entity.Adapt<LqKhxxInfoOutput>(); 77 var output = entity.Adapt<LqKhxxInfoOutput>();
  78 +
  79 + // 获取归属门店名称
  80 + if (!string.IsNullOrEmpty(entity.Gsmd))
  81 + {
  82 + var store = await _db.Queryable<LqMdxxEntity>().Where(s => s.Id == entity.Gsmd).FirstAsync();
  83 + output.gsmdName = store?.Dm ?? "";
  84 + }
  85 +
78 if (!string.IsNullOrEmpty(entity.ExpandUser)) 86 if (!string.IsNullOrEmpty(entity.ExpandUser))
79 { 87 {
80 var expandUser = await _db.Queryable<UserEntity>().Where(u => u.Id == entity.ExpandUser).FirstAsync(); 88 var expandUser = await _db.Queryable<UserEntity>().Where(u => u.Id == entity.ExpandUser).FirstAsync();
@@ -180,7 +188,8 @@ namespace NCC.Extend.LqKhxx @@ -180,7 +188,8 @@ namespace NCC.Extend.LqKhxx
180 totalBillingAmount = it.TotalBillingAmount, 188 totalBillingAmount = it.TotalBillingAmount,
181 remainingRightsAmount = it.RemainingRightsAmount, 189 remainingRightsAmount = it.RemainingRightsAmount,
182 consumeLevel = it.ConsumeLevel, 190 consumeLevel = it.ConsumeLevel,
183 - consumeLevelUpdateTime = it.ConsumeLevelUpdateTime 191 + consumeLevelUpdateTime = it.ConsumeLevelUpdateTime,
  192 + isEffective = it.IsEffective
184 }) 193 })
185 .MergeTable() 194 .MergeTable()
186 .OrderBy(sidx + " " + input.sort) 195 .OrderBy(sidx + " " + input.sort)
@@ -269,6 +278,7 @@ namespace NCC.Extend.LqKhxx @@ -269,6 +278,7 @@ namespace NCC.Extend.LqKhxx
269 dah = it.Dah, 278 dah = it.Dah,
270 xb = it.Xb, 279 xb = it.Xb,
271 gsmd = it.Gsmd, 280 gsmd = it.Gsmd,
  281 + gsmdName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(u => u.Id == it.Gsmd).Select(u => u.Dm),
272 zcsj = it.Zcsj, 282 zcsj = it.Zcsj,
273 khlx = it.Khlx, 283 khlx = it.Khlx,
274 khjd = it.Khjd, 284 khjd = it.Khjd,
@@ -281,6 +291,33 @@ namespace NCC.Extend.LqKhxx @@ -281,6 +291,33 @@ namespace NCC.Extend.LqKhxx
281 yanglsr = it.Yanglsr, 291 yanglsr = it.Yanglsr,
282 yinlsr = it.Yinlsr, 292 yinlsr = it.Yinlsr,
283 createTime = it.CreateTime, 293 createTime = it.CreateTime,
  294 + expandUser = it.ExpandUser,
  295 + mainHealthUser = it.MainHealthUser,
  296 + subHealthUser = it.SubHealthUser,
  297 + expandUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.ExpandUser).Select(u => u.RealName),
  298 + mainHealthUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.MainHealthUser).Select(u => u.RealName),
  299 + subHealthUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.SubHealthUser).Select(u => u.RealName),
  300 + tjrName = SqlFunc.Subqueryable<LqKhxxEntity>().Where(u => u.Id == it.Tjr).Select(u => u.Khmc),
  301 + lastConsumeTime = it.LastConsumeTime,
  302 + isBeautyMember = it.IsBeautyMember,
  303 + isMedicalMember = it.IsMedicalMember,
  304 + isTechMember = it.IsTechMember,
  305 + isEducationMember = it.IsEducationMember,
  306 + beautyMemberTime = it.BeautyMemberTime,
  307 + medicalMemberTime = it.MedicalMemberTime,
  308 + techMemberTime = it.TechMemberTime,
  309 + educationMemberTime = it.EducationMemberTime,
  310 + firstVisitTime = it.FirstVisitTime,
  311 + lastVisitTime = it.LastVisitTime,
  312 + visitDays = it.VisitDays,
  313 + sleepStartTime = it.SleepStartTime,
  314 + sleepDays = it.SleepDays,
  315 + totalConsumeAmount = it.TotalConsumeAmount,
  316 + totalBillingAmount = it.TotalBillingAmount,
  317 + remainingRightsAmount = it.RemainingRightsAmount,
  318 + consumeLevel = it.ConsumeLevel,
  319 + consumeLevelUpdateTime = it.ConsumeLevelUpdateTime,
  320 + isEffective = it.IsEffective
284 }) 321 })
285 .MergeTable() 322 .MergeTable()
286 .OrderBy(sidx + " " + input.sort) 323 .OrderBy(sidx + " " + input.sort)
@@ -339,6 +376,7 @@ namespace NCC.Extend.LqKhxx @@ -339,6 +376,7 @@ namespace NCC.Extend.LqKhxx
339 dah = it.Dah, 376 dah = it.Dah,
340 xb = it.Xb, 377 xb = it.Xb,
341 gsmd = it.Gsmd, 378 gsmd = it.Gsmd,
  379 + gsmdName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(u => u.Id == it.Gsmd).Select(u => u.Dm),
342 zcsj = it.Zcsj, 380 zcsj = it.Zcsj,
343 khlx = it.Khlx, 381 khlx = it.Khlx,
344 khjd = it.Khjd, 382 khjd = it.Khjd,
@@ -354,6 +392,30 @@ namespace NCC.Extend.LqKhxx @@ -354,6 +392,30 @@ namespace NCC.Extend.LqKhxx
354 expandUser = it.ExpandUser, 392 expandUser = it.ExpandUser,
355 mainHealthUser = it.MainHealthUser, 393 mainHealthUser = it.MainHealthUser,
356 subHealthUser = it.SubHealthUser, 394 subHealthUser = it.SubHealthUser,
  395 + expandUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.ExpandUser).Select(u => u.RealName),
  396 + mainHealthUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.MainHealthUser).Select(u => u.RealName),
  397 + subHealthUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.SubHealthUser).Select(u => u.RealName),
  398 + tjrName = SqlFunc.Subqueryable<LqKhxxEntity>().Where(u => u.Id == it.Tjr).Select(u => u.Khmc),
  399 + lastConsumeTime = it.LastConsumeTime,
  400 + isBeautyMember = it.IsBeautyMember,
  401 + isMedicalMember = it.IsMedicalMember,
  402 + isTechMember = it.IsTechMember,
  403 + isEducationMember = it.IsEducationMember,
  404 + beautyMemberTime = it.BeautyMemberTime,
  405 + medicalMemberTime = it.MedicalMemberTime,
  406 + techMemberTime = it.TechMemberTime,
  407 + educationMemberTime = it.EducationMemberTime,
  408 + firstVisitTime = it.FirstVisitTime,
  409 + lastVisitTime = it.LastVisitTime,
  410 + visitDays = it.VisitDays,
  411 + sleepStartTime = it.SleepStartTime,
  412 + sleepDays = it.SleepDays,
  413 + totalConsumeAmount = it.TotalConsumeAmount,
  414 + totalBillingAmount = it.TotalBillingAmount,
  415 + remainingRightsAmount = it.RemainingRightsAmount,
  416 + consumeLevel = it.ConsumeLevel,
  417 + consumeLevelUpdateTime = it.ConsumeLevelUpdateTime,
  418 + isEffective = it.IsEffective
357 }) 419 })
358 .MergeTable() 420 .MergeTable()
359 .OrderBy(sidx + " " + input.sort) 421 .OrderBy(sidx + " " + input.sort)
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -3180,8 +3180,27 @@ namespace NCC.Extend.LqStatistics @@ -3180,8 +3180,27 @@ namespace NCC.Extend.LqStatistics
3180 #region 其他统计模块列表查询接口 3180 #region 其他统计模块列表查询接口
3181 3181
3182 /// <summary> 3182 /// <summary>
3183 - /// 获取金三角开卡业绩统计列表 3183 + /// 获取金三角开卡业绩统计列表(实时统计)
3184 /// </summary> 3184 /// </summary>
  3185 + /// <remarks>
  3186 + /// 实时从业务表统计金三角的开卡业绩数据,包括订单数量、总业绩、首次和最后订单日期
  3187 + ///
  3188 + /// 数据来源:
  3189 + /// - lq_ycsd_jsj: 金三角基础信息表
  3190 + /// - lq_kd_jksyj: 健康师业绩表(开单业绩)
  3191 + /// - lq_mdxx: 门店信息表
  3192 + ///
  3193 + /// 统计逻辑:
  3194 + /// - 按金三角ID、月份、门店分组统计
  3195 + /// - 订单数量:去重统计开单编号(glkdbh)
  3196 + /// - 总业绩:汇总健康师业绩(jksyj),过滤空值和0值
  3197 + /// - 首次/最后订单日期:取业绩时间(yjsj)的最小值和最大值
  3198 + ///
  3199 + /// 性能优化:
  3200 + /// - 使用索引:jsj_id, yjsj, F_IsEffective
  3201 + /// - 先过滤再JOIN,减少数据量
  3202 + /// - 使用子查询优化聚合计算
  3203 + /// </remarks>
3185 /// <param name="input">查询参数</param> 3204 /// <param name="input">查询参数</param>
3186 /// <returns>分页结果</returns> 3205 /// <returns>分页结果</returns>
3187 [HttpPost("get-gold-triangle-statistics-list")] 3206 [HttpPost("get-gold-triangle-statistics-list")]
@@ -3189,40 +3208,100 @@ namespace NCC.Extend.LqStatistics @@ -3189,40 +3208,100 @@ namespace NCC.Extend.LqStatistics
3189 { 3208 {
3190 try 3209 try
3191 { 3210 {
3192 - var query = _db.Queryable<LqStatisticsGoldTriangleEntity>(); 3211 + // 构建WHERE条件
  3212 + var whereConditions = new List<string>();
  3213 + var parameters = new List<SugarParameter>();
3193 3214
3194 - // 添加查询条件  
3195 - query = query.WhereIF(!string.IsNullOrEmpty(input.StatisticsMonth), x => x.StatisticsMonth == input.StatisticsMonth);  
3196 - query = query.WhereIF(!string.IsNullOrEmpty(input.GoldTriangleName), x => x.GoldTriangleName.Contains(input.GoldTriangleName));  
3197 - query = query.WhereIF(!string.IsNullOrEmpty(input.StoreName), x => x.StoreName.Contains(input.StoreName)); 3215 + // 月份条件
  3216 + if (!string.IsNullOrEmpty(input.StatisticsMonth))
  3217 + {
  3218 + whereConditions.Add("jsj.yf = @StatisticsMonth");
  3219 + parameters.Add(new SugarParameter("@StatisticsMonth", input.StatisticsMonth));
  3220 + }
3198 3221
3199 - // 按创建时间降序排序  
3200 - query = query.OrderBy(x => x.CreateTime, OrderByType.Desc); 3222 + // 金三角名称条件
  3223 + if (!string.IsNullOrEmpty(input.GoldTriangleName))
  3224 + {
  3225 + whereConditions.Add("jsj.jsj LIKE @GoldTriangleName");
  3226 + parameters.Add(new SugarParameter("@GoldTriangleName", $"%{input.GoldTriangleName}%"));
  3227 + }
3201 3228
3202 - // 分页查询并映射到DTO  
3203 - var result = await query.Select(it => new LqGoldTriangleStatisticsListOutput 3229 + // 门店名称条件
  3230 + if (!string.IsNullOrEmpty(input.StoreName))
3204 { 3231 {
3205 - Id = it.Id,  
3206 - GoldTriangleId = it.GoldTriangleId,  
3207 - GoldTriangleName = it.GoldTriangleName,  
3208 - StatisticsMonth = it.StatisticsMonth,  
3209 - StoreId = it.StoreId,  
3210 - StoreName = it.StoreName,  
3211 - OrderCount = it.OrderCount,  
3212 - TotalPerformance = it.TotalPerformance,  
3213 - FirstOrderDate = it.FirstOrderDate,  
3214 - LastOrderDate = it.LastOrderDate,  
3215 - CreateTime = it.CreateTime  
3216 - }).ToPagedListAsync(input.PageIndex, input.PageSize); 3232 + whereConditions.Add("md.dm LIKE @StoreName");
  3233 + parameters.Add(new SugarParameter("@StoreName", $"%{input.StoreName}%"));
  3234 + }
  3235 +
  3236 + var whereClause = whereConditions.Any() ? "WHERE " + string.Join(" AND ", whereConditions) : "";
  3237 +
  3238 + // 实时统计SQL - 优化性能
  3239 + // 使用子查询先过滤有效业绩数据,减少JOIN数据量
  3240 + var sql = $@"
  3241 + SELECT
  3242 + CONCAT(jsj.F_Id, '_', jsj.yf) AS Id,
  3243 + jsj.F_Id AS GoldTriangleId,
  3244 + jsj.jsj AS GoldTriangleName,
  3245 + jsj.yf AS StatisticsMonth,
  3246 + jsj.md AS StoreId,
  3247 + COALESCE(md.dm, '') AS StoreName,
  3248 + COALESCE(stats.OrderCount, 0) AS OrderCount,
  3249 + COALESCE(stats.TotalPerformance, 0) AS TotalPerformance,
  3250 + stats.FirstOrderDate,
  3251 + stats.LastOrderDate,
  3252 + NOW() AS CreateTime
  3253 + FROM lq_ycsd_jsj jsj
  3254 + LEFT JOIN lq_mdxx md ON jsj.md = md.F_Id
  3255 + LEFT JOIN (
  3256 + -- 子查询:按金三角ID和月份统计业绩数据(先过滤再聚合,提高效率)
  3257 + SELECT
  3258 + jksyj.jsj_id,
  3259 + YEAR(jksyj.yjsj) * 100 + MONTH(jksyj.yjsj) AS statistics_month,
  3260 + COUNT(DISTINCT jksyj.glkdbh) AS OrderCount,
  3261 + SUM(CAST(jksyj.jksyj AS DECIMAL(18,2))) AS TotalPerformance,
  3262 + MIN(jksyj.yjsj) AS FirstOrderDate,
  3263 + MAX(jksyj.yjsj) AS LastOrderDate
  3264 + FROM lq_kd_jksyj jksyj
  3265 + WHERE jksyj.F_IsEffective = 1
  3266 + AND jksyj.yjsj IS NOT NULL
  3267 + AND jksyj.jksyj IS NOT NULL
  3268 + AND jksyj.jksyj != ''
  3269 + AND jksyj.jksyj != '0'
  3270 + AND jksyj.jsj_id IS NOT NULL
  3271 + AND jksyj.jsj_id != ''
  3272 + GROUP BY jksyj.jsj_id, YEAR(jksyj.yjsj), MONTH(jksyj.yjsj)
  3273 + ) stats ON (
  3274 + stats.jsj_id = jsj.F_Id
  3275 + AND stats.statistics_month = CAST(jsj.yf AS UNSIGNED)
  3276 + )
  3277 + {whereClause}
  3278 + ORDER BY stats.TotalPerformance DESC, jsj.yf DESC
  3279 + LIMIT @PageSize OFFSET @Offset";
  3280 +
  3281 + parameters.Add(new SugarParameter("@PageSize", input.PageSize));
  3282 + parameters.Add(new SugarParameter("@Offset", (input.PageIndex - 1) * input.PageSize));
  3283 +
  3284 + // 查询总数
  3285 + var countSql = $@"
  3286 + SELECT COUNT(DISTINCT jsj.F_Id)
  3287 + FROM lq_ycsd_jsj jsj
  3288 + LEFT JOIN lq_mdxx md ON jsj.md = md.F_Id
  3289 + {whereClause}";
  3290 +
  3291 + var countParameters = parameters.Where(p => p.ParameterName != "@PageSize" && p.ParameterName != "@Offset").ToList();
  3292 + var totalCount = await _db.Ado.GetIntAsync(countSql, countParameters);
  3293 +
  3294 + // 执行查询
  3295 + var result = await _db.Ado.SqlQueryAsync<LqGoldTriangleStatisticsListOutput>(sql, parameters);
3217 3296
3218 return new 3297 return new
3219 { 3298 {
3220 - list = result.list, 3299 + list = result,
3221 pagination = new 3300 pagination = new
3222 { 3301 {
3223 pageIndex = input.PageIndex, 3302 pageIndex = input.PageIndex,
3224 pageSize = input.PageSize, 3303 pageSize = input.PageSize,
3225 - total = result.pagination.Total 3304 + total = totalCount
3226 } 3305 }
3227 }; 3306 };
3228 } 3307 }
@@ -3234,8 +3313,26 @@ namespace NCC.Extend.LqStatistics @@ -3234,8 +3313,26 @@ namespace NCC.Extend.LqStatistics
3234 } 3313 }
3235 3314
3236 /// <summary> 3315 /// <summary>
3237 - /// 获取科技部开单业绩统计列表 3316 + /// 获取科技部开单业绩统计列表(实时统计)
3238 /// </summary> 3317 /// </summary>
  3318 + /// <remarks>
  3319 + /// 实时从业务表统计科技部老师的开单业绩数据,包括订单数量、总业绩等
  3320 + ///
  3321 + /// 数据来源:
  3322 + /// - lq_kd_kjbsyj: 科技部老师开单业绩表
  3323 + /// - lq_kd_pxmx: 开单品项明细表(用于统计项目数量)
  3324 + ///
  3325 + /// 统计逻辑:
  3326 + /// - 按老师ID分组统计
  3327 + /// - 订单数量:去重统计开单品项ID(F_kdpxid)
  3328 + /// - 总业绩:汇总科技部老师业绩(kjblsyj),过滤空值和0值
  3329 + /// - 项目数量:汇总品项明细的项目数量(F_ProjectNumber)
  3330 + ///
  3331 + /// 性能优化:
  3332 + /// - 使用索引:kjbls, yjsj, F_IsEffective
  3333 + /// - 先过滤再聚合,减少数据量
  3334 + /// - 使用子查询优化聚合计算
  3335 + /// </remarks>
3239 /// <param name="input">查询参数</param> 3336 /// <param name="input">查询参数</param>
3240 /// <returns>分页结果</returns> 3337 /// <returns>分页结果</returns>
3241 [HttpPost("get-tech-performance-statistics-list")] 3338 [HttpPost("get-tech-performance-statistics-list")]
@@ -3243,38 +3340,84 @@ namespace NCC.Extend.LqStatistics @@ -3243,38 +3340,84 @@ namespace NCC.Extend.LqStatistics
3243 { 3340 {
3244 try 3341 try
3245 { 3342 {
3246 - var query = _db.Queryable<LqStatisticsTechPerformanceEntity>(); 3343 + // 构建WHERE条件
  3344 + var whereConditions = new List<string>();
  3345 + var parameters = new List<SugarParameter>();
3247 3346
3248 - // 添加查询条件  
3249 - query = query.WhereIF(!string.IsNullOrEmpty(input.StatisticsMonth), x => x.StatisticsMonth == input.StatisticsMonth);  
3250 - query = query.WhereIF(!string.IsNullOrEmpty(input.TeacherName), x => x.TeacherName.Contains(input.TeacherName));  
3251 - query = query.WhereIF(!string.IsNullOrEmpty(input.StoreName), x => x.StoreName.Contains(input.StoreName)); 3347 + // 基础条件
  3348 + var baseConditions = new List<string>
  3349 + {
  3350 + "k.kjbls IS NOT NULL",
  3351 + "k.kjblsxm IS NOT NULL",
  3352 + "k.yjsj IS NOT NULL",
  3353 + "k.kjblsyj IS NOT NULL",
  3354 + "k.kjblsyj != ''",
  3355 + "k.kjblsyj != '0'",
  3356 + "k.F_IsEffective = 1"
  3357 + };
3252 3358
3253 - // 按创建时间降序排序  
3254 - query = query.OrderBy(x => x.CreateTime, OrderByType.Desc); 3359 + // 月份条件
  3360 + if (!string.IsNullOrEmpty(input.StatisticsMonth))
  3361 + {
  3362 + var year = int.Parse(input.StatisticsMonth.Substring(0, 4));
  3363 + var month = int.Parse(input.StatisticsMonth.Substring(4, 2));
  3364 + baseConditions.Add("YEAR(k.yjsj) = @Year");
  3365 + baseConditions.Add("MONTH(k.yjsj) = @Month");
  3366 + parameters.Add(new SugarParameter("@Year", year));
  3367 + parameters.Add(new SugarParameter("@Month", month));
  3368 + }
3255 3369
3256 - // 分页查询并映射到DTO  
3257 - var result = await query.Select(it => new LqTechPerformanceStatisticsListOutput 3370 + // 老师姓名条件
  3371 + if (!string.IsNullOrEmpty(input.TeacherName))
3258 { 3372 {
3259 - Id = it.Id,  
3260 - StatisticsMonth = it.StatisticsMonth,  
3261 - TeacherId = it.TeacherId,  
3262 - TeacherName = it.TeacherName,  
3263 - StoreId = it.StoreId,  
3264 - StoreName = it.StoreName,  
3265 - TotalPerformance = it.TotalPerformance,  
3266 - OrderCount = it.OrderCount,  
3267 - CreateTime = it.CreateTime  
3268 - }).ToPagedListAsync(input.PageIndex, input.PageSize); 3373 + baseConditions.Add("k.kjblsxm LIKE @TeacherName");
  3374 + parameters.Add(new SugarParameter("@TeacherName", $"%{input.TeacherName}%"));
  3375 + }
  3376 +
  3377 + var whereClause = "WHERE " + string.Join(" AND ", baseConditions);
  3378 +
  3379 + // 实时统计SQL - 优化性能
  3380 + var sql = $@"
  3381 + SELECT
  3382 + CONCAT(k.kjbls, '_', DATE_FORMAT(k.yjsj, '%Y%m')) AS Id,
  3383 + DATE_FORMAT(k.yjsj, '%Y%m') AS StatisticsMonth,
  3384 + k.kjbls AS TeacherId,
  3385 + k.kjblsxm AS TeacherName,
  3386 + '' AS StoreId,
  3387 + '' AS StoreName,
  3388 + COUNT(DISTINCT k.F_kdpxid) AS OrderCount,
  3389 + SUM(CAST(COALESCE(k.kjblsyj, 0) AS DECIMAL(18,2))) AS TotalPerformance,
  3390 + NOW() AS CreateTime
  3391 + FROM lq_kd_kjbsyj k
  3392 + LEFT JOIN lq_kd_pxmx pm ON k.F_kdpxid = pm.F_Id AND pm.F_IsEffective = 1
  3393 + {whereClause}
  3394 + GROUP BY k.kjbls, k.kjblsxm, DATE_FORMAT(k.yjsj, '%Y%m')
  3395 + ORDER BY TotalPerformance DESC, StatisticsMonth DESC
  3396 + LIMIT @PageSize OFFSET @Offset";
  3397 +
  3398 + parameters.Add(new SugarParameter("@PageSize", input.PageSize));
  3399 + parameters.Add(new SugarParameter("@Offset", (input.PageIndex - 1) * input.PageSize));
  3400 +
  3401 + // 查询总数
  3402 + var countSql = $@"
  3403 + SELECT COUNT(DISTINCT CONCAT(k.kjbls, '_', DATE_FORMAT(k.yjsj, '%Y%m')))
  3404 + FROM lq_kd_kjbsyj k
  3405 + {whereClause}";
  3406 +
  3407 + var countParameters = parameters.Where(p => p.ParameterName != "@PageSize" && p.ParameterName != "@Offset").ToList();
  3408 + var totalCount = await _db.Ado.GetIntAsync(countSql, countParameters);
  3409 +
  3410 + // 执行查询
  3411 + var result = await _db.Ado.SqlQueryAsync<LqTechPerformanceStatisticsListOutput>(sql, parameters);
3269 3412
3270 return new 3413 return new
3271 { 3414 {
3272 - list = result.list, 3415 + list = result,
3273 pagination = new 3416 pagination = new
3274 { 3417 {
3275 pageIndex = input.PageIndex, 3418 pageIndex = input.PageIndex,
3276 pageSize = input.PageSize, 3419 pageSize = input.PageSize,
3277 - total = result.pagination.Total 3420 + total = totalCount
3278 } 3421 }
3279 }; 3422 };
3280 } 3423 }
@@ -3286,8 +3429,27 @@ namespace NCC.Extend.LqStatistics @@ -3286,8 +3429,27 @@ namespace NCC.Extend.LqStatistics
3286 } 3429 }
3287 3430
3288 /// <summary> 3431 /// <summary>
3289 - /// 获取门店耗卡业绩统计列表 3432 + /// 获取门店耗卡业绩统计列表(实时统计)
3290 /// </summary> 3433 /// </summary>
  3434 + /// <remarks>
  3435 + /// 实时从业务表统计门店的耗卡业绩数据,包括耗卡业绩、项目数量、手工费等
  3436 + ///
  3437 + /// 数据来源:
  3438 + /// - lq_xh_hyhk: 耗卡表
  3439 + /// - lq_xh_pxmx: 耗卡品项明细表
  3440 + /// - lq_mdxx: 门店信息表
  3441 + ///
  3442 + /// 统计逻辑:
  3443 + /// - 按门店分组统计
  3444 + /// - 耗卡业绩:汇总品项明细的总价(F_TotalPrice)
  3445 + /// - 项目数量:汇总品项明细的项目数量(F_ProjectNumber)
  3446 + /// - 手工费:汇总耗卡表的手工费(sgfy)
  3447 + ///
  3448 + /// 性能优化:
  3449 + /// - 使用索引:md, hksj, F_IsEffective
  3450 + /// - 先过滤再聚合,减少数据量
  3451 + /// - 使用子查询优化聚合计算
  3452 + /// </remarks>
3291 /// <param name="input">查询参数</param> 3453 /// <param name="input">查询参数</param>
3292 /// <returns>分页结果</returns> 3454 /// <returns>分页结果</returns>
3293 [HttpPost("get-store-consume-performance-statistics-list")] 3455 [HttpPost("get-store-consume-performance-statistics-list")]
@@ -3295,39 +3457,92 @@ namespace NCC.Extend.LqStatistics @@ -3295,39 +3457,92 @@ namespace NCC.Extend.LqStatistics
3295 { 3457 {
3296 try 3458 try
3297 { 3459 {
3298 - var query = _db.Queryable<LqStatisticsStoreConsumePerformanceEntity>(); 3460 + // 构建WHERE条件
  3461 + var whereConditions = new List<string>();
  3462 + var parameters = new List<SugarParameter>();
3299 3463
3300 - // 添加查询条件  
3301 - query = query.WhereIF(!string.IsNullOrEmpty(input.StatisticsMonth), x => x.StatisticsMonth == input.StatisticsMonth);  
3302 - query = query.WhereIF(!string.IsNullOrEmpty(input.StoreName), x => x.StoreName.Contains(input.StoreName)); 3464 + // 月份条件
  3465 + if (!string.IsNullOrEmpty(input.StatisticsMonth))
  3466 + {
  3467 + whereConditions.Add("DATE_FORMAT(hyhk.hksj, '%Y%m') = @StatisticsMonth");
  3468 + parameters.Add(new SugarParameter("@StatisticsMonth", input.StatisticsMonth));
  3469 + }
3303 3470
3304 - // 按创建时间降序排序  
3305 - query = query.OrderBy(x => x.CreateTime, OrderByType.Desc); 3471 + // 门店名称条件
  3472 + if (!string.IsNullOrEmpty(input.StoreName))
  3473 + {
  3474 + whereConditions.Add("(hyhk.mdmc LIKE @StoreName OR mdxx.dm LIKE @StoreName)");
  3475 + parameters.Add(new SugarParameter("@StoreName", $"%{input.StoreName}%"));
  3476 + }
3306 3477
3307 - // 分页查询并映射到DTO  
3308 - var pagedResult = await query.ToPagedListAsync(input.PageIndex, input.PageSize); 3478 + // 基础条件
  3479 + whereConditions.Add("hyhk.F_IsEffective = 1");
3309 3480
3310 - var outputList = pagedResult.list.Select(it => new LqStoreConsumePerformanceStatisticsListOutput  
3311 - {  
3312 - Id = it.Id,  
3313 - StatisticsMonth = it.StatisticsMonth,  
3314 - StoreId = it.StoreId,  
3315 - StoreName = it.StoreName,  
3316 - TotalPerformance = it.ConsumePerformance, // 使用ConsumePerformance作为总业绩  
3317 - ConsumePerformance = it.ConsumePerformance,  
3318 - OrderCount = (int)it.ConsumeQuantity, // 使用ConsumeQuantity作为订单数量  
3319 - IsNewStore = false, // 暂时设为false,因为Entity中没有这个字段  
3320 - CreateTime = it.CreateTime.HasValue ? it.CreateTime.Value : DateTime.Now  
3321 - }).ToList(); 3481 + var whereClause = "WHERE " + string.Join(" AND ", whereConditions);
  3482 +
  3483 + // 实时统计SQL - 优化性能
  3484 + var sql = $@"
  3485 + SELECT
  3486 + CONCAT(hyhk.md, '_', DATE_FORMAT(hyhk.hksj, '%Y%m')) AS Id,
  3487 + DATE_FORMAT(hyhk.hksj, '%Y%m') AS StatisticsMonth,
  3488 + hyhk.md AS StoreId,
  3489 + COALESCE(hyhk.mdmc, mdxx.dm, '') AS StoreName,
  3490 + COALESCE(SUM(pxmx.F_TotalPrice), 0) AS ConsumePerformance,
  3491 + COALESCE(SUM(pxmx.F_TotalPrice), 0) AS TotalPerformance,
  3492 + CAST(COALESCE(SUM(pxmx.F_ProjectNumber), 0) AS UNSIGNED) AS OrderCount,
  3493 + CASE
  3494 + WHEN EXISTS (
  3495 + SELECT 1 FROM lq_md_xdbhsj xdbh
  3496 + WHERE xdbh.mdid = hyhk.md
  3497 + AND xdbh.sfqy = 1
  3498 + AND xdbh.bhkssj <= DATE_FORMAT(hyhk.hksj, '%Y-%m-%d')
  3499 + AND xdbh.bhjssj >= DATE_FORMAT(hyhk.hksj, '%Y-%m-%d')
  3500 + LIMIT 1
  3501 + ) THEN 1
  3502 + ELSE 0
  3503 + END AS IsNewStore,
  3504 + NOW() AS CreateTime
  3505 + FROM lq_xh_hyhk hyhk
  3506 + LEFT JOIN lq_xh_pxmx pxmx ON hyhk.F_Id = pxmx.F_ConsumeInfoId AND pxmx.F_IsEffective = 1
  3507 + LEFT JOIN lq_mdxx mdxx ON hyhk.md = mdxx.F_Id
  3508 + {whereClause}
  3509 + GROUP BY hyhk.md, hyhk.mdmc, mdxx.dm, DATE_FORMAT(hyhk.hksj, '%Y%m')
  3510 + HAVING ConsumePerformance > 0 OR OrderCount > 0
  3511 + ORDER BY ConsumePerformance DESC, StatisticsMonth DESC
  3512 + LIMIT @PageSize OFFSET @Offset";
  3513 +
  3514 + parameters.Add(new SugarParameter("@PageSize", input.PageSize));
  3515 + parameters.Add(new SugarParameter("@Offset", (input.PageIndex - 1) * input.PageSize));
  3516 +
  3517 + // 查询总数(使用子查询统计满足条件的分组数)
  3518 + var countSql = $@"
  3519 + SELECT COUNT(*)
  3520 + FROM (
  3521 + SELECT
  3522 + hyhk.md,
  3523 + DATE_FORMAT(hyhk.hksj, '%Y%m') AS month_key
  3524 + FROM lq_xh_hyhk hyhk
  3525 + LEFT JOIN lq_xh_pxmx pxmx ON hyhk.F_Id = pxmx.F_ConsumeInfoId AND pxmx.F_IsEffective = 1
  3526 + LEFT JOIN lq_mdxx mdxx ON hyhk.md = mdxx.F_Id
  3527 + {whereClause}
  3528 + GROUP BY hyhk.md, DATE_FORMAT(hyhk.hksj, '%Y%m')
  3529 + HAVING COALESCE(SUM(pxmx.F_TotalPrice), 0) > 0 OR COALESCE(SUM(pxmx.F_ProjectNumber), 0) > 0
  3530 + ) AS grouped_data";
  3531 +
  3532 + var countParameters = parameters.Where(p => p.ParameterName != "@PageSize" && p.ParameterName != "@Offset").ToList();
  3533 + var totalCount = await _db.Ado.GetIntAsync(countSql, countParameters);
  3534 +
  3535 + // 执行查询
  3536 + var result = await _db.Ado.SqlQueryAsync<LqStoreConsumePerformanceStatisticsListOutput>(sql, parameters);
3322 3537
3323 return new 3538 return new
3324 { 3539 {
3325 - list = outputList, 3540 + list = result,
3326 pagination = new 3541 pagination = new
3327 { 3542 {
3328 pageIndex = input.PageIndex, 3543 pageIndex = input.PageIndex,
3329 pageSize = input.PageSize, 3544 pageSize = input.PageSize,
3330 - total = pagedResult.pagination.Total 3545 + total = totalCount
3331 } 3546 }
3332 }; 3547 };
3333 } 3548 }
@@ -3339,8 +3554,28 @@ namespace NCC.Extend.LqStatistics @@ -3339,8 +3554,28 @@ namespace NCC.Extend.LqStatistics
3339 } 3554 }
3340 3555
3341 /// <summary> 3556 /// <summary>
3342 - /// 获取个人消耗业绩统计列表 3557 + /// 获取部门消耗业绩统计列表(实时统计)
3343 /// </summary> 3558 /// </summary>
  3559 + /// <remarks>
  3560 + /// 实时从业务表统计部门员工的消耗业绩数据,包括健康师和科技部老师两种类型
  3561 + ///
  3562 + /// 数据来源:
  3563 + /// - lq_xh_jksyj: 健康师消耗业绩表
  3564 + /// - lq_xh_kjbsyj: 科技部老师消耗业绩表
  3565 + /// - lq_xh_hyhk: 耗卡表(用于获取时间和门店信息)
  3566 + /// - lq_mdxx: 门店信息表
  3567 + ///
  3568 + /// 统计逻辑:
  3569 + /// - 健康师:按健康师ID、门店分组,统计消耗业绩、项目数量、人头、人次
  3570 + /// - 科技部老师:按老师ID、门店分组,统计消耗业绩、项目数量(人头、人次为0)
  3571 + /// - 人头:月度去重客户数
  3572 + /// - 人次:日度去重到店数(每天同一个客户只算一次)
  3573 + ///
  3574 + /// 性能优化:
  3575 + /// - 使用UNION ALL合并健康师和科技部老师数据
  3576 + /// - 使用子查询优化人头和人次统计
  3577 + /// - 使用索引:jkszh/kjblszh, md, hksj, F_IsEffective
  3578 + /// </remarks>
3344 /// <param name="input">查询参数</param> 3579 /// <param name="input">查询参数</param>
3345 /// <returns>分页结果</returns> 3580 /// <returns>分页结果</returns>
3346 [HttpPost("get-department-consume-performance-statistics-list")] 3581 [HttpPost("get-department-consume-performance-statistics-list")]
@@ -3348,45 +3583,196 @@ namespace NCC.Extend.LqStatistics @@ -3348,45 +3583,196 @@ namespace NCC.Extend.LqStatistics
3348 { 3583 {
3349 try 3584 try
3350 { 3585 {
3351 - var query = _db.Queryable<LqStatisticsDepartmentConsumePerformanceEntity>(); 3586 + // 构建WHERE条件
  3587 + var whereConditions = new List<string>();
  3588 + var parameters = new List<SugarParameter>();
3352 3589
3353 - // 添加查询条件  
3354 - query = query.WhereIF(!string.IsNullOrEmpty(input.StatisticsMonth), x => x.StatisticsMonth == input.StatisticsMonth);  
3355 - query = query.WhereIF(!string.IsNullOrEmpty(input.EmployeeName), x => x.UserName.Contains(input.EmployeeName));  
3356 - query = query.WhereIF(!string.IsNullOrEmpty(input.StoreName), x => x.StoreName.Contains(input.StoreName));  
3357 - query = query.WhereIF(!string.IsNullOrEmpty(input.Position), x => x.DepartmentType.Contains(input.Position)); 3590 + // 月份条件(用于子查询)
  3591 + var monthCondition = "";
  3592 + if (!string.IsNullOrEmpty(input.StatisticsMonth))
  3593 + {
  3594 + monthCondition = "AND DATE_FORMAT(hyhk_inner.hksj, '%Y%m') = @StatisticsMonth";
  3595 + whereConditions.Add("DATE_FORMAT(hyhk.hksj, '%Y%m') = @StatisticsMonth");
  3596 + parameters.Add(new SugarParameter("@StatisticsMonth", input.StatisticsMonth));
  3597 + }
3358 3598
3359 - // 按创建时间降序排序  
3360 - query = query.OrderBy(x => x.CreateTime, OrderByType.Desc); 3599 + // 员工姓名条件(需要在UNION的两个查询中都添加)
  3600 + var employeeNameConditionJks = "";
  3601 + var employeeNameConditionKjbs = "";
  3602 + if (!string.IsNullOrEmpty(input.EmployeeName))
  3603 + {
  3604 + employeeNameConditionJks = "AND jksyj.jksxm LIKE @EmployeeName";
  3605 + employeeNameConditionKjbs = "AND kjbsyj.kjblsxm LIKE @EmployeeName";
  3606 + parameters.Add(new SugarParameter("@EmployeeName", $"%{input.EmployeeName}%"));
  3607 + }
3361 3608
3362 - // 分页查询并映射到DTO  
3363 - var pagedResult = await query.ToPagedListAsync(input.PageIndex, input.PageSize); 3609 + // 门店名称条件
  3610 + var storeNameCondition = "";
  3611 + if (!string.IsNullOrEmpty(input.StoreName))
  3612 + {
  3613 + storeNameCondition = "AND (hyhk.mdmc LIKE @StoreName OR md.dm LIKE @StoreName)";
  3614 + parameters.Add(new SugarParameter("@StoreName", $"%{input.StoreName}%"));
  3615 + }
3364 3616
3365 - var outputList = pagedResult.list.Select(it => new LqDepartmentConsumePerformanceStatisticsListOutput 3617 + // 岗位条件
  3618 + var positionCondition = "";
  3619 + if (!string.IsNullOrEmpty(input.Position))
3366 { 3620 {
3367 - Id = it.Id,  
3368 - StatisticsMonth = it.StatisticsMonth,  
3369 - EmployeeId = it.UserId,  
3370 - EmployeeName = it.UserName,  
3371 - StoreId = it.StoreId,  
3372 - StoreName = it.StoreName,  
3373 - Position = it.DepartmentType,  
3374 - TotalPerformance = it.ConsumePerformance, // 使用ConsumePerformance作为总业绩  
3375 - ConsumePerformance = it.ConsumePerformance,  
3376 - OrderCount = it.ConsumeQuantity, // 使用ConsumeQuantity作为订单数量  
3377 - HeadCount = it.HeadCount, // 人头数  
3378 - PersonCount = it.PersonCount, // 人次  
3379 - CreateTime = it.CreateTime.HasValue ? it.CreateTime.Value : DateTime.Now  
3380 - }).ToList(); 3621 + positionCondition = "AND dept_type LIKE @Position";
  3622 + parameters.Add(new SugarParameter("@Position", $"%{input.Position}%"));
  3623 + }
  3624 +
  3625 + var baseWhere = whereConditions.Any() ? string.Join(" AND ", whereConditions) : "";
  3626 + var baseWhereClause = !string.IsNullOrEmpty(baseWhere) ? "WHERE " + baseWhere : "";
  3627 +
  3628 + // 实时统计SQL - 使用UNION ALL合并健康师和科技部老师数据
  3629 + var sql = $@"
  3630 + SELECT
  3631 + CONCAT(dept_type, '_', user_id, '_', store_id, '_', statistics_month) AS Id,
  3632 + statistics_month AS StatisticsMonth,
  3633 + user_id AS EmployeeId,
  3634 + user_name AS EmployeeName,
  3635 + store_id AS StoreId,
  3636 + store_name AS StoreName,
  3637 + dept_type AS Position,
  3638 + consume_performance AS ConsumePerformance,
  3639 + consume_performance AS TotalPerformance,
  3640 + consume_quantity AS OrderCount,
  3641 + head_count AS HeadCount,
  3642 + person_count AS PersonCount,
  3643 + 0 AS RefundAmount,
  3644 + 0 AS RefundCount,
  3645 + NOW() AS CreateTime
  3646 + FROM (
  3647 + -- 健康师消耗业绩统计
  3648 + SELECT
  3649 + '健康师' AS dept_type,
  3650 + jksyj.jkszh AS user_id,
  3651 + jksyj.jksxm AS user_name,
  3652 + hyhk.md AS store_id,
  3653 + COALESCE(hyhk.mdmc, md.dm, '') AS store_name,
  3654 + DATE_FORMAT(hyhk.hksj, '%Y%m') AS statistics_month,
  3655 + COALESCE(SUM(jksyj.jksyj), 0) AS consume_performance,
  3656 + COALESCE(SUM(jksyj.F_kdpxNumber), 0) AS consume_quantity,
  3657 + COALESCE(headcount_stats.head_count, 0) AS head_count,
  3658 + COALESCE(personcount_stats.person_count, 0) AS person_count
  3659 + FROM lq_xh_jksyj jksyj
  3660 + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id AND hyhk.F_IsEffective = 1
  3661 + LEFT JOIN lq_mdxx md ON hyhk.md = md.F_Id
  3662 + LEFT JOIN (
  3663 + -- 人头统计:月度去重客户数
  3664 + SELECT
  3665 + jksyj_inner.jkszh,
  3666 + hyhk_inner.md,
  3667 + COUNT(DISTINCT hyhk_inner.hy) AS head_count
  3668 + FROM lq_xh_jksyj jksyj_inner
  3669 + INNER JOIN lq_xh_hyhk hyhk_inner ON jksyj_inner.glkdbh = hyhk_inner.F_Id AND hyhk_inner.F_IsEffective = 1
  3670 + WHERE jksyj_inner.F_IsEffective = 1
  3671 + {monthCondition}
  3672 + GROUP BY jksyj_inner.jkszh, hyhk_inner.md
  3673 + ) headcount_stats ON jksyj.jkszh = headcount_stats.jkszh AND hyhk.md = headcount_stats.md
  3674 + LEFT JOIN (
  3675 + -- 人次统计:日度去重客户数
  3676 + SELECT
  3677 + jksyj_inner.jkszh,
  3678 + hyhk_inner.md,
  3679 + COUNT(DISTINCT CONCAT(hyhk_inner.hy, '-', DATE(hyhk_inner.hksj))) AS person_count
  3680 + FROM lq_xh_jksyj jksyj_inner
  3681 + INNER JOIN lq_xh_hyhk hyhk_inner ON jksyj_inner.glkdbh = hyhk_inner.F_Id AND hyhk_inner.F_IsEffective = 1
  3682 + WHERE jksyj_inner.F_IsEffective = 1
  3683 + {monthCondition}
  3684 + GROUP BY jksyj_inner.jkszh, hyhk_inner.md
  3685 + ) personcount_stats ON jksyj.jkszh = personcount_stats.jkszh AND hyhk.md = personcount_stats.md
  3686 + WHERE jksyj.F_IsEffective = 1
  3687 + {(!string.IsNullOrEmpty(baseWhere) ? "AND " + baseWhere : "")}
  3688 + {employeeNameConditionJks}
  3689 + {storeNameCondition}
  3690 + GROUP BY jksyj.jkszh, jksyj.jksxm, hyhk.md, hyhk.mdmc, md.dm, DATE_FORMAT(hyhk.hksj, '%Y%m'), headcount_stats.head_count, personcount_stats.person_count, statistics_month
  3691 +
  3692 + UNION ALL
  3693 +
  3694 + -- 科技部老师消耗业绩统计
  3695 + SELECT
  3696 + '科技部老师' AS dept_type,
  3697 + kjbsyj.kjblszh AS user_id,
  3698 + kjbsyj.kjblsxm AS user_name,
  3699 + hyhk.md AS store_id,
  3700 + COALESCE(hyhk.mdmc, md.dm, '') AS store_name,
  3701 + DATE_FORMAT(hyhk.hksj, '%Y%m') AS statistics_month,
  3702 + COALESCE(SUM(kjbsyj.kjblsyj), 0) AS consume_performance,
  3703 + COALESCE(SUM(kjbsyj.F_hdpxNumber), 0) AS consume_quantity,
  3704 + 0 AS head_count,
  3705 + 0 AS person_count
  3706 + FROM lq_xh_kjbsyj kjbsyj
  3707 + INNER JOIN lq_xh_hyhk hyhk ON kjbsyj.glkdbh = hyhk.F_Id AND hyhk.F_IsEffective = 1
  3708 + LEFT JOIN lq_mdxx md ON hyhk.md = md.F_Id
  3709 + WHERE kjbsyj.F_IsEffective = 1
  3710 + {(!string.IsNullOrEmpty(baseWhere) ? "AND " + baseWhere : "")}
  3711 + {employeeNameConditionKjbs}
  3712 + {storeNameCondition}
  3713 + GROUP BY kjbsyj.kjblszh, kjbsyj.kjblsxm, hyhk.md, hyhk.mdmc, md.dm, DATE_FORMAT(hyhk.hksj, '%Y%m')
  3714 + ) combined_stats
  3715 + WHERE 1=1
  3716 + {positionCondition}
  3717 + ORDER BY consume_performance DESC, StatisticsMonth DESC
  3718 + LIMIT @PageSize OFFSET @Offset";
  3719 +
  3720 + parameters.Add(new SugarParameter("@PageSize", input.PageSize));
  3721 + parameters.Add(new SugarParameter("@Offset", (input.PageIndex - 1) * input.PageSize));
  3722 +
  3723 + // 查询总数(需要重新构建,因为UNION ALL的结构)
  3724 + var countSql = $@"
  3725 + SELECT COUNT(*)
  3726 + FROM (
  3727 + SELECT
  3728 + '健康师' AS dept_type,
  3729 + jksyj.jkszh AS user_id,
  3730 + jksyj.jksxm AS user_name,
  3731 + hyhk.md AS store_id,
  3732 + COALESCE(hyhk.mdmc, md.dm, '') AS store_name
  3733 + FROM lq_xh_jksyj jksyj
  3734 + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id AND hyhk.F_IsEffective = 1
  3735 + LEFT JOIN lq_mdxx md ON hyhk.md = md.F_Id
  3736 + WHERE jksyj.F_IsEffective = 1
  3737 + {(!string.IsNullOrEmpty(baseWhere) ? "AND " + baseWhere : "")}
  3738 + {employeeNameConditionJks}
  3739 + {storeNameCondition}
  3740 + GROUP BY jksyj.jkszh, jksyj.jksxm, hyhk.md, hyhk.mdmc, md.dm, DATE_FORMAT(hyhk.hksj, '%Y%m')
  3741 +
  3742 + UNION ALL
  3743 +
  3744 + SELECT
  3745 + '科技部老师' AS dept_type,
  3746 + kjbsyj.kjblszh AS user_id,
  3747 + kjbsyj.kjblsxm AS user_name,
  3748 + hyhk.md AS store_id,
  3749 + COALESCE(hyhk.mdmc, md.dm, '') AS store_name
  3750 + FROM lq_xh_kjbsyj kjbsyj
  3751 + INNER JOIN lq_xh_hyhk hyhk ON kjbsyj.glkdbh = hyhk.F_Id AND hyhk.F_IsEffective = 1
  3752 + LEFT JOIN lq_mdxx md ON hyhk.md = md.F_Id
  3753 + WHERE kjbsyj.F_IsEffective = 1
  3754 + {(!string.IsNullOrEmpty(baseWhere) ? "AND " + baseWhere : "")}
  3755 + {employeeNameConditionKjbs}
  3756 + {storeNameCondition}
  3757 + GROUP BY kjbsyj.kjblszh, kjbsyj.kjblsxm, hyhk.md, hyhk.mdmc, md.dm, DATE_FORMAT(hyhk.hksj, '%Y%m')
  3758 + ) combined_stats
  3759 + WHERE 1=1
  3760 + {positionCondition}";
  3761 +
  3762 + var countParameters = parameters.Where(p => p.ParameterName != "@PageSize" && p.ParameterName != "@Offset").ToList();
  3763 + var totalCount = await _db.Ado.GetIntAsync(countSql, countParameters);
  3764 +
  3765 + // 执行查询
  3766 + var result = await _db.Ado.SqlQueryAsync<LqDepartmentConsumePerformanceStatisticsListOutput>(sql, parameters);
3381 3767
3382 return new 3768 return new
3383 { 3769 {
3384 - list = outputList, 3770 + list = result,
3385 pagination = new 3771 pagination = new
3386 { 3772 {
3387 pageIndex = input.PageIndex, 3773 pageIndex = input.PageIndex,
3388 pageSize = input.PageSize, 3774 pageSize = input.PageSize,
3389 - total = pagedResult.pagination.Total 3775 + total = totalCount
3390 } 3776 }
3391 }; 3777 };
3392 } 3778 }
@@ -4074,6 +4460,22 @@ namespace NCC.Extend.LqStatistics @@ -4074,6 +4460,22 @@ namespace NCC.Extend.LqStatistics
4074 /// 1. 会员必须有购买女神卡的记录 4460 /// 1. 会员必须有购买女神卡的记录
4075 /// 2. 该会员的所有有效开单记录中,所有品项都必须是女神卡(px = "61") 4461 /// 2. 该会员的所有有效开单记录中,所有品项都必须是女神卡(px = "61")
4076 /// 4462 ///
  4463 + /// 示例请求:
  4464 + /// ```json
  4465 + /// {
  4466 + /// "PageIndex": 1,
  4467 + /// "PageSize": 20,
  4468 + /// "StoreId": "门店ID",
  4469 + /// "StoreIds": ["门店ID1", "门店ID2"]
  4470 + /// }
  4471 + /// ```
  4472 + ///
  4473 + /// 参数说明:
  4474 + /// - PageIndex: 当前页码(从1开始)
  4475 + /// - PageSize: 每页数量
  4476 + /// - StoreId: 门店ID(单个门店筛选,与StoreIds二选一)
  4477 + /// - StoreIds: 门店ID列表(支持多门店筛选,与StoreId二选一)
  4478 + ///
4077 /// 返回说明: 4479 /// 返回说明:
4078 /// - list: 会员列表 4480 /// - list: 会员列表
4079 /// - memberId: 会员ID 4481 /// - memberId: 会员ID
@@ -4111,9 +4513,30 @@ namespace NCC.Extend.LqStatistics @@ -4111,9 +4513,30 @@ namespace NCC.Extend.LqStatistics
4111 { 4513 {
4112 try 4514 try
4113 { 4515 {
  4516 + // 构建门店筛选条件
  4517 + var storeFilterConditions = new List<string>();
  4518 + var parameters = new List<SugarParameter>();
  4519 +
  4520 + if (!string.IsNullOrEmpty(input.StoreId))
  4521 + {
  4522 + storeFilterConditions.Add("kd1.djmd = @StoreId");
  4523 + parameters.Add(new SugarParameter("@StoreId", input.StoreId));
  4524 + }
  4525 + else if (input.StoreIds != null && input.StoreIds.Any())
  4526 + {
  4527 + var storeIdParams = string.Join(",", input.StoreIds.Select((_, i) => $"@StoreId{i}"));
  4528 + storeFilterConditions.Add($"kd1.djmd IN ({storeIdParams})");
  4529 + for (int i = 0; i < input.StoreIds.Count; i++)
  4530 + {
  4531 + parameters.Add(new SugarParameter($"@StoreId{i}", input.StoreIds[i]));
  4532 + }
  4533 + }
  4534 +
  4535 + var storeFilterClause = storeFilterConditions.Any() ? "AND " + string.Join(" AND ", storeFilterConditions) : "";
  4536 +
4114 // 使用SQL一次性查询出符合条件的会员ID 4537 // 使用SQL一次性查询出符合条件的会员ID
4115 // 逻辑:会员有购买女神卡的记录,且该会员的所有开单记录的所有品项都是女神卡 4538 // 逻辑:会员有购买女神卡的记录,且该会员的所有开单记录的所有品项都是女神卡
4116 - var sql = @" 4539 + var sql = $@"
4117 SELECT DISTINCT kd1.Kdhy AS MemberId 4540 SELECT DISTINCT kd1.Kdhy AS MemberId
4118 FROM lq_kd_pxmx pxmx1 4541 FROM lq_kd_pxmx pxmx1
4119 INNER JOIN lq_kd_kdjlb kd1 ON pxmx1.glkdbh = kd1.F_Id 4542 INNER JOIN lq_kd_kdjlb kd1 ON pxmx1.glkdbh = kd1.F_Id
@@ -4122,6 +4545,7 @@ namespace NCC.Extend.LqStatistics @@ -4122,6 +4545,7 @@ namespace NCC.Extend.LqStatistics
4122 AND kd1.F_IsEffective = 1 4545 AND kd1.F_IsEffective = 1
4123 AND kd1.Kdhy IS NOT NULL 4546 AND kd1.Kdhy IS NOT NULL
4124 AND kd1.Kdhy != '' 4547 AND kd1.Kdhy != ''
  4548 + {storeFilterClause}
4125 AND NOT EXISTS ( 4549 AND NOT EXISTS (
4126 -- 排除那些有非女神卡品项的会员 4550 -- 排除那些有非女神卡品项的会员
4127 SELECT 1 4551 SELECT 1
@@ -4133,7 +4557,7 @@ namespace NCC.Extend.LqStatistics @@ -4133,7 +4557,7 @@ namespace NCC.Extend.LqStatistics
4133 AND pxmx2.px != '61' 4557 AND pxmx2.px != '61'
4134 )"; 4558 )";
4135 4559
4136 - var validMemberIds = await _db.Ado.SqlQueryAsync<string>(sql); 4560 + var validMemberIds = await _db.Ado.SqlQueryAsync<string>(sql, parameters);
4137 4561
4138 if (!validMemberIds.Any()) 4562 if (!validMemberIds.Any())
4139 { 4563 {
@@ -4299,6 +4723,11 @@ namespace NCC.Extend.LqStatistics @@ -4299,6 +4723,11 @@ namespace NCC.Extend.LqStatistics
4299 /// - LeadCustomerId: 线索池客户(拓客编号) 4723 /// - LeadCustomerId: 线索池客户(拓客编号)
4300 /// - CustomerName: 客户姓名 4724 /// - CustomerName: 客户姓名
4301 /// - ExpansionTime: 拓客时间 4725 /// - ExpansionTime: 拓客时间
  4726 + /// - StoreId: 归属门店ID(从客户信息表获取)
  4727 + /// - StoreName: 归属门店名称
  4728 + /// - Phone: 电话(优先使用拓客记录的电话,如果没有则使用客户信息表的电话)
  4729 + /// - ExpansionUserId: 拓客人员ID
  4730 + /// - ExpansionUserName: 拓客人员姓名
4302 /// - HasInvite: 是否邀约(是/否),通过拓客编号关联邀约表 4731 /// - HasInvite: 是否邀约(是/否),通过拓客编号关联邀约表
4303 /// - HasAppointment: 是否预约(是/否),只统计通过邀约产生的预约(预约表的F_InviteId关联邀约表) 4732 /// - HasAppointment: 是否预约(是/否),只统计通过邀约产生的预约(预约表的F_InviteId关联邀约表)
4304 /// - HasConsume: 是否有消耗(是/否),只统计通过预约产生的耗卡(耗卡表的F_AppointmentId关联预约表) 4733 /// - HasConsume: 是否有消耗(是/否),只统计通过预约产生的耗卡(耗卡表的F_AppointmentId关联预约表)
@@ -4319,6 +4748,11 @@ namespace NCC.Extend.LqStatistics @@ -4319,6 +4748,11 @@ namespace NCC.Extend.LqStatistics
4319 /// "LeadCustomerId": "751248448816153862", 4748 /// "LeadCustomerId": "751248448816153862",
4320 /// "CustomerName": "王女士", 4749 /// "CustomerName": "王女士",
4321 /// "ExpansionTime": "2025-10-24T03:33:10.000Z", 4750 /// "ExpansionTime": "2025-10-24T03:33:10.000Z",
  4751 + /// "StoreId": "1649328471923847169",
  4752 + /// "StoreName": "绿纤紫荆店",
  4753 + /// "Phone": "13800138000",
  4754 + /// "ExpansionUserId": "123456789",
  4755 + /// "ExpansionUserName": "张三",
4322 /// "HasInvite": "否", 4756 /// "HasInvite": "否",
4323 /// "HasAppointment": "否", 4757 /// "HasAppointment": "否",
4324 /// "HasConsume": "否", 4758 /// "HasConsume": "否",
@@ -4392,6 +4826,16 @@ namespace NCC.Extend.LqStatistics @@ -4392,6 +4826,16 @@ namespace NCC.Extend.LqStatistics
4392 tk.F_Id as LeadCustomerId, 4826 tk.F_Id as LeadCustomerId,
4393 tk.F_CustomerName as CustomerName, 4827 tk.F_CustomerName as CustomerName,
4394 tk.F_ExpansionTime as ExpansionTime, 4828 tk.F_ExpansionTime as ExpansionTime,
  4829 + -- 归属门店ID(从客户信息表获取)
  4830 + COALESCE(kh.gsmd, '') as StoreId,
  4831 + -- 归属门店名称
  4832 + COALESCE(md.dm, '') as StoreName,
  4833 + -- 电话(优先使用拓客记录的电话,如果没有则使用客户信息表的电话)
  4834 + COALESCE(NULLIF(tk.F_CustomerPhone, ''), kh.sjh, '') as Phone,
  4835 + -- 拓客人员ID
  4836 + COALESCE(tk.F_ExpansionUserId, '') as ExpansionUserId,
  4837 + -- 拓客人员姓名
  4838 + COALESCE(usr.F_REALNAME, '') as ExpansionUserName,
4395 -- 是否邀约:通过会员ID关联(邀约表的yykh字段存储的是会员ID) 4839 -- 是否邀约:通过会员ID关联(邀约表的yykh字段存储的是会员ID)
4396 CASE WHEN yaoy_stats.has_invite = 1 THEN '是' ELSE '否' END as HasInvite, 4840 CASE WHEN yaoy_stats.has_invite = 1 THEN '是' ELSE '否' END as HasInvite,
4397 -- 是否预约:通过邀约ID关联(只统计通过邀约产生的预约) 4841 -- 是否预约:通过邀约ID关联(只统计通过邀约产生的预约)
@@ -4413,6 +4857,12 @@ namespace NCC.Extend.LqStatistics @@ -4413,6 +4857,12 @@ namespace NCC.Extend.LqStatistics
4413 -- 实际开单记录数(不管是否通过预约产生) 4857 -- 实际开单记录数(不管是否通过预约产生)
4414 COALESCE(kd_actual.count, 0) as ActualBillingCount 4858 COALESCE(kd_actual.count, 0) as ActualBillingCount
4415 FROM lq_tkjlb tk 4859 FROM lq_tkjlb tk
  4860 + -- 关联客户信息表(获取归属门店和电话)
  4861 + LEFT JOIN lq_khxx kh ON kh.F_Id = tk.F_MemberId
  4862 + -- 关联门店表(获取归属门店名称)
  4863 + LEFT JOIN lq_mdxx md ON md.F_Id = kh.gsmd
  4864 + -- 关联用户表(获取拓客人员姓名)
  4865 + LEFT JOIN BASE_USER usr ON usr.F_Id = tk.F_ExpansionUserId AND usr.F_DeleteMark IS NULL
4416 -- 邀约统计子查询(通过会员ID关联:邀约表的yykh字段存储的是会员ID) 4866 -- 邀约统计子查询(通过会员ID关联:邀约表的yykh字段存储的是会员ID)
4417 LEFT JOIN ( 4867 LEFT JOIN (
4418 SELECT 4868 SELECT
@@ -5020,6 +5470,9 @@ namespace NCC.Extend.LqStatistics @@ -5020,6 +5470,9 @@ namespace NCC.Extend.LqStatistics
5020 var khWhereClause = khWhereConditions.Any() ? "WHERE " + string.Join(" AND ", khWhereConditions) : "WHERE kh.gsmd IS NOT NULL"; 5470 var khWhereClause = khWhereConditions.Any() ? "WHERE " + string.Join(" AND ", khWhereConditions) : "WHERE kh.gsmd IS NOT NULL";
5021 var khWhereClauseNoAlias = khWhereConditionsNoAlias.Any() ? "WHERE " + string.Join(" AND ", khWhereConditionsNoAlias) : "WHERE gsmd IS NOT NULL"; 5471 var khWhereClauseNoAlias = khWhereConditionsNoAlias.Any() ? "WHERE " + string.Join(" AND ", khWhereConditionsNoAlias) : "WHERE gsmd IS NOT NULL";
5022 5472
  5473 + // 判断是否有活动筛选
  5474 + var hasEventFilter = !string.IsNullOrEmpty(input.EventId);
  5475 +
5023 // 使用子查询优化性能,避免复杂的JOIN 5476 // 使用子查询优化性能,避免复杂的JOIN
5024 var sql = $@" 5477 var sql = $@"
5025 SELECT 5478 SELECT
@@ -5037,6 +5490,12 @@ namespace NCC.Extend.LqStatistics @@ -5037,6 +5490,12 @@ namespace NCC.Extend.LqStatistics
5037 SELECT gsmd as StoreId FROM lq_khxx {khWhereClauseNoAlias} 5490 SELECT gsmd as StoreId FROM lq_khxx {khWhereClauseNoAlias}
5038 UNION 5491 UNION
5039 SELECT F_StoreId as StoreId FROM lq_tkjlb {whereClauseNoAlias} 5492 SELECT F_StoreId as StoreId FROM lq_tkjlb {whereClauseNoAlias}
  5493 + UNION
  5494 + SELECT F_StoreId as StoreId FROM lq_yaoyjl WHERE F_StoreId IS NOT NULL
  5495 + UNION
  5496 + SELECT djmd as StoreId FROM lq_yyjl WHERE djmd IS NOT NULL
  5497 + UNION
  5498 + SELECT djmd as StoreId FROM lq_kd_kdjlb WHERE djmd IS NOT NULL AND F_IsEffective = 1
5040 ) as all_stores 5499 ) as all_stores
5041 ) as stores 5500 ) as stores
5042 LEFT JOIN lq_mdxx md ON md.F_Id = stores.StoreId 5501 LEFT JOIN lq_mdxx md ON md.F_Id = stores.StoreId
@@ -5058,51 +5517,92 @@ namespace NCC.Extend.LqStatistics @@ -5058,51 +5517,92 @@ namespace NCC.Extend.LqStatistics
5058 {(string.IsNullOrEmpty(whereClause) ? "" : whereClause)} 5517 {(string.IsNullOrEmpty(whereClause) ? "" : whereClause)}
5059 GROUP BY tk.F_StoreId 5518 GROUP BY tk.F_StoreId
5060 ) tk_stats ON tk_stats.StoreId = stores.StoreId 5519 ) tk_stats ON tk_stats.StoreId = stores.StoreId
5061 - -- 邀约数统计 5520 + -- 邀约数统计(如果有活动筛选,通过拓客记录关联;否则直接统计)
5062 LEFT JOIN ( 5521 LEFT JOIN (
  5522 + {(hasEventFilter ? @"
5063 SELECT 5523 SELECT
5064 tk.F_StoreId as StoreId, 5524 tk.F_StoreId as StoreId,
5065 COUNT(DISTINCT yaoy.F_Id) as InviteCount 5525 COUNT(DISTINCT yaoy.F_Id) as InviteCount
5066 - FROM lq_tkjlb tk  
5067 - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk.F_Id  
5068 - {(string.IsNullOrEmpty(whereClause) ? "" : whereClause)}  
5069 - GROUP BY tk.F_StoreId 5526 + FROM lq_yaoyjl yaoy
  5527 + INNER JOIN lq_tkjlb tk ON tk.F_Id = yaoy.tkbh
  5528 + WHERE tk.F_StoreId IS NOT NULL
  5529 + AND tk.F_EventId = @EventId
  5530 + GROUP BY tk.F_StoreId" : @"
  5531 + SELECT
  5532 + yaoy.F_StoreId as StoreId,
  5533 + COUNT(DISTINCT yaoy.F_Id) as InviteCount
  5534 + FROM lq_yaoyjl yaoy
  5535 + WHERE yaoy.F_StoreId IS NOT NULL
  5536 + GROUP BY yaoy.F_StoreId")}
5070 ) yaoy_stats ON yaoy_stats.StoreId = stores.StoreId 5537 ) yaoy_stats ON yaoy_stats.StoreId = stores.StoreId
5071 - -- 预约数统计(通过邀约ID关联 5538 + -- 预约数统计(如果有活动筛选,通过邀约->拓客链路关联;否则直接统计
5072 LEFT JOIN ( 5539 LEFT JOIN (
  5540 + {(hasEventFilter ? @"
5073 SELECT 5541 SELECT
5074 tk.F_StoreId as StoreId, 5542 tk.F_StoreId as StoreId,
5075 COUNT(DISTINCT yy.F_Id) as AppointmentCount 5543 COUNT(DISTINCT yy.F_Id) as AppointmentCount
5076 - FROM lq_tkjlb tk  
5077 - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk.F_Id  
5078 - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id  
5079 - {(string.IsNullOrEmpty(whereClause) ? "" : whereClause)}  
5080 - GROUP BY tk.F_StoreId 5544 + FROM lq_yyjl yy
  5545 + INNER JOIN lq_yaoyjl yaoy ON yaoy.F_Id = yy.F_InviteId
  5546 + INNER JOIN lq_tkjlb tk ON tk.F_Id = yaoy.tkbh
  5547 + WHERE tk.F_StoreId IS NOT NULL
  5548 + AND tk.F_EventId = @EventId
  5549 + GROUP BY tk.F_StoreId" : @"
  5550 + SELECT
  5551 + yy.djmd as StoreId,
  5552 + COUNT(DISTINCT yy.F_Id) as AppointmentCount
  5553 + FROM lq_yyjl yy
  5554 + WHERE yy.djmd IS NOT NULL
  5555 + GROUP BY yy.djmd")}
5081 ) yy_stats ON yy_stats.StoreId = stores.StoreId 5556 ) yy_stats ON yy_stats.StoreId = stores.StoreId
5082 - -- 耗卡数统计(通过预约ID关联 5557 + -- 耗卡数统计(如果有活动筛选,通过预约->邀约->拓客链路关联;否则直接统计
5083 LEFT JOIN ( 5558 LEFT JOIN (
  5559 + {(hasEventFilter ? @"
5084 SELECT 5560 SELECT
5085 tk.F_StoreId as StoreId, 5561 tk.F_StoreId as StoreId,
5086 COUNT(DISTINCT xh.F_Id) as ConsumeCount 5562 COUNT(DISTINCT xh.F_Id) as ConsumeCount
5087 - FROM lq_tkjlb tk  
5088 - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk.F_Id  
5089 - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id 5563 + FROM lq_yyjl yy
5090 INNER JOIN lq_xh_hyhk xh ON xh.F_AppointmentId = yy.F_Id AND xh.F_IsEffective = 1 5564 INNER JOIN lq_xh_hyhk xh ON xh.F_AppointmentId = yy.F_Id AND xh.F_IsEffective = 1
5091 - {(string.IsNullOrEmpty(whereClause) ? "" : whereClause)}  
5092 - GROUP BY tk.F_StoreId 5565 + INNER JOIN lq_yaoyjl yaoy ON yaoy.F_Id = yy.F_InviteId
  5566 + INNER JOIN lq_tkjlb tk ON tk.F_Id = yaoy.tkbh
  5567 + WHERE tk.F_StoreId IS NOT NULL
  5568 + AND tk.F_EventId = @EventId
  5569 + GROUP BY tk.F_StoreId" : @"
  5570 + SELECT
  5571 + yy.djmd as StoreId,
  5572 + COUNT(DISTINCT xh.F_Id) as ConsumeCount
  5573 + FROM lq_yyjl yy
  5574 + INNER JOIN lq_xh_hyhk xh ON xh.F_AppointmentId = yy.F_Id AND xh.F_IsEffective = 1
  5575 + WHERE yy.djmd IS NOT NULL
  5576 + GROUP BY yy.djmd")}
5093 ) xh_stats ON xh_stats.StoreId = stores.StoreId 5577 ) xh_stats ON xh_stats.StoreId = stores.StoreId
5094 - -- 开单数和开单金额统计(通过预约ID关联 5578 + -- 开单数和开单金额统计(如果有活动筛选,通过预约->邀约->拓客链路关联;否则直接统计
5095 LEFT JOIN ( 5579 LEFT JOIN (
  5580 + {(hasEventFilter ? @"
5096 SELECT 5581 SELECT
5097 tk.F_StoreId as StoreId, 5582 tk.F_StoreId as StoreId,
5098 COUNT(DISTINCT kd.F_Id) as BillingCount, 5583 COUNT(DISTINCT kd.F_Id) as BillingCount,
5099 SUM(kd.zdyj) as BillingAmount 5584 SUM(kd.zdyj) as BillingAmount
5100 - FROM lq_tkjlb tk  
5101 - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk.F_Id  
5102 - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id  
5103 - INNER JOIN lq_kd_kdjlb kd ON kd.F_AppointmentId = yy.F_Id AND kd.F_IsEffective = 1  
5104 - {(string.IsNullOrEmpty(whereClause) ? "" : whereClause)}  
5105 - GROUP BY tk.F_StoreId 5585 + FROM lq_kd_kdjlb kd
  5586 + INNER JOIN lq_yyjl yy ON yy.F_Id = kd.F_AppointmentId
  5587 + INNER JOIN lq_yaoyjl yaoy ON yaoy.F_Id = yy.F_InviteId
  5588 + INNER JOIN lq_tkjlb tk ON tk.F_Id = yaoy.tkbh
  5589 + WHERE kd.F_IsEffective = 1
  5590 + AND kd.F_AppointmentId IS NOT NULL
  5591 + AND kd.F_AppointmentId != ''
  5592 + AND tk.F_StoreId IS NOT NULL
  5593 + AND tk.F_EventId = @EventId
  5594 + GROUP BY tk.F_StoreId" : @"
  5595 + SELECT
  5596 + kd.djmd as StoreId,
  5597 + COUNT(DISTINCT kd.F_Id) as BillingCount,
  5598 + SUM(kd.zdyj) as BillingAmount
  5599 + FROM lq_kd_kdjlb kd
  5600 + INNER JOIN lq_yyjl yy ON yy.F_Id = kd.F_AppointmentId
  5601 + WHERE kd.F_IsEffective = 1
  5602 + AND kd.F_AppointmentId IS NOT NULL
  5603 + AND kd.F_AppointmentId != ''
  5604 + AND kd.djmd IS NOT NULL
  5605 + GROUP BY kd.djmd")}
5106 ) kd_stats ON kd_stats.StoreId = stores.StoreId 5606 ) kd_stats ON kd_stats.StoreId = stores.StoreId
5107 WHERE stores.StoreId IS NOT NULL 5607 WHERE stores.StoreId IS NOT NULL
5108 ORDER BY stores.StoreId"; 5608 ORDER BY stores.StoreId";
接口调用说明-修改开单信息.md 0 → 100644
  1 +# 修改开单信息接口调用说明
  2 +
  3 +## 接口信息
  4 +
  5 +- **接口地址**: `PUT /api/Extend/lqkdkdjlb/UpdateBillingInfo`
  6 +- **请求方式**: `PUT`
  7 +- **Content-Type**: `application/json`
  8 +- **需要认证**: 是(需要在Header中携带Authorization token)
  9 +
  10 +## 请求参数
  11 +
  12 +| 参数名 | 类型 | 必填 | 说明 |
  13 +|--------|------|------|------|
  14 +| id | string | 是 | 开单记录ID |
  15 +| scwj | List<FileControlsModel> | 否 | 上传文件列表(不传则不更新) |
  16 +| F_FIleUrl | string | 否 | 方案其他文件URL(不传则不更新) |
  17 +| Bz | string | 否 | 备注(不传则不更新) |
  18 +| Jj | string | 否 | 简介(不传则不更新) |
  19 +
  20 +**注意**: 至少需要提供一个要更新的字段(scwj、F_FIleUrl、Bz或Jj)
  21 +
  22 +## 响应格式
  23 +
  24 +### 成功响应 (200)
  25 +
  26 +```json
  27 +{
  28 + "code": 200,
  29 + "msg": "更新成功",
  30 + "data": {
  31 + "id": "开单记录ID",
  32 + "updatedFields": ["Bz", "Jj"]
  33 + }
  34 +}
  35 +```
  36 +
  37 +### 错误响应
  38 +
  39 +```json
  40 +{
  41 + "code": 400,
  42 + "msg": "错误信息",
  43 + "data": null
  44 +}
  45 +```
  46 +
  47 +## 调用示例
  48 +
  49 +### JavaScript/Axios 示例
  50 +
  51 +```javascript
  52 +import axios from 'axios';
  53 +
  54 +// 更新备注和简介
  55 +const updateBillingInfo = async (billingId, remark, description) => {
  56 + try {
  57 + const token = localStorage.getItem('token'); // 从本地存储获取token
  58 +
  59 + const response = await axios.put(
  60 + '/api/Extend/lqkdkdjlb/UpdateBillingInfo',
  61 + {
  62 + id: billingId,
  63 + Bz: remark, // 备注
  64 + Jj: description // 简介
  65 + },
  66 + {
  67 + headers: {
  68 + 'Authorization': token,
  69 + 'Content-Type': 'application/json'
  70 + }
  71 + }
  72 + );
  73 +
  74 + if (response.data.code === 200) {
  75 + console.log('更新成功', response.data.data);
  76 + return response.data;
  77 + } else {
  78 + console.error('更新失败', response.data.msg);
  79 + throw new Error(response.data.msg);
  80 + }
  81 + } catch (error) {
  82 + console.error('请求失败', error);
  83 + throw error;
  84 + }
  85 +};
  86 +
  87 +// 使用示例
  88 +updateBillingInfo('759263766553560325', '这是备注信息', '这是简介信息');
  89 +```
  90 +
  91 +### 只更新备注
  92 +
  93 +```javascript
  94 +const updateRemark = async (billingId, remark) => {
  95 + const response = await axios.put(
  96 + '/api/Extend/lqkdkdjlb/UpdateBillingInfo',
  97 + {
  98 + id: billingId,
  99 + Bz: remark
  100 + },
  101 + {
  102 + headers: {
  103 + 'Authorization': localStorage.getItem('token'),
  104 + 'Content-Type': 'application/json'
  105 + }
  106 + }
  107 + );
  108 + return response.data;
  109 +};
  110 +```
  111 +
  112 +### 只更新简介
  113 +
  114 +```javascript
  115 +const updateDescription = async (billingId, description) => {
  116 + const response = await axios.put(
  117 + '/api/Extend/lqkdkdjlb/UpdateBillingInfo',
  118 + {
  119 + id: billingId,
  120 + Jj: description
  121 + },
  122 + {
  123 + headers: {
  124 + 'Authorization': localStorage.getItem('token'),
  125 + 'Content-Type': 'application/json'
  126 + }
  127 + }
  128 + );
  129 + return response.data;
  130 +};
  131 +```
  132 +
  133 +### 同时更新文件、备注和简介
  134 +
  135 +```javascript
  136 +const updateAll = async (billingId, files, remark, description, fileUrl) => {
  137 + const response = await axios.put(
  138 + '/api/Extend/lqkdkdjlb/UpdateBillingInfo',
  139 + {
  140 + id: billingId,
  141 + scwj: files, // 文件列表
  142 + F_FIleUrl: fileUrl, // 方案其他文件URL
  143 + Bz: remark, // 备注
  144 + Jj: description // 简介
  145 + },
  146 + {
  147 + headers: {
  148 + 'Authorization': localStorage.getItem('token'),
  149 + 'Content-Type': 'application/json'
  150 + }
  151 + }
  152 + );
  153 + return response.data;
  154 +};
  155 +```
  156 +
  157 +### cURL 示例
  158 +
  159 +```bash
  160 +# 更新备注和简介
  161 +curl -X PUT "http://localhost:2011/api/Extend/lqkdkdjlb/UpdateBillingInfo" \
  162 + -H "Authorization: Bearer YOUR_TOKEN" \
  163 + -H "Content-Type: application/json" \
  164 + -d '{
  165 + "id": "759263766553560325",
  166 + "Bz": "这是备注信息",
  167 + "Jj": "这是简介信息"
  168 + }'
  169 +
  170 +# 只更新备注
  171 +curl -X PUT "http://localhost:2011/api/Extend/lqkdkdjlb/UpdateBillingInfo" \
  172 + -H "Authorization: Bearer YOUR_TOKEN" \
  173 + -H "Content-Type: application/json" \
  174 + -d '{
  175 + "id": "759263766553560325",
  176 + "Bz": "这是备注信息"
  177 + }'
  178 +
  179 +# 只更新简介
  180 +curl -X PUT "http://localhost:2011/api/Extend/lqkdkdjlb/UpdateBillingInfo" \
  181 + -H "Authorization: Bearer YOUR_TOKEN" \
  182 + -H "Content-Type: application/json" \
  183 + -d '{
  184 + "id": "759263766553560325",
  185 + "Jj": "这是简介信息"
  186 + }'
  187 +```
  188 +
  189 +## 注意事项
  190 +
  191 +1. **服务重启**: 修改代码后需要重启后端服务才能生效
  192 +2. **Token获取**: 需要先调用登录接口获取token
  193 +3. **字段可选**: 所有字段都是可选的,只需要传入要更新的字段即可
  194 +4. **至少一个字段**: 必须至少提供一个要更新的字段,否则会返回错误
  195 +
  196 +## 错误码说明
  197 +
  198 +- `200`: 更新成功
  199 +- `400`: 参数错误或开单记录不存在
  200 +- `500`: 服务器错误
  201 +