Blame view

netcore/src/Modularity/Extend/NCC.Extend/LqYyjlService.cs 47.3 KB
9c60e8ba   “wangming”   ```
1
  using System;
d98270e2   “wangming”   Enhance LqEventSe...
2
3
4
5
6
7
8
9
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;
  using Mapster;
  using Microsoft.AspNetCore.Mvc;
  using NCC.ClayObject;
  using NCC.Common.Configuration;
  using NCC.Common.Core.Manager;
96009bc9   hexiaodong   hxd
10
11
12
  using NCC.Common.Enum;
  using NCC.Common.Extension;
  using NCC.Common.Filter;
d98270e2   “wangming”   Enhance LqEventSe...
13
14
15
  using NCC.Common.Helper;
  using NCC.Common.Model.NPOI;
  using NCC.DataEncryption;
96009bc9   hexiaodong   hxd
16
17
  using NCC.Dependency;
  using NCC.DynamicApiController;
d98270e2   “wangming”   Enhance LqEventSe...
18
  using NCC.Extend.Entitys.Dto.LqYyjl;
b5df6609   “wangming”   ```
19
20
21
22
  using NCC.Extend.Entitys.Dto.LqYyjlPx;
  using NCC.Extend.Entitys.Dto.LqYyjlPxJks;
  using NCC.Extend.Entitys.Enum;
  using NCC.Extend.Entitys.lq_md_room;
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
23
  using NCC.Extend.Entitys.lq_mdxx;
b5df6609   “wangming”   ```
24
  using NCC.Extend.Entitys.lq_xh_hyhk;
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
25
  using NCC.Extend.Entitys.lq_yaoyjl;
d98270e2   “wangming”   Enhance LqEventSe...
26
  using NCC.Extend.Entitys.lq_yyjl;
b5df6609   “wangming”   ```
27
28
  using NCC.Extend.Entitys.lq_yyjl_px;
  using NCC.Extend.Entitys.lq_yyjl_px_jks;
96009bc9   hexiaodong   hxd
29
  using NCC.Extend.Interfaces.LqYyjl;
d98270e2   “wangming”   Enhance LqEventSe...
30
31
  using NCC.FriendlyException;
  using NCC.JsonSerialization;
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
32
  using NCC.System.Entitys.Permission;
96009bc9   hexiaodong   hxd
33
  using SqlSugar;
96009bc9   hexiaodong   hxd
34
  using Yitter.IdGenerator;
