Commit 72cec4b58974d5a803cc5ec716077addda87f53c

Authored by “wangming”
1 parent 631fcb03

feat: 添加转化率统计和升单类型功能

- 添加转化率统计关联字段(邀约ID、预约ID)
- 添加预约表未成交说明字段
- 添加开单记录表升单类型字段(升生美、升科美、升医美)
- 优化开单记录查询中的预约时间处理(处理null值)
- 添加判断客户是否开过生美/科美/医美开单的三个方法
- 添加相关SQL脚本
Showing 20 changed files with 599 additions and 90 deletions
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbCrInput.cs
... ... @@ -146,6 +146,11 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
146 146 public decimal supplementAmount { get; set; }
147 147  
148 148 /// <summary>
  149 + /// 预约记录ID
  150 + /// </summary>
  151 + public string appointmentId { get; set; }
  152 +
  153 + /// <summary>
149 154 /// 扣款信息
150 155 /// </summary>
151 156 public List<LqKdDeductinfoCrInput> lqKdKdjlbDeductList { get; set; }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbInfoOutput.cs
... ... @@ -173,6 +173,16 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
173 173 public string activityName { get; set; }
174 174  
175 175 /// <summary>
  176 + /// 预约记录ID
  177 + /// </summary>
  178 + public string appointmentId { get; set; }
  179 +
  180 + /// <summary>
  181 + /// 预约记录时间
  182 + /// </summary>
  183 + public DateTime? appointmentTime { get; set; }
  184 +
  185 + /// <summary>
176 186 /// 健康师业绩
177 187 /// </summary>
178 188 public List<LqKdJksyjInfoOutput> lqKdJksyjList { get; set; }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbListOutput.cs
... ... @@ -167,6 +167,15 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
167 167 public string createUserName { get; set; }
168 168  
169 169 /// <summary>
  170 + /// 预约记录ID
  171 + /// </summary>
  172 + public string appointmentId { get; set; }
  173 + /// <summary>
  174 + /// 预约记录时间
  175 + /// </summary>
  176 + public DateTime? appointmentTime { get; set; }
  177 +
  178 + /// <summary>
170 179 /// 开单品项明细列表
171 180 /// </summary>
172 181 public List<LqKdPxmxInfoOutput> ItemDetails { get; set; }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlCrInput.cs
... ... @@ -48,5 +48,10 @@ namespace NCC.Extend.Entitys.Dto.LqYaoyjl
48 48 /// </summary>
49 49 public string lxjl { get; set; }
50 50  
  51 + /// <summary>
  52 + /// 拓客编号
  53 + /// </summary>
  54 + public string tkbh { get; set; }
  55 +
51 56 }
52 57 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlListOutput.cs
... ... @@ -53,6 +53,11 @@ namespace NCC.Extend.Entitys.Dto.LqYaoyjl
53 53 public string lxjl { get; set; }
54 54  
55 55 /// <summary>
  56 + /// 拓客编号
  57 + /// </summary>
  58 + public string tkbh { get; set; }
  59 +
  60 + /// <summary>
56 61 /// 所属门店ID
57 62 /// </summary>
58 63 public string storeId { get; set; }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlCrInput.cs
... ... @@ -73,5 +73,10 @@ namespace NCC.Extend.Entitys.Dto.LqYyjl
73 73 /// </summary>
74 74 public string F_Status { get; set; }
75 75  
  76 + /// <summary>
  77 + /// 邀约记录ID
  78 + /// </summary>
  79 + public string InviteId { get; set; }
  80 +
76 81 }
77 82 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlInfoOutput.cs
... ... @@ -88,5 +88,19 @@ namespace NCC.Extend.Entitys.Dto.LqYyjl
88 88 /// </summary>
89 89 public string F_Status { get; set; }
90 90  
  91 + /// <summary>
  92 + /// 未成交说明
  93 + /// </summary>
  94 + public string NoDealRemark { get; set; }
  95 +
  96 + /// <summary>
  97 + /// 邀约记录ID
  98 + /// </summary>
  99 + public string InviteId { get; set; }
  100 +
  101 + /// <summary>
  102 + /// 邀约记录时间
  103 + /// </summary>
  104 + public DateTime? InviteTime { get; set; }
91 105 }
92 106 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlListOutput.cs
... ... @@ -87,5 +87,20 @@ namespace NCC.Extend.Entitys.Dto.LqYyjl
87 87 /// </summary>
88 88 public string F_Status { get; set; }
89 89  
  90 + /// <summary>
  91 + /// 未成交说明
  92 + /// </summary>
  93 + public string NoDealRemark { get; set; }
  94 +
  95 + /// <summary>
  96 + /// 邀约记录ID
  97 + /// </summary>
  98 + public string InviteId { get; set; }
  99 +
  100 + /// <summary>
  101 + /// 邀约记录时间
  102 + /// </summary>
  103 + public DateTime? InviteTime { get; set; }
  104 +
90 105 }
91 106 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kdjlb/LqKdKdjlbEntity.cs
... ... @@ -207,6 +207,12 @@ namespace NCC.Extend.Entitys.lq_kd_kdjlb
207 207 public string ActivityId { get; set; }
208 208  
209 209 /// <summary>
  210 + /// 预约记录ID(关联lq_yyjl.F_Id,用于统计预约→开单转化率)
  211 + /// </summary>
  212 + [SugarColumn(ColumnName = "F_AppointmentId")]
  213 + public string AppointmentId { get; set; }
  214 +
  215 + /// <summary>
210 216 /// 已缴欠款
211 217 /// </summary>
212 218 [SugarColumn(ColumnName = "F_PaidDebt")]
... ... @@ -223,5 +229,23 @@ namespace NCC.Extend.Entitys.lq_kd_kdjlb
223 229 /// </summary>
224 230 [SugarColumn(ColumnName = "F_SupplementAmount")]
225 231 public decimal SupplementAmount { get; set; }
  232 +
  233 + /// <summary>
  234 + /// 升生美(再次开单时,包含生美品项的订单)
  235 + /// </summary>
  236 + [SugarColumn(ColumnName = "F_UpgradeLifeBeauty")]
  237 + public string UpgradeLifeBeauty { get; set; }
  238 +
  239 + /// <summary>
  240 + /// 升科美(再次开单时,包含科美品项的订单)
  241 + /// </summary>
  242 + [SugarColumn(ColumnName = "F_UpgradeTechBeauty")]
  243 + public string UpgradeTechBeauty { get; set; }
  244 +
  245 + /// <summary>
  246 + /// 升医美(再次开单时,包含医美品项的订单)
  247 + /// </summary>
  248 + [SugarColumn(ColumnName = "F_UpgradeMedicalBeauty")]
  249 + public string UpgradeMedicalBeauty { get; set; }
226 250 }
227 251 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_hyhk/LqXhHyhkEntity.cs
... ... @@ -144,5 +144,11 @@ namespace NCC.Extend.Entitys.lq_xh_hyhk
144 144 /// </summary>
145 145 [SugarColumn(ColumnName = "F_OvertimeSgfy")]
146 146 public decimal? OvertimeSgfy { get; set; }
  147 +
  148 + /// <summary>
  149 + /// 预约记录ID(关联lq_yyjl.F_Id,用于统计预约→耗卡转化率)
  150 + /// </summary>
  151 + [SugarColumn(ColumnName = "F_AppointmentId")]
  152 + public string AppointmentId { get; set; }
