Commit 83e25a9eb361b7bace0eb8c2be0cba26d0a24fab

Authored by “wangming”
1 parent 73e7bf69

feat: 统一日报服务时间参数格式,支持自定义时间范围

- 新增GetTimeRange辅助方法,统一处理时间参数
- 更新所有日报服务方法的输入参数为StartTime和EndTime
- 未传入参数时默认统计本月数据
- 支持灵活指定任意时间范围进行统计
- 修复order关键字冲突问题,改为ord别名

涉及方法:
- GetStoreDailyStatistics: 门店每日运营统计
- GetStorePerformanceCompletion: 门店业绩完成情况
- GetBusinessUnitPerformanceCompletion: 事业部业绩完成情况
- GetTianwangGroupPerformanceCompletion: 天王团业绩完成情况
- GetManagerPerformanceCompletion: 经理业绩完成情况
- GetManagerSummaryPerformanceCompletion: 经理汇总业绩
- GetTechTeacherDailyStatistics: 科技部老师统计
Showing 15 changed files with 1499 additions and 0 deletions
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 事业部业绩完成情况输入参数
  8 + /// </summary>
  9 + public class BusinessUnitPerformanceCompletionInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 事业部ID列表(可选,为空则查询所有事业部)
  23 + /// </summary>
  24 + public List<string> BusinessUnitIds { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 事业部业绩完成情况输出结果
  8 + /// </summary>
  9 + public class BusinessUnitPerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 事业部ID
  13 + /// </summary>
  14 + public string BusinessUnitId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 事业部名称
  18 + /// </summary>
  19 + public string BusinessUnitName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 目标业绩(管理门店的生命线总和)
  23 + /// </summary>
  24 + public decimal TargetPerformance { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 完成业绩(管理门店的开单业绩总和)
  28 + /// </summary>
  29 + public decimal CompletedPerformance { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 完成率(百分比)
  33 + /// </summary>
  34 + public decimal CompletionRate { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 门店数量
  38 + /// </summary>
  39 + public int StoreCount { get; set; }
  40 + }
  41 +}
  42 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/ManagerPerformanceCompletionInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 经理业绩完成情况输入参数
  8 + /// </summary>
  9 + public class ManagerPerformanceCompletionInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 经理用户ID(可选)
  23 + /// </summary>
  24 + public string ManagerId { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/ManagerPerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 经理业绩完成情况输出结果
  8 + /// </summary>
  9 + public class ManagerPerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 经理用户ID
  13 + /// </summary>
  14 + public string ManagerId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 经理姓名
  18 + /// </summary>
  19 + public string ManagerName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 门店ID
  23 + /// </summary>
  24 + public string StoreId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 门店名称
  28 + /// </summary>
  29 + public string StoreName { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 目标业绩一阶段(smx1)
  33 + /// </summary>
  34 + public decimal Target1 { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 目标业绩二阶段(smx2)
  38 + /// </summary>
  39 + public decimal? Target2 { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 目标业绩三阶段(smx3)
  43 + /// </summary>
  44 + public decimal? Target3 { get; set; }
  45 +
  46 + /// <summary>
  47 + /// 完成业绩
  48 + /// </summary>
  49 + public decimal CompletedPerformance { get; set; }
  50 +
  51 + /// <summary>
  52 + /// 完成率一阶段(%)
  53 + /// </summary>
  54 + public decimal CompletionRate1 { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 完成率二阶段(%)
  58 + /// </summary>
  59 + public decimal? CompletionRate2 { get; set; }
  60 +
  61 + /// <summary>
  62 + /// 完成率三阶段(%)
  63 + /// </summary>
  64 + public decimal? CompletionRate3 { get; set; }
  65 + }
  66 +}
  67 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/ManagerSummaryPerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 经理汇总业绩完成情况输出结果
  8 + /// </summary>
  9 + public class ManagerSummaryPerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 经理用户ID
  13 + /// </summary>
  14 + public string ManagerId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 经理姓名
  18 + /// </summary>
  19 + public string ManagerName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 总目标业绩一阶段
  23 + /// </summary>
  24 + public decimal TotalTarget1 { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 总目标业绩二阶段
  28 + /// </summary>
  29 + public decimal TotalTarget2 { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 总目标业绩三阶段
  33 + /// </summary>
  34 + public decimal TotalTarget3 { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 总完成业绩
  38 + /// </summary>
  39 + public decimal TotalCompletedPerformance { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 总完成率一阶段(%)
  43 + /// </summary>
  44 + public decimal TotalCompletionRate1 { get; set; }
  45 +
  46 + /// <summary>
  47 + /// 总完成率二阶段(%)
  48 + /// </summary>
  49 + public decimal TotalCompletionRate2 { get; set; }
  50 +
  51 + /// <summary>
  52 + /// 总完成率三阶段(%)
  53 + /// </summary>
  54 + public decimal TotalCompletionRate3 { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 管理门店数量
  58 + /// </summary>
  59 + public int StoreCount { get; set; }
  60 + }
  61 +}
  62 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StoreDailyStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 门店每日统计输入参数
  8 + /// </summary>
  9 + public class StoreDailyStatisticsInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 门店ID列表(可选,为空则查询所有门店)
  23 + /// </summary>
  24 + public List<string> StoreIds { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StoreDailyStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 门店每日统计输出结果
  8 + /// </summary>
  9 + public class StoreDailyStatisticsOutput
  10 + {
  11 + /// <summary>
  12 + /// 门店ID
  13 + /// </summary>
  14 + public string StoreId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 门店名称
  18 + /// </summary>
  19 + public string StoreName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 在职员工数
  23 + /// </summary>
  24 + public int EmployeeCount { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 人头数(去重后的消费会员数)
  28 + /// </summary>
  29 + public int HeadCount { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 人次(日度去重客户数)
  33 + /// </summary>
  34 + public int PersonCount { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 项目数(消耗的项目总数)
  38 + /// </summary>
  39 + public int ProjectCount { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 消耗业绩(总金额)
  43 + /// </summary>
  44 + public decimal ConsumePerformance { get; set; }
  45 + }
  46 +}
  47 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StorePerformanceCompletionInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 门店业绩完成情况输入参数
  8 + /// </summary>
  9 + public class StorePerformanceCompletionInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 门店ID列表(可选,为空则查询所有门店)
  23 + /// </summary>
  24 + public List<string> StoreIds { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StorePerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 门店业绩完成情况输出结果
  8 + /// </summary>
  9 + public class StorePerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 门店ID
  13 + /// </summary>
  14 + public string StoreId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 门店名称
  18 + /// </summary>
  19 + public string StoreName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 目标业绩(生命线)
  23 + /// </summary>
  24 + public decimal TargetPerformance { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 完成业绩(开单业绩)
  28 + /// </summary>
  29 + public decimal CompletedPerformance { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 完成率(百分比)
  33 + /// </summary>
  34 + public decimal CompletionRate { get; set; }
  35 + }
  36 +}
  37 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TechTeacherDailyStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 科技部老师统计输入参数
  8 + /// </summary>
  9 + public class TechTeacherDailyStatisticsInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 科技部ID(可选,不传则查询所有科技部)
  23 + /// </summary>
  24 + public string TechDepartmentId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 老师ID列表(可选)
  28 + /// </summary>
  29 + public List<string> TeacherIds { get; set; }
  30 + }
  31 +}
  32 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TechTeacherDailyStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 科技部老师统计输出结果
  8 + /// </summary>
  9 + public class TechTeacherDailyStatisticsOutput
  10 + {
  11 + /// <summary>
  12 + /// 科技部ID
  13 + /// </summary>
  14 + public string TechDepartmentId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 科技部名称
  18 + /// </summary>
  19 + public string TechDepartmentName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 老师ID
  23 + /// </summary>
  24 + public string TeacherId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 老师姓名
  28 + /// </summary>
  29 + public string TeacherName { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 见客数(不同客户数量)
  33 + /// </summary>
  34 + public int CustomerCount { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 消耗项目数
  38 + /// </summary>
  39 + public int ConsumeProjectCount { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 消耗业绩
  43 + /// </summary>
  44 + public decimal ConsumeAchievement { get; set; }
  45 +
  46 + /// <summary>
  47 + /// 开单业绩
  48 + /// </summary>
  49 + public decimal OrderAchievement { get; set; }
  50 + }
  51 +}
  52 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 天王团业绩完成情况输入参数
  8 + /// </summary>
  9 + public class TianwangGroupPerformanceCompletionInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 天王团部门ID列表(可选,为空则查询所有天王团)
  23 + /// </summary>
  24 + public List<string> DepartmentIds { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 天王团业绩完成情况输出结果
  8 + /// </summary>
  9 + public class TianwangGroupPerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 部门ID
  13 + /// </summary>
  14 + public string DepartmentId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 部门名称
  18 + /// </summary>
  19 + public string DepartmentName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 目标业绩(管理门店的生命线总和)
  23 + /// </summary>
  24 + public decimal TargetPerformance { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 完成业绩(管理门店的开单业绩总和)
  28 + /// </summary>
  29 + public decimal CompletedPerformance { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 完成率(百分比)
  33 + /// </summary>
  34 + public decimal CompletionRate { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 门店数量
  38 + /// </summary>
  39 + public int StoreCount { get; set; }
  40 + }
  41 +}
  42 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqDailyReport/ILqDailyReportService.cs 0 → 100644
  1 +using System.Collections.Generic;
  2 +using System.Threading.Tasks;
  3 +using NCC.Extend.Entitys.Dto.LqDailyReport;
  4 +
  5 +namespace NCC.Extend.Interfaces.LqDailyReport
  6 +{
  7 + /// <summary>
  8 + /// 日报服务接口
  9 + /// </summary>
  10 + public interface ILqDailyReportService
  11 + {
  12 + /// <summary>
  13 + /// 获取门店每日运营统计数据
  14 + /// </summary>
  15 + /// <param name="input">查询参数</param>
  16 + /// <returns>门店统计数据列表</returns>
  17 + Task<List<StoreDailyStatisticsOutput>> GetStoreDailyStatistics(StoreDailyStatisticsInput input);
  18 +
  19 + /// <summary>
  20 + /// 获取门店业绩完成情况
  21 + /// </summary>
  22 + /// <param name="input">查询参数</param>
  23 + /// <returns>门店业绩完成情况列表</returns>
  24 + Task<List<StorePerformanceCompletionOutput>> GetStorePerformanceCompletion(StorePerformanceCompletionInput input);
  25 +
  26 + /// <summary>
  27 + /// 获取事业部业绩完成情况
  28 + /// </summary>
  29 + /// <param name="input">查询参数</param>
  30 + /// <returns>事业部业绩完成情况列表</returns>
  31 + Task<List<BusinessUnitPerformanceCompletionOutput>> GetBusinessUnitPerformanceCompletion(BusinessUnitPerformanceCompletionInput input);
  32 +
  33 + /// <summary>
  34 + /// 获取天王团业绩完成情况
  35 + /// </summary>
  36 + /// <param name="input">查询参数</param>
  37 + /// <returns>天王团业绩完成情况列表</returns>
  38 + Task<List<TianwangGroupPerformanceCompletionOutput>> GetTianwangGroupPerformanceCompletion(TianwangGroupPerformanceCompletionInput input);
  39 +
  40 + /// <summary>
  41 + /// 获取经理业绩完成情况
  42 + /// </summary>
  43 + /// <param name="input">查询参数</param>
  44 + /// <returns>经理业绩完成情况列表</returns>
  45 + Task<List<ManagerPerformanceCompletionOutput>> GetManagerPerformanceCompletion(ManagerPerformanceCompletionInput input);
  46 +
  47 + /// <summary>
  48 + /// 获取经理汇总业绩完成情况
  49 + /// </summary>
  50 + /// <param name="input">查询参数</param>
  51 + /// <returns>经理汇总业绩完成情况列表</returns>
  52 + Task<List<ManagerSummaryPerformanceCompletionOutput>> GetManagerSummaryPerformanceCompletion(ManagerPerformanceCompletionInput input);
  53 +
  54 + /// <summary>
  55 + /// 获取科技部老师日报统计
  56 + /// </summary>
  57 + /// <param name="input">查询参数</param>
  58 + /// <returns>科技部老师统计列表</returns>
  59 + Task<List<TechTeacherDailyStatisticsOutput>> GetTechTeacherDailyStatistics(TechTeacherDailyStatisticsInput input);
  60 + }
  61 +}
  62 +
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs 0 → 100644
  1 +using NCC.Common.Core.Manager;
  2 +using NCC.Common.Enum;
  3 +using NCC.Common.Extension;
  4 +using NCC.Common.Filter;
  5 +using NCC.Dependency;
  6 +using NCC.DynamicApiController;
  7 +using NCC.FriendlyException;
  8 +using NCC.Extend.Interfaces.LqDailyReport;
  9 +using Mapster;
  10 +using Microsoft.AspNetCore.Mvc;
  11 +using SqlSugar;
  12 +using System;
  13 +using System.Collections.Generic;
  14 +using System.Linq;
  15 +using System.Threading.Tasks;
  16 +using Yitter.IdGenerator;
  17 +using NCC.Common.Helper;
  18 +using NCC.JsonSerialization;
  19 +using NCC.Common.Model.NPOI;
  20 +using NCC.Common.Configuration;
  21 +using NCC.DataEncryption;
  22 +using NCC.ClayObject;
  23 +using NCC.Extend.Entitys.Dto.LqDailyReport;
  24 +using NCC.Extend.Entitys.lq_kd_kdjlb;
  25 +
  26 +namespace NCC.Extend
  27 +{
  28 + /// <summary>
  29 + /// 日报服务
  30 + /// </summary>
  31 + [ApiDescriptionSettings(Tag = "绿纤日报服务", Name = "LqDailyReport", Order = 200)]
  32 + [Route("api/Extend/[controller]")]
  33 + public class LqDailyReportService : ILqDailyReportService, IDynamicApiController, ITransient
  34 + {
  35 + private readonly ISqlSugarRepository<LqKdKdjlbEntity> _repository;
  36 + private readonly SqlSugarScope _db;
  37 + private readonly IUserManager _userManager;
  38 +
  39 + /// <summary>
  40 + /// 初始化一个<see cref="LqDailyReportService"/>类型的新实例
  41 + /// </summary>
  42 + public LqDailyReportService(
  43 + ISqlSugarRepository<LqKdKdjlbEntity> repository,
  44 + IUserManager userManager)
  45 + {
  46 + _repository = repository;
  47 + _db = _repository.Context;
  48 + _userManager = userManager;
  49 + }
  50 +
  51 + /// <summary>
  52 + /// 获取时间范围,如果未传入则默认为本月
  53 + /// </summary>
  54 + private (DateTime startDate, DateTime endDate) GetTimeRange(DateTime? startTime, DateTime? endTime)
  55 + {
  56 + var now = DateTime.Now;
  57 + var start = startTime ?? new DateTime(now.Year, now.Month, 1);
  58 + var end = endTime ?? now;
  59 +
  60 + // 确保开始时间不晚于结束时间
  61 + if (start > end)
  62 + {
  63 + var temp = start;
  64 + start = end;
  65 + end = temp;
  66 + }
  67 +
  68 + return (start, end);
  69 + }
  70 +
  71 + #region 获取门店每日运营统计数据
  72 + /// <summary>
  73 + /// 获取门店每日运营统计数据
  74 + /// </summary>
  75 + /// <remarks>
  76 + /// 根据指定时间范围统计各门店的运营数据,包括在职员工数、人头数、人次、项目数、消耗业绩等信息
  77 + ///
  78 + /// 示例请求:
  79 + /// ```json
  80 + /// {
  81 + /// "startTime": "2025-10-01",
  82 + /// "endTime": "2025-10-27",
  83 + /// "storeIds": ["门店ID1", "门店ID2"]
  84 + /// }
  85 + /// ```
  86 + ///
  87 + /// 参数说明:
  88 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  89 + /// - endTime: 结束时间,可选,不传则默认为今天
  90 + /// - storeIds: 门店ID列表,可选,不传则查询所有门店
  91 + ///
  92 + /// 返回说明:
  93 + /// - StoreId: 门店ID
  94 + /// - StoreName: 门店名称
  95 + /// - EmployeeCount: 在职员工数(F_EnabledMark=1且有岗位)
  96 + /// - HeadCount: 人头数(去重后的消费会员数)
  97 + /// - PersonCount: 人次(日度去重客户数,同一天同一客户只算一次)
  98 + /// - ProjectCount: 项目数(消耗的项目总数)
  99 + /// - ConsumePerformance: 消耗业绩(总金额)
  100 + /// </remarks>
  101 + /// <param name="input">查询参数</param>
  102 + /// <returns>门店统计数据列表</returns>
  103 + /// <response code="200">成功返回门店统计列表</response>
  104 + /// <response code="400">日期格式错误或参数无效</response>
  105 + /// <response code="500">服务器内部错误</response>
  106 + [HttpPost("get-store-daily-statistics")]
  107 + public async Task<List<StoreDailyStatisticsOutput>> GetStoreDailyStatistics(StoreDailyStatisticsInput input)
  108 + {
  109 + // 获取时间范围
  110 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  111 +
  112 + // 构建门店过滤条件
  113 + var storeFilter = "";
  114 + if (input.StoreIds != null && input.StoreIds.Any())
  115 + {
  116 + var storeIdsStr = string.Join("','", input.StoreIds);
  117 + storeFilter = $"AND consume.Md IN ('{storeIdsStr}')";
  118 + }
  119 +
  120 + // SQL查询:获取门店每日运营数据
  121 + var sql = $@"
  122 + SELECT
  123 + consume.Md as StoreId,
  124 + MAX(store.dm) as StoreName,
  125 + -- 在职员工数
  126 + COALESCE((
  127 + SELECT COUNT(DISTINCT u.F_Id)
  128 + FROM BASE_USER u
  129 + WHERE u.F_MDID = consume.Md
  130 + AND u.F_EnabledMark = 1
  131 + AND (u.F_DeleteMark IS NULL OR u.F_DeleteMark = 0)
  132 + AND u.F_GW IS NOT NULL
  133 + ), 0) as EmployeeCount,
  134 + -- 人头数(去重后的消费会员数)
  135 + COALESCE(COUNT(DISTINCT consume.Hy), 0) as HeadCount,
  136 + -- 人次(日度去重客户数)
  137 + COALESCE(COUNT(DISTINCT CONCAT(consume.Hy, '-', DATE(consume.Hksj))), 0) as PersonCount,
  138 + -- 项目数(消耗的项目总数)
  139 + COALESCE(SUM(project.F_ProjectNumber), 0) as ProjectCount,
  140 + -- 消耗业绩(总金额)
  141 + COALESCE(SUM(project.F_TotalPrice), 0) as ConsumePerformance
  142 + FROM lq_xh_hyhk consume
  143 + LEFT JOIN lq_mdxx store ON consume.Md = store.F_Id
  144 + LEFT JOIN lq_xh_pxmx project ON consume.F_Id = project.F_ConsumeInfoId AND project.F_IsEffective = 1
  145 + WHERE consume.F_IsEffective = 1
  146 + AND DATE(consume.Hksj) >= '{startDate:yyyy-MM-dd}'
  147 + AND DATE(consume.Hksj) <= '{endDate:yyyy-MM-dd}'
  148 + {storeFilter}
  149 + GROUP BY consume.Md
  150 + ORDER BY ConsumePerformance DESC";
  151 +
  152 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  153 +
  154 + var outputList = result.Select(item => new StoreDailyStatisticsOutput
  155 + {
  156 + StoreId = item.StoreId,
  157 + StoreName = item.StoreName,
  158 + EmployeeCount = Convert.ToInt32(item.EmployeeCount),
  159 + HeadCount = Convert.ToInt32(item.HeadCount),
  160 + PersonCount = Convert.ToInt32(item.PersonCount),
  161 + ProjectCount = Convert.ToInt32(item.ProjectCount),
  162 + ConsumePerformance = Convert.ToDecimal(item.ConsumePerformance)
  163 + }).ToList();
  164 + return outputList;
  165 + }
  166 + #endregion
  167 +
  168 + #region 获取门店业绩完成情况
  169 + /// <summary>
  170 + /// 获取门店业绩完成情况
  171 + /// </summary>
  172 + /// <remarks>
  173 + /// 根据指定日期统计各门店的业绩完成情况,包括目标业绩、完成业绩、完成率
  174 + ///
  175 + /// 示例请求:
  176 + /// ```json
  177 + /// {
  178 + /// "startTime": "2025-10-01",
  179 + /// "endTime": "2025-10-22",
  180 + /// "storeIds": ["门店ID1", "门店ID2"]
  181 + /// }
  182 + /// ```
  183 + ///
  184 + /// 参数说明:
  185 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  186 + /// - endTime: 结束时间,可选,不传则默认为今天
  187 + /// - storeIds: 门店ID列表,可选,不传则查询所有门店
  188 + ///
  189 + /// 返回说明:
  190 + /// - StoreId: 门店ID
  191 + /// - StoreName: 门店名称
  192 + /// - TargetPerformance: 目标业绩(生命线,来自门店信息表)
  193 + /// - CompletedPerformance: 完成业绩(当月1号至统计日期的开单业绩总和)
  194 + /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100)
  195 + /// </remarks>
  196 + /// <param name="input">查询参数</param>
  197 + /// <returns>门店业绩完成情况列表</returns>
  198 + /// <response code="200">成功返回门店业绩完成情况列表</response>
  199 + /// <response code="400">日期格式错误或参数无效</response>
  200 + /// <response code="500">服务器内部错误</response>
  201 + [HttpPost("get-store-performance-completion")]
  202 + public async Task<List<StorePerformanceCompletionOutput>> GetStorePerformanceCompletion(StorePerformanceCompletionInput input)
  203 + {
  204 + // 获取时间范围
  205 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  206 +
  207 + // 构建门店过滤条件
  208 + var storeFilter = "";
  209 + if (input.StoreIds != null && input.StoreIds.Any())
  210 + {
  211 + var storeIdsStr = string.Join("','", input.StoreIds);
  212 + storeFilter = $"AND store.F_Id IN ('{storeIdsStr}')";
  213 + }
  214 +
  215 + // SQL查询:获取门店业绩完成情况
  216 + var sql = $@"
  217 + SELECT
  218 + store.F_Id as StoreId,
  219 + store.dm as StoreName,
  220 + -- 目标业绩(生命线)
  221 + COALESCE(store.xsyj, 0) as TargetPerformance,
  222 + -- 完成业绩(开单业绩总和)
  223 + COALESCE((
  224 + SELECT SUM(billing.sfyj)
  225 + FROM lq_kd_kdjlb billing
  226 + WHERE billing.djmd = store.F_Id
  227 + AND billing.F_IsEffective = 1
  228 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  229 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  230 + ), 0) as CompletedPerformance
  231 + FROM lq_mdxx store
  232 + WHERE 1=1 {storeFilter}
  233 + ORDER BY CompletedPerformance DESC";
  234 +
  235 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  236 +
  237 + var outputList = result.Select(item =>
  238 + {
  239 + var targetPerformance = Convert.ToDecimal(item.TargetPerformance);
  240 + var completedPerformance = Convert.ToDecimal(item.CompletedPerformance);
  241 + var completionRate = targetPerformance > 0 ? (completedPerformance / targetPerformance * 100m) : 0m;
  242 +
  243 + return new StorePerformanceCompletionOutput
  244 + {
  245 + StoreId = item.StoreId,
  246 + StoreName = item.StoreName,
  247 + TargetPerformance = targetPerformance,
  248 + CompletedPerformance = completedPerformance,
  249 + CompletionRate = decimal.Round(completionRate, 2)
  250 + };
  251 + }).ToList();
  252 +
  253 + return outputList;
  254 + }
  255 + #endregion
  256 +
  257 + #region 获取事业部业绩完成情况
  258 + /// <summary>
  259 + /// 获取事业部业绩完成情况
  260 + /// </summary>
  261 + /// <remarks>
  262 + /// 根据指定日期统计各事业部的业绩完成情况,包括目标业绩、完成业绩、完成率
  263 + ///
  264 + /// 示例请求:
  265 + /// ```json
  266 + /// {
  267 + /// "startTime": "2025-10-01",
  268 + /// "endTime": "2025-10-22",
  269 + /// "businessUnitIds": ["事业部ID1", "事业部ID2"]
  270 + /// }
  271 + /// ```
  272 + ///
  273 + /// 参数说明:
  274 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  275 + /// - endTime: 结束时间,可选,不传则默认为今天
  276 + /// - businessUnitIds: 事业部ID列表,可选,不传则查询所有事业部
  277 + ///
  278 + /// 返回说明:
  279 + /// - BusinessUnitId: 事业部ID
  280 + /// - BusinessUnitName: 事业部名称
  281 + /// - TargetPerformance: 目标业绩(管理门店的生命线总和)
  282 + /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和)
  283 + /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100)
  284 + /// - StoreCount: 门店数量
  285 + /// </remarks>
  286 + /// <param name="input">查询参数</param>
  287 + /// <returns>事业部业绩完成情况列表</returns>
  288 + /// <response code="200">成功返回事业部业绩完成情况列表</response>
  289 + /// <response code="400">日期格式错误或参数无效</response>
  290 + /// <response code="500">服务器内部错误</response>
  291 + [HttpPost("get-business-unit-performance-completion")]
  292 + public async Task<List<BusinessUnitPerformanceCompletionOutput>> GetBusinessUnitPerformanceCompletion(BusinessUnitPerformanceCompletionInput input)
  293 + {
  294 + // 获取时间范围
  295 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  296 +
  297 + // 构建事业部过滤条件
  298 + var businessUnitFilter = "";
  299 + if (input.BusinessUnitIds != null && input.BusinessUnitIds.Any())
  300 + {
  301 + var filterUnitIdsStr = string.Join("','", input.BusinessUnitIds);
  302 + businessUnitFilter = $"AND o.F_Id IN ('{filterUnitIdsStr}')";
  303 + }
  304 +
  305 + // 分步查询,提高效率
  306 + // 第一步:获取事业部列表及目标业绩(只统计事业一部到事业六部)
  307 + var businessUnitSql = $@"
  308 + SELECT
  309 + o.F_Id as BusinessUnitId,
  310 + o.F_FullName as BusinessUnitName,
  311 + COALESCE(SUM(store.xsyj), 0) as TargetPerformance,
  312 + COUNT(DISTINCT store.F_Id) as StoreCount
  313 + FROM base_organize o
  314 + LEFT JOIN lq_mdxx store ON store.syb = o.F_Id
  315 + WHERE o.F_Category = 'department'
  316 + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
  317 + AND o.F_FullName IN ('事业一部', '事业二部', '事业三部', '事业四部', '事业五部', '事业六部')
  318 + {businessUnitFilter}
  319 + GROUP BY o.F_Id, o.F_FullName";
  320 +
  321 + var businessUnits = await _db.Ado.SqlQueryAsync<dynamic>(businessUnitSql);
  322 +
  323 + if (businessUnits == null || !businessUnits.Any())
  324 + {
  325 + return new List<BusinessUnitPerformanceCompletionOutput>();
  326 + }
  327 +
  328 + var businessUnitDict = new Dictionary<string, BusinessUnitPerformanceCompletionOutput>();
  329 +
  330 + foreach (var unit in businessUnits)
  331 + {
  332 + businessUnitDict[unit.BusinessUnitId.ToString()] = new BusinessUnitPerformanceCompletionOutput
  333 + {
  334 + BusinessUnitId = unit.BusinessUnitId.ToString(),
  335 + BusinessUnitName = unit.BusinessUnitName.ToString(),
  336 + TargetPerformance = Convert.ToDecimal(unit.TargetPerformance),
  337 + CompletedPerformance = 0,
  338 + CompletionRate = 0,
  339 + StoreCount = Convert.ToInt32(unit.StoreCount)
  340 + };
  341 + }
  342 +
  343 + // 第二步:统计各事业部的完成业绩
  344 + var businessUnitIds = businessUnitDict.Keys.ToList();
  345 + var unitIdsStr = string.Join("','", businessUnitIds);
  346 +
  347 + var completedPerformanceSql = $@"
  348 + SELECT
  349 + store.syb as BusinessUnitId,
  350 + COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance
  351 + FROM lq_kd_kdjlb billing
  352 + INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id
  353 + INNER JOIN base_organize o ON store.syb = o.F_Id
  354 + WHERE billing.F_IsEffective = 1
  355 + AND o.F_Category = 'department'
  356 + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
  357 + AND o.F_FullName IN ('事业一部', '事业二部', '事业三部', '事业四部', '事业五部', '事业六部')
  358 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  359 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  360 + AND store.syb IN ('{unitIdsStr}')
  361 + GROUP BY store.syb";
  362 +
  363 + var completedPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(completedPerformanceSql);
  364 +
  365 + // 第三步:合并数据并计算完成率
  366 + foreach (var item in completedPerformanceData)
  367 + {
  368 + var businessUnitId = item.BusinessUnitId.ToString();
  369 + if (businessUnitDict.ContainsKey(businessUnitId))
  370 + {
  371 + var completedPerformance = Convert.ToDecimal(item.CompletedPerformance);
  372 + var unit = businessUnitDict[businessUnitId];
  373 + unit.CompletedPerformance = completedPerformance;
  374 +
  375 + var completionRate = unit.TargetPerformance > 0
  376 + ? (completedPerformance / unit.TargetPerformance * 100m)
  377 + : 0m;
  378 + unit.CompletionRate = decimal.Round(completionRate, 2);
  379 + }
  380 + }
  381 +
  382 + var outputList = businessUnitDict.Values
  383 + .OrderByDescending(x => x.CompletedPerformance)
  384 + .ToList();
  385 +
  386 + return outputList;
  387 + }
  388 + #endregion
  389 +
  390 + #region 获取天王团业绩完成情况
  391 + /// <summary>
  392 + /// 获取天王团业绩完成情况
  393 + /// </summary>
  394 + /// <remarks>
  395 + /// 根据指定日期统计各天王团的业绩完成情况,包括目标业绩、完成业绩、完成率
  396 + ///
  397 + /// 示例请求:
  398 + /// ```json
  399 + /// {
  400 + /// "startTime": "2025-10-01",
  401 + /// "endTime": "2025-10-22",
  402 + /// "departmentIds": ["部门ID1", "部门ID2"]
  403 + /// }
  404 + /// ```
  405 + ///
  406 + /// 参数说明:
  407 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  408 + /// - endTime: 结束时间,可选,不传则默认为今天
  409 + /// - departmentIds: 天王团部门ID列表,可选,不传则查询所有天王团
  410 + ///
  411 + /// 返回说明:
  412 + /// - DepartmentId: 部门ID
  413 + /// - DepartmentName: 部门名称(教育一部、教育二部、科技一部、科技二部、大项目一部、大项目二部)
  414 + /// - TargetPerformance: 目标业绩(管理门店的生命线总和)
  415 + /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和)
  416 + /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100)
  417 + /// - StoreCount: 门店数量
  418 + /// </remarks>
  419 + /// <param name="input">查询参数</param>
  420 + /// <returns>天王团业绩完成情况列表</returns>
  421 + /// <response code="200">成功返回天王团业绩完成情况列表</response>
  422 + /// <response code="400">日期格式错误或参数无效</response>
  423 + /// <response code="500">服务器内部错误</response>
  424 + [HttpPost("get-tianwang-group-performance-completion")]
  425 + public async Task<List<TianwangGroupPerformanceCompletionOutput>> GetTianwangGroupPerformanceCompletion(TianwangGroupPerformanceCompletionInput input)
  426 + {
  427 + // 获取时间范围
  428 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  429 +
  430 + // 构建部门过滤条件
  431 + var departmentFilter = "";
  432 + if (input.DepartmentIds != null && input.DepartmentIds.Any())
  433 + {
  434 + var filterDeptIdsStr = string.Join("','", input.DepartmentIds);
  435 + departmentFilter = $"AND o.F_Id IN ('{filterDeptIdsStr}')";
  436 + }
  437 +
  438 + // 天王团部门信息(字段名和部门名称的映射)
  439 + var tianwangDepartments = new Dictionary<string, (string fieldName, string[] departmentNames)>
  440 + {
  441 + { "jyb", ("jyb", new[] { "教育一部", "教育二部" }) },
  442 + { "kjb", ("kjb", new[] { "科技一部", "科技二部" }) },
  443 + { "dxmb", ("dxmb", new[] { "大项目一部", "大项目二部" }) }
  444 + };
  445 +
  446 + // 分步查询,提高效率
  447 + var departmentDict = new Dictionary<string, TianwangGroupPerformanceCompletionOutput>();
  448 + var allDeptNames = new List<string>();
  449 +
  450 + // 循环查询每个天王团类型
  451 + foreach (var deptType in tianwangDepartments)
  452 + {
  453 + var deptNamesStr = string.Join("','", deptType.Value.departmentNames);
  454 + allDeptNames.AddRange(deptType.Value.departmentNames);
  455 +
  456 + // 第一步:获取天王团列表及目标业绩
  457 + var departmentSql = $@"
  458 + SELECT
  459 + o.F_Id as DepartmentId,
  460 + o.F_FullName as DepartmentName,
  461 + COALESCE(SUM(store.xsyj), 0) as TargetPerformance,
  462 + COUNT(DISTINCT store.F_Id) as StoreCount
  463 + FROM base_organize o
  464 + LEFT JOIN lq_mdxx store ON store.{deptType.Key} = o.F_Id
  465 + WHERE o.F_Category = 'department'
  466 + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
  467 + AND o.F_FullName IN ('{deptNamesStr}')
  468 + {departmentFilter}
  469 + GROUP BY o.F_Id, o.F_FullName";
  470 +
  471 + var departments = await _db.Ado.SqlQueryAsync<dynamic>(departmentSql);
  472 +
  473 + foreach (var dept in departments ?? Enumerable.Empty<dynamic>())
  474 + {
  475 + if (!departmentDict.ContainsKey(dept.DepartmentId.ToString()))
  476 + {
  477 + departmentDict[dept.DepartmentId.ToString()] = new TianwangGroupPerformanceCompletionOutput
  478 + {
  479 + DepartmentId = dept.DepartmentId.ToString(),
  480 + DepartmentName = dept.DepartmentName.ToString(),
  481 + TargetPerformance = Convert.ToDecimal(dept.TargetPerformance),
  482 + CompletedPerformance = 0,
  483 + CompletionRate = 0,
  484 + StoreCount = Convert.ToInt32(dept.StoreCount)
  485 + };
  486 + }
  487 + }
  488 + }
  489 +
  490 + if (!departmentDict.Any())
  491 + {
  492 + return new List<TianwangGroupPerformanceCompletionOutput>();
  493 + }
  494 +
  495 + // 第二步:统计各天王团的完成业绩(分类型统计)
  496 + var completedPerformanceDataList = new List<dynamic>();
  497 +
  498 + foreach (var deptType in tianwangDepartments)
  499 + {
  500 + var deptIdsStrForQuery = string.Join("','", departmentDict.Keys);
  501 +
  502 + var completedPerformanceSql = $@"
  503 + SELECT
  504 + o.F_Id as DepartmentId,
  505 + COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance
  506 + FROM lq_kd_kdjlb billing
  507 + INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id
  508 + INNER JOIN base_organize o ON store.{deptType.Key} = o.F_Id
  509 + WHERE billing.F_IsEffective = 1
  510 + AND o.F_Category = 'department'
  511 + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
  512 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  513 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  514 + AND o.F_Id IN ('{deptIdsStrForQuery}')
  515 + GROUP BY o.F_Id";
  516 +
  517 + var completedPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(completedPerformanceSql);
  518 + if (completedPerformanceData != null)
  519 + {
  520 + completedPerformanceDataList.AddRange(completedPerformanceData);
  521 + }
  522 + }
  523 +
  524 + // 第三步:合并数据并计算完成率(按部门ID去重累加)
  525 + var departmentPerformanceDict = new Dictionary<string, decimal>();
  526 + foreach (var item in completedPerformanceDataList)
  527 + {
  528 + var departmentId = item.DepartmentId.ToString();
  529 + var completedPerformance = Convert.ToDecimal(item.CompletedPerformance);
  530 +
  531 + if (departmentPerformanceDict.ContainsKey(departmentId))
  532 + {
  533 + departmentPerformanceDict[departmentId] += completedPerformance;
  534 + }
  535 + else
  536 + {
  537 + departmentPerformanceDict[departmentId] = completedPerformance;
  538 + }
  539 + }
  540 +
  541 + // 计算完成率
  542 + foreach (var kvp in departmentPerformanceDict)
  543 + {
  544 + if (departmentDict.ContainsKey(kvp.Key))
  545 + {
  546 + var dept = departmentDict[kvp.Key];
  547 + dept.CompletedPerformance = kvp.Value;
  548 +
  549 + var completionRate = dept.TargetPerformance > 0
  550 + ? (kvp.Value / dept.TargetPerformance * 100m)
  551 + : 0m;
  552 + dept.CompletionRate = decimal.Round(completionRate, 2);
  553 + }
  554 + }
  555 +
  556 + var outputList = departmentDict.Values
  557 + .OrderByDescending(x => x.CompletedPerformance)
  558 + .ToList();
  559 +
  560 + return outputList;
  561 + }
  562 + #endregion
  563 +
  564 + #region 获取经理业绩完成情况
  565 + /// <summary>
  566 + /// 获取经理业绩完成情况
  567 + /// </summary>
  568 + /// <remarks>
  569 + /// 根据指定日期统计各经理在各门店的业绩完成情况,包括目标业绩(三个阶段)、完成业绩、完成率
  570 + ///
  571 + /// 示例请求:
  572 + /// ```json
  573 + /// {
  574 + /// "startTime": "2025-10-01",
  575 + /// "endTime": "2025-10-22",
  576 + /// "managerId": "经理ID"
  577 + /// }
  578 + /// ```
  579 + ///
  580 + /// 参数说明:
  581 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  582 + /// - endTime: 结束时间,可选,不传则默认为今天
  583 + /// - managerId: 经理用户ID,可选,不传则查询所有经理
  584 + ///
  585 + /// 返回说明:
  586 + /// - ManagerId: 经理用户ID
  587 + /// - ManagerName: 经理姓名
  588 + /// - StoreId/StoreName: 门店信息
  589 + /// - Target1/2/3: 目标业绩一/二/三阶段(smx1/smx2/smx3)
  590 + /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和)
  591 + /// - CompletionRate1/2/3: 完成率各阶段(百分比)
  592 + /// </remarks>
  593 + /// <param name="input">查询参数</param>
  594 + /// <returns>经理业绩完成情况列表</returns>
  595 + /// <response code="200">成功返回经理业绩完成情况列表</response>
  596 + /// <response code="400">日期格式错误或参数无效</response>
  597 + /// <response code="500">服务器内部错误</response>
  598 + [HttpPost("get-manager-performance-completion")]
  599 + public async Task<List<ManagerPerformanceCompletionOutput>> GetManagerPerformanceCompletion(ManagerPerformanceCompletionInput input)
  600 + {
  601 + // 获取时间范围
  602 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  603 +
  604 + // 构建经理过滤条件
  605 + var managerFilter = "";
  606 + if (!string.IsNullOrWhiteSpace(input.ManagerId))
  607 + {
  608 + managerFilter = $"AND target.zjl_userid = '{input.ManagerId}'";
  609 + }
  610 +
  611 + // SQL查询:获取经理在各门店的目标业绩和完成业绩
  612 + var sql = $@"
  613 + SELECT
  614 + target.zjl_userid as ManagerId,
  615 + u.F_RealName as ManagerName,
  616 + target.md_id as StoreId,
  617 + store.dm as StoreName,
  618 + target.smx1 as Target1,
  619 + target.smx2 as Target2,
  620 + target.smx3 as Target3,
  621 + -- 完成业绩
  622 + COALESCE((
  623 + SELECT SUM(billing.sfyj)
  624 + FROM lq_kd_kdjlb billing
  625 + WHERE billing.djmd = target.md_id
  626 + AND billing.F_IsEffective = 1
  627 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  628 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  629 + ), 0) as CompletedPerformance
  630 + FROM lq_zjl_mdsmxsz target
  631 + INNER JOIN BASE_USER u ON target.zjl_userid = u.F_Id
  632 + INNER JOIN lq_mdxx store ON target.md_id = store.F_Id
  633 + WHERE target.deletemark = 0
  634 + {managerFilter}
  635 + ORDER BY target.zjl_userid, store.dm";
  636 +
  637 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  638 +
  639 + var outputList = result.Select(item =>
  640 + {
  641 + var target1 = Convert.ToDecimal(item.Target1);
  642 + var target2 = item.Target2 != null ? Convert.ToDecimal(item.Target2) : 0;
  643 + var target3 = item.Target3 != null ? Convert.ToDecimal(item.Target3) : 0;
  644 + var completed = Convert.ToDecimal(item.CompletedPerformance);
  645 +
  646 + var rate1 = target1 > 0 ? (completed / target1 * 100m) : 0m;
  647 + var rate2 = target2 > 0 ? (completed / target2 * 100m) : 0m;
  648 + var rate3 = target3 > 0 ? (completed / target3 * 100m) : 0m;
  649 +
  650 + return new ManagerPerformanceCompletionOutput
  651 + {
  652 + ManagerId = item.ManagerId.ToString(),
  653 + ManagerName = item.ManagerName.ToString(),
  654 + StoreId = item.StoreId.ToString(),
  655 + StoreName = item.StoreName.ToString(),
  656 + Target1 = target1,
  657 + Target2 = target2 > 0 ? target2 : (decimal?)null,
  658 + Target3 = target3 > 0 ? target3 : (decimal?)null,
  659 + CompletedPerformance = completed,
  660 + CompletionRate1 = decimal.Round(rate1, 2),
  661 + CompletionRate2 = target2 > 0 ? decimal.Round(rate2, 2) : (decimal?)null,
  662 + CompletionRate3 = target3 > 0 ? decimal.Round(rate3, 2) : (decimal?)null
  663 + };
  664 + }).ToList();
  665 +
  666 + return outputList;
  667 + }
  668 +
  669 + /// <summary>
  670 + /// 获取经理汇总业绩完成情况
  671 + /// </summary>
  672 + /// <remarks>
  673 + /// 根据指定日期统计各经理的总业绩完成情况,汇总所有管理门店的数据
  674 + ///
  675 + /// 示例请求:
  676 + /// ```json
  677 + /// {
  678 + /// "startTime": "2025-10-01",
  679 + /// "endTime": "2025-10-22",
  680 + /// "managerId": "经理ID"
  681 + /// }
  682 + /// ```
  683 + ///
  684 + /// 参数说明:
  685 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  686 + /// - endTime: 结束时间,可选,不传则默认为今天
  687 + /// - managerId: 经理用户ID,可选,不传则查询所有经理
  688 + ///
  689 + /// 返回说明:
  690 + /// - ManagerId: 经理用户ID
  691 + /// - ManagerName: 经理姓名
  692 + /// - TotalTarget1/2/3: 总目标业绩一/二/三阶段
  693 + /// - TotalCompletedPerformance: 总完成业绩(指定时间范围内的开单业绩总和)
  694 + /// - TotalCompletionRate1/2/3: 总完成率各阶段
  695 + /// - StoreCount: 管理门店数量
  696 + /// </remarks>
  697 + /// <param name="input">查询参数</param>
  698 + /// <returns>经理汇总业绩完成情况列表</returns>
  699 + /// <response code="200">成功返回经理汇总业绩完成情况列表</response>
  700 + /// <response code="400">日期格式错误或参数无效</response>
  701 + /// <response code="500">服务器内部错误</response>
  702 + [HttpPost("get-manager-summary-performance-completion")]
  703 + public async Task<List<ManagerSummaryPerformanceCompletionOutput>> GetManagerSummaryPerformanceCompletion(ManagerPerformanceCompletionInput input)
  704 + {
  705 + // 获取时间范围
  706 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  707 +
  708 + // 构建经理过滤条件
  709 + var managerFilter = "";
  710 + if (!string.IsNullOrWhiteSpace(input.ManagerId))
  711 + {
  712 + managerFilter = $"AND target.zjl_userid = '{input.ManagerId}'";
  713 + }
  714 +
  715 + // SQL查询:获取经理汇总业绩
  716 + var sql = $@"
  717 + SELECT
  718 + target.zjl_userid as ManagerId,
  719 + u.F_RealName as ManagerName,
  720 + SUM(target.smx1) as TotalTarget1,
  721 + COALESCE(SUM(target.smx2), 0) as TotalTarget2,
  722 + COALESCE(SUM(target.smx3), 0) as TotalTarget3,
  723 + COUNT(DISTINCT target.md_id) as StoreCount,
  724 + -- 总完成业绩
  725 + SUM(COALESCE((
  726 + SELECT SUM(billing.sfyj)
  727 + FROM lq_kd_kdjlb billing
  728 + WHERE billing.djmd = target.md_id
  729 + AND billing.F_IsEffective = 1
  730 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  731 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  732 + ), 0)) as TotalCompletedPerformance
  733 + FROM lq_zjl_mdsmxsz target
  734 + INNER JOIN BASE_USER u ON target.zjl_userid = u.F_Id
  735 + INNER JOIN lq_mdxx store ON target.md_id = store.F_Id
  736 + WHERE target.deletemark = 0
  737 + {managerFilter}
  738 + GROUP BY target.zjl_userid, u.F_RealName
  739 + ORDER BY TotalCompletedPerformance DESC";
  740 +
  741 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  742 +
  743 + var outputList = result.Select(item =>
  744 + {
  745 + var totalTarget1 = Convert.ToDecimal(item.TotalTarget1);
  746 + var totalTarget2 = Convert.ToDecimal(item.TotalTarget2);
  747 + var totalTarget3 = Convert.ToDecimal(item.TotalTarget3);
  748 + var totalCompleted = Convert.ToDecimal(item.TotalCompletedPerformance);
  749 +
  750 + var rate1 = totalTarget1 > 0 ? (totalCompleted / totalTarget1 * 100m) : 0m;
  751 + var rate2 = totalTarget2 > 0 ? (totalCompleted / totalTarget2 * 100m) : 0m;
  752 + var rate3 = totalTarget3 > 0 ? (totalCompleted / totalTarget3 * 100m) : 0m;
  753 +
  754 + return new ManagerSummaryPerformanceCompletionOutput
  755 + {
  756 + ManagerId = item.ManagerId.ToString(),
  757 + ManagerName = item.ManagerName.ToString(),
  758 + TotalTarget1 = totalTarget1,
  759 + TotalTarget2 = totalTarget2,
  760 + TotalTarget3 = totalTarget3,
  761 + TotalCompletedPerformance = totalCompleted,
  762 + TotalCompletionRate1 = decimal.Round(rate1, 2),
  763 + TotalCompletionRate2 = decimal.Round(rate2, 2),
  764 + TotalCompletionRate3 = decimal.Round(rate3, 2),
  765 + StoreCount = Convert.ToInt32(item.StoreCount)
  766 + };
  767 + }).ToList();
  768 +
  769 + return outputList;
  770 + }
  771 + #endregion
  772 +
  773 + #region 获取科技部老师统计
  774 + /// <summary>
  775 + /// 获取科技部老师统计
  776 + /// </summary>
  777 + /// <remarks>
  778 + /// 根据指定日期统计科技部老师的业绩情况,包括见客数、消耗项目数、消耗业绩、开单业绩
  779 + ///
  780 + /// 示例请求:
  781 + /// ```json
  782 + /// {
  783 + /// "startTime": "2025-10-01",
  784 + /// "endTime": "2025-10-22",
  785 + /// "techDepartmentId": "科技部ID",
  786 + /// "teacherIds": ["老师ID1", "老师ID2"]
  787 + /// }
  788 + /// ```
  789 + ///
  790 + /// 参数说明:
  791 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  792 + /// - endTime: 结束时间,可选,不传则默认为今天
  793 + /// - techDepartmentId: 科技部ID,可选,不传则查询所有科技部
  794 + /// - teacherIds: 老师ID列表,可选
  795 + ///
  796 + /// 返回说明:
  797 + /// - TechDepartmentId: 科技部ID
  798 + /// - TechDepartmentName: 科技部名称
  799 + /// - TeacherId: 老师ID
  800 + /// - TeacherName: 老师姓名
  801 + /// - CustomerCount: 见客数(不同客户数量)
  802 + /// - ConsumeProjectCount: 消耗项目数
  803 + /// - ConsumeAchievement: 消耗业绩
  804 + /// - OrderAchievement: 开单业绩
  805 + /// </remarks>
  806 + /// <param name="input">查询参数</param>
  807 + /// <returns>科技部老师统计列表</returns>
  808 + /// <response code="200">成功返回科技部老师统计列表</response>
  809 + /// <response code="400">日期格式错误或参数无效</response>
  810 + /// <response code="500">服务器内部错误</response>
  811 + [HttpPost("get-tech-teacher-daily-statistics")]
  812 + public async Task<List<TechTeacherDailyStatisticsOutput>> GetTechTeacherDailyStatistics(TechTeacherDailyStatisticsInput input)
  813 + {
  814 + // 获取时间范围
  815 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  816 +
  817 + // 构建过滤条件
  818 + var techFilter = "";
  819 + if (!string.IsNullOrWhiteSpace(input.TechDepartmentId))
  820 + {
  821 + techFilter = $"AND techDept.F_Id = '{input.TechDepartmentId}'";
  822 + }
  823 +
  824 + var teacherFilter = "";
  825 + if (input.TeacherIds != null && input.TeacherIds.Any())
  826 + {
  827 + var teacherIdsStr = string.Join("','", input.TeacherIds);
  828 + teacherFilter = $"AND consume.kjbls IN ('{teacherIdsStr}')";
  829 + }
  830 +
  831 + var teacherFilterForOrder = "";
  832 + if (input.TeacherIds != null && input.TeacherIds.Any())
  833 + {
  834 + var teacherIdsStr = string.Join("','", input.TeacherIds);
  835 + teacherFilterForOrder = $"AND ord.kjbls IN ('{teacherIdsStr}')";
  836 + }
  837 +
  838 + // SQL查询:统计科技部老师的消耗业绩、见客数、项目数
  839 + var consumeSql = $@"
  840 + SELECT
  841 + techDept.F_Id as TechDepartmentId,
  842 + techDept.F_FullName as TechDepartmentName,
  843 + consume.kjbls as TeacherId,
  844 + consume.kjblsxm as TeacherName,
  845 + COUNT(DISTINCT hyhk.hy) as CustomerCount,
  846 + SUM(consume.F_hdpxNumber) as ConsumeProjectCount,
  847 + SUM(consume.kjblsyj) as ConsumeAchievement
  848 + FROM lq_xh_kjbsyj consume
  849 + INNER JOIN lq_xh_hyhk hyhk ON consume.glkdbh = hyhk.F_Id
  850 + INNER JOIN lq_mdxx store ON hyhk.md = store.F_Id
  851 + LEFT JOIN base_organize techDept ON store.kjb = techDept.F_Id
  852 + WHERE consume.F_IsEffective = 1
  853 + AND hyhk.F_IsEffective = 1
  854 + AND DATE(hyhk.hksj) >= '{startDate:yyyy-MM-dd}'
  855 + AND DATE(hyhk.hksj) <= '{endDate:yyyy-MM-dd}'
  856 + {techFilter}
  857 + {teacherFilter}
  858 + GROUP BY techDept.F_Id, techDept.F_FullName, consume.kjbls, consume.kjblsxm";
  859 +
  860 + var consumeResult = await _db.Ado.SqlQueryAsync<dynamic>(consumeSql);
  861 +
  862 + // 查询开单业绩
  863 + var orderSql = $@"
  864 + SELECT
  865 + techDept.F_Id as TechDepartmentId,
  866 + ord.kjbls as TeacherId,
  867 + SUM(ord.kjblsyj) as OrderAchievement
  868 + FROM lq_kd_kjbsyj ord
  869 + INNER JOIN lq_kd_kdjlb kdjlb ON ord.glkdbh = kdjlb.F_Id
  870 + INNER JOIN lq_mdxx store ON kdjlb.djmd = store.F_Id
  871 + LEFT JOIN base_organize techDept ON store.kjb = techDept.F_Id
  872 + WHERE ord.F_IsEffective = 1
  873 + AND kdjlb.F_IsEffective = 1
  874 + AND DATE(kdjlb.kdrq) >= '{startDate:yyyy-MM-dd}'
  875 + AND DATE(kdjlb.kdrq) <= '{endDate:yyyy-MM-dd}'
  876 + {techFilter}
  877 + {teacherFilterForOrder}
  878 + GROUP BY techDept.F_Id, ord.kjbls";
  879 +
  880 + var orderResult = await _db.Ado.SqlQueryAsync<dynamic>(orderSql);
  881 +
  882 + // 合并数据
  883 + var teacherDict = new Dictionary<string, TechTeacherDailyStatisticsOutput>();
  884 +
  885 + foreach (var item in consumeResult ?? Enumerable.Empty<dynamic>())
  886 + {
  887 + var key = $"{item.TechDepartmentId}_{item.TeacherId}";
  888 + teacherDict[key] = new TechTeacherDailyStatisticsOutput
  889 + {
  890 + TechDepartmentId = item.TechDepartmentId?.ToString(),
  891 + TechDepartmentName = item.TechDepartmentName?.ToString(),
  892 + TeacherId = item.TeacherId?.ToString(),
  893 + TeacherName = item.TeacherName?.ToString(),
  894 + CustomerCount = Convert.ToInt32(item.CustomerCount),
  895 + ConsumeProjectCount = Convert.ToInt32(item.ConsumeProjectCount),
  896 + ConsumeAchievement = Convert.ToDecimal(item.ConsumeAchievement),
  897 + OrderAchievement = 0
  898 + };
  899 + }
  900 +
  901 + // 合并开单业绩
  902 + foreach (var item in orderResult ?? Enumerable.Empty<dynamic>())
  903 + {
  904 + var key = $"{item.TechDepartmentId}_{item.TeacherId}";
  905 + if (teacherDict.ContainsKey(key))
  906 + {
  907 + teacherDict[key].OrderAchievement = Convert.ToDecimal(item.OrderAchievement);
  908 + }
  909 + }
  910 +
  911 + var outputList = teacherDict.Values
  912 + .OrderBy(x => x.TechDepartmentName)
  913 + .ThenBy(x => x.TeacherName)
  914 + .ToList();
  915 +
  916 + return outputList;
  917 + }
  918 + #endregion
  919 + }
  920 +}
  921 +
... ...