96009bc9   hexiaodong   hxd
35
36
37
38
39
40
  
  namespace NCC.Extend.LqYyjl
  {
      /// <summary>
      /// 预约记录服务
      /// </summary>
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
41
      [ApiDescriptionSettings(Tag = "绿纤预约记录服务", Name = "LqYyjl", Order = 200)]
96009bc9   hexiaodong   hxd
42
43
44
45
46
47
48
49
50
51
      [Route("api/Extend/[controller]")]
      public class LqYyjlService : ILqYyjlService, IDynamicApiController, ITransient
      {
          private readonly ISqlSugarRepository<LqYyjlEntity> _lqYyjlRepository;
          private readonly SqlSugarScope _db;
          private readonly IUserManager _userManager;
  
          /// <summary>
          /// 初始化一个<see cref="LqYyjlService"/>类型的新实例
          /// </summary>
d98270e2   “wangming”   Enhance LqEventSe...
52
          public LqYyjlService(ISqlSugarRepository<LqYyjlEntity> lqYyjlRepository, IUserManager userManager)
96009bc9   hexiaodong   hxd
53
          {
d98270e2   “wangming”   Enhance LqEventSe...
54
              _lqYyjlRepository = lqYyjlRepository;
96009bc9   hexiaodong   hxd
55
56
57
              _db = _lqYyjlRepository.Context;
              _userManager = userManager;
          }
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
58
  
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
59
          #region 预约记录
96009bc9   hexiaodong   hxd
60
61
62
63
64
65
66
67
68
69
          /// <summary>
          /// 获取预约记录
          /// </summary>
          /// <param name="id">参数</param>
          /// <returns></returns>
          [HttpGet("{id}")]
          public async Task<dynamic> GetInfo(string id)
          {
              var entity = await _db.Queryable<LqYyjlEntity>().FirstAsync(p => p.Id == id);
              var output = entity.Adapt<LqYyjlInfoOutput>();
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  
              // 获取门店名称
              if (!string.IsNullOrEmpty(entity.Djmd))
              {
                  var store = await _db.Queryable<LqMdxxEntity>().Where(u => u.Id == entity.Djmd).FirstAsync();
                  output.djmdName = store?.Dm;
              }
  
              // 获取预约人姓名
              if (!string.IsNullOrEmpty(entity.Yyr))
              {
                  var user = await _db.Queryable<UserEntity>().Where(u => u.Id == entity.Yyr).FirstAsync();
                  output.yyrName = user?.RealName;
              }
  
              // 获取预约健康师姓名
              if (!string.IsNullOrEmpty(entity.Yyjks))
              {
                  var healthCoach = await _db.Queryable<UserEntity>().Where(u => u.Id == entity.Yyjks).FirstAsync();
                  output.yyjksName = healthCoach?.RealName;
              }
  
9c60e8ba   “wangming”   ```
92
              //获取沟通记录
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
93
94
95
96
97
98
              if (!string.IsNullOrEmpty(entity.InviteId))
              {
                  var invite = await _db.Queryable<LqYaoyjlEntity>().Where(u => u.Id == entity.InviteId).FirstAsync();
                  output.InviteTime = invite?.Yysj;
              }
  
b5df6609   “wangming”   ```
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
              if (!string.IsNullOrEmpty(entity.RoomId))
              {
                  output.roomName = entity.RoomName;
                  if (string.IsNullOrEmpty(output.roomName))
                  {
                      output.roomName = await _db.Queryable<LqMdRoomEntity>()
                          .Where(r => r.Id == entity.RoomId)
                          .Select(r => r.RoomName)
                          .FirstAsync();
                  }
              }
  
              output.hasEffectiveConsume = await _db.Queryable<LqXhHyhkEntity>()
                  .AnyAsync(x => x.AppointmentId == id && x.IsEffective == StatusEnum.有效.GetHashCode());
  
9c60e8ba   “wangming”   ```
114
115
116
117
118
119
120
121
122
123
124
125
126
127
              output.relatedConsumeList = await _db.Queryable<LqXhHyhkEntity>()
                  .Where(x => x.AppointmentId == id && x.IsEffective == StatusEnum.有效.GetHashCode())
                  .OrderBy(x => x.CreateTime, OrderByType.Desc)
                  .Select(x => new LqYyjlRelatedConsumeOutput
                  {
                      id = x.Id,
                      hksj = x.Hksj,
                      xfje = x.Xfje,
                      hymc = x.Hymc,
                  })
                  .ToListAsync();
  
              await ResolveYyjlRelatedConsumeAsync(output, entity);
  
b5df6609   “wangming”   ```
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
              var pxEntities = await _db.Queryable<LqYyjlPxEntity>()
                  .Where(x => x.YyjlId == id && x.IsEffective == 1)
                  .OrderBy(x => x.SortNo)
                  .OrderBy(x => x.CreateTime)
                  .ToListAsync();
              output.lqYyjlPxList = pxEntities.Adapt<List<LqYyjlPxOutput>>();
  
              var jksEntities = await _db.Queryable<LqYyjlPxJksEntity>()
                  .Where(x => x.YyjlId == id && x.IsEffective == 1)
                  .OrderBy(x => x.YyjlPxId)
                  .OrderBy(x => x.SortNo)
                  .OrderBy(x => x.CreateTime)
                  .ToListAsync();
              var jksByPxId = jksEntities
                  .GroupBy(x => x.YyjlPxId)
                  .ToDictionary(
                      g => g.Key,
                      g => g.Select(e => new LqYyjlPxJksOutput
                      {
                          id = e.Id,
                          yyjlId = e.YyjlId,
                          yyjlPxId = e.YyjlPxId,
                          jks = e.Jks,
                          jksxm = e.Jksxm,
                          sortNo = e.SortNo,
                          isEffective = e.IsEffective,
                      }).ToList());
              foreach (var pxOut in output.lqYyjlPxList)
              {
                  pxOut.jksList = jksByPxId.TryGetValue(pxOut.id, out var jl)
                      ? jl
                      : new List<LqYyjlPxJksOutput>();
              }
  
96009bc9   hexiaodong   hxd
162
163
              return output;
          }
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
164
          #endregion
96009bc9   hexiaodong   hxd
165
  
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
166
          #region 预约记录列表
96009bc9   hexiaodong   hxd
167
          /// <summary>
d98270e2   “wangming”   Enhance LqEventSe...
168
169
170
171
          /// 获取预约记录列表
          /// </summary>
          /// <param name="input">请求参数</param>
          /// <returns></returns>
96009bc9   hexiaodong   hxd
172
173
174
175
          [HttpGet("")]
          public async Task<dynamic> GetList([FromQuery] LqYyjlListQueryInput input)
          {
              var sidx = input.sidx == null ? "id" : input.sidx;
b5df6609   “wangming”   ```
176
177
178
179
180
181
              var (startCzsj, endCzsj) = TryParseTsRange(input.czsj);
              var (startYysj, endYysj) = TryParseTsRange(input.yysj);
              var (startYyjs, endYyjs) = TryParseTsRange(input.yyjs);
              var hasCzsj = startCzsj.HasValue && endCzsj.HasValue;
              var hasYysj = startYysj.HasValue && endYysj.HasValue;
              var hasYyjs = startYyjs.HasValue && endYyjs.HasValue;
96009bc9   hexiaodong   hxd
182
183
184
185
186
187
188
              var data = await _db.Queryable<LqYyjlEntity>()
                  .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id))
                  .WhereIF(!string.IsNullOrEmpty(input.djmd), p => p.Djmd.Equals(input.djmd))
                  .WhereIF(!string.IsNullOrEmpty(input.yyr), p => p.Yyr.Equals(input.yyr))
                  .WhereIF(!string.IsNullOrEmpty(input.gklx), p => p.Gklx.Contains(input.gklx))
                  .WhereIF(!string.IsNullOrEmpty(input.yytyxm), p => p.Yytyxm.Equals(input.yytyxm))
                  .WhereIF(!string.IsNullOrEmpty(input.czr), p => p.Czr.Equals(input.czr))
b5df6609   “wangming”   ```
189
190
                  .WhereIF(hasCzsj, p => p.Czsj >= new DateTime(startCzsj.Value.Year, startCzsj.Value.Month, startCzsj.Value.Day, 0, 0, 0))
                  .WhereIF(hasCzsj, p => p.Czsj <= new DateTime(endCzsj.Value.Year, endCzsj.Value.Month, endCzsj.Value.Day, 23, 59, 59))
96009bc9   hexiaodong   hxd
191
192
193
                  .WhereIF(!string.IsNullOrEmpty(input.gk), p => p.Gk.Equals(input.gk))
                  .WhereIF(!string.IsNullOrEmpty(input.gkxm), p => p.Gkxm.Contains(input.gkxm))
                  .WhereIF(!string.IsNullOrEmpty(input.yyjks), p => p.Yyjks.Equals(input.yyjks))
b5df6609   “wangming”   ```
194
195
196
197
                  .WhereIF(hasYysj, p => p.Yysj >= new DateTime(startYysj.Value.Year, startYysj.Value.Month, startYysj.Value.Day, 0, 0, 0))
                  .WhereIF(hasYysj, p => p.Yysj <= new DateTime(endYysj.Value.Year, endYysj.Value.Month, endYysj.Value.Day, 23, 59, 59))
                  .WhereIF(hasYyjs, p => p.Yyjs >= new DateTime(startYyjs.Value.Year, startYyjs.Value.Month, startYyjs.Value.Day, 0, 0, 0))
                  .WhereIF(hasYyjs, p => p.Yyjs <= new DateTime(endYyjs.Value.Year, endYyjs.Value.Month, endYyjs.Value.Day, 23, 59, 59))
35bb18d3   “wangming”   更新多个.DS_Store文件,删...
198
                  .WhereIF(!string.IsNullOrEmpty(input.F_Status), p => p.F_Status.Equals(input.F_Status))
d98270e2   “wangming”   Enhance LqEventSe...
199
                  .Select(it => new LqYyjlListOutput
96009bc9   hexiaodong   hxd
200
201
                  {
                      id = it.Id,
d98270e2   “wangming”   Enhance LqEventSe...
202
203
204
205
206
207
208
209
210
211
212
213
                      djmd = it.Djmd,
                      yyr = it.Yyr,
                      gklx = it.Gklx,
                      yytyxm = it.Yytyxm,
                      czr = it.Czr,
                      czsj = it.Czsj,
                      gk = it.Gk,
                      gkxm = it.Gkxm,
                      yyjks = it.Yyjks,
                      yysj = it.Yysj,
                      yyjs = it.Yyjs,
                      F_Status = it.F_Status,
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
214
215
216
                      yyrName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Yyr).Select(u => u.RealName),
                      yyjksName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Yyjks).Select(u => u.RealName),
                      djmdName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(u => u.Id == it.Djmd).Select(u => u.Dm),
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
217
218
219
                      NoDealRemark = it.NoDealRemark,
                      InviteId = it.InviteId,
                      InviteTime = SqlFunc.Subqueryable<LqYaoyjlEntity>().Where(u => u.Id == it.InviteId).Select(u => SqlFunc.ToDate(u.Yysj)),
b5df6609   “wangming”   ```
220
221
222
223
224
225
226
227
228
                      roomId = it.RoomId,
                      roomName = it.RoomName,
                      serviceStartTime = it.ServiceStartTime,
                      serviceEndTime = it.ServiceEndTime,
                      remark = it.Remark,
                      consumeId = it.ConsumeId,
                      hasEffectiveConsume = SqlFunc.Subqueryable<LqXhHyhkEntity>()
                          .Where(h => h.AppointmentId == it.Id && h.IsEffective == StatusEnum.有效.GetHashCode())
                          .Any(),
d98270e2   “wangming”   Enhance LqEventSe...
229
230
231
232
                  })
                  .MergeTable()
                  .OrderBy(sidx + " " + input.sort)
                  .ToPagedListAsync(input.currentPage, input.pageSize);
9c60e8ba   “wangming”   ```
233
234
              await FillYyjlProjectSummaryAsync(data.list);
              await FillYyjlConsumeIdAsync(data.list);
d98270e2   “wangming”   Enhance LqEventSe...
235
              return PageResult<LqYyjlListOutput>.SqlSugarPageResult(data);
96009bc9   hexiaodong   hxd
236
          }
b5df6609   “wangming”   ```
237
238
  
          /// <summary>
9c60e8ba   “wangming”   ```
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
          /// 填充列表行的预约计划品项摘要
          /// </summary>
          private async Task FillYyjlProjectSummaryAsync(IEnumerable<LqYyjlListOutput> rows)
          {
              if (rows == null || !rows.Any()) return;
              var ids = rows.Select(x => x.id).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
              if (ids.Count == 0) return;
  
              var pxList = await _db.Queryable<LqYyjlPxEntity>()
                  .Where(x => ids.Contains(x.YyjlId) && x.IsEffective == 1)
                  .OrderBy(x => x.SortNo)
                  .OrderBy(x => x.CreateTime)
                  .ToListAsync();
  
              var summaryMap = pxList
                  .GroupBy(x => x.YyjlId)
                  .ToDictionary(
                      g => g.Key,
                      g => string.Join("、", g.Select(FormatYyjlPxSummary).Where(s => !string.IsNullOrEmpty(s))));
  
              foreach (var row in rows)
              {
                  if (summaryMap.TryGetValue(row.id, out var summary) && !string.IsNullOrWhiteSpace(summary))
                      row.projectSummary = summary;
              }
          }
  
          private static string FormatYyjlPxSummary(LqYyjlPxEntity px)
          {
              if (px == null) return string.Empty;
              var name = (px.Pxmc ?? string.Empty).Trim();
              if (string.IsNullOrEmpty(name)) return string.Empty;
              var num = px.ProjectNumber.HasValue && px.ProjectNumber.Value > 0
                  ? (int)px.ProjectNumber.Value
                  : 1;
              return $"{name}×{num}";
          }
  
          /// <summary>
          /// 列表行补全/校验关联耗卡编号(剔除无效 ConsumeId,并按 AppointmentId 反查)
          /// </summary>
          private async Task FillYyjlConsumeIdAsync(IEnumerable<LqYyjlListOutput> rows)
          {
              if (rows == null || !rows.Any()) return;
  
              var rowList = rows as IList<LqYyjlListOutput> ?? rows.ToList();
  
              var listedConsumeIds = rowList
                  .Where(r => !string.IsNullOrEmpty(r.consumeId))
                  .Select(r => r.consumeId)
                  .Distinct()
                  .ToList();
              if (listedConsumeIds.Count > 0)
              {
                  var existingConsumeIds = await _db.Queryable<LqXhHyhkEntity>()
                      .Where(x => listedConsumeIds.Contains(x.Id))
                      .Select(x => x.Id)
                      .ToListAsync();
                  var existingSet = new HashSet<string>(existingConsumeIds);
                  foreach (var row in rowList)
                  {
                      if (!string.IsNullOrEmpty(row.consumeId) && !existingSet.Contains(row.consumeId))
                          row.consumeId = null;
                  }
              }
  
              var needAppointmentIds = rowList
                  .Where(r => string.IsNullOrEmpty(r.consumeId) && IsTransferredConsumeStatus(r.F_Status))
                  .Select(r => r.id)
                  .Where(id => !string.IsNullOrEmpty(id))
                  .Distinct()
                  .ToList();
              if (needAppointmentIds.Count == 0) return;
  
              var consumeRows = await _db.Queryable<LqXhHyhkEntity>()
                  .Where(x => needAppointmentIds.Contains(x.AppointmentId))
                  .OrderBy(x => x.CreateTime, OrderByType.Desc)
                  .Select(x => new { x.Id, x.AppointmentId, x.IsEffective })
                  .ToListAsync();
  
              var consumeMap = consumeRows
                  .GroupBy(x => x.AppointmentId)
                  .ToDictionary(
                      g => g.Key,
                      g =>
                      {
                          var effective = g.FirstOrDefault(x => x.IsEffective == StatusEnum.有效.GetHashCode());
                          return (effective ?? g.First()).Id;
                      });
  
              foreach (var row in rowList)
              {
                  if (!string.IsNullOrEmpty(row.consumeId)) continue;
                  if (consumeMap.TryGetValue(row.id, out var consumeId))
                      row.consumeId = consumeId;
              }
          }
  
          /// <summary>
          /// 预约详情:校验 ConsumeId 是否真实存在,并合并关联耗卡列表
          /// </summary>
          private async Task ResolveYyjlRelatedConsumeAsync(LqYyjlInfoOutput output, LqYyjlEntity entity)
          {
              var related = output.relatedConsumeList ?? new List<LqYyjlRelatedConsumeOutput>();
              var relatedIds = new HashSet<string>(related.Select(x => x.id));
  
              if (!string.IsNullOrEmpty(entity.ConsumeId))
              {
                  var linked = await _db.Queryable<LqXhHyhkEntity>()
                      .Where(x => x.Id == entity.ConsumeId)
                      .FirstAsync();
                  if (linked != null)
                  {
                      if (!relatedIds.Contains(linked.Id))
                      {
                          related.Insert(0, new LqYyjlRelatedConsumeOutput
                          {
                              id = linked.Id,
                              hksj = linked.Hksj,
                              xfje = linked.Xfje,
                              hymc = linked.Hymc,
                          });
                      }
                      output.consumeId = linked.Id;
                  }
                  else
                  {
                      output.consumeId = null;
                  }
              }
              else if (related.Count > 0)
              {
                  output.consumeId = related[0].id;
              }
  
              output.relatedConsumeList = related;
          }
  
          private static bool IsTransferredConsumeStatus(string status)
          {
              var s = (status ?? string.Empty).Trim();
              return s == LqYyjlStatusTexts.TransferredToConsume || s == LqYyjlStatusTexts.Completed;
          }
  
          /// <summary>
b5df6609   “wangming”   ```
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
          /// 解析「时间戳,时间戳」范围参数,非法值(空/NaN/非数字)返回 (null,null)
          /// </summary>
          private static (DateTime? start, DateTime? end) TryParseTsRange(string raw)
          {
              if (string.IsNullOrWhiteSpace(raw)) return (null, null);
              var parts = raw.Split(',');
              if (parts.Length < 2) return (null, null);
              var s = (parts[0] ?? string.Empty).Trim();
              var e = (parts[parts.Length - 1] ?? string.Empty).Trim();
              if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(e)) return (null, null);
              if (!long.TryParse(s, out _) || !long.TryParse(e, out _)) return (null, null);
              try
              {
                  return (Ext.GetDateTime(s), Ext.GetDateTime(e));
              }
              catch
              {
                  return (null, null);
              }
          }
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
404
          #endregion
