From 72cec4b58974d5a803cc5ec716077addda87f53c Mon Sep 17 00:00:00 2001 From: “wangming” <“wangming@antissoft.com”> Date: Mon, 17 Nov 2025 18:14:58 +0800 Subject: [PATCH] feat: 添加转化率统计和升单类型功能 --- netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbCrInput.cs | 5 +++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbInfoOutput.cs | 10 ++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbListOutput.cs | 9 +++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlCrInput.cs | 5 +++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlListOutput.cs | 5 +++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlCrInput.cs | 5 +++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlInfoOutput.cs | 14 ++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlListOutput.cs | 15 +++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kdjlb/LqKdKdjlbEntity.cs | 24 ++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_hyhk/LqXhHyhkEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_yyjl/LqYyjlEntity.cs | 12 ++++++++++++ netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs | 44 ++++++++++++++------------------------------ netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend/LqReimbursementApplicationService.cs | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------- netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs | 2 +- netcore/src/Modularity/Extend/NCC.Extend/LqYaoyjlService.cs | 2 ++ netcore/src/Modularity/Extend/NCC.Extend/LqYyjlService.cs | 41 ++++++++++++++++++++++++++++++++++++++++- sql/查询邀约时间和联系时间不在同一天的记录.sql | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/添加开单记录表升单类型字段.sql | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/添加转化率统计关联字段.sql | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 20 files changed, 599 insertions(+), 90 deletions(-) create mode 100644 sql/查询邀约时间和联系时间不在同一天的记录.sql create mode 100644 sql/添加开单记录表升单类型字段.sql create mode 100644 sql/添加转化率统计关联字段.sql diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbCrInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbCrInput.cs index 431f4bc..79db752 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbCrInput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbCrInput.cs @@ -146,6 +146,11 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb public decimal supplementAmount { get; set; } /// + /// 预约记录ID + /// + public string appointmentId { get; set; } + + /// /// 扣款信息 /// public List lqKdKdjlbDeductList { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbInfoOutput.cs index 498657a..53c5625 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbInfoOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbInfoOutput.cs @@ -173,6 +173,16 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb public string activityName { get; set; } /// + /// 预约记录ID + /// + public string appointmentId { get; set; } + + /// + /// 预约记录时间 + /// + public DateTime? appointmentTime { get; set; } + + /// /// 健康师业绩 /// public List lqKdJksyjList { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbListOutput.cs index 6ab7a25..46f5eff 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbListOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/LqKdKdjlbListOutput.cs @@ -167,6 +167,15 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb public string createUserName { get; set; } /// + /// 预约记录ID + /// + public string appointmentId { get; set; } + /// + /// 预约记录时间 + /// + public DateTime? appointmentTime { get; set; } + + /// /// 开单品项明细列表 /// public List ItemDetails { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlCrInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlCrInput.cs index 7455f34..9789687 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlCrInput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlCrInput.cs @@ -48,5 +48,10 @@ namespace NCC.Extend.Entitys.Dto.LqYaoyjl /// public string lxjl { get; set; } + /// + /// 拓客编号 + /// + public string tkbh { get; set; } + } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlListOutput.cs index 4f9d354..af0549c 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlListOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYaoyjl/LqYaoyjlListOutput.cs @@ -53,6 +53,11 @@ namespace NCC.Extend.Entitys.Dto.LqYaoyjl public string lxjl { get; set; } /// + /// 拓客编号 + /// + public string tkbh { get; set; } + + /// /// 所属门店ID /// public string storeId { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlCrInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlCrInput.cs index b060522..83ed150 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlCrInput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlCrInput.cs @@ -73,5 +73,10 @@ namespace NCC.Extend.Entitys.Dto.LqYyjl /// public string F_Status { get; set; } + /// + /// 邀约记录ID + /// + public string InviteId { get; set; } + } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlInfoOutput.cs index 843a62d..b98398d 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlInfoOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlInfoOutput.cs @@ -88,5 +88,19 @@ namespace NCC.Extend.Entitys.Dto.LqYyjl /// public string F_Status { get; set; } + /// + /// 未成交说明 + /// + public string NoDealRemark { get; set; } + + /// + /// 邀约记录ID + /// + public string InviteId { get; set; } + + /// + /// 邀约记录时间 + /// + public DateTime? InviteTime { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlListOutput.cs index 7b2369d..6eaf1d2 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlListOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqYyjl/LqYyjlListOutput.cs @@ -87,5 +87,20 @@ namespace NCC.Extend.Entitys.Dto.LqYyjl /// public string F_Status { get; set; } + /// + /// 未成交说明 + /// + public string NoDealRemark { get; set; } + + /// + /// 邀约记录ID + /// + public string InviteId { get; set; } + + /// + /// 邀约记录时间 + /// + public DateTime? InviteTime { get; set; } + } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kdjlb/LqKdKdjlbEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kdjlb/LqKdKdjlbEntity.cs index cf18be8..5f21321 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kdjlb/LqKdKdjlbEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kdjlb/LqKdKdjlbEntity.cs @@ -207,6 +207,12 @@ namespace NCC.Extend.Entitys.lq_kd_kdjlb public string ActivityId { get; set; } /// + /// 预约记录ID(关联lq_yyjl.F_Id,用于统计预约→开单转化率) + /// + [SugarColumn(ColumnName = "F_AppointmentId")] + public string AppointmentId { get; set; } + + /// /// 已缴欠款 /// [SugarColumn(ColumnName = "F_PaidDebt")] @@ -223,5 +229,23 @@ namespace NCC.Extend.Entitys.lq_kd_kdjlb /// [SugarColumn(ColumnName = "F_SupplementAmount")] public decimal SupplementAmount { get; set; } + + /// + /// 升生美(再次开单时,包含生美品项的订单) + /// + [SugarColumn(ColumnName = "F_UpgradeLifeBeauty")] + public string UpgradeLifeBeauty { get; set; } + + /// + /// 升科美(再次开单时,包含科美品项的订单) + /// + [SugarColumn(ColumnName = "F_UpgradeTechBeauty")] + public string UpgradeTechBeauty { get; set; } + + /// + /// 升医美(再次开单时,包含医美品项的订单) + /// + [SugarColumn(ColumnName = "F_UpgradeMedicalBeauty")] + public string UpgradeMedicalBeauty { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_hyhk/LqXhHyhkEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_hyhk/LqXhHyhkEntity.cs index 8c5ab50..d6e383c 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_hyhk/LqXhHyhkEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_hyhk/LqXhHyhkEntity.cs @@ -144,5 +144,11 @@ namespace NCC.Extend.Entitys.lq_xh_hyhk /// [SugarColumn(ColumnName = "F_OvertimeSgfy")] public decimal? OvertimeSgfy { get; set; } + + /// + /// 预约记录ID(关联lq_yyjl.F_Id,用于统计预约→耗卡转化率) + /// + [SugarColumn(ColumnName = "F_AppointmentId")] + public string AppointmentId { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_yyjl/LqYyjlEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_yyjl/LqYyjlEntity.cs index 576621c..122c638 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_yyjl/LqYyjlEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_yyjl/LqYyjlEntity.cs @@ -90,6 +90,18 @@ namespace NCC.Extend.Entitys.lq_yyjl public string F_Status { get; set; } /// + /// 邀约记录ID(关联lq_yaoyjl.F_Id,用于统计邀约→预约转化率) + /// + [SugarColumn(ColumnName = "F_InviteId")] + public string InviteId { get; set; } + + /// + /// 未成交说明 + /// + [SugarColumn(ColumnName = "F_NoDealRemark")] + public string NoDealRemark { get; set; } + + /// /// 创建时间 /// [SugarColumn(ColumnName = "F_CreateTime")] diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs index b8dff6a..8e165ec 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs @@ -46,6 +46,7 @@ using NCC.Extend.Entitys.lq_package_info; using NCC.Extend.Entitys.lq_mdxx; using NCC.Extend.Entitys.lq_card_transfer_log; using NCC.Extend.Entitys.Dto.LqDailyReport; +using NCC.Extend.Entitys.lq_yyjl; namespace NCC.Extend.LqKdKdjlb { @@ -124,8 +125,11 @@ namespace NCC.Extend.LqKdKdjlb { throw NCCException.Oh(ErrorCode.COM1005, "开单记录不存在"); } - var output = entity.Adapt(); + if (!string.IsNullOrEmpty(entity.AppointmentId)) + { + output.appointmentTime = await _db.Queryable().Where(x => x.Id == entity.AppointmentId).Select(x => x.Yysj).FirstAsync(); + } if (output.activityId != null) { output.activityName = await _db.Queryable().Where(x => x.Id == entity.ActivityId).Select(x => x.ActivityName).FirstAsync(); @@ -317,6 +321,8 @@ namespace NCC.Extend.LqKdKdjlb createUserName = SqlFunc.Subqueryable().Where(x => x.Id == it.CreateUser).Select(x => x.RealName), activityId = it.ActivityId, activityName = SqlFunc.Subqueryable().Where(x => x.Id == it.ActivityId).Select(x => x.ActivityName), + appointmentId = it.AppointmentId, + appointmentTime = it.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable().Where(x => x.Id == it.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)), }) .MergeTable() .OrderBy(sidx + " " + input.sort) @@ -516,6 +522,8 @@ namespace NCC.Extend.LqKdKdjlb createUserName = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.CreateUser).Select(x => x.RealName), activityId = kdjlb.ActivityId, activityName = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.ActivityId).Select(x => x.ActivityName), + appointmentId = kdjlb.AppointmentId, + appointmentTime = kdjlb.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)), }) .MergeTable() .Distinct() // 去重,因为一个开单可能对应多个健康师业绩记录 @@ -670,6 +678,8 @@ namespace NCC.Extend.LqKdKdjlb createUserName = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.CreateUser).Select(x => x.RealName), activityId = kdjlb.ActivityId, activityName = SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.ActivityId).Select(x => x.ActivityName), + appointmentId = kdjlb.AppointmentId, + appointmentTime = kdjlb.AppointmentId == null ? (DateTime?)null : SqlFunc.Subqueryable().Where(x => x.Id == kdjlb.AppointmentId).Select(x => SqlFunc.ToDate(x.Yysj)), }) .MergeTable() .Distinct() // 去重,因为一个开单可能对应多个科技部老师业绩记录 @@ -747,35 +757,6 @@ namespace NCC.Extend.LqKdKdjlb { //开启事务 _db.BeginTran(); - //判断是否有作废关联id - //暂时先不需要 - // if (!string.IsNullOrEmpty(input.cancelRefId)) - // { - // //查询作废关联id - // var cancelRefEntity = await _db.Queryable().FirstAsync(p => p.Id == input.cancelRefId); - // if (cancelRefEntity == null || cancelRefEntity.IsEffective == StatusEnum.无效.GetHashCode()) - // { - // throw NCCException.Oh("该开单记录已经作废"); - // } - // // 检查作废关联记录是否可以作废 - // var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(input.cancelRefId); - // if (!canCancel) - // { - // throw NCCException.Oh(errorMessage); - // } - - // //将作废关联id的IsEffective设置为无效 - // cancelRefEntity.IsEffective = StatusEnum.无效.GetHashCode(); - // await _db.Updateable(cancelRefEntity).ExecuteCommandAsync(); - // //把品项明细表的IsEffective设置为无效 - // await _db.Updateable().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdPxmxEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync(); - // //把健康师业绩表的IsEffective设置为无效 - // await _db.Updateable().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdJksyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync(); - // //把科技部老师业绩表的IsEffective设置为无效 - // await _db.Updateable().Where(p => p.Glkdbh == input.cancelRefId).UpdateColumns(p => new LqKdKjbsyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync(); - // //把扣款信息表的IsEffective设置为无效 - // await _db.Updateable().Where(p => p.BillingId == input.cancelRefId).UpdateColumns(p => new LqKdDeductinfoEntity { IsEffective = StatusEnum.无效.GetHashCode() }).ExecuteCommandAsync(); - // } //判断是否有补缴开单ID if (!string.IsNullOrEmpty(input.supplementBillingId)) @@ -796,6 +777,8 @@ namespace NCC.Extend.LqKdKdjlb //新增开单记录表记录 entity.CreateUser = userInfo.userId; entity.DeductAmount = input.lqKdKdjlbDeductList.Sum(x => x.Amount ?? 0);//计算储扣总金额 + + //保存开单记录 var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); //循环品相信息 // 收集所有需要插入的实体,然后批量插入 @@ -2113,6 +2096,7 @@ namespace NCC.Extend.LqKdKdjlb isEffective = it.IsEffective, createUser = it.CreateUser, createUserName = SqlFunc.Subqueryable().Where(x => x.Id == it.CreateUser).Select(x => x.RealName), + appointmentId = it.AppointmentId, }) .MergeTable() .OrderBy(sidx + " " + input.sort) diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs index 8f431f7..238389a 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs @@ -28,6 +28,7 @@ using NCC.Extend.Entitys.lq_khxx; using NCC.Extend.Entitys.lq_mdxx; using NCC.Extend.Entitys.lq_xh_hyhk; using NCC.Extend.Entitys.lq_xh_pxmx; +using NCC.Extend.Entitys.lq_xmzl; using NCC.Extend.Interfaces.LqKhxx; using NCC.System.Entitys.Permission; using NCC.FriendlyException; @@ -366,6 +367,89 @@ namespace NCC.Extend.LqKhxx } #endregion + #region 判断客户是否开过指定类型的开单 + /// + /// 判断客户是否开过生美的开单 + /// + /// 会员ID + /// 1表示有,0表示没有 + [HttpGet("has-life-beauty-billing/{memberId}")] + public async Task HasLifeBeautyBilling(string memberId) + { + if (string.IsNullOrEmpty(memberId)) + { + throw NCCException.Oh("会员ID不能为空"); + } + + // 查询该会员是否有开过生美品项的开单 + var hasBilling = await _db.Queryable() + .InnerJoin((kdjlb, pxmx) => kdjlb.Id == pxmx.Glkdbh) + .InnerJoin((kdjlb, pxmx, xmzl) => pxmx.Px == xmzl.Id) + .Where((kdjlb, pxmx, xmzl) => kdjlb.Kdhy == memberId + && kdjlb.IsEffective == StatusEnum.有效.GetHashCode() + && pxmx.IsEffective == StatusEnum.有效.GetHashCode() + && xmzl.IsEffective == StatusEnum.有效.GetHashCode() + && xmzl.Qt2 == "生美") + .AnyAsync(); + + return hasBilling ? 1 : 0; + } + + /// + /// 判断客户是否开过科美的开单 + /// + /// 会员ID + /// 1表示有,0表示没有 + [HttpGet("has-tech-beauty-billing/{memberId}")] + public async Task HasTechBeautyBilling(string memberId) + { + if (string.IsNullOrEmpty(memberId)) + { + throw NCCException.Oh("会员ID不能为空"); + } + + // 查询该会员是否有开过科美品项的开单 + var hasBilling = await _db.Queryable() + .InnerJoin((kdjlb, pxmx) => kdjlb.Id == pxmx.Glkdbh) + .InnerJoin((kdjlb, pxmx, xmzl) => pxmx.Px == xmzl.Id) + .Where((kdjlb, pxmx, xmzl) => kdjlb.Kdhy == memberId + && kdjlb.IsEffective == StatusEnum.有效.GetHashCode() + && pxmx.IsEffective == StatusEnum.有效.GetHashCode() + && xmzl.IsEffective == StatusEnum.有效.GetHashCode() + && xmzl.Qt2 == "科美") + .AnyAsync(); + + return hasBilling ? 1 : 0; + } + + /// + /// 判断客户是否开过医美的开单 + /// + /// 会员ID + /// 1表示有,0表示没有 + [HttpGet("has-medical-beauty-billing/{memberId}")] + public async Task HasMedicalBeautyBilling(string memberId) + { + if (string.IsNullOrEmpty(memberId)) + { + throw NCCException.Oh("会员ID不能为空"); + } + + // 查询该会员是否有开过医美品项的开单 + var hasBilling = await _db.Queryable() + .InnerJoin((kdjlb, pxmx) => kdjlb.Id == pxmx.Glkdbh) + .InnerJoin((kdjlb, pxmx, xmzl) => pxmx.Px == xmzl.Id) + .Where((kdjlb, pxmx, xmzl) => kdjlb.Kdhy == memberId + && kdjlb.IsEffective == StatusEnum.有效.GetHashCode() + && pxmx.IsEffective == StatusEnum.有效.GetHashCode() + && xmzl.IsEffective == StatusEnum.有效.GetHashCode() + && xmzl.Qt2 == "医美") + .AnyAsync(); + + return hasBilling ? 1 : 0; + } + #endregion + #region 导出客户资料 /// /// 导出客户资料 diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqReimbursementApplicationService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqReimbursementApplicationService.cs index 7f01ba1..8738a6b 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqReimbursementApplicationService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqReimbursementApplicationService.cs @@ -28,7 +28,7 @@ namespace NCC.Extend.LqReimbursementApplication /// /// 报销申请表服务 /// - [ApiDescriptionSettings(Tag = "Extend",Name = "LqReimbursementApplication", Order = 200)] + [ApiDescriptionSettings(Tag = "Extend", Name = "LqReimbursementApplication", Order = 200)] [Route("api/Extend/[controller]")] public class LqReimbursementApplicationService : ILqReimbursementApplicationService, IDynamicApiController, ITransient { @@ -43,7 +43,7 @@ namespace NCC.Extend.LqReimbursementApplication ISqlSugarRepository lqReimbursementApplicationRepository, IUserManager userManager) { - _lqReimbursementApplicationRepository = lqReimbursementApplicationRepository; + _lqReimbursementApplicationRepository = lqReimbursementApplicationRepository; _db = _lqReimbursementApplicationRepository.Context; _userManager = userManager; } @@ -86,23 +86,23 @@ namespace NCC.Extend.LqReimbursementApplication .WhereIF(!string.IsNullOrEmpty(input.amount), p => p.Amount.Contains(input.amount)) .WhereIF(!string.IsNullOrEmpty(input.approveUser), p => p.ApproveUser.Equals(input.approveUser)) .WhereIF(!string.IsNullOrEmpty(input.approveStatus), p => p.ApproveStatus.Contains(input.approveStatus)) - // .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0)) + // .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0)) //.WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59)) .WhereIF(!string.IsNullOrEmpty(input.purchaseRecordsId), p => p.PurchaseRecordsId.Contains(input.purchaseRecordsId)) - .Select(it=> new LqReimbursementApplicationListOutput + .Select(it => new LqReimbursementApplicationListOutput { id = it.Id, - applicationUserId=it.ApplicationUserId, - applicationUserName=it.ApplicationUserName, - applicationStoreId=it.ApplicationStoreId, - applicationTime=it.ApplicationTime, - amount=it.Amount, - approveUser=it.ApproveUser, - approveStatus=it.ApproveStatus, - approveTime=it.ApproveTime, - purchaseRecordsId=it.PurchaseRecordsId, - }).MergeTable().OrderBy(sidx+" "+input.sort).ToPagedListAsync(input.currentPage, input.pageSize); - return PageResult.SqlSugarPageResult(data); + applicationUserId = it.ApplicationUserId, + applicationUserName = it.ApplicationUserName, + applicationStoreId = it.ApplicationStoreId, + applicationTime = it.ApplicationTime, + amount = it.Amount, + approveUser = it.ApproveUser, + approveStatus = it.ApproveStatus, + approveTime = it.ApproveTime, + purchaseRecordsId = it.PurchaseRecordsId, + }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); + return PageResult.SqlSugarPageResult(data); } /// @@ -116,16 +116,16 @@ namespace NCC.Extend.LqReimbursementApplication var userInfo = await _userManager.GetUserInfo(); var entity = input.Adapt(); entity.Id = YitIdHelper.NextId().ToString(); - + try { //开启事务 _db.BeginTran(); - + // 保存报销申请表 var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); - + // 更新购买记录的审批单编号和审批状态为"待审批" if (input.selectedPurchaseRecordIds != null && input.selectedPurchaseRecordIds.Count > 0) { @@ -134,14 +134,14 @@ namespace NCC.Extend.LqReimbursementApplication .SetColumns(it => it.ApplicationId == entity.Id) .Where(it => input.selectedPurchaseRecordIds.Contains(it.Id)) .ExecuteCommandAsync(); - + // 再更新ApproveStatus(分开更新确保都能执行) await _db.Updateable() .SetColumns(it => it.ApproveStatus == "待审批") .Where(it => input.selectedPurchaseRecordIds.Contains(it.Id)) .ExecuteCommandAsync(); } - + //关闭事务 _db.CommitTran(); } @@ -178,23 +178,23 @@ namespace NCC.Extend.LqReimbursementApplication .WhereIF(!string.IsNullOrEmpty(input.amount), p => p.Amount.Contains(input.amount)) .WhereIF(!string.IsNullOrEmpty(input.approveUser), p => p.ApproveUser.Equals(input.approveUser)) .WhereIF(!string.IsNullOrEmpty(input.approveStatus), p => p.ApproveStatus.Contains(input.approveStatus)) - // .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0)) - // .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59)) + // .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0)) + // .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59)) .WhereIF(!string.IsNullOrEmpty(input.purchaseRecordsId), p => p.PurchaseRecordsId.Contains(input.purchaseRecordsId)) - .Select(it=> new LqReimbursementApplicationListOutput + .Select(it => new LqReimbursementApplicationListOutput { id = it.Id, - applicationUserId=it.ApplicationUserId, - applicationUserName=it.ApplicationUserName, - applicationStoreId=it.ApplicationStoreId, - applicationTime=it.ApplicationTime, - amount=it.Amount, - approveUser=it.ApproveUser, - approveStatus=it.ApproveStatus, - approveTime=it.ApproveTime, - purchaseRecordsId=it.PurchaseRecordsId, - }).MergeTable().OrderBy(sidx+" "+input.sort).ToListAsync(); - return data; + applicationUserId = it.ApplicationUserId, + applicationUserName = it.ApplicationUserName, + applicationStoreId = it.ApplicationStoreId, + applicationTime = it.ApplicationTime, + amount = it.Amount, + approveUser = it.ApproveUser, + approveStatus = it.ApproveStatus, + approveTime = it.ApproveTime, + purchaseRecordsId = it.PurchaseRecordsId, + }).MergeTable().OrderBy(sidx + " " + input.sort).ToListAsync(); + return data; } /// @@ -216,7 +216,7 @@ namespace NCC.Extend.LqReimbursementApplication { exportData = await this.GetNoPagingList(input); } - List 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(); + List 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(); ExcelConfig excelconfig = new ExcelConfig(); excelconfig.FileName = "报销申请表.xls"; excelconfig.HeadFont = "微软雅黑"; @@ -259,7 +259,7 @@ namespace NCC.Extend.LqReimbursementApplication //开启事务 _db.BeginTran(); //批量删除报销申请表 - await _db.Deleteable().In(d => d.Id,ids).ExecuteCommandAsync(); + await _db.Deleteable().In(d => d.Id, ids).ExecuteCommandAsync(); //关闭事务 _db.CommitTran(); } @@ -285,7 +285,7 @@ namespace NCC.Extend.LqReimbursementApplication { //开启事务 _db.BeginTran(); - + // 获取原有的关联购买记录ID var oldEntity = await _db.Queryable().FirstAsync(p => p.Id == id); var oldIds = new List(); @@ -294,10 +294,10 @@ namespace NCC.Extend.LqReimbursementApplication // 获取原有购买记录ID列表 oldIds = oldEntity.PurchaseRecordsId.Split(',').Where(x => !string.IsNullOrEmpty(x)).ToList(); } - + // 获取新的购买记录ID列表 var newIds = input.selectedPurchaseRecordIds ?? new List(); - + // 确保 purchaseRecordsId 字段包含所有选中的记录ID(逗号分隔) if (newIds.Count > 0) { @@ -307,27 +307,27 @@ namespace NCC.Extend.LqReimbursementApplication { input.purchaseRecordsId = null; } - + // 找出需要移除关联的记录(在旧列表中但不在新列表中) var idsToRemove = oldIds.Where(x => !newIds.Contains(x)).ToList(); if (idsToRemove.Count > 0) { // 清除这些购买记录的审批单编号和审批状态 await _db.Updateable() - .SetColumns(it => new LqPurchaseRecordsEntity - { + .SetColumns(it => new LqPurchaseRecordsEntity + { ApplicationId = null, ApproveStatus = "未审批" }) .Where(it => idsToRemove.Contains(it.Id)) .ExecuteCommandAsync(); } - + // 更新报销申请表(确保 purchaseRecordsId 字段被正确更新) var entity = input.Adapt(); var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001); - + // 更新所有选中的购买记录的审批单编号和审批状态为"待审批" // 包括新追加的记录和已存在的记录(确保状态正确) if (newIds.Count > 0) @@ -337,14 +337,14 @@ namespace NCC.Extend.LqReimbursementApplication .SetColumns(it => it.ApplicationId == id) .Where(it => newIds.Contains(it.Id)) .ExecuteCommandAsync(); - + // 再更新ApproveStatus(分开更新确保都能执行) await _db.Updateable() .SetColumns(it => it.ApproveStatus == "待审批") .Where(it => newIds.Contains(it.Id)) .ExecuteCommandAsync(); } - + //关闭事务 _db.CommitTran(); } @@ -380,18 +380,18 @@ namespace NCC.Extend.LqReimbursementApplication var userInfo = await _userManager.GetUserInfo(); var entity = await _db.Queryable().FirstAsync(p => p.Id == id); _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); - + try { //开启事务 _db.BeginTran(); - + // 更新申请表的审批状态 entity.ApproveStatus = "已审批"; entity.ApproveTime = DateTime.Now; entity.ApproveUser = userInfo.userId; await _db.Updateable(entity).ExecuteCommandAsync(); - + // 更新关联的购买记录 if (!string.IsNullOrEmpty(entity.PurchaseRecordsId)) { @@ -399,8 +399,8 @@ namespace NCC.Extend.LqReimbursementApplication if (purchaseIds.Count > 0) { await _db.Updateable() - .SetColumns(it => new LqPurchaseRecordsEntity - { + .SetColumns(it => new LqPurchaseRecordsEntity + { ApproveStatus = "已审批", ApproveTime = DateTime.Now, ApproveUser = userInfo.userId @@ -409,7 +409,7 @@ namespace NCC.Extend.LqReimbursementApplication .ExecuteCommandAsync(); } } - + //关闭事务 _db.CommitTran(); } @@ -432,18 +432,18 @@ namespace NCC.Extend.LqReimbursementApplication var userInfo = await _userManager.GetUserInfo(); var entity = await _db.Queryable().FirstAsync(p => p.Id == id); _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); - + try { //开启事务 _db.BeginTran(); - + // 更新申请表的审批状态 entity.ApproveStatus = "未通过"; entity.ApproveTime = DateTime.Now; entity.ApproveUser = userInfo.userId; await _db.Updateable(entity).ExecuteCommandAsync(); - + // 更新关联的购买记录 if (!string.IsNullOrEmpty(entity.PurchaseRecordsId)) { @@ -451,8 +451,8 @@ namespace NCC.Extend.LqReimbursementApplication if (purchaseIds.Count > 0) { await _db.Updateable() - .SetColumns(it => new LqPurchaseRecordsEntity - { + .SetColumns(it => new LqPurchaseRecordsEntity + { ApproveStatus = "未通过", ApproveTime = DateTime.Now, ApproveUser = userInfo.userId @@ -461,7 +461,7 @@ namespace NCC.Extend.LqReimbursementApplication .ExecuteCommandAsync(); } } - + //关闭事务 _db.CommitTran(); } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs index bdf54a4..d43be6e 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs @@ -1127,7 +1127,7 @@ namespace NCC.Extend.LqTkjlb FROM lq_tkjlb tk INNER JOIN lq_kd_kdjlb kd ON tk.F_MemberId = kd.kdhy AND kd.F_IsEffective = 1 - WHERE 1=1 {timeFilter} {eventFilter} {storeFilter}"; + WHERE 1=1 AND kd.sfyj > 0 {timeFilter} {eventFilter} {storeFilter}"; var kdResult = await _db.Ado.SqlQueryAsync(kdSql); var kdCount = Convert.ToInt32(kdResult?.FirstOrDefault()?.kd_count ?? 0); diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqYaoyjlService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqYaoyjlService.cs index dd1d95d..4fce802 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqYaoyjlService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqYaoyjlService.cs @@ -108,6 +108,7 @@ namespace NCC.Extend.LqYaoyjl storeName = SqlFunc.Subqueryable().Where(u => u.Id == it.StoreId).Select(u => u.Dm), createTime = it.CreateTime, yyrName = SqlFunc.Subqueryable().Where(u => u.Id == it.Yyr).Select(u => u.RealName), + tkbh = it.Tkbh, }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(data); } @@ -171,6 +172,7 @@ namespace NCC.Extend.LqYaoyjl lxsj = it.Lxsj, lxjl = it.Lxjl, yyrName = SqlFunc.Subqueryable().Where(u => u.MobilePhone == it.Yyr).Select(u => u.RealName), + tkbh = it.Tkbh, }).MergeTable().OrderBy(sidx + " " + input.sort).ToListAsync(); return data; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqYyjlService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqYyjlService.cs index 52087a6..916fa11 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqYyjlService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqYyjlService.cs @@ -17,6 +17,7 @@ using NCC.Dependency; using NCC.DynamicApiController; using NCC.Extend.Entitys.Dto.LqYyjl; using NCC.Extend.Entitys.lq_mdxx; +using NCC.Extend.Entitys.lq_yaoyjl; using NCC.Extend.Entitys.lq_yyjl; using NCC.Extend.Interfaces.LqYyjl; using NCC.FriendlyException; @@ -47,6 +48,7 @@ namespace NCC.Extend.LqYyjl _db = _lqYyjlRepository.Context; _userManager = userManager; } + #region 预约记录 /// /// 获取预约记录 @@ -80,6 +82,13 @@ namespace NCC.Extend.LqYyjl output.yyjksName = healthCoach?.RealName; } + //获取邀约记录 + if (!string.IsNullOrEmpty(entity.InviteId)) + { + var invite = await _db.Queryable().Where(u => u.Id == entity.InviteId).FirstAsync(); + output.InviteTime = invite?.Yysj; + } + return output; } #endregion @@ -138,6 +147,9 @@ namespace NCC.Extend.LqYyjl yyrName = SqlFunc.Subqueryable().Where(u => u.Id == it.Yyr).Select(u => u.RealName), yyjksName = SqlFunc.Subqueryable().Where(u => u.Id == it.Yyjks).Select(u => u.RealName), djmdName = SqlFunc.Subqueryable().Where(u => u.Id == it.Djmd).Select(u => u.Dm), + NoDealRemark = it.NoDealRemark, + InviteId = it.InviteId, + InviteTime = SqlFunc.Subqueryable().Where(u => u.Id == it.InviteId).Select(u => SqlFunc.ToDate(u.Yysj)), }) .MergeTable() .OrderBy(sidx + " " + input.sort) @@ -155,12 +167,20 @@ namespace NCC.Extend.LqYyjl [HttpPost("")] public async Task Create([FromBody] LqYyjlCrInput input) { - var userInfo = await _userManager.GetUserInfo(); var entity = input.Adapt(); entity.Id = YitIdHelper.NextId().ToString(); entity.Czr = _userManager.UserId; entity.Czsj = DateTime.Now; entity.CreateTime = DateTime.Now; + //判断邀约记录是否存在 + if (!string.IsNullOrEmpty(input.InviteId)) + { + var invite = await _db.Queryable().Where(u => u.Id == input.InviteId).FirstAsync(); + if (invite == null) + { + throw NCCException.Oh("邀约记录不存在"); + } + } var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); @@ -337,5 +357,24 @@ namespace NCC.Extend.LqYyjl throw NCCException.Oh(ErrorCode.COM1002); } #endregion + + #region 添加未成交说明 + /// + /// 添加未成交说明 + /// + /// 主键 + /// 参数 + /// + [HttpPost("AddNoDealRemark/{id}")] + public async Task AddNoDealRemark(string id, [FromBody] string noDealRemark) + { + var entity = await _db.Queryable().FirstAsync(p => p.Id == id); + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); + entity.NoDealRemark = noDealRemark; + var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); + if (!(isOk > 0)) + throw NCCException.Oh(ErrorCode.COM1001); + } + #endregion } } diff --git a/sql/查询邀约时间和联系时间不在同一天的记录.sql b/sql/查询邀约时间和联系时间不在同一天的记录.sql new file mode 100644 index 0000000..14892f7 --- /dev/null +++ b/sql/查询邀约时间和联系时间不在同一天的记录.sql @@ -0,0 +1,75 @@ +-- ============================================ +-- 查询 lq_yaoyjl 表中邀约时间(yysj)和联系时间(lxsj)不在同一天的记录 +-- ============================================ +-- 说明:查询邀约时间和联系时间不在同一天的数据,用于数据校验或分析 +-- +-- 查询逻辑: +-- 1. 两个时间都不为空,但日期不同 +-- 2. 其中一个为空,另一个不为空(视为不在同一天) +-- 3. 两个都为空的情况不包含在结果中(因为无法判断是否在同一天) + +-- ============================================ +-- 基础查询:查询不在同一天的记录 +-- ============================================ +SELECT + F_Id AS 邀约编号, + yyr AS 邀约人, + yysj AS 邀约时间, + DATE(yysj) AS 邀约日期, + lxsj AS 联系时间, + DATE(lxsj) AS 联系日期, + yykh AS 邀约客户, + yykhxm AS 邀约客户姓名, + F_CreateTime AS 创建时间 +FROM lq_yaoyjl +WHERE ( + -- 两个时间都不为空,但日期不同 + (yysj IS NOT NULL AND lxsj IS NOT NULL AND DATE(yysj) != DATE(lxsj)) + OR + -- 邀约时间为空,联系时间不为空 + (yysj IS NULL AND lxsj IS NOT NULL) + OR + -- 联系时间为空,邀约时间不为空 + (yysj IS NOT NULL AND lxsj IS NULL) +) +ORDER BY F_CreateTime DESC; + +-- ============================================ +-- 统计查询:统计不在同一天的记录数量 +-- ============================================ +SELECT + COUNT(*) AS 不在同一天的记录数, + COUNT(CASE WHEN yysj IS NOT NULL AND lxsj IS NOT NULL AND DATE(yysj) != DATE(lxsj) THEN 1 END) AS 两个时间都有但日期不同, + COUNT(CASE WHEN yysj IS NULL AND lxsj IS NOT NULL THEN 1 END) AS 邀约时间为空, + COUNT(CASE WHEN yysj IS NOT NULL AND lxsj IS NULL THEN 1 END) AS 联系时间为空 +FROM lq_yaoyjl +WHERE ( + (yysj IS NOT NULL AND lxsj IS NOT NULL AND DATE(yysj) != DATE(lxsj)) + OR (yysj IS NULL AND lxsj IS NOT NULL) + OR (yysj IS NOT NULL AND lxsj IS NULL) +); + +-- ============================================ +-- 详细分析查询:按日期差异分组统计 +-- ============================================ +SELECT + CASE + WHEN yysj IS NULL AND lxsj IS NOT NULL THEN '邀约时间为空' + WHEN yysj IS NOT NULL AND lxsj IS NULL THEN '联系时间为空' + WHEN yysj IS NOT NULL AND lxsj IS NOT NULL THEN CONCAT('相差', DATEDIFF(lxsj, yysj), '天') + END AS 日期差异类型, + COUNT(*) AS 记录数 +FROM lq_yaoyjl +WHERE ( + (yysj IS NOT NULL AND lxsj IS NOT NULL AND DATE(yysj) != DATE(lxsj)) + OR (yysj IS NULL AND lxsj IS NOT NULL) + OR (yysj IS NOT NULL AND lxsj IS NULL) +) +GROUP BY + CASE + WHEN yysj IS NULL AND lxsj IS NOT NULL THEN '邀约时间为空' + WHEN yysj IS NOT NULL AND lxsj IS NULL THEN '联系时间为空' + WHEN yysj IS NOT NULL AND lxsj IS NOT NULL THEN CONCAT('相差', DATEDIFF(lxsj, yysj), '天') + END +ORDER BY 记录数 DESC; + diff --git a/sql/添加开单记录表升单类型字段.sql b/sql/添加开单记录表升单类型字段.sql new file mode 100644 index 0000000..284fa86 --- /dev/null +++ b/sql/添加开单记录表升单类型字段.sql @@ -0,0 +1,97 @@ +-- ============================================ +-- 为开单记录表添加升单类型字段 +-- ============================================ +-- 说明:此脚本为开单记录表添加升单类型标记字段,用于标识再次开单时包含的品项类型 +-- +-- 字段说明: +-- 1. F_UpgradeLifeBeauty:升生美(再次开单时,包含生美品项的订单) +-- 2. F_UpgradeTechBeauty:升科美(再次开单时,包含科美品项的订单) +-- 3. F_UpgradeMedicalBeauty:升医美(再次开单时,包含医美品项的订单) +-- +-- 业务含义: +-- - 升单:已经开过会员再次消费属于升单 +-- - 这三个字段用于标记升单中是否包含对应类型的品项(生美、科美、医美) +-- +-- 注意事项: +-- - 字段类型为VARCHAR(10),可存储"是"/"否"或其他标记值 +-- - 所有字段允许为NULL,因为历史数据可能没有这些标记 +-- - 字段位置:放在 F_SupplementAmount 之后 + +-- ============================================ +-- 1. lq_kd_kdjlb(开单记录表) - 添加升单类型字段 +-- ============================================ +ALTER TABLE lq_kd_kdjlb +ADD COLUMN F_UpgradeLifeBeauty VARCHAR(10) NULL COMMENT '升生美(再次开单时,包含生美品项的订单)' AFTER F_SupplementAmount, +ADD COLUMN F_UpgradeTechBeauty VARCHAR(10) NULL COMMENT '升科美(再次开单时,包含科美品项的订单)' AFTER F_UpgradeLifeBeauty, +ADD COLUMN F_UpgradeMedicalBeauty VARCHAR(10) NULL COMMENT '升医美(再次开单时,包含医美品项的订单)' AFTER F_UpgradeTechBeauty; + +-- ============================================ +-- 2. 验证字段创建 +-- ============================================ +-- 验证 lq_kd_kdjlb 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_kd_kdjlb' +-- AND (COLUMN_NAME = 'F_UpgradeLifeBeauty' +-- OR COLUMN_NAME = 'F_UpgradeTechBeauty' +-- OR COLUMN_NAME = 'F_UpgradeMedicalBeauty'); + +-- ============================================ +-- 3. 业务逻辑说明(供参考) +-- ============================================ +-- 判断逻辑: +-- 1. 判断是否为升单:查询该会员(kdhy)是否有历史开单记录 +-- 2. 判断是否包含生美:查询该开单的品项明细(lq_kd_pxmx)中是否有品项类型(lq_xmzl.qt2)为"生美"的记录 +-- 3. 判断是否包含科美:查询该开单的品项明细中是否有品项类型为"科美"的记录 +-- 4. 判断是否包含医美:查询该开单的品项明细中是否有品项类型为"医美"的记录 +-- +-- 示例SQL(判断升单并标记品项类型): +-- UPDATE lq_kd_kdjlb kdjlb +-- SET +-- F_UpgradeLifeBeauty = CASE +-- WHEN EXISTS ( +-- SELECT 1 FROM lq_kd_kdjlb kd2 +-- WHERE kd2.kdhy = kdjlb.kdhy +-- AND kd2.F_Id != kdjlb.F_Id +-- AND kd2.kdrq < kdjlb.kdrq +-- ) AND EXISTS ( +-- SELECT 1 FROM lq_kd_pxmx pxmx +-- INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id +-- WHERE pxmx.glkdbh = kdjlb.F_Id +-- AND pxmx.F_IsEffective = 1 +-- AND xmzl.qt2 = '生美' +-- ) THEN '是' +-- ELSE '否' +-- END, +-- F_UpgradeTechBeauty = CASE +-- WHEN EXISTS ( +-- SELECT 1 FROM lq_kd_kdjlb kd2 +-- WHERE kd2.kdhy = kdjlb.kdhy +-- AND kd2.F_Id != kdjlb.F_Id +-- AND kd2.kdrq < kdjlb.kdrq +-- ) AND EXISTS ( +-- SELECT 1 FROM lq_kd_pxmx pxmx +-- INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id +-- WHERE pxmx.glkdbh = kdjlb.F_Id +-- AND pxmx.F_IsEffective = 1 +-- AND xmzl.qt2 = '科美' +-- ) THEN '是' +-- ELSE '否' +-- END, +-- F_UpgradeMedicalBeauty = CASE +-- WHEN EXISTS ( +-- SELECT 1 FROM lq_kd_kdjlb kd2 +-- WHERE kd2.kdhy = kdjlb.kdhy +-- AND kd2.F_Id != kdjlb.F_Id +-- AND kd2.kdrq < kdjlb.kdrq +-- ) AND EXISTS ( +-- SELECT 1 FROM lq_kd_pxmx pxmx +-- INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id +-- WHERE pxmx.glkdbh = kdjlb.F_Id +-- AND pxmx.F_IsEffective = 1 +-- AND xmzl.qt2 = '医美' +-- ) THEN '是' +-- ELSE '否' +-- END +-- WHERE kdjlb.F_IsEffective = 1; + diff --git a/sql/添加转化率统计关联字段.sql b/sql/添加转化率统计关联字段.sql new file mode 100644 index 0000000..a8547b1 --- /dev/null +++ b/sql/添加转化率统计关联字段.sql @@ -0,0 +1,118 @@ +-- ============================================ +-- 为转化率统计添加关联字段 +-- ============================================ +-- 说明:此脚本为转化率统计功能添加关联字段,用于追踪邀约→预约→开单/耗卡的完整业务链路 +-- 执行顺序:按照业务链路顺序依次执行 +-- +-- 业务链路: +-- 邀约记录 (lq_yaoyjl) → 预约记录 (lq_yyjl) → 开单记录 (lq_kd_kdjlb) / 耗卡记录 (lq_xh_hyhk) +-- +-- 字段说明: +-- 1. lq_yyjl.F_InviteId:关联邀约记录ID,用于统计邀约→预约转化率 +-- 2. lq_kd_kdjlb.F_AppointmentId:关联预约记录ID,用于统计预约→开单转化率 +-- 3. lq_xh_hyhk.F_AppointmentId:关联预约记录ID,用于统计预约→耗卡转化率 +-- +-- 注意事项: +-- - 所有字段允许为NULL,因为历史数据可能没有这些关联 +-- - 字段类型为VARCHAR(50),与其他ID字段保持一致 +-- - 已为所有字段创建索引,以优化查询性能 + +-- ============================================ +-- 1. lq_yyjl(预约记录表) - 添加邀约ID字段和未成交说明字段 +-- ============================================ +-- 说明:关联邀约记录,用于统计邀约→预约转化率 +ALTER TABLE lq_yyjl +ADD COLUMN F_InviteId VARCHAR(50) NULL COMMENT '邀约记录ID(关联lq_yaoyjl.F_Id,用于统计邀约→预约转化率)' AFTER F_Status, +ADD COLUMN F_NoDealRemark VARCHAR(2000) NULL COMMENT '未成交说明' AFTER F_InviteId; + +-- 创建索引以优化查询性能 +CREATE INDEX idx_yyjl_invite_id ON lq_yyjl(F_InviteId); + +-- ============================================ +-- 2. lq_kd_kdjlb(开单记录表) - 添加预约ID字段 +-- ============================================ +-- 说明:关联预约记录,用于统计预约→开单转化率 +ALTER TABLE lq_kd_kdjlb +ADD COLUMN F_AppointmentId VARCHAR(50) NULL COMMENT '预约记录ID(关联lq_yyjl.F_Id,用于统计预约→开单转化率)' AFTER F_ActivityId; + +-- 创建索引以优化查询性能 +CREATE INDEX idx_kdjlb_appointment_id ON lq_kd_kdjlb(F_AppointmentId); + +-- ============================================ +-- 3. lq_xh_hyhk(耗卡记录表) - 添加预约ID字段 +-- ============================================ +-- 说明:关联预约记录,用于统计预约→耗卡转化率 +ALTER TABLE lq_xh_hyhk +ADD COLUMN F_AppointmentId VARCHAR(50) NULL COMMENT '预约记录ID(关联lq_yyjl.F_Id,用于统计预约→耗卡转化率)' AFTER F_OvertimeSgfy; + +-- 创建索引以优化查询性能 +CREATE INDEX idx_hyhk_appointment_id ON lq_xh_hyhk(F_AppointmentId); + +-- ============================================ +-- 4. 验证字段创建 +-- ============================================ +-- 验证 lq_yyjl 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_yyjl' +-- AND (COLUMN_NAME = 'F_InviteId' OR COLUMN_NAME = 'F_NoDealRemark'); + +-- 验证 lq_kd_kdjlb 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_kd_kdjlb' +-- AND COLUMN_NAME = 'F_AppointmentId'; + +-- 验证 lq_xh_hyhk 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_xh_hyhk' +-- AND COLUMN_NAME = 'F_AppointmentId'; + +-- ============================================ +-- 5. 验证索引创建 +-- ============================================ +-- 验证索引 +-- SHOW INDEX FROM lq_yyjl WHERE Key_name = 'idx_yyjl_invite_id'; +-- SHOW INDEX FROM lq_kd_kdjlb WHERE Key_name = 'idx_kdjlb_appointment_id'; +-- SHOW INDEX FROM lq_xh_hyhk WHERE Key_name = 'idx_hyhk_appointment_id'; + +-- ============================================ +-- 6. 转化率统计示例SQL(供参考) +-- ============================================ +-- 示例1:邀约→预约转化率 +-- SELECT +-- COUNT(DISTINCT yyjl.F_Id) AS 预约数量, +-- COUNT(DISTINCT yyjl.F_InviteId) AS 来自邀约的预约数量, +-- COUNT(DISTINCT yyjl.F_InviteId) * 100.0 / COUNT(DISTINCT yyjl.F_Id) AS 转化率百分比 +-- FROM lq_yyjl yyjl +-- WHERE yyjl.F_CreateTime >= '2024-01-01'; + +-- 示例2:预约→开单转化率 +-- SELECT +-- COUNT(DISTINCT yyjl.F_Id) AS 预约数量, +-- COUNT(DISTINCT kdjlb.F_Id) AS 开单数量, +-- COUNT(DISTINCT kdjlb.F_Id) * 100.0 / COUNT(DISTINCT yyjl.F_Id) AS 转化率百分比 +-- FROM lq_yyjl yyjl +-- LEFT JOIN lq_kd_kdjlb kdjlb ON kdjlb.F_AppointmentId = yyjl.F_Id AND kdjlb.F_IsEffective = 1 +-- WHERE yyjl.F_CreateTime >= '2024-01-01'; + +-- 示例3:预约→耗卡转化率 +-- SELECT +-- COUNT(DISTINCT yyjl.F_Id) AS 预约数量, +-- COUNT(DISTINCT hyhk.F_Id) AS 耗卡数量, +-- COUNT(DISTINCT hyhk.F_Id) * 100.0 / COUNT(DISTINCT yyjl.F_Id) AS 转化率百分比 +-- FROM lq_yyjl yyjl +-- LEFT JOIN lq_xh_hyhk hyhk ON hyhk.F_AppointmentId = yyjl.F_Id AND hyhk.F_IsEffective = 1 +-- WHERE yyjl.F_CreateTime >= '2024-01-01'; + +-- 示例4:邀约→开单转化率(跨步转化) +-- SELECT +-- COUNT(DISTINCT yaoyjl.F_Id) AS 邀约数量, +-- COUNT(DISTINCT kdjlb.F_Id) AS 开单数量, +-- COUNT(DISTINCT kdjlb.F_Id) * 100.0 / COUNT(DISTINCT yaoyjl.F_Id) AS 转化率百分比 +-- FROM lq_yaoyjl yaoyjl +-- LEFT JOIN lq_yyjl yyjl ON yyjl.F_InviteId = yaoyjl.F_Id +-- LEFT JOIN lq_kd_kdjlb kdjlb ON kdjlb.F_AppointmentId = yyjl.F_Id AND kdjlb.F_IsEffective = 1 +-- WHERE yaoyjl.F_CreateTime >= '2024-01-01'; + -- libgit2 0.21.4