147 153 }
148 154 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_yyjl/LqYyjlEntity.cs
... ... @@ -90,6 +90,18 @@ namespace NCC.Extend.Entitys.lq_yyjl
90 90 public string F_Status { get; set; }
91 91  
92 92 /// <summary>
  93 + /// 邀约记录ID(关联lq_yaoyjl.F_Id,用于统计邀约→预约转化率)
  94 + /// </summary>
  95 + [SugarColumn(ColumnName = "F_InviteId")]
  96 + public string InviteId { get; set; }
  97 +
  98 + /// <summary>
  99 + /// 未成交说明
  100 + /// </summary>
  101 + [SugarColumn(ColumnName = "F_NoDealRemark")]
  102 + public string NoDealRemark { get; set; }
  103 +
  104 + /// <summary>
93 105 /// 创建时间
94 106 /// </summary>
95 107 [SugarColumn(ColumnName = "F_CreateTime")]
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
... ... @@ -46,6 +46,7 @@ using NCC.Extend.Entitys.lq_package_info;
46 46 using NCC.Extend.Entitys.lq_mdxx;
47 47 using NCC.Extend.Entitys.lq_card_transfer_log;
48 48 using NCC.Extend.Entitys.Dto.LqDailyReport;
  49 +using NCC.Extend.Entitys.lq_yyjl;
49 50  
50 51 namespace NCC.Extend.LqKdKdjlb
51 52 {
... ... @@ -124,8 +125,11 @@ namespace NCC.Extend.LqKdKdjlb
124 125 {
125 126 throw NCCException.Oh(ErrorCode.COM1005, "开单记录不存在");
126 127 }
127   -
128 128 var output = entity.Adapt<LqKdKdjlbInfoOutput>();
  129 + if (!string.IsNullOrEmpty(entity.AppointmentId))
  130 + {
  131 + output.appointmentTime = await _db.Queryable<LqYyjlEntity>().Where(x => x.Id == entity.AppointmentId).Select(x => x.Yysj).FirstAsync();
  132 + }
129 133 if (output.activityId != null)
130 134 {
131 135 output.activityName = await _db.Queryable<LqPackageInfoEntity>().Where(x => x.Id == entity.ActivityId).Select(x => x.ActivityName).FirstAsync();
... ... @@ -317,6 +321,8 @@ namespace NCC.Extend.LqKdKdjlb
317 321 createUserName = SqlFunc.Subqueryable<UserEntity>().Where(x => x.Id == it.CreateUser).Select(x => x.RealName),
318 322 activityId = it.ActivityId,
319 323 activityName = SqlFunc.Subqueryable<LqPackageInfoEntity>().Where(x => x.Id == it.ActivityId).Select(x => x.ActivityName),
  324 + appointmentId = it.AppointmentId,
  325 + appointmentTime = it.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable<LqYyjlEntity>().Where(x => x.Id == it.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)),
320 326 })
321 327 .MergeTable()
322 328 .OrderBy(sidx + " " + input.sort)
... ... @@ -516,6 +522,8 @@ namespace NCC.Extend.LqKdKdjlb
516 522 createUserName = SqlFunc.Subqueryable<UserEntity>().Where(x => x.Id == kdjlb.CreateUser).Select(x => x.RealName),
517 523 activityId = kdjlb.ActivityId,
518 524 activityName = SqlFunc.Subqueryable<LqPackageInfoEntity>().Where(x => x.Id == kdjlb.ActivityId).Select(x => x.ActivityName),
  525 + appointmentId = kdjlb.AppointmentId,
  526 + appointmentTime = kdjlb.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable<LqYyjlEntity>().Where(x => x.Id == kdjlb.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)),
519 527 })
520 528 .MergeTable()
521 529 .Distinct() // 去重,因为一个开单可能对应多个健康师业绩记录
... ... @@ -670,6 +678,8 @@ namespace NCC.Extend.LqKdKdjlb
670 678 createUserName = SqlFunc.Subqueryable<UserEntity>().Where(x => x.Id == kdjlb.CreateUser).Select(x => x.RealName),
671 679 activityId = kdjlb.ActivityId,
672 680 activityName = SqlFunc.Subqueryable<LqPackageInfoEntity>().Where(x => x.Id == kdjlb.ActivityId).Select(x => x.ActivityName),
  681 + appointmentId = kdjlb.AppointmentId,
  682 + appointmentTime = kdjlb.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable<LqYyjlEntity>().Where(x => x.Id == kdjlb.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)),
673 683 })
674 684 .MergeTable()
675 685 .Distinct() // 去重,因为一个开单可能对应多个科技部老师业绩记录
... ... @@ -747,35 +757,6 @@ namespace NCC.Extend.LqKdKdjlb
747 757 {
748 758 //开启事务
749 759 _db.BeginTran();
750   - //判断是否有作废关联id
751   - //暂时先不需要
752   - // if (!string.IsNullOrEmpty(input.cancelRefId))
753   - // {
754   - // //查询作废关联id
755   - // var cancelRefEntity = await _db.Queryable<LqKdKdjlbEntity>().FirstAsync(p => p.Id == input.cancelRefId);
756   - // if (cancelRefEntity == null || cancelRefEntity.IsEffective == StatusEnum.无效.GetHashCode())
757   - // {
758   - // throw NCCException.Oh("该开单记录已经作废");
759   - // }
760   - // // 检查作废关联记录是否可以作废
761   - // var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(input.cancelRefId);
762   - // if (!canCancel)
763   - // {
764   - // throw NCCException.Oh(errorMessage);
765   - // }
766   -
767   - // //将作废关联id的IsEffective设置为无效
768   - // cancelRefEntity.IsEffective = StatusEnum.无效.GetHashCode();
769   - // await _db.Updateable(cancelRefEntity).ExecuteCommandAsync();
770   - // //把品项明细表的IsEffective设置为无效
771   - // await _db.Updateable<LqKdPxmxEntity>().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdPxmxEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync();
772   - // //把健康师业绩表的IsEffective设置为无效
773   - // await _db.Updateable<LqKdJksyjEntity>().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdJksyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync();
774   - // //把科技部老师业绩表的IsEffective设置为无效
775   - // await _db.Updateable<LqKdKjbsyjEntity>().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdKjbsyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync();
776   - // //把扣款信息表的IsEffective设置为无效
777   - // await _db.Updateable<LqKdDeductinfoEntity>().Where(p => p.BillingId == input.cancelRefId).UpdateColumns(p => new LqKdDeductinfoEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync();
778   - // }
779 760  
780 761 //判断是否有补缴开单ID
781 762 if (!string.IsNullOrEmpty(input.supplementBillingId))
... ... @@ -796,6 +777,8 @@ namespace NCC.Extend.LqKdKdjlb
796 777 //新增开单记录表记录
797 778 entity.CreateUser = userInfo.userId;
798 779 entity.DeductAmount = input.lqKdKdjlbDeductList.Sum(x => x.Amount ?? 0);//计算储扣总金额
  780 +
  781 + //保存开单记录
799 782 var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync();
800 783 //循环品相信息
801 784 // 收集所有需要插入的实体,然后批量插入
... ... @@ -2113,6 +2096,7 @@ namespace NCC.Extend.LqKdKdjlb
2113 2096 isEffective = it.IsEffective,
2114 2097 createUser = it.CreateUser,
2115 2098 createUserName = SqlFunc.Subqueryable<UserEntity>().Where(x => x.Id == it.CreateUser).Select(x => x.RealName),
  2099 + appointmentId = it.AppointmentId,
2116 2100 })
2117 2101 .MergeTable()
2118 2102 .OrderBy(sidx + " " + input.sort)
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
... ... @@ -28,6 +28,7 @@ using NCC.Extend.Entitys.lq_khxx;
28 28 using NCC.Extend.Entitys.lq_mdxx;
29 29 using NCC.Extend.Entitys.lq_xh_hyhk;
30 30 using NCC.Extend.Entitys.lq_xh_pxmx;
  31 +using NCC.Extend.Entitys.lq_xmzl;