96009bc9   hexiaodong   hxd
405
  
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
406
          #region 新建预约记录
96009bc9   hexiaodong   hxd
407
408
409
410
411
412
413
414
          /// <summary>
          /// 新建预约记录
          /// </summary>
          /// <param name="input">参数</param>
          /// <returns></returns>
          [HttpPost("")]
          public async Task Create([FromBody] LqYyjlCrInput input)
          {
96009bc9   hexiaodong   hxd
415
416
417
              var entity = input.Adapt<LqYyjlEntity>();
              entity.Id = YitIdHelper.NextId().ToString();
              entity.Czr = _userManager.UserId;
d98270e2   “wangming”   Enhance LqEventSe...
418
              entity.Czsj = DateTime.Now;
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
419
              entity.CreateTime = DateTime.Now;
b5df6609   “wangming”   ```
420
421
422
423
              ApplyPrimaryYyjksFromFirstPxJksList(entity, input.lqYyjlPxList);
              if (string.IsNullOrWhiteSpace(entity.F_Status))
                  entity.F_Status = LqYyjlStatusTexts.Booked;
  
9c60e8ba   “wangming”   ```
424
              //判断沟通记录是否存在
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
425
426
427
428
429
              if (!string.IsNullOrEmpty(input.InviteId))
              {
                  var invite = await _db.Queryable<LqYaoyjlEntity>().Where(u => u.Id == input.InviteId).FirstAsync();
                  if (invite == null)
                  {
9c60e8ba   “wangming”   ```
430
                      throw NCCException.Oh("沟通记录不存在");
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
431
432
                  }
              }
b5df6609   “wangming”   ```
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
  
              if (!string.IsNullOrEmpty(entity.RoomId) && !string.IsNullOrEmpty(entity.Djmd))
              {
                  var room = await _db.Queryable<LqMdRoomEntity>()
                      .Where(r => r.Id == entity.RoomId && r.StoreId == entity.Djmd && r.Enabled == 1)
                      .FirstAsync();
                  entity.RoomName = room?.RoomName ?? entity.RoomName;
              }
  
              try
              {
                  _db.BeginTran();
                  var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync();
                  if (!(isOk > 0))
                      throw NCCException.Oh(ErrorCode.COM1000);
                  await InsertEffectiveYyjlPxListAsync(entity.Id, input.lqYyjlPxList);
                  _db.CommitTran();
              }
              catch (Exception)
              {
                  _db.RollbackTran();
                  throw;
              }
96009bc9   hexiaodong   hxd
456
          }
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
457
          #endregion
96009bc9   hexiaodong   hxd
458
  
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
459
          #region 预约记录无分页列表
96009bc9   hexiaodong   hxd
460
          /// <summary>
d98270e2   “wangming”   Enhance LqEventSe...
461
462
463
464
          /// 获取预约记录无分页列表
          /// </summary>
          /// <param name="input">请求参数</param>
          /// <returns></returns>
96009bc9   hexiaodong   hxd
465
466
467
468
          [NonAction]
          public async Task<dynamic> GetNoPagingList([FromQuery] LqYyjlListQueryInput input)
          {
              var sidx = input.sidx == null ? "id" : input.sidx;
b5df6609   “wangming”   ```
469
470
471
472
473
474
              var (startCzsj, endCzsj) = TryParseTsRange(input.czsj);
              var (startYysj, endYysj) = TryParseTsRange(input.yysj);
              var (startYyjs, endYyjs) = TryParseTsRange(input.yyjs);
              var hasCzsj = startCzsj.HasValue && endCzsj.HasValue;
              var hasYysj = startYysj.HasValue && endYysj.HasValue;
              var hasYyjs = startYyjs.HasValue && endYyjs.HasValue;
96009bc9   hexiaodong   hxd
475
476
477
478
479
480
481
              var data = await _db.Queryable<LqYyjlEntity>()
                  .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id))
                  .WhereIF(!string.IsNullOrEmpty(input.djmd), p => p.Djmd.Equals(input.djmd))
                  .WhereIF(!string.IsNullOrEmpty(input.yyr), p => p.Yyr.Equals(input.yyr))
                  .WhereIF(!string.IsNullOrEmpty(input.gklx), p => p.Gklx.Contains(input.gklx))
                  .WhereIF(!string.IsNullOrEmpty(input.yytyxm), p => p.Yytyxm.Equals(input.yytyxm))
                  .WhereIF(!string.IsNullOrEmpty(input.czr), p => p.Czr.Equals(input.czr))
b5df6609   “wangming”   ```
482
483
                  .WhereIF(hasCzsj, p => p.Czsj >= new DateTime(startCzsj.Value.Year, startCzsj.Value.Month, startCzsj.Value.Day, 0, 0, 0))
                  .WhereIF(hasCzsj, p => p.Czsj <= new DateTime(endCzsj.Value.Year, endCzsj.Value.Month, endCzsj.Value.Day, 23, 59, 59))
96009bc9   hexiaodong   hxd
484
485
486
                  .WhereIF(!string.IsNullOrEmpty(input.gk), p => p.Gk.Equals(input.gk))
                  .WhereIF(!string.IsNullOrEmpty(input.gkxm), p => p.Gkxm.Contains(input.gkxm))
                  .WhereIF(!string.IsNullOrEmpty(input.yyjks), p => p.Yyjks.Equals(input.yyjks))
b5df6609   “wangming”   ```
487
488
489
490
                  .WhereIF(hasYysj, p => p.Yysj >= new DateTime(startYysj.Value.Year, startYysj.Value.Month, startYysj.Value.Day, 0, 0, 0))
                  .WhereIF(hasYysj, p => p.Yysj <= new DateTime(endYysj.Value.Year, endYysj.Value.Month, endYysj.Value.Day, 23, 59, 59))
                  .WhereIF(hasYyjs, p => p.Yyjs >= new DateTime(startYyjs.Value.Year, startYyjs.Value.Month, startYyjs.Value.Day, 0, 0, 0))
                  .WhereIF(hasYyjs, p => p.Yyjs <= new DateTime(endYyjs.Value.Year, endYyjs.Value.Month, endYyjs.Value.Day, 23, 59, 59))
35bb18d3   “wangming”   更新多个.DS_Store文件,删...
491
                  .WhereIF(!string.IsNullOrEmpty(input.F_Status), p => p.F_Status.Equals(input.F_Status))
d98270e2   “wangming”   Enhance LqEventSe...
492
                  .Select(it => new LqYyjlListOutput
96009bc9   hexiaodong   hxd
493
494
                  {
                      id = it.Id,
d98270e2   “wangming”   Enhance LqEventSe...
495
496
497
498
499
500
501
502
503
504
505
506
                      djmd = it.Djmd,
                      yyr = it.Yyr,
                      gklx = it.Gklx,
                      yytyxm = it.Yytyxm,
                      czr = it.Czr,
                      czsj = it.Czsj,
                      gk = it.Gk,
                      gkxm = it.Gkxm,
                      yyjks = it.Yyjks,
                      yysj = it.Yysj,
                      yyjs = it.Yyjs,
                      F_Status = it.F_Status,
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
507
508
509
                      yyrName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Yyr).Select(u => u.RealName),
                      yyjksName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Yyjks).Select(u => u.RealName),
                      djmdName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(u => u.Id == it.Djmd).Select(u => u.Dm),
d98270e2   “wangming”   Enhance LqEventSe...
510
511
512
513
514
                  })
                  .MergeTable()
                  .OrderBy(sidx + " " + input.sort)
                  .ToListAsync();
              return data;
96009bc9   hexiaodong   hxd
515
          }
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
516
          #endregion
96009bc9   hexiaodong   hxd
517
  
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
518
          #region 导出预约记录
96009bc9   hexiaodong   hxd
519
          /// <summary>
d98270e2   “wangming”   Enhance LqEventSe...
520
521
522
523
          /// 导出预约记录
          /// </summary>
          /// <param name="input">请求参数</param>
          /// <returns></returns>
96009bc9   hexiaodong   hxd
524
525
526
527
528
529
530
531
532
533
534
535
536
537
          [HttpGet("Actions/Export")]
          public async Task<dynamic> Export([FromQuery] LqYyjlListQueryInput input)
          {
              var userInfo = await _userManager.GetUserInfo();
              var exportData = new List<LqYyjlListOutput>();
              if (input.dataType == 0)
              {
                  var data = Clay.Object(await this.GetList(input));
                  exportData = data.Solidify<PageResult<LqYyjlListOutput>>().list;
              }
              else
              {
                  exportData = await this.GetNoPagingList(input);
              }
d98270e2   “wangming”   Enhance LqEventSe...
538
              List<ParamsModel> paramList =
9c60e8ba   “wangming”   ```
539
                  "[{\"value\":\"预约编号\",\"field\":\"id\"},{\"value\":\"单据门店\",\"field\":\"djmd\"},{\"value\":\"沟通人\",\"field\":\"yyr\"},{\"value\":\"顾客姓名\",\"field\":\"gkxm\"},{\"value\":\"操作人\",\"field\":\"czr\"},{\"value\":\"操作时间\",\"field\":\"czsj\"},{\"value\":\"预约体验项目\",\"field\":\"yytyxm\"},{\"value\":\"顾客类型\",\"field\":\"gklx\"},{\"value\":\"预约健康师\",\"field\":\"yyjks\"},{\"value\":\"预约开始时间\",\"field\":\"yysj\"},{\"value\":\"预约结束时间\",\"field\":\"yyjs\"},{\"value\":\"顾客\",\"field\":\"gk\"},{\"value\":\"预约状态\",\"field\":\"F_Status\"},]".ToList<ParamsModel>();
96009bc9   hexiaodong   hxd
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
              ExcelConfig excelconfig = new ExcelConfig();
              excelconfig.FileName = "预约记录.xls";
              excelconfig.HeadFont = "微软雅黑";
              excelconfig.HeadPoint = 10;
              excelconfig.IsAllSizeColumn = true;
              excelconfig.ColumnModel = new List<ExcelColumnModel>();
              List<string> selectKeyList = input.selectKey.Split(',').ToList();
              foreach (var item in selectKeyList)
              {
                  var isExist = paramList.Find(p => p.field == item);
                  if (isExist != null)
                  {
                      excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = isExist.field, ExcelColumn = isExist.value });
                  }
              }
              var addPath = FileVariable.TemporaryFilePath + excelconfig.FileName;
              ExcelExportHelper<LqYyjlListOutput>.Export(exportData, excelconfig, addPath);
              var fileName = _userManager.UserId + "|" + addPath + "|xls";
d98270e2   “wangming”   Enhance LqEventSe...
558
              var output = new { name = excelconfig.FileName, url = "/api/File/Download?encryption=" + DESCEncryption.Encrypt(fileName, "NCC") };
96009bc9   hexiaodong   hxd
559
560
              return output;
          }
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
561
          #endregion
