Commit 65e1ea9f1c805734dfb2964db4c4a45b43d81d38
1 parent
b08c5340
fix: 修复员工业绩统计和人次统计逻辑
主要变更: 1. 修复人次统计逻辑,改为使用客户ID和日期的组合去重(COUNT(DISTINCT CONCAT(hy, '-', DATE(hksj)))) 2. 新增员工业绩统计接口GetEmployeePerformanceStatistics,支持查询员工在指定月份的完整业绩数据 3. 修复品项统计的门店过滤问题,使用SqlFunc.Subqueryable避免别名冲突 4. 修复门店顾客详情SQL字段错误(F_Status改为yysj,F_CreateTime改为kdrq/hksj) 5. 新增员工业绩统计输入输出DTO类 功能特性: - 支持查询员工的拓客、邀约、预约、开单、消耗、退卡、人头、人次等完整统计 - 所有SQL改为字符串拼接方式,确保参数正确传递 - 分步统计设计,便于维护和优化
Showing
7 changed files
with
413 additions
and
64 deletions
antis-ncc-admin/.env.development
| ... | ... | @@ -2,6 +2,6 @@ |
| 2 | 2 | |
| 3 | 3 | VUE_CLI_BABEL_TRANSPILE_MODULES = true |
| 4 | 4 | # VUE_APP_BASE_API = 'http://lvqian.antissoft.com' |
| 5 | -VUE_APP_BASE_API = 'http://erp_test.lvqianmeiye.com' | |
| 6 | -# VUE_APP_BASE_API = 'http://localhost:2011' | |
| 5 | +# VUE_APP_BASE_API = 'http://erp_test.lvqianmeiye.com' | |
| 6 | +VUE_APP_BASE_API = 'http://localhost:2011' | |
| 7 | 7 | VUE_APP_BASE_WSS = 'ws://192.168.110.45:2011/websocket' | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs
| ... | ... | @@ -10,91 +10,91 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsage |
| 10 | 10 | /// <summary> |
| 11 | 11 | /// 使用记录ID |
| 12 | 12 | /// </summary> |
| 13 | - public string Id { get; set; } | |
| 13 | + public string id { get; set; } | |
| 14 | 14 | |
| 15 | 15 | /// <summary> |
| 16 | 16 | /// 产品ID |
| 17 | 17 | /// </summary> |
| 18 | - public string ProductId { get; set; } | |
| 18 | + public string productId { get; set; } | |
| 19 | 19 | |
| 20 | 20 | /// <summary> |
| 21 | 21 | /// 产品名称 |
| 22 | 22 | /// </summary> |
| 23 | - public string ProductName { get; set; } | |
| 23 | + public string productName { get; set; } | |
| 24 | 24 | |
| 25 | 25 | /// <summary> |
| 26 | 26 | /// 产品类别 |
| 27 | 27 | /// </summary> |
| 28 | - public string ProductCategory { get; set; } | |
| 28 | + public string productCategory { get; set; } | |
| 29 | 29 | |
| 30 | 30 | /// <summary> |
| 31 | 31 | /// 产品价格 |
| 32 | 32 | /// </summary> |
| 33 | - public decimal ProductPrice { get; set; } | |
| 33 | + public decimal productPrice { get; set; } | |
| 34 | 34 | |
| 35 | 35 | /// <summary> |
| 36 | 36 | /// 门店ID |
| 37 | 37 | /// </summary> |
| 38 | - public string StoreId { get; set; } | |
| 38 | + public string storeId { get; set; } | |
| 39 | 39 | |
| 40 | 40 | /// <summary> |
| 41 | 41 | /// 门店名称 |
| 42 | 42 | /// </summary> |
| 43 | - public string StoreName { get; set; } | |
| 43 | + public string storeName { get; set; } | |
| 44 | 44 | |
| 45 | 45 | /// <summary> |
| 46 | 46 | /// 使用时间 |
| 47 | 47 | /// </summary> |
| 48 | - public DateTime UsageTime { get; set; } | |
| 48 | + public DateTime usageTime { get; set; } | |
| 49 | 49 | |
| 50 | 50 | /// <summary> |
| 51 | 51 | /// 使用数量 |
| 52 | 52 | /// </summary> |
| 53 | - public int UsageQuantity { get; set; } | |
| 53 | + public int usageQuantity { get; set; } | |
| 54 | 54 | |
| 55 | 55 | /// <summary> |
| 56 | 56 | /// 关联消耗ID |
| 57 | 57 | /// </summary> |
| 58 | - public string RelatedConsumeId { get; set; } | |
| 58 | + public string relatedConsumeId { get; set; } | |
| 59 | 59 | |
| 60 | 60 | /// <summary> |
| 61 | 61 | /// 创建人ID |
| 62 | 62 | /// </summary> |
| 63 | - public string CreateUser { get; set; } | |
| 63 | + public string createUser { get; set; } | |
| 64 | 64 | |
| 65 | 65 | /// <summary> |
| 66 | 66 | /// 创建人姓名 |
| 67 | 67 | /// </summary> |
| 68 | - public string CreateUserName { get; set; } | |
| 68 | + public string createUserName { get; set; } | |
| 69 | 69 | |
| 70 | 70 | /// <summary> |
| 71 | 71 | /// 创建时间 |
| 72 | 72 | /// </summary> |
| 73 | - public DateTime CreateTime { get; set; } | |
| 73 | + public DateTime createTime { get; set; } | |
| 74 | 74 | |
| 75 | 75 | /// <summary> |
| 76 | 76 | /// 更新人ID |
| 77 | 77 | /// </summary> |
| 78 | - public string UpdateUser { get; set; } | |
| 78 | + public string updateUser { get; set; } | |
| 79 | 79 | |
| 80 | 80 | /// <summary> |
| 81 | 81 | /// 更新人姓名 |
| 82 | 82 | /// </summary> |
| 83 | - public string UpdateUserName { get; set; } | |
| 83 | + public string updateUserName { get; set; } | |
| 84 | 84 | |
| 85 | 85 | /// <summary> |
| 86 | 86 | /// 更新时间 |
| 87 | 87 | /// </summary> |
| 88 | - public DateTime? UpdateTime { get; set; } | |
| 88 | + public DateTime? updateTime { get; set; } | |
| 89 | 89 | |
| 90 | 90 | /// <summary> |
| 91 | 91 | /// 是否有效(1:有效 0:无效) |
| 92 | 92 | /// </summary> |
| 93 | - public int IsEffective { get; set; } | |
| 93 | + public int isEffective { get; set; } | |
| 94 | 94 | |
| 95 | 95 | /// <summary> |
| 96 | 96 | /// 使用总价值 |
| 97 | 97 | /// </summary> |
| 98 | - public decimal UsageTotalValue { get; set; } | |
| 98 | + public decimal usageTotalValue { get; set; } | |
| 99 | 99 | } |
| 100 | 100 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/EmployeePerformanceStatisticsInput.cs
0 → 100644
| 1 | +using System.ComponentModel.DataAnnotations; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqStatistics | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 员工业绩统计查询输入参数 | |
| 7 | + /// </summary> | |
| 8 | + public class EmployeePerformanceStatisticsInput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 员工ID(必填) | |
| 12 | + /// </summary> | |
| 13 | + [Required(ErrorMessage = "员工ID不能为空")] | |
| 14 | + public string UserId { get; set; } | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 统计月份(格式:YYYYMM,如202510) | |
| 18 | + /// </summary> | |
| 19 | + [Required(ErrorMessage = "统计月份不能为空")] | |
| 20 | + public string StatisticsMonth { get; set; } | |
| 21 | + } | |
| 22 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/EmployeePerformanceStatisticsOutput.cs
0 → 100644
| 1 | +namespace NCC.Extend.Entitys.Dto.LqStatistics | |
| 2 | +{ | |
| 3 | + /// <summary> | |
| 4 | + /// 员工业绩统计数据输出 | |
| 5 | + /// </summary> | |
| 6 | + public class EmployeePerformanceStatisticsOutput | |
| 7 | + { | |
| 8 | + /// <summary> | |
| 9 | + /// 员工ID | |
| 10 | + /// </summary> | |
| 11 | + public string UserId { get; set; } | |
| 12 | + | |
| 13 | + /// <summary> | |
| 14 | + /// 统计月份 | |
| 15 | + /// </summary> | |
| 16 | + public string StatisticsMonth { get; set; } | |
| 17 | + | |
| 18 | + /// <summary> | |
| 19 | + /// 拓客人数 | |
| 20 | + /// </summary> | |
| 21 | + public int InvitationCount { get; set; } | |
| 22 | + | |
| 23 | + /// <summary> | |
| 24 | + /// 邀约人数 | |
| 25 | + /// </summary> | |
| 26 | + public int InviteCount { get; set; } | |
| 27 | + | |
| 28 | + /// <summary> | |
| 29 | + /// 预约人数 | |
| 30 | + /// </summary> | |
| 31 | + public int AppointmentCount { get; set; } | |
| 32 | + | |
| 33 | + /// <summary> | |
| 34 | + /// 开单数量 | |
| 35 | + /// </summary> | |
| 36 | + public int BillingCount { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 开单金额 | |
| 40 | + /// </summary> | |
| 41 | + public decimal BillingAmount { get; set; } | |
| 42 | + | |
| 43 | + /// <summary> | |
| 44 | + /// 消耗数量 | |
| 45 | + /// </summary> | |
| 46 | + public int ConsumeCount { get; set; } | |
| 47 | + | |
| 48 | + /// <summary> | |
| 49 | + /// 消耗金额 | |
| 50 | + /// </summary> | |
| 51 | + public decimal ConsumeAmount { get; set; } | |
| 52 | + | |
| 53 | + /// <summary> | |
| 54 | + /// 退卡数量 | |
| 55 | + /// </summary> | |
| 56 | + public int RefundCount { get; set; } | |
| 57 | + | |
| 58 | + /// <summary> | |
| 59 | + /// 退卡金额 | |
| 60 | + /// </summary> | |
| 61 | + public decimal RefundAmount { get; set; } | |
| 62 | + | |
| 63 | + /// <summary> | |
| 64 | + /// 人头(月度去重客户数) | |
| 65 | + /// </summary> | |
| 66 | + public int HeadCount { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 人次(日度去重客户数) | |
| 70 | + /// </summary> | |
| 71 | + public int PersonCount { get; set; } | |
| 72 | + } | |
| 73 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
| ... | ... | @@ -12,6 +12,7 @@ using NCC.Extend.Entitys.Dto.LqInventoryUsage; |
| 12 | 12 | using NCC.Extend.Entitys.Enum; |
| 13 | 13 | using NCC.Extend.Entitys.lq_inventory; |
| 14 | 14 | using NCC.Extend.Entitys.lq_inventory_usage; |
| 15 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 15 | 16 | using NCC.Extend.Interfaces.LqInventoryUsage; |
| 16 | 17 | using NCC.FriendlyException; |
| 17 | 18 | using NCC.System.Entitys.Permission; |
| ... | ... | @@ -184,23 +185,23 @@ namespace NCC.Extend |
| 184 | 185 | .WhereIF(input.IsEffective.HasValue, x => x.IsEffective == input.IsEffective.Value) |
| 185 | 186 | .Select(x => new LqInventoryUsageListOutput |
| 186 | 187 | { |
| 187 | - Id = x.Id, | |
| 188 | - ProductId = x.ProductId, | |
| 189 | - ProductName = "", | |
| 190 | - ProductCategory = "", | |
| 191 | - ProductPrice = 0, | |
| 192 | - StoreId = x.StoreId, | |
| 193 | - StoreName = "", | |
| 194 | - UsageTime = x.UsageTime, | |
| 195 | - UsageQuantity = x.UsageQuantity, | |
| 196 | - RelatedConsumeId = x.RelatedConsumeId, | |
| 197 | - CreateUser = x.CreateUser, | |
| 198 | - CreateUserName = "", | |
| 199 | - CreateTime = x.CreateTime, | |
| 200 | - UpdateUser = x.UpdateUser, | |
| 201 | - UpdateUserName = "", | |
| 202 | - UpdateTime = x.UpdateTime, | |
| 203 | - IsEffective = x.IsEffective | |
| 188 | + id = x.Id, | |
| 189 | + productId = x.ProductId, | |
| 190 | + productName = "", | |
| 191 | + productCategory = "", | |
| 192 | + productPrice = 0, | |
| 193 | + storeId = x.StoreId, | |
| 194 | + storeName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(u => u.Id == x.StoreId).Select(u => u.Dm), | |
| 195 | + usageTime = x.UsageTime, | |
| 196 | + usageQuantity = x.UsageQuantity, | |
| 197 | + relatedConsumeId = x.RelatedConsumeId, | |
| 198 | + createUser = x.CreateUser, | |
| 199 | + createUserName = "", | |
| 200 | + createTime = x.CreateTime, | |
| 201 | + updateUser = x.UpdateUser, | |
| 202 | + updateUserName = "", | |
| 203 | + updateTime = x.UpdateTime, | |
| 204 | + isEffective = x.IsEffective | |
| 204 | 205 | }) |
| 205 | 206 | .MergeTable() |
| 206 | 207 | .OrderBy(sidx + " " + input.sort) |
| ... | ... | @@ -209,30 +210,30 @@ namespace NCC.Extend |
| 209 | 210 | // 补充产品信息和用户信息 |
| 210 | 211 | foreach (var item in data.list) |
| 211 | 212 | { |
| 212 | - if (!string.IsNullOrEmpty(item.ProductId)) | |
| 213 | + if (!string.IsNullOrEmpty(item.productId)) | |
| 213 | 214 | { |
| 214 | - var product = await _db.Queryable<LqInventoryEntity>().Where(p => p.Id == item.ProductId).FirstAsync(); | |
| 215 | + var product = await _db.Queryable<LqInventoryEntity>().Where(p => p.Id == item.productId).FirstAsync(); | |
| 215 | 216 | if (product != null) |
| 216 | 217 | { |
| 217 | - item.ProductName = product.ProductName; | |
| 218 | - item.ProductCategory = product.ProductCategory; | |
| 219 | - item.ProductPrice = product.Price; | |
| 218 | + item.productName = product.ProductName; | |
| 219 | + item.productCategory = product.ProductCategory; | |
| 220 | + item.productPrice = product.Price; | |
| 220 | 221 | } |
| 221 | 222 | } |
| 222 | - if (!string.IsNullOrEmpty(item.StoreId)) | |
| 223 | + if (!string.IsNullOrEmpty(item.storeId)) | |
| 223 | 224 | { |
| 224 | - var store = await _db.Queryable<UserEntity>().Where(u => u.Id == item.StoreId).FirstAsync(); | |
| 225 | - item.StoreName = store?.RealName ?? ""; | |
| 225 | + var store = await _db.Queryable<UserEntity>().Where(u => u.Id == item.storeId).FirstAsync(); | |
| 226 | + item.storeName = store?.RealName ?? ""; | |
| 226 | 227 | } |
| 227 | - if (!string.IsNullOrEmpty(item.CreateUser)) | |
| 228 | + if (!string.IsNullOrEmpty(item.createUser)) | |
| 228 | 229 | { |
| 229 | - var createUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.CreateUser).FirstAsync(); | |
| 230 | - item.CreateUserName = createUser?.RealName ?? ""; | |
| 230 | + var createUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.createUser).FirstAsync(); | |
| 231 | + item.createUserName = createUser?.RealName ?? ""; | |
| 231 | 232 | } |
| 232 | - if (!string.IsNullOrEmpty(item.UpdateUser)) | |
| 233 | + if (!string.IsNullOrEmpty(item.updateUser)) | |
| 233 | 234 | { |
| 234 | - var updateUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.UpdateUser).FirstAsync(); | |
| 235 | - item.UpdateUserName = updateUser?.RealName ?? ""; | |
| 235 | + var updateUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.updateUser).FirstAsync(); | |
| 236 | + item.updateUserName = updateUser?.RealName ?? ""; | |
| 236 | 237 | } |
| 237 | 238 | } |
| 238 | 239 | |
| ... | ... | @@ -240,15 +241,15 @@ namespace NCC.Extend |
| 240 | 241 | if (!string.IsNullOrWhiteSpace(input.ProductName) || !string.IsNullOrWhiteSpace(input.ProductCategory)) |
| 241 | 242 | { |
| 242 | 243 | data.list = data.list.Where(x => |
| 243 | - (string.IsNullOrWhiteSpace(input.ProductName) || x.ProductName.Contains(input.ProductName)) && | |
| 244 | - (string.IsNullOrWhiteSpace(input.ProductCategory) || x.ProductCategory.Contains(input.ProductCategory)) | |
| 244 | + (string.IsNullOrWhiteSpace(input.ProductName) || x.productName.Contains(input.ProductName)) && | |
| 245 | + (string.IsNullOrWhiteSpace(input.ProductCategory) || x.productCategory.Contains(input.ProductCategory)) | |
| 245 | 246 | ).ToList(); |
| 246 | 247 | } |
| 247 | 248 | |
| 248 | 249 | // 应用门店名称的过滤条件 |
| 249 | 250 | if (!string.IsNullOrWhiteSpace(input.StoreName)) |
| 250 | 251 | { |
| 251 | - data.list = data.list.Where(x => x.StoreName.Contains(input.StoreName)).ToList(); | |
| 252 | + data.list = data.list.Where(x => x.storeName.Contains(input.StoreName)).ToList(); | |
| 252 | 253 | } |
| 253 | 254 | return PageResult<LqInventoryUsageListOutput>.SqlSugarPageResult(data); |
| 254 | 255 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
| ... | ... | @@ -2247,11 +2247,11 @@ namespace NCC.Extend.LqStatistics |
| 2247 | 2247 | GROUP BY jksyj.jkszh, hyhk.md |
| 2248 | 2248 | ) headcount_stats ON jksyj.jkszh = headcount_stats.jkszh AND hyhk.md = headcount_stats.md |
| 2249 | 2249 | LEFT JOIN ( |
| 2250 | - -- 人次统计:日度去重到店数 | |
| 2250 | + -- 人次统计:日度去重客户数(每天同一个客户只算一次) | |
| 2251 | 2251 | SELECT |
| 2252 | 2252 | jksyj.jkszh, |
| 2253 | 2253 | hyhk.md, |
| 2254 | - COUNT(DISTINCT DATE(hyhk.hksj)) as F_PersonCount | |
| 2254 | + COUNT(DISTINCT CONCAT(hyhk.hy, '-', DATE(hyhk.hksj))) as F_PersonCount | |
| 2255 | 2255 | FROM lq_xh_jksyj jksyj |
| 2256 | 2256 | INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id AND hyhk.F_IsEffective = 1 |
| 2257 | 2257 | WHERE jksyj.F_IsEffective = 1 |
| ... | ... | @@ -3382,5 +3382,250 @@ namespace NCC.Extend.LqStatistics |
| 3382 | 3382 | |
| 3383 | 3383 | #endregion |
| 3384 | 3384 | |
| 3385 | + #region 员工业绩统计 | |
| 3386 | + | |
| 3387 | + /// <summary> | |
| 3388 | + /// 获取员工业绩统计数据 | |
| 3389 | + /// </summary> | |
| 3390 | + /// <remarks> | |
| 3391 | + /// 根据员工ID和月份统计员工的完整业绩数据 | |
| 3392 | + /// 包括:拓客人数、邀约人数、预约人数、开单、消耗、退卡、人头、人次 | |
| 3393 | + /// | |
| 3394 | + /// 示例请求: | |
| 3395 | + /// ```json | |
| 3396 | + /// { | |
| 3397 | + /// "userId": "员工ID", | |
| 3398 | + /// "statisticsMonth": "202510" | |
| 3399 | + /// } | |
| 3400 | + /// ``` | |
| 3401 | + /// | |
| 3402 | + /// 参数说明: | |
| 3403 | + /// - userId: 员工ID(必填) | |
| 3404 | + /// - statisticsMonth: 统计月份,格式YYYYMM(必填) | |
| 3405 | + /// | |
| 3406 | + /// 返回字段说明: | |
| 3407 | + /// - UserId: 员工ID | |
| 3408 | + /// - StatisticsMonth: 统计月份 | |
| 3409 | + /// - InvitationCount: 拓客人数 | |
| 3410 | + /// - InviteCount: 邀约人数 | |
| 3411 | + /// - AppointmentCount: 预约人数 | |
| 3412 | + /// - BillingCount: 开单数量 | |
| 3413 | + /// - BillingAmount: 开单金额 | |
| 3414 | + /// - ConsumeCount: 消耗数量 | |
| 3415 | + /// - ConsumeAmount: 消耗金额 | |
| 3416 | + /// - RefundCount: 退卡数量 | |
| 3417 | + /// - RefundAmount: 退卡金额 | |
| 3418 | + /// - HeadCount: 人头(月度去重客户数) | |
| 3419 | + /// - PersonCount: 人次(日度去重客户数) | |
| 3420 | + /// </remarks> | |
| 3421 | + /// <param name="input">查询参数</param> | |
| 3422 | + /// <returns>员工业绩统计数据</returns> | |
| 3423 | + /// <response code="200">成功返回统计数据</response> | |
| 3424 | + /// <response code="400">参数错误</response> | |
| 3425 | + /// <response code="500">服务器错误</response> | |
| 3426 | + [HttpPost("get-employee-performance-statistics")] | |
| 3427 | + public async Task<object> GetEmployeePerformanceStatistics(EmployeePerformanceStatisticsInput input) | |
| 3428 | + { | |
| 3429 | + try | |
| 3430 | + { | |
| 3431 | + if (input == null || string.IsNullOrEmpty(input.StatisticsMonth) || input.StatisticsMonth.Length != 6) | |
| 3432 | + { | |
| 3433 | + throw NCCException.Oh("统计月份格式错误,请使用YYYYMM格式"); | |
| 3434 | + } | |
| 3435 | + | |
| 3436 | + var statisticsMonth = input.StatisticsMonth; | |
| 3437 | + | |
| 3438 | + // 分步统计,确保效率和可维护性 | |
| 3439 | + | |
| 3440 | + // 1. 拓客人数统计 | |
| 3441 | + var invitationCount = await GetInvitationCount(input.UserId, statisticsMonth); | |
| 3442 | + | |
| 3443 | + // 2. 邀约人数统计 | |
| 3444 | + var inviteCount = await GetInviteCount(input.UserId, statisticsMonth); | |
| 3445 | + | |
| 3446 | + // 3. 预约人数统计 | |
| 3447 | + var appointmentCount = await GetAppointmentCount(input.UserId, statisticsMonth); | |
| 3448 | + | |
| 3449 | + // 4. 开单统计(数量、金额) | |
| 3450 | + var billingStats = await GetBillingStats(input.UserId, statisticsMonth); | |
| 3451 | + | |
| 3452 | + // 5. 消耗统计(数量、金额) | |
| 3453 | + var consumeStats = await GetConsumeStats(input.UserId, statisticsMonth); | |
| 3454 | + | |
| 3455 | + // 6. 退卡统计(数量、金额) | |
| 3456 | + var refundStats = await GetRefundStats(input.UserId, statisticsMonth); | |
| 3457 | + | |
| 3458 | + // 7. 人头统计 | |
| 3459 | + var headCount = await GetHeadCount(input.UserId, statisticsMonth); | |
| 3460 | + | |
| 3461 | + // 8. 人次统计 | |
| 3462 | + var personCount = await GetPersonCount(input.UserId, statisticsMonth); | |
| 3463 | + | |
| 3464 | + return new EmployeePerformanceStatisticsOutput | |
| 3465 | + { | |
| 3466 | + UserId = input.UserId, | |
| 3467 | + StatisticsMonth = statisticsMonth, | |
| 3468 | + InvitationCount = invitationCount, | |
| 3469 | + InviteCount = inviteCount, | |
| 3470 | + AppointmentCount = appointmentCount, | |
| 3471 | + BillingCount = billingStats.Count, | |
| 3472 | + BillingAmount = billingStats.Amount, | |
| 3473 | + ConsumeCount = consumeStats.Count, | |
| 3474 | + ConsumeAmount = consumeStats.Amount, | |
| 3475 | + RefundCount = refundStats.Count, | |
| 3476 | + RefundAmount = refundStats.Amount, | |
| 3477 | + HeadCount = headCount, | |
| 3478 | + PersonCount = personCount | |
| 3479 | + }; | |
| 3480 | + } | |
| 3481 | + catch (Exception ex) | |
| 3482 | + { | |
| 3483 | + _logger.LogError(ex, $"获取员工业绩统计数据失败 - 员工ID: {input?.UserId}, 月份: {input?.StatisticsMonth}"); | |
| 3484 | + throw NCCException.Oh($"获取员工业绩统计数据失败: {ex.Message}"); | |
| 3485 | + } | |
| 3486 | + } | |
| 3487 | + | |
| 3488 | + /// <summary> | |
| 3489 | + /// 统计拓客人数 | |
| 3490 | + /// </summary> | |
| 3491 | + private async Task<int> GetInvitationCount(string userId, string month) | |
| 3492 | + { | |
| 3493 | + var sql = $@" | |
| 3494 | + SELECT COUNT(*) as Count | |
| 3495 | + FROM lq_tkjlb | |
| 3496 | + WHERE F_ExpansionUserId = '{userId}' | |
| 3497 | + AND DATE_FORMAT(F_ExpansionTime, '%Y%m') = '{month}'"; | |
| 3498 | + | |
| 3499 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 3500 | + return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0); | |
| 3501 | + } | |
| 3502 | + | |
| 3503 | + /// <summary> | |
| 3504 | + /// 统计邀约人数 | |
| 3505 | + /// </summary> | |
| 3506 | + private async Task<int> GetInviteCount(string userId, string month) | |
| 3507 | + { | |
| 3508 | + var sql = $@" | |
| 3509 | + SELECT COUNT(DISTINCT yykh) as Count | |
| 3510 | + FROM lq_yaoyjl | |
| 3511 | + WHERE yyr = '{userId}' | |
| 3512 | + AND DATE_FORMAT(yysj, '%Y%m') = '{month}'"; | |
| 3513 | + | |
| 3514 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 3515 | + return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0); | |
| 3516 | + } | |
| 3517 | + | |
| 3518 | + /// <summary> | |
| 3519 | + /// 统计预约人数 | |
| 3520 | + /// </summary> | |
| 3521 | + private async Task<int> GetAppointmentCount(string userId, string month) | |
| 3522 | + { | |
| 3523 | + var sql = $@" | |
| 3524 | + SELECT COUNT(DISTINCT gk) as Count | |
| 3525 | + FROM lq_yyjl | |
| 3526 | + WHERE yyr = '{userId}' | |
| 3527 | + AND DATE_FORMAT(F_CreateTime, '%Y%m') = '{month}'"; | |
| 3528 | + | |
| 3529 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 3530 | + return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0); | |
| 3531 | + } | |
| 3532 | + | |
| 3533 | + /// <summary> | |
| 3534 | + /// 统计开单(数量和金额) | |
| 3535 | + /// </summary> | |
| 3536 | + private async Task<(int Count, decimal Amount)> GetBillingStats(string userId, string month) | |
| 3537 | + { | |
| 3538 | + var sql = $@" | |
| 3539 | + SELECT | |
| 3540 | + COUNT(*) as Count, | |
| 3541 | + COALESCE(SUM(jksyj), 0) as Amount | |
| 3542 | + FROM lq_kd_jksyj | |
| 3543 | + WHERE jkszh = '{userId}' | |
| 3544 | + AND F_IsEffective = 1 | |
| 3545 | + AND DATE_FORMAT(yjsj, '%Y%m') = '{month}'"; | |
| 3546 | + | |
| 3547 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 3548 | + var data = result.FirstOrDefault(); | |
| 3549 | + return (Convert.ToInt32(data?.Count ?? 0), Convert.ToDecimal(data?.Amount ?? 0)); | |
| 3550 | + } | |
| 3551 | + | |
| 3552 | + /// <summary> | |
| 3553 | + /// 统计消耗(数量和金额) | |
| 3554 | + /// </summary> | |
| 3555 | + private async Task<(int Count, decimal Amount)> GetConsumeStats(string userId, string month) | |
| 3556 | + { | |
| 3557 | + var sql = $@" | |
| 3558 | + SELECT | |
| 3559 | + COUNT(*) as Count, | |
| 3560 | + COALESCE(SUM(jksyj.jksyj), 0) as Amount | |
| 3561 | + FROM lq_xh_jksyj jksyj | |
| 3562 | + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id | |
| 3563 | + WHERE jksyj.jkszh = '{userId}' | |
| 3564 | + AND jksyj.F_IsEffective = 1 | |
| 3565 | + AND hyhk.F_IsEffective = 1 | |
| 3566 | + AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '{month}'"; | |
| 3567 | + | |
| 3568 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 3569 | + var data = result.FirstOrDefault(); | |
| 3570 | + return (Convert.ToInt32(data?.Count ?? 0), Convert.ToDecimal(data?.Amount ?? 0)); | |
| 3571 | + } | |
| 3572 | + | |
| 3573 | + /// <summary> | |
| 3574 | + /// 统计退卡(数量和金额) | |
| 3575 | + /// </summary> | |
| 3576 | + private async Task<(int Count, decimal Amount)> GetRefundStats(string userId, string month) | |
| 3577 | + { | |
| 3578 | + var sql = $@" | |
| 3579 | + SELECT | |
| 3580 | + COUNT(*) as Count, | |
| 3581 | + COALESCE(SUM(jksyj), 0) as Amount | |
| 3582 | + FROM lq_hytk_jksyj | |
| 3583 | + WHERE jkszh = '{userId}' | |
| 3584 | + AND F_IsEffective = 1 | |
| 3585 | + AND DATE_FORMAT(tksj, '%Y%m') = '{month}'"; | |
| 3586 | + | |
| 3587 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 3588 | + var data = result.FirstOrDefault(); | |
| 3589 | + return (Convert.ToInt32(data?.Count ?? 0), Convert.ToDecimal(data?.Amount ?? 0)); | |
| 3590 | + } | |
| 3591 | + | |
| 3592 | + /// <summary> | |
| 3593 | + /// 统计人头(月度去重客户数) | |
| 3594 | + /// </summary> | |
| 3595 | + private async Task<int> GetHeadCount(string userId, string month) | |
| 3596 | + { | |
| 3597 | + var sql = $@" | |
| 3598 | + SELECT COUNT(DISTINCT hyhk.hy) as Count | |
| 3599 | + FROM lq_xh_jksyj jksyj | |
| 3600 | + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id | |
| 3601 | + WHERE jksyj.jkszh = '{userId}' | |
| 3602 | + AND jksyj.F_IsEffective = 1 | |
| 3603 | + AND hyhk.F_IsEffective = 1 | |
| 3604 | + AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '{month}'"; | |
| 3605 | + | |
| 3606 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 3607 | + return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0); | |
| 3608 | + } | |
| 3609 | + | |
| 3610 | + /// <summary> | |
| 3611 | + /// 统计人次(日度去重客户数) | |
| 3612 | + /// </summary> | |
| 3613 | + private async Task<int> GetPersonCount(string userId, string month) | |
| 3614 | + { | |
| 3615 | + var sql = $@" | |
| 3616 | + SELECT COUNT(DISTINCT CONCAT(hyhk.hy, '-', DATE(hyhk.hksj))) as Count | |
| 3617 | + FROM lq_xh_jksyj jksyj | |
| 3618 | + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id | |
| 3619 | + WHERE jksyj.jkszh = '{userId}' | |
| 3620 | + AND jksyj.F_IsEffective = 1 | |
| 3621 | + AND hyhk.F_IsEffective = 1 | |
| 3622 | + AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '{month}'"; | |
| 3623 | + | |
| 3624 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | |
| 3625 | + return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0); | |
| 3626 | + } | |
| 3627 | + | |
| 3628 | + #endregion | |
| 3629 | + | |
| 3385 | 3630 | } |
| 3386 | 3631 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqXmzlService.cs
| ... | ... | @@ -536,8 +536,10 @@ namespace NCC.Extend.LqXmzl |
| 536 | 536 | // 门店过滤(通过开单记录关联) |
| 537 | 537 | if (!string.IsNullOrEmpty(input.StoreId)) |
| 538 | 538 | { |
| 539 | - query = query.InnerJoin<LqKdKdjlbEntity>((px, kd) => px.Glkdbh == kd.Id) | |
| 540 | - .Where((px, kd) => kd.Djmd == input.StoreId && kd.IsEffective == 1); | |
| 539 | + // 使用子查询过滤门店 - 在SQL层面过滤,避免别名问题 | |
| 540 | + query = query.Where(x => SqlFunc.Subqueryable<LqKdKdjlbEntity>() | |
| 541 | + .Where(kd => kd.Id == x.Glkdbh && kd.Djmd == input.StoreId && kd.IsEffective == 1) | |
| 542 | + .Any()); | |
| 541 | 543 | } |
| 542 | 544 | |
| 543 | 545 | var result = await query |
| ... | ... | @@ -568,8 +570,10 @@ namespace NCC.Extend.LqXmzl |
| 568 | 570 | |
| 569 | 571 | if (!string.IsNullOrEmpty(input.StoreId)) |
| 570 | 572 | { |
| 571 | - memberCountQuery = memberCountQuery.InnerJoin<LqKdKdjlbEntity>((px, kd) => px.Glkdbh == kd.Id) | |
| 572 | - .Where((px, kd) => kd.Djmd == input.StoreId && kd.IsEffective == 1); | |
| 573 | + // 使用子查询过滤门店 - 在SQL层面过滤,避免别名问题 | |
| 574 | + memberCountQuery = memberCountQuery.Where(x => SqlFunc.Subqueryable<LqKdKdjlbEntity>() | |
| 575 | + .Where(kd => kd.Id == x.Glkdbh && kd.Djmd == input.StoreId && kd.IsEffective == 1) | |
| 576 | + .Any()); | |
| 573 | 577 | } |
| 574 | 578 | |
| 575 | 579 | var memberStats = await memberCountQuery |
| ... | ... | @@ -605,8 +609,10 @@ namespace NCC.Extend.LqXmzl |
| 605 | 609 | // 门店过滤(通过耗卡记录关联) |
| 606 | 610 | if (!string.IsNullOrEmpty(input.StoreId)) |
| 607 | 611 | { |
| 608 | - query = query.InnerJoin<LqXhHyhkEntity>((px, xh) => px.ConsumeInfoId == xh.Id) | |
| 609 | - .Where((px, xh) => xh.Md == input.StoreId && xh.IsEffective == 1); | |
| 612 | + // 使用子查询过滤门店 - 在SQL层面过滤,避免别名问题 | |
| 613 | + query = query.Where(x => SqlFunc.Subqueryable<LqXhHyhkEntity>() | |
| 614 | + .Where(xh => xh.Id == x.ConsumeInfoId && xh.Md == input.StoreId && xh.IsEffective == 1) | |
| 615 | + .Any()); | |
| 610 | 616 | } |
| 611 | 617 | |
| 612 | 618 | var result = await query |
| ... | ... | @@ -645,8 +651,10 @@ namespace NCC.Extend.LqXmzl |
| 645 | 651 | // 门店过滤(通过退卡记录关联) |
| 646 | 652 | if (!string.IsNullOrEmpty(input.StoreId)) |
| 647 | 653 | { |
| 648 | - query = query.InnerJoin<LqHytkHytkEntity>((mx, hytk) => mx.RefundInfoId == hytk.Id) | |
| 649 | - .Where((mx, hytk) => hytk.Md == input.StoreId && hytk.IsEffective == 1); | |
| 654 | + // 使用子查询过滤门店 - 在SQL层面过滤,避免别名问题 | |
| 655 | + query = query.Where(x => SqlFunc.Subqueryable<LqHytkHytkEntity>() | |
| 656 | + .Where(hytk => hytk.Id == x.RefundInfoId && hytk.Md == input.StoreId && hytk.IsEffective == 1) | |
| 657 | + .Any()); | |
| 650 | 658 | } |
| 651 | 659 | |
| 652 | 660 | var result = await query | ... | ... |