31 32 using NCC.Extend.Interfaces.LqKhxx;
32 33 using NCC.System.Entitys.Permission;
33 34 using NCC.FriendlyException;
... ... @@ -366,6 +367,89 @@ namespace NCC.Extend.LqKhxx
366 367 }
367 368 #endregion
368 369  
  370 + #region 判断客户是否开过指定类型的开单
  371 + /// <summary>
  372 + /// 判断客户是否开过生美的开单
  373 + /// </summary>
  374 + /// <param name="memberId">会员ID</param>
  375 + /// <returns>1表示有,0表示没有</returns>
  376 + [HttpGet("has-life-beauty-billing/{memberId}")]
  377 + public async Task<int> HasLifeBeautyBilling(string memberId)
  378 + {
  379 + if (string.IsNullOrEmpty(memberId))
  380 + {
  381 + throw NCCException.Oh("会员ID不能为空");
  382 + }
  383 +
  384 + // 查询该会员是否有开过生美品项的开单
  385 + var hasBilling = await _db.Queryable<LqKdKdjlbEntity>()
  386 + .InnerJoin<LqKdPxmxEntity>((kdjlb, pxmx) => kdjlb.Id == pxmx.Glkdbh)
  387 + .InnerJoin<LqXmzlEntity>((kdjlb, pxmx, xmzl) => pxmx.Px == xmzl.Id)
  388 + .Where((kdjlb, pxmx, xmzl) => kdjlb.Kdhy == memberId
  389 + && kdjlb.IsEffective == StatusEnum.有效.GetHashCode()
  390 + && pxmx.IsEffective == StatusEnum.有效.GetHashCode()
  391 + && xmzl.IsEffective == StatusEnum.有效.GetHashCode()
  392 + && xmzl.Qt2 == "生美")
  393 + .AnyAsync();
  394 +
  395 + return hasBilling ? 1 : 0;
  396 + }
  397 +
  398 + /// <summary>
  399 + /// 判断客户是否开过科美的开单
  400 + /// </summary>
  401 + /// <param name="memberId">会员ID</param>
  402 + /// <returns>1表示有,0表示没有</returns>
  403 + [HttpGet("has-tech-beauty-billing/{memberId}")]
  404 + public async Task<int> HasTechBeautyBilling(string memberId)
  405 + {
  406 + if (string.IsNullOrEmpty(memberId))
  407 + {
  408 + throw NCCException.Oh("会员ID不能为空");
  409 + }
  410 +
  411 + // 查询该会员是否有开过科美品项的开单
  412 + var hasBilling = await _db.Queryable<LqKdKdjlbEntity>()
  413 + .InnerJoin<LqKdPxmxEntity>((kdjlb, pxmx) => kdjlb.Id == pxmx.Glkdbh)
  414 + .InnerJoin<LqXmzlEntity>((kdjlb, pxmx, xmzl) => pxmx.Px == xmzl.Id)
  415 + .Where((kdjlb, pxmx, xmzl) => kdjlb.Kdhy == memberId
  416 + && kdjlb.IsEffective == StatusEnum.有效.GetHashCode()
  417 + && pxmx.IsEffective == StatusEnum.有效.GetHashCode()
  418 + && xmzl.IsEffective == StatusEnum.有效.GetHashCode()
  419 + && xmzl.Qt2 == "科美")
  420 + .AnyAsync();
  421 +
  422 + return hasBilling ? 1 : 0;
  423 + }
  424 +
  425 + /// <summary>
  426 + /// 判断客户是否开过医美的开单
  427 + /// </summary>
  428 + /// <param name="memberId">会员ID</param>
  429 + /// <returns>1表示有,0表示没有</returns>
  430 + [HttpGet("has-medical-beauty-billing/{memberId}")]
  431 + public async Task<int> HasMedicalBeautyBilling(string memberId)
  432 + {
  433 + if (string.IsNullOrEmpty(memberId))
  434 + {
  435 + throw NCCException.Oh("会员ID不能为空");
  436 + }
  437 +
  438 + // 查询该会员是否有开过医美品项的开单
  439 + var hasBilling = await _db.Queryable<LqKdKdjlbEntity>()
  440 + .InnerJoin<LqKdPxmxEntity>((kdjlb, pxmx) => kdjlb.Id == pxmx.Glkdbh)
  441 + .InnerJoin<LqXmzlEntity>((kdjlb, pxmx, xmzl) => pxmx.Px == xmzl.Id)
  442 + .Where((kdjlb, pxmx, xmzl) => kdjlb.Kdhy == memberId
  443 + && kdjlb.IsEffective == StatusEnum.有效.GetHashCode()
  444 + && pxmx.IsEffective == StatusEnum.有效.GetHashCode()
  445 + && xmzl.IsEffective == StatusEnum.有效.GetHashCode()
  446 + && xmzl.Qt2 == "医美")
  447 + .AnyAsync();
  448 +
  449 + return hasBilling ? 1 : 0;
  450 + }
  451 + #endregion
  452 +
369 453 #region 导出客户资料
370 454 /// <summary>
371 455 /// 导出客户资料
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqReimbursementApplicationService.cs
... ... @@ -28,7 +28,7 @@ namespace NCC.Extend.LqReimbursementApplication
28 28 /// <summary>
29 29 /// 报销申请表服务
30 30 /// </summary>
31   - [ApiDescriptionSettings(Tag = "Extend",Name = "LqReimbursementApplication", Order = 200)]
  31 + [ApiDescriptionSettings(Tag = "Extend", Name = "LqReimbursementApplication", Order = 200)]
32 32 [Route("api/Extend/[controller]")]
33 33 public class LqReimbursementApplicationService : ILqReimbursementApplicationService, IDynamicApiController, ITransient
34 34 {
... ... @@ -43,7 +43,7 @@ namespace NCC.Extend.LqReimbursementApplication
43 43 ISqlSugarRepository<LqReimbursementApplicationEntity> lqReimbursementApplicationRepository,
44 44 IUserManager userManager)
45 45 {
46   - _lqReimbursementApplicationRepository = lqReimbursementApplicationRepository;
  46 + _lqReimbursementApplicationRepository = lqReimbursementApplicationRepository;
47 47 _db = _lqReimbursementApplicationRepository.Context;
48 48 _userManager = userManager;
49 49 }
... ... @@ -86,23 +86,23 @@ namespace NCC.Extend.LqReimbursementApplication
86 86 .WhereIF(!string.IsNullOrEmpty(input.amount), p => p.Amount.Contains(input.amount))
87 87 .WhereIF(!string.IsNullOrEmpty(input.approveUser), p => p.ApproveUser.Equals(input.approveUser))
88 88 .WhereIF(!string.IsNullOrEmpty(input.approveStatus), p => p.ApproveStatus.Contains(input.approveStatus))
89   - // .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0))
  89 + // .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0))