96009bc9   hexiaodong   hxd
562
  
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
563
          #region 批量删除预约记录
96009bc9   hexiaodong   hxd
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
          /// <summary>
          /// 批量删除预约记录
          /// </summary>
          /// <param name="ids">主键数组</param>
          /// <returns></returns>
          [HttpPost("batchRemove")]
          public async Task BatchRemove([FromBody] List<string> ids)
          {
              var entitys = await _db.Queryable<LqYyjlEntity>().In(it => it.Id, ids).ToListAsync();
              if (entitys.Count > 0)
              {
                  try
                  {
                      //开启事务
                      _db.BeginTran();
                      //批量删除预约记录
d98270e2   “wangming”   Enhance LqEventSe...
580
                      await _db.Deleteable<LqYyjlEntity>().In(d => d.Id, ids).ExecuteCommandAsync();
96009bc9   hexiaodong   hxd
581
582
583
584
585
586
587
588
589
590
591
                      //关闭事务
                      _db.CommitTran();
                  }
                  catch (Exception)
                  {
                      //回滚事务
                      _db.RollbackTran();
                      throw NCCException.Oh(ErrorCode.COM1002);
                  }
              }
          }
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
592
          #endregion
96009bc9   hexiaodong   hxd
593
  
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
594
          #region 更新预约记录
