Commit 75c8f70997d81958e1c7d952916d15ce89f72806
1 parent
430949ed
feat: 1. 拓客明细页面是否字段使用tag样式显示;2. 6个接口支持多门店筛选;3. 门店日报统计接口添加在职人数字段
- 拓客明细页面:是否邀约、是否预约、是否消耗、是否开卡字段使用el-tag样式,绿色表示是,灰色表示否 - 多门店筛选接口: * 会员品项接口:BillingStoreId改为支持BillingStoreIds数组 * 开单明细接口:StoreId改为支持StoreIds数组 * 耗卡明细接口:StoreId改为支持StoreIds数组 * 会员升单统计接口:新增StoreIds多门店筛选 * 储扣列表接口:StoreId改为支持StoreIds数组(已存在,优化逻辑) * 门店整体统计接口:StoreId改为支持StoreIds数组 - 门店日报统计接口:添加EmployeeCount在职人数字段,从lq_mdxx.zzrs获取
Showing
16 changed files
with
209 additions
and
24 deletions
antis-ncc-admin/.env.development
| 1 | 1 | # 开发 |
| 2 | 2 | |
| 3 | 3 | VUE_CLI_BABEL_TRANSPILE_MODULES = true |
| 4 | -VUE_APP_BASE_API = 'https://erp.lvqianmeiye.com' | |
| 4 | +# VUE_APP_BASE_API = 'https://erp.lvqianmeiye.com' | |
| 5 | 5 | # VUE_APP_BASE_API = 'http://erp_test.lvqianmeiye.com' |
| 6 | -# VUE_APP_BASE_API = 'http://localhost:2011' | |
| 6 | +VUE_APP_BASE_API = 'http://localhost:2011' | |
| 7 | 7 | # VUE_APP_BASE_API = 'http://localhost:2011' |
| 8 | 8 | VUE_APP_IMG_API = '' |
| 9 | 9 | VUE_APP_BASE_WSS = 'ws://192.168.110.45:2011/websocket' | ... | ... |
antis-ncc-admin/src/components/kpi-drill/tk-analysis.vue
| ... | ... | @@ -43,7 +43,15 @@ |
| 43 | 43 | <el-table-column v-for="col in columns" :key="col.prop" :prop="col.prop" :label="col.label" |
| 44 | 44 | :width="col.width" :min-width="col.minWidth"> |
| 45 | 45 | <template slot-scope="scope"> |
| 46 | - <span>{{ scope.row[col.prop] || '—' }}</span> | |
| 46 | + <!-- 是否类字段使用tag样式 --> | |
| 47 | + <el-tag | |
| 48 | + v-if="['hasInvite', 'hasAppointment', 'hasConsume', 'hasBilling'].includes(col.prop)" | |
| 49 | + :type="scope.row[col.prop] === '是' ? 'success' : 'info'" | |
| 50 | + size="small"> | |
| 51 | + {{ scope.row[col.prop] || '否' }} | |
| 52 | + </el-tag> | |
| 53 | + <!-- 其他字段使用普通文本 --> | |
| 54 | + <span v-else>{{ scope.row[col.prop] || '—' }}</span> | |
| 47 | 55 | </template> |
| 48 | 56 | </el-table-column> |
| 49 | 57 | </el-table> | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StoreDailyStatisticsOutput.cs
| ... | ... | @@ -19,6 +19,11 @@ namespace NCC.Extend.Entitys.Dto.LqDailyReport |
| 19 | 19 | public string StoreName { get; set; } |
| 20 | 20 | |
| 21 | 21 | /// <summary> |
| 22 | + /// 在职人数 | |
| 23 | + /// </summary> | |
| 24 | + public int EmployeeCount { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 22 | 27 | /// 人头数(去重后的消费会员数) |
| 23 | 28 | /// </summary> |
| 24 | 29 | public int HeadCount { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs
| 1 | +using System.Collections.Generic; | |
| 1 | 2 | using NCC.Common.Filter; |
| 2 | 3 | |
| 3 | 4 | namespace NCC.Extend.Entitys.Dto.LqKdKdjlb |
| ... | ... | @@ -78,9 +79,14 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb |
| 78 | 79 | public string SourceType { get; set; } |
| 79 | 80 | |
| 80 | 81 | /// <summary> |
| 81 | - /// 门店ID | |
| 82 | + /// 门店ID(单门店,兼容旧版本) | |
| 82 | 83 | /// </summary> |
| 83 | 84 | public string StoreId { get; set; } |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 门店ID列表(多门店筛选) | |
| 88 | + /// </summary> | |
| 89 | + public List<string> StoreIds { get; set; } | |
| 84 | 90 | } |
| 85 | 91 | } |
| 86 | 92 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsQueryInput.cs
| 1 | -using NCC.Common.Filter; | |
| 2 | 1 | using System; |
| 2 | +using System.Collections.Generic; | |
| 3 | +using NCC.Common.Filter; | |
| 3 | 4 | |
| 4 | 5 | namespace NCC.Extend.Entitys.Dto.LqKdKdjlb |
| 5 | 6 | { |
| ... | ... | @@ -24,11 +25,16 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb |
| 24 | 25 | public string DepartmentId { get; set; } |
| 25 | 26 | |
| 26 | 27 | /// <summary> |
| 27 | - /// 门店ID | |
| 28 | + /// 门店ID(单门店,兼容旧版本) | |
| 28 | 29 | /// </summary> |
| 29 | 30 | public string StoreId { get; set; } |
| 30 | 31 | |
| 31 | 32 | /// <summary> |
| 33 | + /// 门店ID列表(多门店筛选) | |
| 34 | + /// </summary> | |
| 35 | + public List<string> StoreIds { get; set; } | |
| 36 | + | |
| 37 | + /// <summary> | |
| 32 | 38 | /// 健康师姓名 |
| 33 | 39 | /// </summary> |
| 34 | 40 | public string EmployeeName { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoQueryInput.cs
| 1 | 1 | using System; |
| 2 | +using System.Collections.Generic; | |
| 2 | 3 | using NCC.Common.Filter; |
| 3 | 4 | |
| 4 | 5 | namespace NCC.Extend.Entitys.Dto.LqKhxx |
| ... | ... | @@ -19,11 +20,16 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx |
| 19 | 20 | public string BelongStoreId { get; set; } |
| 20 | 21 | |
| 21 | 22 | /// <summary> |
| 22 | - /// 开单门店ID | |
| 23 | + /// 开单门店ID(单门店,兼容旧版本) | |
| 23 | 24 | /// </summary> |
| 24 | 25 | public string BillingStoreId { get; set; } |
| 25 | 26 | |
| 26 | 27 | /// <summary> |
| 28 | + /// 开单门店ID列表(多门店筛选) | |
| 29 | + /// </summary> | |
| 30 | + public List<string> BillingStoreIds { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 27 | 33 | /// 会员类型 |
| 28 | 34 | /// </summary> |
| 29 | 35 | public int MemberType { get; set; } = -1; | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/MemberUpgradeStatisticsListQueryInput.cs
| ... | ... | @@ -37,6 +37,11 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics |
| 37 | 37 | /// 是否升生美(true-是,false-否,null-不筛选) |
| 38 | 38 | /// </summary> |
| 39 | 39 | public bool? HasUpgradeLifeBeauty { get; set; } |
| 40 | + | |
| 41 | + /// <summary> | |
| 42 | + /// 门店ID列表(多门店筛选) | |
| 43 | + /// </summary> | |
| 44 | + public List<string> StoreIds { get; set; } | |
| 40 | 45 | } |
| 41 | 46 | } |
| 42 | 47 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTkjlb/LqTkjlbListOutput.cs
| ... | ... | @@ -117,5 +117,10 @@ namespace NCC.Extend.Entitys.Dto.LqTkjlb |
| 117 | 117 | /// 是否开卡(是/否) |
| 118 | 118 | /// </summary> |
| 119 | 119 | public string hasBilling { get; set; } |
| 120 | + | |
| 121 | + /// <summary> | |
| 122 | + /// 会员ID(内部使用,不返回给前端) | |
| 123 | + /// </summary> | |
| 124 | + public string memberId { get; set; } | |
| 120 | 125 | } |
| 121 | 126 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqXhPxmx/ConsumeItemDetailListQueryInput.cs
| 1 | +using System.Collections.Generic; | |
| 1 | 2 | using NCC.Common.Filter; |
| 2 | 3 | |
| 3 | 4 | namespace NCC.Extend.Entitys.Dto.LqXhPxmx |
| ... | ... | @@ -73,9 +74,14 @@ namespace NCC.Extend.Entitys.Dto.LqXhPxmx |
| 73 | 74 | public string SourceType { get; set; } |
| 74 | 75 | |
| 75 | 76 | /// <summary> |
| 76 | - /// 门店ID | |
| 77 | + /// 门店ID(单门店,兼容旧版本) | |
| 77 | 78 | /// </summary> |
| 78 | 79 | public string StoreId { get; set; } |
| 80 | + | |
| 81 | + /// <summary> | |
| 82 | + /// 门店ID列表(多门店筛选) | |
| 83 | + /// </summary> | |
| 84 | + public List<string> StoreIds { get; set; } | |
| 79 | 85 | } |
| 80 | 86 | } |
| 81 | 87 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
| ... | ... | @@ -99,6 +99,7 @@ namespace NCC.Extend |
| 99 | 99 | /// 返回说明: |
| 100 | 100 | /// - StoreId: 门店ID |
| 101 | 101 | /// - StoreName: 门店名称 |
| 102 | + /// - EmployeeCount: 在职人数 | |
| 102 | 103 | /// - HeadCount: 人头数(去重后的消费会员数) |
| 103 | 104 | /// - PersonCount: 人次(日度去重客户数,同一天同一客户只算一次) |
| 104 | 105 | /// - ProjectCount: 项目数(消耗的项目总数,从品项明细表统计) |
| ... | ... | @@ -134,6 +135,8 @@ namespace NCC.Extend |
| 134 | 135 | SELECT |
| 135 | 136 | consume.Md as StoreId, |
| 136 | 137 | MAX(store.dm) as StoreName, |
| 138 | + -- 在职人数(从门店表获取) | |
| 139 | + MAX(COALESCE(store.zzrs, 0)) as EmployeeCount, | |
| 137 | 140 | -- 人头数(去重后的消费会员数) |
| 138 | 141 | COALESCE(COUNT(DISTINCT consume.Hy), 0) as HeadCount, |
| 139 | 142 | -- 人次(日度去重客户数) |
| ... | ... | @@ -185,6 +188,7 @@ namespace NCC.Extend |
| 185 | 188 | { |
| 186 | 189 | StoreId = item.StoreId, |
| 187 | 190 | StoreName = item.StoreName, |
| 191 | + EmployeeCount = Convert.ToInt32(item.EmployeeCount ?? 0), | |
| 188 | 192 | HeadCount = Convert.ToInt32(item.HeadCount), |
| 189 | 193 | PersonCount = Convert.ToInt32(item.PersonCount), |
| 190 | 194 | ProjectCount = Convert.ToInt32(item.ProjectCount), | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
| ... | ... | @@ -3635,10 +3635,25 @@ namespace NCC.Extend.LqKdKdjlb |
| 3635 | 3635 | parameters.Add(new SugarParameter("@departmentId", input.DepartmentId)); |
| 3636 | 3636 | } |
| 3637 | 3637 | |
| 3638 | - if (!string.IsNullOrEmpty(input.StoreId)) | |
| 3638 | + // 处理多门店筛选:优先使用StoreIds,如果没有则使用StoreId | |
| 3639 | + var storeIds = new List<string>(); | |
| 3640 | + if (input.StoreIds != null && input.StoreIds.Any()) | |
| 3639 | 3641 | { |
| 3640 | - conditions.Add("u.F_MDID = @storeId"); | |
| 3641 | - parameters.Add(new SugarParameter("@storeId", input.StoreId)); | |
| 3642 | + storeIds = input.StoreIds.Where(x => !string.IsNullOrEmpty(x)).ToList(); | |
| 3643 | + } | |
| 3644 | + else if (!string.IsNullOrEmpty(input.StoreId)) | |
| 3645 | + { | |
| 3646 | + storeIds = new List<string> { input.StoreId }; | |
| 3647 | + } | |
| 3648 | + | |
| 3649 | + if (storeIds.Any()) | |
| 3650 | + { | |
| 3651 | + var storeIdParams = string.Join(",", storeIds.Select((_, i) => $"@storeId{i}")); | |
| 3652 | + conditions.Add($"u.F_MDID IN ({storeIdParams})"); | |
| 3653 | + for (int i = 0; i < storeIds.Count; i++) | |
| 3654 | + { | |
| 3655 | + parameters.Add(new SugarParameter($"@storeId{i}", storeIds[i])); | |
| 3656 | + } | |
| 3642 | 3657 | } |
| 3643 | 3658 | |
| 3644 | 3659 | if (!string.IsNullOrEmpty(input.EmployeeName)) |
| ... | ... | @@ -3839,9 +3854,20 @@ namespace NCC.Extend.LqKdKdjlb |
| 3839 | 3854 | .WhereIF(!string.IsNullOrEmpty(input.ItemType), pxmx => pxmx.ItemCategory == input.ItemType); |
| 3840 | 3855 | |
| 3841 | 3856 | // 2. 通过 EXISTS 子查询筛选关联字段(在分页前筛选,确保分页准确) |
| 3857 | + // 处理多门店筛选:优先使用StoreIds,如果没有则使用StoreId | |
| 3858 | + var filterStoreIds = new List<string>(); | |
| 3859 | + if (input.StoreIds != null && input.StoreIds.Any()) | |
| 3860 | + { | |
| 3861 | + filterStoreIds = input.StoreIds.Where(x => !string.IsNullOrEmpty(x)).ToList(); | |
| 3862 | + } | |
| 3863 | + else if (!string.IsNullOrEmpty(input.StoreId)) | |
| 3864 | + { | |
| 3865 | + filterStoreIds = new List<string> { input.StoreId }; | |
| 3866 | + } | |
| 3867 | + | |
| 3842 | 3868 | baseQuery = baseQuery.WhereIF(!string.IsNullOrEmpty(input.MemberName), pxmx => SqlFunc.Subqueryable<LqKhxxEntity>().Where(x => x.Id == pxmx.MemberId && x.Khmc != null && x.Khmc.Contains(input.MemberName)).Any()) |
| 3843 | 3869 | .WhereIF(!string.IsNullOrEmpty(input.MemberPhone), pxmx => SqlFunc.Subqueryable<LqKhxxEntity>().Where(x => x.Id == pxmx.MemberId && x.Sjh == input.MemberPhone).Any()) |
| 3844 | - .WhereIF(!string.IsNullOrEmpty(input.StoreId), pxmx => SqlFunc.Subqueryable<LqKdKdjlbEntity>().Where(x => x.Id == pxmx.Glkdbh && x.Djmd == input.StoreId).Any()); | |
| 3870 | + .WhereIF(filterStoreIds.Any(), pxmx => SqlFunc.Subqueryable<LqKdKdjlbEntity>().Where(x => x.Id == pxmx.Glkdbh && filterStoreIds.Contains(x.Djmd)).Any()); | |
| 3845 | 3871 | |
| 3846 | 3872 | // 3. 先分页查询主表数据(查询实体类,提高性能) |
| 3847 | 3873 | var pagedData = await baseQuery.OrderBy(sidx + " " + sort).ToPagedListAsync(input.currentPage, input.pageSize); | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
| ... | ... | @@ -1145,7 +1145,18 @@ namespace NCC.Extend.LqKhxx |
| 1145 | 1145 | } |
| 1146 | 1146 | |
| 1147 | 1147 | // 如果指定了开单门店、开单人、品项ID等条件,需要通过开单表进行筛选 |
| 1148 | - if (!string.IsNullOrEmpty(input.BillingStoreId) || | |
| 1148 | + // 处理多门店筛选:优先使用BillingStoreIds,如果没有则使用BillingStoreId | |
| 1149 | + var billingStoreIds = new List<string>(); | |
| 1150 | + if (input.BillingStoreIds != null && input.BillingStoreIds.Any()) | |
| 1151 | + { | |
| 1152 | + billingStoreIds = input.BillingStoreIds.Where(x => !string.IsNullOrEmpty(x)).ToList(); | |
| 1153 | + } | |
| 1154 | + else if (!string.IsNullOrEmpty(input.BillingStoreId)) | |
| 1155 | + { | |
| 1156 | + billingStoreIds = new List<string> { input.BillingStoreId }; | |
| 1157 | + } | |
| 1158 | + | |
| 1159 | + if (billingStoreIds.Any() || | |
| 1149 | 1160 | !string.IsNullOrEmpty(input.BillingUserId) || |
| 1150 | 1161 | !string.IsNullOrEmpty(input.PurchaseItemId) || |
| 1151 | 1162 | !string.IsNullOrEmpty(input.GiftItemId) || |
| ... | ... | @@ -1155,9 +1166,9 @@ namespace NCC.Extend.LqKhxx |
| 1155 | 1166 | var billingQuery = _db.Queryable<LqKdKdjlbEntity>() |
| 1156 | 1167 | .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()); |
| 1157 | 1168 | |
| 1158 | - if (!string.IsNullOrEmpty(input.BillingStoreId)) | |
| 1169 | + if (billingStoreIds.Any()) | |
| 1159 | 1170 | { |
| 1160 | - billingQuery = billingQuery.Where(x => x.Djmd == input.BillingStoreId); | |
| 1171 | + billingQuery = billingQuery.Where(x => billingStoreIds.Contains(x.Djmd)); | |
| 1161 | 1172 | } |
| 1162 | 1173 | |
| 1163 | 1174 | if (!string.IsNullOrEmpty(input.BillingUserId)) | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
| ... | ... | @@ -503,7 +503,7 @@ namespace NCC.Extend |
| 503 | 503 | /// - TopAmountMemberName: 开单金额最高会员姓名 |
| 504 | 504 | /// - TopAmountValue: 该会员整月实付金额 |
| 505 | 505 | /// - TopTimesMemberName: 开单次数最多会员姓名 |
| 506 | - /// - TopTimesCount: 该会员整月开单次数 | |
| 506 | + /// - TopTimesCount: 该会员整月开单次数(按天去重,同一天多次开单只算一次) | |
| 507 | 507 | /// - DebtTotal: 整月欠款总额(sfyj+ck金额以外的qk累加) |
| 508 | 508 | /// - TopDebtMemberName: 欠款最多会员姓名 |
| 509 | 509 | /// - TopDebtValue: 欠款最多会员的欠款金额 |
| ... | ... | @@ -598,15 +598,16 @@ namespace NCC.Extend |
| 598 | 598 | .OrderBy(x => x.Date) |
| 599 | 599 | .ToList(); |
| 600 | 600 | |
| 601 | - // 3. 会员极值:按会员聚合金额和次数 | |
| 601 | + // 3. 会员极值:按会员聚合金额和次数(开单次数按天去重) | |
| 602 | 602 | var memberAgg = rawList |
| 603 | - .Where(x => !string.IsNullOrEmpty(x.MemberId)) | |
| 604 | - .GroupBy(x => x.MemberId) | |
| 603 | + .Where(x => !string.IsNullOrEmpty(x.MemberId) && x.BillingDate.HasValue) | |
| 604 | + .GroupBy(x => new { x.MemberId, Date = x.BillingDate.Value.Date }) | |
| 605 | + .GroupBy(g => g.Key.MemberId) | |
| 605 | 606 | .Select(g => new |
| 606 | 607 | { |
| 607 | 608 | MemberId = g.Key, |
| 608 | - Amount = g.Sum(x => x.Amount), | |
| 609 | - Times = g.Count() | |
| 609 | + Amount = g.SelectMany(dayGroup => dayGroup).Sum(x => x.Amount), | |
| 610 | + Times = g.Count() // 按天去重后的开单天数 | |
| 610 | 611 | }) |
| 611 | 612 | .ToList(); |
| 612 | 613 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
| ... | ... | @@ -4898,6 +4898,7 @@ namespace NCC.Extend.LqStatistics |
| 4898 | 4898 | ) xh_stats ON xh_stats.member_id = tk.F_MemberId |
| 4899 | 4899 | -- 开单统计子查询(只统计通过预约产生的开单,包含金额和品项) |
| 4900 | 4900 | -- 通过会员ID关联:线索池 -> 邀约 -> 预约 -> 开单(通过预约ID,且会员ID匹配) |
| 4901 | + -- 注意:剔除品项ID是61且金额是0的记录 | |
| 4901 | 4902 | LEFT JOIN ( |
| 4902 | 4903 | SELECT |
| 4903 | 4904 | tk_inner.F_MemberId as member_id, |
| ... | ... | @@ -4908,7 +4909,14 @@ namespace NCC.Extend.LqStatistics |
| 4908 | 4909 | INNER JOIN lq_yaoyjl yaoy ON yaoy.yykh = tk_inner.F_MemberId |
| 4909 | 4910 | INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id AND yy.gk = tk_inner.F_MemberId |
| 4910 | 4911 | INNER JOIN lq_kd_kdjlb kd ON kd.F_AppointmentId = yy.F_Id AND kd.kdhy = tk_inner.F_MemberId AND kd.F_IsEffective = 1 |
| 4912 | + INNER JOIN ( | |
| 4913 | + SELECT DISTINCT glkdbh | |
| 4914 | + FROM lq_kd_pxmx | |
| 4915 | + WHERE F_IsEffective = 1 | |
| 4916 | + AND NOT (px = '61' AND (COALESCE(F_ActualPrice, 0) = 0 AND COALESCE(F_TotalPrice, 0) = 0)) | |
| 4917 | + ) valid_px ON valid_px.glkdbh = kd.F_Id | |
| 4911 | 4918 | LEFT JOIN lq_kd_pxmx kdpx ON kdpx.glkdbh = kd.F_Id AND kdpx.F_IsEffective = 1 |
| 4919 | + AND NOT (kdpx.px = '61' AND (COALESCE(kdpx.F_ActualPrice, 0) = 0 AND COALESCE(kdpx.F_TotalPrice, 0) = 0)) | |
| 4912 | 4920 | GROUP BY tk_inner.F_MemberId |
| 4913 | 4921 | ) kd_stats ON kd_stats.member_id = tk.F_MemberId |
| 4914 | 4922 | -- 实际预约记录数统计(不管是否通过邀约产生) |
| ... | ... | @@ -5229,6 +5237,7 @@ namespace NCC.Extend.LqStatistics |
| 5229 | 5237 | ) xh_stats ON xh_stats.member_id = tk.F_MemberId |
| 5230 | 5238 | -- 开单统计子查询(只统计通过预约产生的开单,包含金额和品项) |
| 5231 | 5239 | -- 通过会员ID关联:线索池 -> 邀约 -> 预约 -> 开单(通过预约ID,且会员ID匹配) |
| 5240 | + -- 注意:剔除品项ID是61且金额是0的记录 | |
| 5232 | 5241 | LEFT JOIN ( |
| 5233 | 5242 | SELECT |
| 5234 | 5243 | tk_inner.F_MemberId as member_id, |
| ... | ... | @@ -5239,7 +5248,14 @@ namespace NCC.Extend.LqStatistics |
| 5239 | 5248 | INNER JOIN lq_yaoyjl yaoy ON yaoy.yykh = tk_inner.F_MemberId |
| 5240 | 5249 | INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id AND yy.gk = tk_inner.F_MemberId |
| 5241 | 5250 | INNER JOIN lq_kd_kdjlb kd ON kd.F_AppointmentId = yy.F_Id AND kd.kdhy = tk_inner.F_MemberId AND kd.F_IsEffective = 1 |
| 5251 | + INNER JOIN ( | |
| 5252 | + SELECT DISTINCT glkdbh | |
| 5253 | + FROM lq_kd_pxmx | |
| 5254 | + WHERE F_IsEffective = 1 | |
| 5255 | + AND NOT (px = '61' AND (COALESCE(F_ActualPrice, 0) = 0 AND COALESCE(F_TotalPrice, 0) = 0)) | |
| 5256 | + ) valid_px ON valid_px.glkdbh = kd.F_Id | |
| 5242 | 5257 | LEFT JOIN lq_kd_pxmx kdpx ON kdpx.glkdbh = kd.F_Id AND kdpx.F_IsEffective = 1 |
| 5258 | + AND NOT (kdpx.px = '61' AND (COALESCE(kdpx.F_ActualPrice, 0) = 0 AND COALESCE(kdpx.F_TotalPrice, 0) = 0)) | |
| 5243 | 5259 | GROUP BY tk_inner.F_MemberId |
| 5244 | 5260 | ) kd_stats ON kd_stats.member_id = tk.F_MemberId |
| 5245 | 5261 | -- 实际预约记录数统计(不管是否通过邀约产生) |
| ... | ... | @@ -5703,6 +5719,17 @@ namespace NCC.Extend.LqStatistics |
| 5703 | 5719 | } |
| 5704 | 5720 | } |
| 5705 | 5721 | |
| 5722 | + // 添加多门店筛选 | |
| 5723 | + if (input.StoreIds != null && input.StoreIds.Any()) | |
| 5724 | + { | |
| 5725 | + var storeIdParams = string.Join(",", input.StoreIds.Select((_, i) => $"@StoreId{i}")); | |
| 5726 | + whereConditions.Add($"kd.Djmd IN ({storeIdParams})"); | |
| 5727 | + for (int i = 0; i < input.StoreIds.Count; i++) | |
| 5728 | + { | |
| 5729 | + parameters.Add(new SugarParameter($"@StoreId{i}", input.StoreIds[i])); | |
| 5730 | + } | |
| 5731 | + } | |
| 5732 | + | |
| 5706 | 5733 | var whereClause = whereConditions.Any() ? "AND " + string.Join(" AND ", whereConditions) : ""; |
| 5707 | 5734 | |
| 5708 | 5735 | // 构建HAVING条件(用于筛选升单条件) | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs
| ... | ... | @@ -147,12 +147,70 @@ namespace NCC.Extend.LqTkjlb |
| 147 | 147 | hasAppointment = SqlFunc.Subqueryable<LqYyjlEntity>().Where(y => y.Gk == it.MemberId).Any() ? "是" : "否", |
| 148 | 148 | // 是否消耗:通过会员ID关联耗卡表(hy字段存储的是会员ID) |
| 149 | 149 | hasConsume = SqlFunc.Subqueryable<LqXhHyhkEntity>().Where(x => x.Hy == it.MemberId).Any() ? "是" : "否", |
| 150 | - // 是否开卡:通过会员ID关联开单表(kdhy字段存储的是会员ID) | |
| 151 | - hasBilling = SqlFunc.Subqueryable<LqKdKdjlbEntity>().Where(k => k.Kdhy == it.MemberId).Any() ? "是" : "否" | |
| 150 | + // 是否开卡:通过会员ID关联开单表(kdhy字段存储的是会员ID),剔除品项ID是61(女神卡)且金额是0的记录 | |
| 151 | + // 先设置为否,后面在内存中处理 | |
| 152 | + hasBilling = "否" | |
| 152 | 153 | }) |
| 153 | 154 | .MergeTable() |
| 154 | 155 | .OrderBy(sidx + " " + input.sort) |
| 155 | 156 | .ToPagedListAsync(input.currentPage, input.pageSize); |
| 157 | + | |
| 158 | + // 批量查询有有效开单的会员ID(剔除品项ID是61且金额是0的记录) | |
| 159 | + // 先查询所有拓客记录的会员ID | |
| 160 | + var tkIds = data.list?.Select(x => x.id).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList() ?? new List<string>(); | |
| 161 | + var memberIdDict = new Dictionary<string, string>(); // tkId -> MemberId | |
| 162 | + | |
| 163 | + if (tkIds.Any()) | |
| 164 | + { | |
| 165 | + // 查询拓客记录的会员ID | |
| 166 | + var tkMembers = await _db.Queryable<LqTkjlbEntity>() | |
| 167 | + .Where(x => tkIds.Contains(x.Id)) | |
| 168 | + .Select(x => new { x.Id, x.MemberId }) | |
| 169 | + .ToListAsync(); | |
| 170 | + memberIdDict = tkMembers?.Where(x => !string.IsNullOrEmpty(x.MemberId)) | |
| 171 | + .ToDictionary(x => x.Id, x => x.MemberId) ?? new Dictionary<string, string>(); | |
| 172 | + } | |
| 173 | + | |
| 174 | + var memberIds = memberIdDict.Values.Distinct().ToList(); | |
| 175 | + var hasBillingMemberIds = new HashSet<string>(); | |
| 176 | + | |
| 177 | + if (memberIds.Any()) | |
| 178 | + { | |
| 179 | + var sql = $@" | |
| 180 | + SELECT DISTINCT kd.kdhy as MemberId | |
| 181 | + FROM lq_kd_kdjlb kd | |
| 182 | + INNER JOIN ( | |
| 183 | + SELECT DISTINCT glkdbh | |
| 184 | + FROM lq_kd_pxmx | |
| 185 | + WHERE F_IsEffective = 1 | |
| 186 | + AND NOT (px = '61' AND (COALESCE(F_ActualPrice, 0) = 0 AND COALESCE(F_TotalPrice, 0) = 0)) | |
| 187 | + ) valid_px ON valid_px.glkdbh = kd.F_Id | |
| 188 | + WHERE kd.F_IsEffective = 1 | |
| 189 | + AND kd.kdhy IN ('{string.Join("','", memberIds)}')"; | |
| 190 | + var billingMembers = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 191 | + if (billingMembers != null) | |
| 192 | + { | |
| 193 | + var memberIdList = billingMembers.Select(x => x.MemberId?.ToString()).Where(x => !string.IsNullOrEmpty(x)).Cast<string>().ToList(); | |
| 194 | + hasBillingMemberIds = new HashSet<string>(memberIdList); | |
| 195 | + } | |
| 196 | + } | |
| 197 | + | |
| 198 | + // 更新hasBilling字段 | |
| 199 | + if (data.list != null) | |
| 200 | + { | |
| 201 | + foreach (var item in data.list) | |
| 202 | + { | |
| 203 | + if (memberIdDict.TryGetValue(item.id, out var memberId) && !string.IsNullOrEmpty(memberId)) | |
| 204 | + { | |
| 205 | + item.hasBilling = hasBillingMemberIds.Contains(memberId) ? "是" : "否"; | |
| 206 | + } | |
| 207 | + else | |
| 208 | + { | |
| 209 | + item.hasBilling = "否"; | |
| 210 | + } | |
| 211 | + } | |
| 212 | + } | |
| 213 | + | |
| 156 | 214 | return PageResult<LqTkjlbListOutput>.SqlSugarPageResult(data); |
| 157 | 215 | } |
| 158 | 216 | #endregion | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
| ... | ... | @@ -2036,9 +2036,20 @@ namespace NCC.Extend.LqXhHyhk |
| 2036 | 2036 | .WhereIF(!string.IsNullOrEmpty(input.ItemType), pxmx => pxmx.ItemCategory == input.ItemType); |
| 2037 | 2037 | |
| 2038 | 2038 | // 2. 通过 EXISTS 子查询筛选关联字段(在分页前筛选,确保分页准确) |
| 2039 | + // 处理多门店筛选:优先使用StoreIds,如果没有则使用StoreId | |
| 2040 | + var filterStoreIds = new List<string>(); | |
| 2041 | + if (input.StoreIds != null && input.StoreIds.Any()) | |
| 2042 | + { | |
| 2043 | + filterStoreIds = input.StoreIds.Where(x => !string.IsNullOrEmpty(x)).ToList(); | |
| 2044 | + } | |
| 2045 | + else if (!string.IsNullOrEmpty(input.StoreId)) | |
| 2046 | + { | |
| 2047 | + filterStoreIds = new List<string> { input.StoreId }; | |
| 2048 | + } | |
| 2049 | + | |
| 2039 | 2050 | baseQuery = baseQuery.WhereIF(!string.IsNullOrEmpty(input.MemberName), pxmx => SqlFunc.Subqueryable<LqKhxxEntity>().Where(x => x.Id == pxmx.MemberId && x.Khmc != null && x.Khmc.Contains(input.MemberName)).Any()) |
| 2040 | 2051 | .WhereIF(!string.IsNullOrEmpty(input.MemberPhone), pxmx => SqlFunc.Subqueryable<LqKhxxEntity>().Where(x => x.Id == pxmx.MemberId && x.Sjh == input.MemberPhone).Any()) |
| 2041 | - .WhereIF(!string.IsNullOrEmpty(input.StoreId), pxmx => SqlFunc.Subqueryable<LqXhHyhkEntity>().Where(x => x.Id == pxmx.ConsumeInfoId && x.Md == input.StoreId).Any()); | |
| 2052 | + .WhereIF(filterStoreIds.Any(), pxmx => SqlFunc.Subqueryable<LqXhHyhkEntity>().Where(x => x.Id == pxmx.ConsumeInfoId && filterStoreIds.Contains(x.Md)).Any()); | |
| 2042 | 2053 | |
| 2043 | 2054 | // 3. 先分页查询主表数据(查询实体类,提高性能) |
| 2044 | 2055 | var pagedData = await baseQuery.OrderBy(sidx + " " + sort).ToPagedListAsync(input.currentPage, input.pageSize); | ... | ... |