90 90 //.WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59))
91 91 .WhereIF(!string.IsNullOrEmpty(input.purchaseRecordsId), p => p.PurchaseRecordsId.Contains(input.purchaseRecordsId))
92   - .Select(it=> new LqReimbursementApplicationListOutput
  92 + .Select(it => new LqReimbursementApplicationListOutput
93 93 {
94 94 id = it.Id,
95   - applicationUserId=it.ApplicationUserId,
96   - applicationUserName=it.ApplicationUserName,
97   - applicationStoreId=it.ApplicationStoreId,
98   - applicationTime=it.ApplicationTime,
99   - amount=it.Amount,
100   - approveUser=it.ApproveUser,
101   - approveStatus=it.ApproveStatus,
102   - approveTime=it.ApproveTime,
103   - purchaseRecordsId=it.PurchaseRecordsId,
104   - }).MergeTable().OrderBy(sidx+" "+input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
105   - return PageResult<LqReimbursementApplicationListOutput>.SqlSugarPageResult(data);
  95 + applicationUserId = it.ApplicationUserId,
  96 + applicationUserName = it.ApplicationUserName,
  97 + applicationStoreId = it.ApplicationStoreId,
  98 + applicationTime = it.ApplicationTime,
  99 + amount = it.Amount,
  100 + approveUser = it.ApproveUser,
  101 + approveStatus = it.ApproveStatus,
  102 + approveTime = it.ApproveTime,
  103 + purchaseRecordsId = it.PurchaseRecordsId,
  104 + }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
  105 + return PageResult<LqReimbursementApplicationListOutput>.SqlSugarPageResult(data);
106 106 }
107 107  
108 108 /// <summary>
... ... @@ -116,16 +116,16 @@ namespace NCC.Extend.LqReimbursementApplication
116 116 var userInfo = await _userManager.GetUserInfo();
117 117 var entity = input.Adapt<LqReimbursementApplicationEntity>();
118 118 entity.Id = YitIdHelper.NextId().ToString();
119   -
  119 +
120 120 try
121 121 {
122 122 //开启事务
123 123 _db.BeginTran();
124   -
  124 +
125 125 // 保存报销申请表
126 126 var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync();
127 127 if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
128   -
  128 +
129 129 // 更新购买记录的审批单编号和审批状态为"待审批"
130 130 if (input.selectedPurchaseRecordIds != null && input.selectedPurchaseRecordIds.Count > 0)
131 131 {
... ... @@ -134,14 +134,14 @@ namespace NCC.Extend.LqReimbursementApplication
134 134 .SetColumns(it => it.ApplicationId == entity.Id)
135 135 .Where(it => input.selectedPurchaseRecordIds.Contains(it.Id))
136 136 .ExecuteCommandAsync();
137   -
  137 +
138 138 // 再更新ApproveStatus(分开更新确保都能执行)
139 139 await _db.Updateable<LqPurchaseRecordsEntity>()
140 140 .SetColumns(it => it.ApproveStatus == "待审批")
141 141 .Where(it => input.selectedPurchaseRecordIds.Contains(it.Id))
142 142 .ExecuteCommandAsync();
143 143 }
144   -
  144 +
145 145 //关闭事务
146 146 _db.CommitTran();
147 147 }
... ... @@ -178,23 +178,23 @@ namespace NCC.Extend.LqReimbursementApplication
178 178 .WhereIF(!string.IsNullOrEmpty(input.amount), p => p.Amount.Contains(input.amount))
179 179 .WhereIF(!string.IsNullOrEmpty(input.approveUser), p => p.ApproveUser.Equals(input.approveUser))
180 180 .WhereIF(!string.IsNullOrEmpty(input.approveStatus), p => p.ApproveStatus.Contains(input.approveStatus))
181   - // .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0))
182   - // .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59))
  181 + // .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0))
  182 + // .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59))
183 183 .WhereIF(!string.IsNullOrEmpty(input.purchaseRecordsId), p => p.PurchaseRecordsId.Contains(input.purchaseRecordsId))
184   - .Select(it=> new LqReimbursementApplicationListOutput
  184 + .Select(it => new LqReimbursementApplicationListOutput
185 185 {
186 186 id = it.Id,
187   - applicationUserId=it.ApplicationUserId,
188   - applicationUserName=it.ApplicationUserName,
189   - applicationStoreId=it.ApplicationStoreId,
190   - applicationTime=it.ApplicationTime,
191   - amount=it.Amount,
192   - approveUser=it.ApproveUser,
193   - approveStatus=it.ApproveStatus,
194   - approveTime=it.ApproveTime,
195   - purchaseRecordsId=it.PurchaseRecordsId,
196   - }).MergeTable().OrderBy(sidx+" "+input.sort).ToListAsync();
197   - return data;
  187 + applicationUserId = it.ApplicationUserId,
  188 + applicationUserName = it.ApplicationUserName,
  189 + applicationStoreId = it.ApplicationStoreId,
  190 + applicationTime = it.ApplicationTime,
  191 + amount = it.Amount,
  192 + approveUser = it.ApproveUser,
  193 + approveStatus = it.ApproveStatus,
  194 + approveTime = it.ApproveTime,
  195 + purchaseRecordsId = it.PurchaseRecordsId,
  196 + }).MergeTable().OrderBy(sidx + " " + input.sort).ToListAsync();
  197 + return data;
198 198 }
199 199  
200 200 /// <summary>
... ... @@ -216,7 +216,7 @@ namespace NCC.Extend.LqReimbursementApplication
216 216 {
217 217 exportData = await this.GetNoPagingList(input);
218 218 }
219   - List<ParamsModel> paramList = "[{\"value\":\"申请编号\",\"field\":\"id\"},{\"value\":\"申请人编号\",\"field\":\"applicationUserId\"},{\"value\":\"申请人姓名\",\"field\":\"applicationUserName\"},{\"value\":\"申请门店\",\"field\":\"applicationStoreId\"},{\"value\":\"申请时间\",\"field\":\"applicationTime\"},{\"value\":\"总金额\",\"field\":\"amount\"},{\"value\":\"审批人\",\"field\":\"approveUser\"},{\"value\":\"审批结果\",\"field\":\"approveStatus\"},{\"value\":\"审批时间\",\"field\":\"approveTime\"},{\"value\":\"关联购买编号\",\"field\":\"purchaseRecordsId\"},]".ToList<ParamsModel>();
  219 + List<ParamsModel> paramList = "[{\"value\":\"申请编号\",\"field\":\"id\"},{\"value\":\"申请人编号\",\"field\":\"applicationUserId\"},{\"value\":\"申请人姓名\",\"field\":\"applicationUserName\"},{\"value\":\"申请门店\",\"field\":\"applicationStoreId\"},{\"value\":\"申请时间\",\"field\":\"applicationTime\"},{\"value\":\"总金额\",\"field\":\"amount\"},{\"value\":\"审批人\",\"field\":\"approveUser\"},{\"value\":\"审批结果\",\"field\":\"approveStatus\"},{\"value\":\"审批时间\",\"field\":\"approveTime\"},{\"value\":\"关联购买编号\",\"field\":\"purchaseRecordsId\"},]".ToList<ParamsModel>();
220 220 ExcelConfig excelconfig = new ExcelConfig();
221 221 excelconfig.FileName = "报销申请表.xls";
222 222 excelconfig.HeadFont = "微软雅黑";
... ... @@ -259,7 +259,7 @@ namespace NCC.Extend.LqReimbursementApplication
259 259 //开启事务
260 260 _db.BeginTran();
261 261 //批量删除报销申请表
262   - await _db.Deleteable<LqReimbursementApplicationEntity>().In(d => d.Id,ids).ExecuteCommandAsync();
  262 + await _db.Deleteable<LqReimbursementApplicationEntity>().In(d => d.Id, ids).ExecuteCommandAsync();
263 263 //关闭事务
264 264 _db.CommitTran();
265 265 }
... ... @@ -285,7 +285,7 @@ namespace NCC.Extend.LqReimbursementApplication
285 285 {
286 286 //开启事务
287 287 _db.BeginTran();
288   -
  288 +
289 289 // 获取原有的关联购买记录ID
290 290 var oldEntity = await _db.Queryable<LqReimbursementApplicationEntity>().FirstAsync(p => p.Id == id);
291 291 var oldIds = new List<string>();
... ... @@ -294,10 +294,10 @@ namespace NCC.Extend.LqReimbursementApplication
294 294 // 获取原有购买记录ID列表
295 295 oldIds = oldEntity.PurchaseRecordsId.Split(',').Where(x => !string.IsNullOrEmpty(x)).ToList();
296 296 }
297   -
  297 +