96009bc9   hexiaodong   hxd
595
596
597
598
599
600
601
602
603
          /// <summary>
          /// 更新预约记录
          /// </summary>
          /// <param name="id">主键</param>
          /// <param name="input">参数</param>
          /// <returns></returns>
          [HttpPut("{id}")]
          public async Task Update(string id, [FromBody] LqYyjlUpInput input)
          {
b5df6609   “wangming”   ```
604
605
606
607
608
              var existing = await _db.Queryable<LqYyjlEntity>().FirstAsync(p => p.Id == id);
              _ = existing ?? throw NCCException.Oh(ErrorCode.COM1005);
              if (LqYyjlStatusHelper.IsBlockedForMasterEdit(existing.F_Status))
                  throw NCCException.Oh("当前预约状态不允许修改");
  
96009bc9   hexiaodong   hxd
609
              var entity = input.Adapt<LqYyjlEntity>();
b5df6609   “wangming”   ```
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
              entity.Id = id;
              ApplyPrimaryYyjksFromFirstPxJksList(entity, input.lqYyjlPxList);
  
              if (!string.IsNullOrEmpty(entity.RoomId) && !string.IsNullOrEmpty(entity.Djmd))
              {
                  var room = await _db.Queryable<LqMdRoomEntity>()
                      .Where(r => r.Id == entity.RoomId && r.StoreId == entity.Djmd && r.Enabled == 1)
                      .FirstAsync();
                  entity.RoomName = room?.RoomName ?? entity.RoomName;
              }
  
              try
              {
                  _db.BeginTran();
                  var isOk = await _db.Updateable(entity).Where(x => x.Id == id).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
                  if (!(isOk > 0))
                      throw NCCException.Oh(ErrorCode.COM1001);
  
                  await _db.Updateable<LqYyjlPxJksEntity>()
                      .SetColumns(x => new LqYyjlPxJksEntity { IsEffective = 0, LastModifyTime = DateTime.Now })
                      .Where(x => x.YyjlId == id && x.IsEffective == 1)
                      .ExecuteCommandAsync();
  
                  await _db.Updateable<LqYyjlPxEntity>()
                      .SetColumns(x => new LqYyjlPxEntity { IsEffective = 0, LastModifyTime = DateTime.Now })
                      .Where(x => x.YyjlId == id && x.IsEffective == 1)
                      .ExecuteCommandAsync();
  
                  await InsertEffectiveYyjlPxListAsync(id, input.lqYyjlPxList);
                  _db.CommitTran();
              }
              catch (Exception)
              {
                  _db.RollbackTran();
                  throw;
              }
96009bc9   hexiaodong   hxd
646
          }
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
647
          #endregion
96009bc9   hexiaodong   hxd
648
  
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
649
          #region 删除预约记录
96009bc9   hexiaodong   hxd
650
651
652
653
654
655
656
657
658
659
          /// <summary>
          /// 删除预约记录
          /// </summary>
          /// <returns></returns>
          [HttpDelete("{id}")]
          public async Task Delete(string id)
          {
              var entity = await _db.Queryable<LqYyjlEntity>().FirstAsync(p => p.Id == id);
              _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
              var isOk = await _db.Deleteable<LqYyjlEntity>().Where(d => d.Id == id).ExecuteCommandAsync();
d98270e2   “wangming”   Enhance LqEventSe...
660
661
              if (!(isOk > 0))
                  throw NCCException.Oh(ErrorCode.COM1002);
96009bc9   hexiaodong   hxd
662
          }
7f83b333   “wangming”   重构:删除部门管理模块并修复预约记录服务
663
          #endregion
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
664
665
666
667
668
  
          #region 添加未成交说明
          /// <summary>
          /// 添加未成交说明
          /// </summary>
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
669
670
          /// <param name="input">参数</param>
          /// <returns></returns>
14cbcfd9   “wangming”   feat: 添加人次记录表和品项分类字段
671
672
          [HttpPost("AddNoDealRemark")]
          public async Task AddNoDealRemark([FromBody] LqYyjlAddNoDealRemarkInput input)
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
673
          {
14cbcfd9   “wangming”   feat: 添加人次记录表和品项分类字段
674
              var entity = await _db.Queryable<LqYyjlEntity>().FirstAsync(p => p.Id == input.id);
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
675
              _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
14cbcfd9   “wangming”   feat: 添加人次记录表和品项分类字段
676
              entity.NoDealRemark = input.noDealRemark;
72cec4b5   “wangming”   feat: 添加转化率统计和升单类型功能
677
678
679
680
681
              var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
              if (!(isOk > 0))
                  throw NCCException.Oh(ErrorCode.COM1001);
          }
          #endregion
763801a5   “wangming”   门店驾驶舱功能优化:新增统计接口、...
682
  