298 298 // 获取新的购买记录ID列表
299 299 var newIds = input.selectedPurchaseRecordIds ?? new List<string>();
300   -
  300 +
301 301 // 确保 purchaseRecordsId 字段包含所有选中的记录ID(逗号分隔)
302 302 if (newIds.Count > 0)
303 303 {
... ... @@ -307,27 +307,27 @@ namespace NCC.Extend.LqReimbursementApplication
307 307 {
308 308 input.purchaseRecordsId = null;
309 309 }
310   -
  310 +
311 311 // 找出需要移除关联的记录(在旧列表中但不在新列表中)
312 312 var idsToRemove = oldIds.Where(x => !newIds.Contains(x)).ToList();
313 313 if (idsToRemove.Count > 0)
314 314 {
315 315 // 清除这些购买记录的审批单编号和审批状态
316 316 await _db.Updateable<LqPurchaseRecordsEntity>()
317   - .SetColumns(it => new LqPurchaseRecordsEntity
318   - {
  317 + .SetColumns(it => new LqPurchaseRecordsEntity
  318 + {
319 319 ApplicationId = null,
320 320 ApproveStatus = "未审批"
321 321 })
322 322 .Where(it => idsToRemove.Contains(it.Id))
323 323 .ExecuteCommandAsync();
324 324 }
325   -
  325 +
326 326 // 更新报销申请表(确保 purchaseRecordsId 字段被正确更新)
327 327 var entity = input.Adapt<LqReimbursementApplicationEntity>();
328 328 var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
329 329 if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001);
330   -
  330 +
331 331 // 更新所有选中的购买记录的审批单编号和审批状态为"待审批"
332 332 // 包括新追加的记录和已存在的记录(确保状态正确)
333 333 if (newIds.Count > 0)
... ... @@ -337,14 +337,14 @@ namespace NCC.Extend.LqReimbursementApplication
337 337 .SetColumns(it => it.ApplicationId == id)
338 338 .Where(it => newIds.Contains(it.Id))
339 339 .ExecuteCommandAsync();
340   -
  340 +
341 341 // 再更新ApproveStatus(分开更新确保都能执行)
342 342 await _db.Updateable<LqPurchaseRecordsEntity>()
343 343 .SetColumns(it => it.ApproveStatus == "待审批")
344 344 .Where(it => newIds.Contains(it.Id))
345 345 .ExecuteCommandAsync();
346 346 }
347   -
  347 +
348 348 //关闭事务
349 349 _db.CommitTran();
350 350 }
... ... @@ -380,18 +380,18 @@ namespace NCC.Extend.LqReimbursementApplication
380 380 var userInfo = await _userManager.GetUserInfo();
381 381 var entity = await _db.Queryable<LqReimbursementApplicationEntity>().FirstAsync(p => p.Id == id);
382 382 _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
383   -
  383 +
384 384 try
385 385 {
386 386 //开启事务
387 387 _db.BeginTran();
388   -
  388 +
389 389 // 更新申请表的审批状态
390 390 entity.ApproveStatus = "已审批";
391 391 entity.ApproveTime = DateTime.Now;
392 392 entity.ApproveUser = userInfo.userId;
393 393 await _db.Updateable(entity).ExecuteCommandAsync();
394   -
  394 +
395 395 // 更新关联的购买记录
396 396 if (!string.IsNullOrEmpty(entity.PurchaseRecordsId))
397 397 {
... ... @@ -399,8 +399,8 @@ namespace NCC.Extend.LqReimbursementApplication
399 399 if (purchaseIds.Count > 0)
400 400 {
401 401 await _db.Updateable<LqPurchaseRecordsEntity>()
402   - .SetColumns(it => new LqPurchaseRecordsEntity
403   - {
  402 + .SetColumns(it => new LqPurchaseRecordsEntity
  403 + {
404 404 ApproveStatus = "已审批",
405 405 ApproveTime = DateTime.Now,
406 406 ApproveUser = userInfo.userId
... ... @@ -409,7 +409,7 @@ namespace NCC.Extend.LqReimbursementApplication
409 409 .ExecuteCommandAsync();
410 410 }
411 411 }
412   -
  412 +
413 413 //关闭事务
414 414 _db.CommitTran();
415 415 }
... ... @@ -432,18 +432,18 @@ namespace NCC.Extend.LqReimbursementApplication
432 432 var userInfo = await _userManager.GetUserInfo();
433 433 var entity = await _db.Queryable<LqReimbursementApplicationEntity>().FirstAsync(p => p.Id == id);
434 434 _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
435   -
  435 +
436 436 try
437 437 {
438 438 //开启事务
439 439 _db.BeginTran();
440   -
  440 +
441 441 // 更新申请表的审批状态
442 442 entity.ApproveStatus = "未通过";
443 443 entity.ApproveTime = DateTime.Now;
444 444 entity.ApproveUser = userInfo.userId;
445 445 await _db.Updateable(entity).ExecuteCommandAsync();
446   -
  446 +
447 447 // 更新关联的购买记录
448 448 if (!string.IsNullOrEmpty(entity.PurchaseRecordsId))
449 449 {
... ... @@ -451,8 +451,8 @@ namespace NCC.Extend.LqReimbursementApplication
451 451 if (purchaseIds.Count > 0)
452 452 {
453 453 await _db.Updateable<LqPurchaseRecordsEntity>()
454   - .SetColumns(it => new LqPurchaseRecordsEntity
455   - {
  454 + .SetColumns(it => new LqPurchaseRecordsEntity
  455 + {
456 456 ApproveStatus = "未通过",
457 457 ApproveTime = DateTime.Now,
458 458 ApproveUser = userInfo.userId
... ... @@ -461,7 +461,7 @@ namespace NCC.Extend.LqReimbursementApplication
461 461 .ExecuteCommandAsync();
462 462 }
463 463 }
464   -
  464 +
465 465 //关闭事务
466 466 _db.CommitTran();
467 467 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs
... ... @@ -1127,7 +1127,7 @@ namespace NCC.Extend.LqTkjlb
1127 1127 FROM lq_tkjlb tk
1128 1128 INNER JOIN lq_kd_kdjlb kd ON tk.F_MemberId = kd.kdhy
1129 1129 AND kd.F_IsEffective = 1
1130   - WHERE 1=1 {timeFilter} {eventFilter} {storeFilter}";
  1130 + WHERE 1=1 AND kd.sfyj > 0 {timeFilter} {eventFilter} {storeFilter}";
1131 1131  
1132 1132 var kdResult = await _db.Ado.SqlQueryAsync<dynamic>(kdSql);
1133 1133 var kdCount = Convert.ToInt32(kdResult?.FirstOrDefault()?.kd_count ?? 0);
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqYaoyjlService.cs
... ... @@ -108,6 +108,7 @@ namespace NCC.Extend.LqYaoyjl
108 108 storeName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(u => u.Id == it.StoreId).Select(u => u.Dm),
109 109 createTime = it.CreateTime,
110 110 yyrName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Yyr).Select(u => u.RealName),
  111 + tkbh = it.Tkbh,
111 112 }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
112 113 return PageResult<LqYaoyjlListOutput>.SqlSugarPageResult(data);
113 114 }
... ... @@ -171,6 +172,7 @@ namespace NCC.Extend.LqYaoyjl
171 172 lxsj = it.Lxsj,
172 173 lxjl = it.Lxjl,
173 174 yyrName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.MobilePhone == it.Yyr).Select(u => u.RealName),
  175 + tkbh = it.Tkbh,
174 176 }).MergeTable().OrderBy(sidx + " " + input.sort).ToListAsync();
175 177 return data;
176 178 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqYyjlService.cs
... ... @@ -17,6 +17,7 @@ using NCC.Dependency;
17 17 using NCC.DynamicApiController;
18 18 using NCC.Extend.Entitys.Dto.LqYyjl;
19 19 using NCC.Extend.Entitys.lq_mdxx;
  20 +using NCC.Extend.Entitys.lq_yaoyjl;
20 21 using NCC.Extend.Entitys.lq_yyjl;
21 22 using NCC.Extend.Interfaces.LqYyjl;
22 23 using NCC.FriendlyException;
... ... @@ -47,6 +48,7 @@ namespace NCC.Extend.LqYyjl
47 48 _db = _lqYyjlRepository.Context;
48 49 _userManager = userManager;
49 50 }
  51 +
50 52 #region 预约记录
51 53 /// <summary>
52 54 /// 获取预约记录
... ... @@ -80,6 +82,13 @@ namespace NCC.Extend.LqYyjl
80 82 output.yyjksName = healthCoach?.RealName;
81 83 }
82 84  
  85 + //获取邀约记录
  86 + if (!string.IsNullOrEmpty(entity.InviteId))
  87 + {
  88 + var invite = await _db.Queryable<LqYaoyjlEntity>().Where(u => u.Id == entity.InviteId).FirstAsync();
  89 + output.InviteTime = invite?.Yysj;
  90 + }
  91 +
83 92 return output;
84 93 }
85 94 #endregion
... ... @@ -138,6 +147,9 @@ namespace NCC.Extend.LqYyjl
138 147 yyrName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Yyr).Select(u => u.RealName),
139 148 yyjksName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Yyjks).Select(u => u.RealName),
140 149 djmdName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(u => u.Id == it.Djmd).Select(u => u.Dm),
  150 + NoDealRemark = it.NoDealRemark,
  151 + InviteId = it.InviteId,
  152 + InviteTime = SqlFunc.Subqueryable<LqYaoyjlEntity>().Where(u => u.Id == it.InviteId).Select(u => SqlFunc.ToDate(u.Yysj)),
141 153 })
142 154 .MergeTable()
143 155 .OrderBy(sidx + " " + input.sort)
... ... @@ -155,12 +167,20 @@ namespace NCC.Extend.LqYyjl
155 167 [HttpPost("")]
156 168 public async Task Create([FromBody] LqYyjlCrInput input)
157 169 {
158   - var userInfo = await _userManager.GetUserInfo();
159 170 var entity = input.Adapt<LqYyjlEntity>();
160 171 entity.Id = YitIdHelper.NextId().ToString();
161 172 entity.Czr = _userManager.UserId;
162 173 entity.Czsj = DateTime.Now;
163 174 entity.CreateTime = DateTime.Now;
  175 + //判断邀约记录是否存在
  176 + if (!string.IsNullOrEmpty(input.InviteId))
  177 + {
  178 + var invite = await _db.Queryable<LqYaoyjlEntity>().Where(u => u.Id == input.InviteId).FirstAsync();
  179 + if (invite == null)
  180 + {
  181 + throw NCCException.Oh("邀约记录不存在");
  182 + }
  183 + }
164 184 var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync();
165 185 if (!(isOk > 0))
166 186 throw NCCException.Oh(ErrorCode.COM1000);
... ... @@ -337,5 +357,24 @@ namespace NCC.Extend.LqYyjl
337 357 throw NCCException.Oh(ErrorCode.COM1002);
338 358 }
339 359 #endregion
  360 +
  361 + #region 添加未成交说明
  362 + /// <summary>
  363 + /// 添加未成交说明
  364 + /// </summary>
  365 + /// <param name="id">主键</param>
  366 + /// <param name="input">参数</param>
  367 + /// <returns></returns>
  368 + [HttpPost("AddNoDealRemark/{id}")]
  369 + public async Task AddNoDealRemark(string id, [FromBody] string noDealRemark)
  370 + {
  371 + var entity = await _db.Queryable<LqYyjlEntity>().FirstAsync(p => p.Id == id);
  372 + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
  373 + entity.NoDealRemark = noDealRemark;
  374 + var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
  375 + if (!(isOk > 0))
  376 + throw NCCException.Oh(ErrorCode.COM1001);
  377 + }
  378 + #endregion
340 379 }
341 380 }
... ...
sql/查询邀约时间和联系时间不在同一天的记录.sql 0 → 100644
  1 +-- ============================================
  2 +-- 查询 lq_yaoyjl 表中邀约时间(yysj)和联系时间(lxsj)不在同一天的记录
  3 +-- ============================================
  4 +-- 说明:查询邀约时间和联系时间不在同一天的数据,用于数据校验或分析
  5 +--
  6 +-- 查询逻辑:
  7 +-- 1. 两个时间都不为空,但日期不同
  8 +-- 2. 其中一个为空,另一个不为空(视为不在同一天)
  9 +-- 3. 两个都为空的情况不包含在结果中(因为无法判断是否在同一天)
  10 +
  11 +-- ============================================
  12 +-- 基础查询:查询不在同一天的记录
  13 +-- ============================================
  14 +SELECT
  15 + F_Id AS 邀约编号,
  16 + yyr AS 邀约人,
  17 + yysj AS 邀约时间,
  18 + DATE(yysj) AS 邀约日期,
  19 + lxsj AS 联系时间,
  20 + DATE(lxsj) AS 联系日期,
  21 + yykh AS 邀约客户,
  22 + yykhxm AS 邀约客户姓名,
  23 + F_CreateTime AS 创建时间
  24 +FROM lq_yaoyjl
  25 +WHERE (
  26 + -- 两个时间都不为空,但日期不同
  27 + (yysj IS NOT NULL AND lxsj IS NOT NULL AND DATE(yysj) != DATE(lxsj))
  28 + OR
  29 + -- 邀约时间为空,联系时间不为空
  30 + (yysj IS NULL AND lxsj IS NOT NULL)
  31 + OR
  32 + -- 联系时间为空,邀约时间不为空
  33 + (yysj IS NOT NULL AND lxsj IS NULL)
  34 +)
  35 +ORDER BY F_CreateTime DESC;
  36 +
  37 +-- ============================================
  38 +-- 统计查询:统计不在同一天的记录数量
  39 +-- ============================================
  40 +SELECT
  41 + COUNT(*) AS 不在同一天的记录数,
  42 + COUNT(CASE WHEN yysj IS NOT NULL AND lxsj IS NOT NULL AND DATE(yysj) != DATE(lxsj) THEN 1 END) AS 两个时间都有但日期不同,
  43 + COUNT(CASE WHEN yysj IS NULL AND lxsj IS NOT NULL THEN 1 END) AS 邀约时间为空,
  44 + COUNT(CASE WHEN yysj IS NOT NULL AND lxsj IS NULL THEN 1 END) AS 联系时间为空
  45 +FROM lq_yaoyjl
  46 +WHERE (
  47 + (yysj IS NOT NULL AND lxsj IS NOT NULL AND DATE(yysj) != DATE(lxsj))
  48 + OR (yysj IS NULL AND lxsj IS NOT NULL)
  49 + OR (yysj IS NOT NULL AND lxsj IS NULL)
  50 +);
  51 +
  52 +-- ============================================
  53 +-- 详细分析查询:按日期差异分组统计
  54 +-- ============================================
  55 +SELECT
  56 + CASE
  57 + WHEN yysj IS NULL AND lxsj IS NOT NULL THEN '邀约时间为空'
  58 + WHEN yysj IS NOT NULL AND lxsj IS NULL THEN '联系时间为空'
  59 + WHEN yysj IS NOT NULL AND lxsj IS NOT NULL THEN CONCAT('相差', DATEDIFF(lxsj, yysj), '天')
  60 + END AS 日期差异类型,
  61 + COUNT(*) AS 记录数
  62 +FROM lq_yaoyjl
  63 +WHERE (
  64 + (yysj IS NOT NULL AND lxsj IS NOT NULL AND DATE(yysj) != DATE(lxsj))
  65 + OR (yysj IS NULL AND lxsj IS NOT NULL)
  66 + OR (yysj IS NOT NULL AND lxsj IS NULL)
  67 +)
  68 +GROUP BY
  69 + CASE
  70 + WHEN yysj IS NULL AND lxsj IS NOT NULL THEN '邀约时间为空'
  71 + WHEN yysj IS NOT NULL AND lxsj IS NULL THEN '联系时间为空'
  72 + WHEN yysj IS NOT NULL AND lxsj IS NOT NULL THEN CONCAT('相差', DATEDIFF(lxsj, yysj), '天')
  73 + END
  74 +ORDER BY 记录数 DESC;
  75 +