b5df6609   “wangming”   ```
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
          #region 预约动作(开始服务 / 取消 / 完成)
  
          /// <summary>
          /// 开始服务:已预约  服务中,写入服务开始时间。
          /// </summary>
          /// <param name="id">预约主键</param>
          [HttpPost("{id}/StartService")]
          public async Task StartService(string id)
          {
              var yyjl = await _db.Queryable<LqYyjlEntity>().FirstAsync(p => p.Id == id);
              _ = yyjl ?? throw NCCException.Oh(ErrorCode.COM1005);
              if (!LqYyjlStatusHelper.CanStartService(yyjl.F_Status))
                  throw NCCException.Oh("当前预约状态不允许开始服务");
  
              yyjl.F_Status = LqYyjlStatusTexts.InService;
              yyjl.ServiceStartTime = DateTime.Now;
              var ok = await _db.Updateable(yyjl)
                  .UpdateColumns(x => new { x.F_Status, x.ServiceStartTime })
                  .ExecuteCommandAsync();
              if (!(ok > 0))
                  throw NCCException.Oh(ErrorCode.COM1001);
          }
  
          /// <summary>
          /// 取消预约:已预约 / 服务中  已取消;已转耗卡需先作废耗卡;已完成不可取消。
          /// </summary>
          /// <param name="id">预约主键</param>
          /// <param name="input">可选未成交说明</param>
          [HttpPost("{id}/Cancel")]
          public async Task Cancel(string id, [FromBody] LqYyjlCancelInput input)
          {
              var yyjl = await _db.Queryable<LqYyjlEntity>().FirstAsync(p => p.Id == id);
              _ = yyjl ?? throw NCCException.Oh(ErrorCode.COM1005);
              var normalized = LqYyjlStatusHelper.NormalizeStored(yyjl.F_Status);
              if (normalized == LqYyjlStatusTexts.TransferredToConsume)
                  throw NCCException.Oh("请先作废耗卡");
              if (normalized == LqYyjlStatusTexts.Completed)
                  throw NCCException.Oh("已完成不可取消");
              if (!LqYyjlStatusHelper.CanCancel(yyjl.F_Status))
                  throw NCCException.Oh("当前预约状态不允许取消");
  
              yyjl.F_Status = LqYyjlStatusTexts.Cancelled;
              if (!string.IsNullOrWhiteSpace(input?.noDealRemark))
                  yyjl.NoDealRemark = input.noDealRemark;
  
              var ok = await _db.Updateable(yyjl)
                  .UpdateColumns(x => new { x.F_Status, x.NoDealRemark })
                  .ExecuteCommandAsync();
              if (!(ok > 0))
                  throw NCCException.Oh(ErrorCode.COM1001);
          }
  
          /// <summary>
          /// 完成预约:已转耗卡  已完成;要求已有关联有效耗卡。
          /// </summary>
          /// <param name="id">预约主键</param>
          [HttpPost("{id}/Complete")]
          public async Task Complete(string id)
          {
              var yyjl = await _db.Queryable<LqYyjlEntity>().FirstAsync(p => p.Id == id);
              _ = yyjl ?? throw NCCException.Oh(ErrorCode.COM1005);
              if (!LqYyjlStatusHelper.CanComplete(yyjl.F_Status))
                  throw NCCException.Oh("当前预约状态不允许完成");
              if (string.IsNullOrWhiteSpace(yyjl.ConsumeId))
                  throw NCCException.Oh("未关联有效耗卡,无法完成");
  
              var consumeOk = await _db.Queryable<LqXhHyhkEntity>()
                  .AnyAsync(x => x.Id == yyjl.ConsumeId && x.IsEffective == StatusEnum.有效.GetHashCode());
              if (!consumeOk)
                  throw NCCException.Oh("关联耗卡无效或已作废,无法完成");
  
              yyjl.F_Status = LqYyjlStatusTexts.Completed;
              var ok = await _db.Updateable(yyjl).UpdateColumns(x => new { x.F_Status }).ExecuteCommandAsync();
              if (!(ok > 0))
                  throw NCCException.Oh(ErrorCode.COM1001);
          }
  
          #endregion
  
          #region 门店排班一览
  
          /// <summary>
          /// 门店员工排班:按门店与预约开始时间范围返回预约块(含多健康师、品项摘要)
          /// </summary>
          /// <param name="djmd">门店ID</param>
          /// <param name="yysj">预约开始时间范围,格式:毫秒时间戳,毫秒时间戳</param>
          /// <returns>排班预约列表</returns>
          [HttpGet("StoreSchedule")]
          public async Task<List<LqYyjlScheduleItemOutput>> GetStoreSchedule([FromQuery] string djmd, [FromQuery] string yysj)
          {
              if (string.IsNullOrWhiteSpace(djmd))
              {
                  throw NCCException.Oh("门店ID不能为空");
              }
  
              var (startYysj, endYysj) = TryParseTsRange(yysj);
              if (!startYysj.HasValue || !endYysj.HasValue)
              {
                  throw NCCException.Oh("预约时间范围无效,请使用毫秒时间戳,毫秒时间戳");
              }
  
              var dayStart = new DateTime(startYysj.Value.Year, startYysj.Value.Month, startYysj.Value.Day, 0, 0, 0);
              var dayEnd = new DateTime(endYysj.Value.Year, endYysj.Value.Month, endYysj.Value.Day, 23, 59, 59);
  
              var entities = await _db.Queryable<LqYyjlEntity>()
                  .Where(x => x.Djmd == djmd)
                  .Where(x => x.F_Status != "已取消")
                  .Where(x => x.Yysj >= dayStart && x.Yysj <= dayEnd)
                  .OrderBy(x => x.Yysj)
                  .ToListAsync();
  
              if (entities == null || entities.Count == 0)
              {
                  return new List<LqYyjlScheduleItemOutput>();
              }
  
              var yyjlIds = entities.Select(x => x.Id).Distinct().ToList();
  
              var jksEntities = await _db.Queryable<LqYyjlPxJksEntity>()
                  .Where(x => yyjlIds.Contains(x.YyjlId) && x.IsEffective == 1)
                  .OrderBy(x => x.SortNo)
                  .ToListAsync();
  
              var jksByYyjl = jksEntities
                  .GroupBy(x => x.YyjlId)
                  .ToDictionary(g => g.Key, g => g.ToList());
  
              var pxEntities = await _db.Queryable<LqYyjlPxEntity>()
                  .Where(x => yyjlIds.Contains(x.YyjlId) && x.IsEffective == 1)
                  .OrderBy(x => x.SortNo)
                  .ToListAsync();
  
              var pxByYyjl = pxEntities
                  .GroupBy(x => x.YyjlId)
                  .ToDictionary(g => g.Key, g => g.ToList());
  
              var coachIdSet = new HashSet<string>(StringComparer.Ordinal);
              foreach (var j in jksEntities)
              {
                  if (!string.IsNullOrWhiteSpace(j.Jks)) coachIdSet.Add(j.Jks);
              }
              foreach (var e in entities)
              {
                  if (!string.IsNullOrWhiteSpace(e.Yyjks)) coachIdSet.Add(e.Yyjks);
              }
  
              var coachNameMap = new Dictionary<string, string>(StringComparer.Ordinal);
              if (coachIdSet.Count > 0)
              {
                  var coachIds = coachIdSet.ToList();
                  var coaches = await _db.Queryable<UserEntity>()
                      .Where(u => coachIds.Contains(u.Id))
                      .Select(u => new { u.Id, u.RealName })
                      .ToListAsync();
                  coachNameMap = coaches.ToDictionary(k => k.Id, v => v.RealName ?? v.Id);
              }
  
              var result = new List<LqYyjlScheduleItemOutput>();
              foreach (var entity in entities)
              {
                  var therapistIds = new List<string>();
                  var therapistNames = new List<string>();
                  if (jksByYyjl.TryGetValue(entity.Id, out var jksList) && jksList != null && jksList.Count > 0)
                  {
                      foreach (var j in jksList)
                      {
                          if (string.IsNullOrWhiteSpace(j.Jks)) continue;
                          if (!therapistIds.Contains(j.Jks))
                          {
                              therapistIds.Add(j.Jks);
                              var nm = !string.IsNullOrWhiteSpace(j.Jksxm)
                                  ? j.Jksxm
                                  : (coachNameMap.TryGetValue(j.Jks, out var cn) ? cn : j.Jks);
                              therapistNames.Add(nm);
                          }
                      }
                  }
  
                  if (therapistIds.Count == 0 && !string.IsNullOrWhiteSpace(entity.Yyjks))
                  {
                      therapistIds.Add(entity.Yyjks);
                      therapistNames.Add(coachNameMap.TryGetValue(entity.Yyjks, out var cn) ? cn : entity.Yyjks);
                  }
  
                  var itemLabels = new List<string>();
                  if (pxByYyjl.TryGetValue(entity.Id, out var pxList) && pxList != null)
                  {
                      foreach (var px in pxList)
                      {
                          var pn = px.ProjectNumber > 0 ? px.ProjectNumber : 1;
                          var name = string.IsNullOrWhiteSpace(px.Pxmc) ? px.Px : px.Pxmc;
                          if (!string.IsNullOrWhiteSpace(name))
                          {
                              itemLabels.Add($"{name}×{pn}");
                          }
                      }
                  }
  
                  result.Add(new LqYyjlScheduleItemOutput
                  {
                      id = entity.Id,
                      gk = entity.Gk,
                      gkxm = entity.Gkxm,
                      yysj = entity.Yysj,
                      yyjs = entity.Yyjs,
                      F_Status = entity.F_Status,
                      roomId = entity.RoomId,
                      roomName = entity.RoomName,
                      remark = entity.Remark,
                      therapistIds = therapistIds,
                      therapistNames = therapistNames,
                      itemLabels = itemLabels
                  });
              }
  
              return result;
          }
  
          #endregion
  
763801a5   “wangming”   门店驾驶舱功能优化:新增统计接口、...
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
          #region 我的今日预约
  
          /// <summary>
          /// 获取我的今日预约
          /// </summary>
          /// <remarks>
          /// 查询当前登录用户今天预约的客户列表
          /// 
          /// 查询逻辑:
          /// 1. 查询当前用户(yyr字段)的预约记录
          /// 2. 筛选预约开始时间(yysj)在今天范围内的记录
          /// 3. 关联查询客户、健康师、门店等信息
          /// 4. 按预约时间排序返回
          /// </remarks>
          /// <returns>今日预约客户列表</returns>
          /// <response code="200">查询成功</response>
          /// <response code="500">服务器错误</response>
          [HttpGet("GetMyTodayAppointments")]
          public async Task<List<MyTodayAppointmentOutput>> GetMyTodayAppointmentsAsync()
          {
              try
              {
                  var currentUserId = _userManager.UserId;
                  var today = DateTime.Now.Date;
  
                  // 查询当前用户今天的预约记录
                  var appointments = await _db.Queryable<LqYyjlEntity>()
                      .Where(x => x.Yyr == currentUserId)
                      .Where(x => x.Yysj != null && x.Yysj.Value.Date == today)
                      .OrderBy(x => x.Yysj)
                      .ToListAsync();
  
                  if (!appointments.Any())
                  {
                      return new List<MyTodayAppointmentOutput>();
                  }
  
                  // 获取门店信息
                  var storeIds = appointments.Where(x => !string.IsNullOrEmpty(x.Djmd)).Select(x => x.Djmd).Distinct().ToList();
                  var stores = new Dictionary<string, string>();
                  if (storeIds.Any())
                  {
                      var storeList = await _db.Queryable<LqMdxxEntity>()
                          .Where(x => storeIds.Contains(x.Id))
                          .Select(x => new { x.Id, x.Dm })
                          .ToListAsync();
                      stores = storeList.ToDictionary(k => k.Id, v => v.Dm);
                  }
  
                  // 获取健康师信息
                  var healthCoachIds = appointments.Where(x => !string.IsNullOrEmpty(x.Yyjks)).Select(x => x.Yyjks).Distinct().ToList();
                  var healthCoaches = new Dictionary<string, string>();
                  if (healthCoachIds.Any())
                  {
                      var healthCoachList = await _db.Queryable<UserEntity>()
                          .Where(x => healthCoachIds.Contains(x.Id))
                          .Select(x => new { x.Id, x.RealName })
                          .ToListAsync();
                      healthCoaches = healthCoachList.ToDictionary(k => k.Id, v => v.RealName);
                  }
  
                  // 构建返回结果
                  var result = appointments.Select(x =>
                  {
                      var startTime = x.Yysj?.ToString("HH:mm") ?? "";
                      var endTime = x.Yyjs?.ToString("HH:mm") ?? "";
                      var timeDisplay = !string.IsNullOrEmpty(endTime) ? $"{startTime}-{endTime}" : startTime;
  
                      return new MyTodayAppointmentOutput
                      {
                          AppointmentId = x.Id,
                          CustomerId = x.Gk,
                          CustomerName = x.Gkxm,
                          CustomerType = x.Gklx,
                          ProjectName = x.Yytyxm,
                          AppointmentStartTime = x.Yysj,
                          AppointmentEndTime = x.Yyjs,
                          HealthCoachId = x.Yyjks,
                          HealthCoachName = !string.IsNullOrEmpty(x.Yyjks) && healthCoaches.ContainsKey(x.Yyjks) ? healthCoaches[x.Yyjks] : null,
                          Status = x.F_Status,
                          StoreId = x.Djmd,
                          StoreName = !string.IsNullOrEmpty(x.Djmd) && stores.ContainsKey(x.Djmd) ? stores[x.Djmd] : null,
                          AppointmentTimeDisplay = timeDisplay
                      };
                  }).ToList();
  
                  return result;
              }
              catch (Exception ex)
              {
                  throw NCCException.Oh($"获取我的今日预约失败:{ex.Message}");
              }
          }
  
          #endregion
b5df6609   “wangming”   ```
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
  
          /// <summary>
          /// 若首行品项存在计划健康师列表,则同步主表预约健康师为首条(兼容旧端:无列表则不覆盖)。
          /// </summary>
          private static void ApplyPrimaryYyjksFromFirstPxJksList(LqYyjlEntity entity, List<LqYyjlPxCrInput> pxRows)
          {
              if (pxRows == null || pxRows.Count == 0)
                  return;
              var row0 = pxRows[0];
              if (row0?.jksList == null || row0.jksList.Count == 0)
                  return;
              var head = row0.jksList[0];
              if (!string.IsNullOrWhiteSpace(head?.jks))
                  entity.Yyjks = head.jks;
          }
  
          /// <summary>
          /// 插入预约计划品项(仅包含有品项编码的行,标记有效);同步写入品项-计划健康师子表。
          /// </summary>
          private async Task InsertEffectiveYyjlPxListAsync(string yyjlId, List<LqYyjlPxCrInput> rows)
          {
              if (rows == null || !rows.Any())
                  return;
  
              var now = DateTime.Now;
              var pxList = new List<LqYyjlPxEntity>();
              var jksAll = new List<LqYyjlPxJksEntity>();
              foreach (var row in rows.Where(r => r != null && !string.IsNullOrWhiteSpace(r.px)))
              {
                  if (row.jksList != null && row.jksList.Any())
                  {
                      if (row.jksList.Select(x => x.jks).Distinct().Count() != row.jksList.Count)
                          throw NCCException.Oh("同一品项不可重复添加同一健康师");
                  }
  
                  var pxId = YitIdHelper.NextId().ToString();
                  LqYyjlPxJksCrInput firstPlan = null;
                  if (row.jksList != null && row.jksList.Count > 0)
                      firstPlan = row.jksList[0];
  
                  pxList.Add(
                      new LqYyjlPxEntity
                      {
                          Id = pxId,
                          YyjlId = yyjlId,
                          Px = row.px,
                          Pxmc = row.pxmc,
                          Pxjg = row.pxjg,
                          ProjectNumber = row.projectNumber,
                          SourceType = row.sourceType,
                          BillingItemId = row.billingItemId,
                          Jks = firstPlan != null ? firstPlan.jks : row.jks,
                          Jksxm = firstPlan != null ? firstPlan.jksxm : row.jksxm,
                          Remark = row.remark,
                          SortNo = row.sortNo,
                          IsEffective = 1,
                          CreateTime = now,
                      });
  
                  if (row.jksList != null && row.jksList.Count > 0)
                  {
                      for (var i = 0; i < row.jksList.Count; i++)
                      {
                          var j = row.jksList[i];
                          if (string.IsNullOrWhiteSpace(j.jks))
                              throw NCCException.Oh("健康师不能为空");
                          jksAll.Add(
                              new LqYyjlPxJksEntity
                              {
                                  Id = YitIdHelper.NextId().ToString(),
                                  YyjlId = yyjlId,
                                  YyjlPxId = pxId,
                                  Jks = j.jks,
                                  Jksxm = j.jksxm,
                                  SortNo = j.sortNo != 0 ? j.sortNo : i,
                                  IsEffective = 1,
                                  CreateTime = now,
                              });
                      }
                  }
              }
  
              if (pxList.Any())
                  await _db.Insertable(pxList).ExecuteCommandAsync();
              if (jksAll.Any())
                  await _db.Insertable(jksAll).ExecuteCommandAsync();
          }
96009bc9   hexiaodong   hxd
1085
1086
      }
  }