... ...
sql/添加开单记录表升单类型字段.sql 0 → 100644
  1 +-- ============================================
  2 +-- 为开单记录表添加升单类型字段
  3 +-- ============================================
  4 +-- 说明:此脚本为开单记录表添加升单类型标记字段,用于标识再次开单时包含的品项类型
  5 +--
  6 +-- 字段说明:
  7 +-- 1. F_UpgradeLifeBeauty:升生美(再次开单时,包含生美品项的订单)
  8 +-- 2. F_UpgradeTechBeauty:升科美(再次开单时,包含科美品项的订单)
  9 +-- 3. F_UpgradeMedicalBeauty:升医美(再次开单时,包含医美品项的订单)
  10 +--
  11 +-- 业务含义:
  12 +-- - 升单:已经开过会员再次消费属于升单
  13 +-- - 这三个字段用于标记升单中是否包含对应类型的品项(生美、科美、医美)
  14 +--
  15 +-- 注意事项:
  16 +-- - 字段类型为VARCHAR(10),可存储"是"/"否"或其他标记值
  17 +-- - 所有字段允许为NULL,因为历史数据可能没有这些标记
  18 +-- - 字段位置:放在 F_SupplementAmount 之后
  19 +
  20 +-- ============================================
  21 +-- 1. lq_kd_kdjlb(开单记录表) - 添加升单类型字段
  22 +-- ============================================
  23 +ALTER TABLE lq_kd_kdjlb
  24 +ADD COLUMN F_UpgradeLifeBeauty VARCHAR(10) NULL COMMENT '升生美(再次开单时,包含生美品项的订单)' AFTER F_SupplementAmount,
  25 +ADD COLUMN F_UpgradeTechBeauty VARCHAR(10) NULL COMMENT '升科美(再次开单时,包含科美品项的订单)' AFTER F_UpgradeLifeBeauty,
  26 +ADD COLUMN F_UpgradeMedicalBeauty VARCHAR(10) NULL COMMENT '升医美(再次开单时,包含医美品项的订单)' AFTER F_UpgradeTechBeauty;
  27 +
  28 +-- ============================================
  29 +-- 2. 验证字段创建
  30 +-- ============================================
  31 +-- 验证 lq_kd_kdjlb 表
  32 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT
  33 +-- FROM INFORMATION_SCHEMA.COLUMNS
  34 +-- WHERE TABLE_NAME = 'lq_kd_kdjlb'
  35 +-- AND (COLUMN_NAME = 'F_UpgradeLifeBeauty'
  36 +-- OR COLUMN_NAME = 'F_UpgradeTechBeauty'
  37 +-- OR COLUMN_NAME = 'F_UpgradeMedicalBeauty');
  38 +
  39 +-- ============================================
  40 +-- 3. 业务逻辑说明(供参考)
  41 +-- ============================================
  42 +-- 判断逻辑:
  43 +-- 1. 判断是否为升单:查询该会员(kdhy)是否有历史开单记录
  44 +-- 2. 判断是否包含生美:查询该开单的品项明细(lq_kd_pxmx)中是否有品项类型(lq_xmzl.qt2)为"生美"的记录
  45 +-- 3. 判断是否包含科美:查询该开单的品项明细中是否有品项类型为"科美"的记录
  46 +-- 4. 判断是否包含医美:查询该开单的品项明细中是否有品项类型为"医美"的记录
  47 +--
  48 +-- 示例SQL(判断升单并标记品项类型):
  49 +-- UPDATE lq_kd_kdjlb kdjlb
  50 +-- SET
  51 +-- F_UpgradeLifeBeauty = CASE
  52 +-- WHEN EXISTS (
  53 +-- SELECT 1 FROM lq_kd_kdjlb kd2
  54 +-- WHERE kd2.kdhy = kdjlb.kdhy
  55 +-- AND kd2.F_Id != kdjlb.F_Id
  56 +-- AND kd2.kdrq < kdjlb.kdrq
  57 +-- ) AND EXISTS (
  58 +-- SELECT 1 FROM lq_kd_pxmx pxmx
  59 +-- INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id
  60 +-- WHERE pxmx.glkdbh = kdjlb.F_Id
  61 +-- AND pxmx.F_IsEffective = 1
  62 +-- AND xmzl.qt2 = '生美'
  63 +-- ) THEN '是'
  64 +-- ELSE '否'
  65 +-- END,
  66 +-- F_UpgradeTechBeauty = CASE
  67 +-- WHEN EXISTS (
  68 +-- SELECT 1 FROM lq_kd_kdjlb kd2
  69 +-- WHERE kd2.kdhy = kdjlb.kdhy
  70 +-- AND kd2.F_Id != kdjlb.F_Id
  71 +-- AND kd2.kdrq < kdjlb.kdrq
  72 +-- ) AND EXISTS (
  73 +-- SELECT 1 FROM lq_kd_pxmx pxmx
  74 +-- INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id
  75 +-- WHERE pxmx.glkdbh = kdjlb.F_Id
  76 +-- AND pxmx.F_IsEffective = 1
  77 +-- AND xmzl.qt2 = '科美'
  78 +-- ) THEN '是'
  79 +-- ELSE '否'
  80 +-- END,
  81 +-- F_UpgradeMedicalBeauty = CASE
  82 +-- WHEN EXISTS (
  83 +-- SELECT 1 FROM lq_kd_kdjlb kd2
  84 +-- WHERE kd2.kdhy = kdjlb.kdhy
  85 +-- AND kd2.F_Id != kdjlb.F_Id
  86 +-- AND kd2.kdrq < kdjlb.kdrq
  87 +-- ) AND EXISTS (
  88 +-- SELECT 1 FROM lq_kd_pxmx pxmx
  89 +-- INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id
  90 +-- WHERE pxmx.glkdbh = kdjlb.F_Id
  91 +-- AND pxmx.F_IsEffective = 1
  92 +-- AND xmzl.qt2 = '医美'
  93 +-- ) THEN '是'
  94 +-- ELSE '否'
  95 +-- END
  96 +-- WHERE kdjlb.F_IsEffective = 1;
  97 +
... ...
sql/添加转化率统计关联字段.sql 0 → 100644
  1 +-- ============================================
  2 +-- 为转化率统计添加关联字段
  3 +-- ============================================
  4 +-- 说明:此脚本为转化率统计功能添加关联字段,用于追踪邀约→预约→开单/耗卡的完整业务链路
  5 +-- 执行顺序:按照业务链路顺序依次执行
  6 +--
  7 +-- 业务链路:
  8 +-- 邀约记录 (lq_yaoyjl) → 预约记录 (lq_yyjl) → 开单记录 (lq_kd_kdjlb) / 耗卡记录 (lq_xh_hyhk)
  9 +--
  10 +-- 字段说明:
  11 +-- 1. lq_yyjl.F_InviteId:关联邀约记录ID,用于统计邀约→预约转化率
  12 +-- 2. lq_kd_kdjlb.F_AppointmentId:关联预约记录ID,用于统计预约→开单转化率
  13 +-- 3. lq_xh_hyhk.F_AppointmentId:关联预约记录ID,用于统计预约→耗卡转化率
  14 +--
  15 +-- 注意事项:
  16 +-- - 所有字段允许为NULL,因为历史数据可能没有这些关联
  17 +-- - 字段类型为VARCHAR(50),与其他ID字段保持一致
  18 +-- - 已为所有字段创建索引,以优化查询性能
  19 +
  20 +-- ============================================
  21 +-- 1. lq_yyjl(预约记录表) - 添加邀约ID字段和未成交说明字段
  22 +-- ============================================
  23 +-- 说明:关联邀约记录,用于统计邀约→预约转化率
  24 +ALTER TABLE lq_yyjl
  25 +ADD COLUMN F_InviteId VARCHAR(50) NULL COMMENT '邀约记录ID(关联lq_yaoyjl.F_Id,用于统计邀约→预约转化率)' AFTER F_Status,
  26 +ADD COLUMN F_NoDealRemark VARCHAR(2000) NULL COMMENT '未成交说明' AFTER F_InviteId;
  27 +
  28 +-- 创建索引以优化查询性能
  29 +CREATE INDEX idx_yyjl_invite_id ON lq_yyjl(F_InviteId);
  30 +
  31 +-- ============================================
  32 +-- 2. lq_kd_kdjlb(开单记录表) - 添加预约ID字段
  33 +-- ============================================
  34 +-- 说明:关联预约记录,用于统计预约→开单转化率
  35 +ALTER TABLE lq_kd_kdjlb
  36 +ADD COLUMN F_AppointmentId VARCHAR(50) NULL COMMENT '预约记录ID(关联lq_yyjl.F_Id,用于统计预约→开单转化率)' AFTER F_ActivityId;
  37 +
  38 +-- 创建索引以优化查询性能
  39 +CREATE INDEX idx_kdjlb_appointment_id ON lq_kd_kdjlb(F_AppointmentId);
  40 +
  41 +-- ============================================
  42 +-- 3. lq_xh_hyhk(耗卡记录表) - 添加预约ID字段
  43 +-- ============================================
  44 +-- 说明:关联预约记录,用于统计预约→耗卡转化率
  45 +ALTER TABLE lq_xh_hyhk
  46 +ADD COLUMN F_AppointmentId VARCHAR(50) NULL COMMENT '预约记录ID(关联lq_yyjl.F_Id,用于统计预约→耗卡转化率)' AFTER F_OvertimeSgfy;
  47 +
  48 +-- 创建索引以优化查询性能
  49 +CREATE INDEX idx_hyhk_appointment_id ON lq_xh_hyhk(F_AppointmentId);
  50 +
  51 +-- ============================================
  52 +-- 4. 验证字段创建
  53 +-- ============================================
  54 +-- 验证 lq_yyjl 表
  55 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT
  56 +-- FROM INFORMATION_SCHEMA.COLUMNS
  57 +-- WHERE TABLE_NAME = 'lq_yyjl'
  58 +-- AND (COLUMN_NAME = 'F_InviteId' OR COLUMN_NAME = 'F_NoDealRemark');
  59 +
  60 +-- 验证 lq_kd_kdjlb 表
  61 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT
  62 +-- FROM INFORMATION_SCHEMA.COLUMNS
  63 +-- WHERE TABLE_NAME = 'lq_kd_kdjlb'
  64 +-- AND COLUMN_NAME = 'F_AppointmentId';
  65 +
  66 +-- 验证 lq_xh_hyhk 表
  67 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT
  68 +-- FROM INFORMATION_SCHEMA.COLUMNS
  69 +-- WHERE TABLE_NAME = 'lq_xh_hyhk'
  70 +-- AND COLUMN_NAME = 'F_AppointmentId';
  71 +
  72 +-- ============================================
  73 +-- 5. 验证索引创建
  74 +-- ============================================
  75 +-- 验证索引
  76 +-- SHOW INDEX FROM lq_yyjl WHERE Key_name = 'idx_yyjl_invite_id';
  77 +-- SHOW INDEX FROM lq_kd_kdjlb WHERE Key_name = 'idx_kdjlb_appointment_id';
  78 +-- SHOW INDEX FROM lq_xh_hyhk WHERE Key_name = 'idx_hyhk_appointment_id';
  79 +
  80 +-- ============================================
  81 +-- 6. 转化率统计示例SQL(供参考)
  82 +-- ============================================
  83 +-- 示例1:邀约→预约转化率
  84 +-- SELECT
  85 +-- COUNT(DISTINCT yyjl.F_Id) AS 预约数量,
  86 +-- COUNT(DISTINCT yyjl.F_InviteId) AS 来自邀约的预约数量,
  87 +-- COUNT(DISTINCT yyjl.F_InviteId) * 100.0 / COUNT(DISTINCT yyjl.F_Id) AS 转化率百分比
  88 +-- FROM lq_yyjl yyjl
  89 +-- WHERE yyjl.F_CreateTime >= '2024-01-01';
  90 +
  91 +-- 示例2:预约→开单转化率
  92 +-- SELECT
  93 +-- COUNT(DISTINCT yyjl.F_Id) AS 预约数量,
  94 +-- COUNT(DISTINCT kdjlb.F_Id) AS 开单数量,
  95 +-- COUNT(DISTINCT kdjlb.F_Id) * 100.0 / COUNT(DISTINCT yyjl.F_Id) AS 转化率百分比
  96 +-- FROM lq_yyjl yyjl
  97 +-- LEFT JOIN lq_kd_kdjlb kdjlb ON kdjlb.F_AppointmentId = yyjl.F_Id AND kdjlb.F_IsEffective = 1
  98 +-- WHERE yyjl.F_CreateTime >= '2024-01-01';
  99 +
  100 +-- 示例3:预约→耗卡转化率
  101 +-- SELECT
  102 +-- COUNT(DISTINCT yyjl.F_Id) AS 预约数量,
  103 +-- COUNT(DISTINCT hyhk.F_Id) AS 耗卡数量,
  104 +-- COUNT(DISTINCT hyhk.F_Id) * 100.0 / COUNT(DISTINCT yyjl.F_Id) AS 转化率百分比
  105 +-- FROM lq_yyjl yyjl
  106 +-- LEFT JOIN lq_xh_hyhk hyhk ON hyhk.F_AppointmentId = yyjl.F_Id AND hyhk.F_IsEffective = 1
  107 +-- WHERE yyjl.F_CreateTime >= '2024-01-01';
  108 +
  109 +-- 示例4:邀约→开单转化率(跨步转化)
  110 +-- SELECT
  111 +-- COUNT(DISTINCT yaoyjl.F_Id) AS 邀约数量,
  112 +-- COUNT(DISTINCT kdjlb.F_Id) AS 开单数量,
  113 +-- COUNT(DISTINCT kdjlb.F_Id) * 100.0 / COUNT(DISTINCT yaoyjl.F_Id) AS 转化率百分比
  114 +-- FROM lq_yaoyjl yaoyjl
  115 +-- LEFT JOIN lq_yyjl yyjl ON yyjl.F_InviteId = yaoyjl.F_Id
  116 +-- LEFT JOIN lq_kd_kdjlb kdjlb ON kdjlb.F_AppointmentId = yyjl.F_Id AND kdjlb.F_IsEffective = 1
  117 +-- WHERE yaoyjl.F_CreateTime >= '2024-01-01';
  118 +
... ...