Commit e96beca8aecd10686f265a2aa8c9d398e866b95f

Authored by “wangming”
1 parent 87aea27c

更新多个.DS_Store文件,删除不再使用的SQL视图文件,重构LqStatisticsService和WeChatBotService,新增门店开单业绩统计…

…和科技部老师业绩统计功能,优化查询逻辑和代码结构,确保代码整洁性和功能完整性。
.DS_Store
No preview for this file type
[已用]创建科技部老师业绩统计视图.sql 0 → 100644
  1 +-- 创建科技部老师业绩流水视图
  2 +-- 包含所有业务类型的流水记录,并包含对应的信息表ID和品项次数
  3 +
  4 +CREATE OR REPLACE VIEW v_tech_teacher_flow AS
  5 +SELECT
  6 + teacher_id,
  7 + teacher_name,
  8 + teacher_account,
  9 + business_type,
  10 + achievement,
  11 + labor_cost,
  12 + project_count,
  13 + customer_id,
  14 + customer_name,
  15 + business_date,
  16 + source_id,
  17 + source_table,
  18 + item_count
  19 +FROM (
  20 + -- 开卡流水
  21 + SELECT
  22 + k.kjbls as teacher_id,
  23 + k.kjblsxm as teacher_name,
  24 + k.kjblszh as teacher_account,
  25 + '开卡' as business_type,
  26 + CAST(COALESCE(k.kjblsyj, 0) AS DECIMAL(10,2)) as achievement,
  27 + CAST(COALESCE(k.F_LaborCost, 0) AS DECIMAL(10,2)) as labor_cost,
  28 + CAST(COALESCE(pm.F_ProjectNumber, 0) AS DECIMAL(10,2)) as project_count,
  29 + o.kdhy as customer_id,
  30 + o.kdhyc as customer_name,
  31 + k.yjsj as business_date,
  32 + k.F_kdpxid as source_id,
  33 + 'lq_kd_pxmx' as source_table,
  34 + COALESCE(pm.F_ProjectNumber, 0) as item_count
  35 + FROM lq_kd_kjbsyj k
  36 + LEFT JOIN lq_kd_kdjlb o ON k.glkdbh = o.F_Id
  37 + LEFT JOIN lq_kd_pxmx pm ON k.F_kdpxid = pm.F_Id
  38 + WHERE k.kjbls IS NOT NULL AND k.kjblsxm IS NOT NULL
  39 +
  40 + UNION ALL
  41 +
  42 + -- 耗卡流水
  43 + SELECT
  44 + x.kjbls as teacher_id,
  45 + x.kjblsxm as teacher_name,
  46 + x.kjblszh as teacher_account,
  47 + '耗卡' as business_type,
  48 + CAST(COALESCE(x.kjblsyj, 0) AS DECIMAL(10,2)) as achievement,
  49 + CAST(COALESCE(x.F_LaborCost, 0) AS DECIMAL(10,2)) as labor_cost,
  50 + CAST(COALESCE(pm.F_ProjectNumber, 0) AS DECIMAL(10,2)) as project_count,
  51 + h.hy as customer_id,
  52 + h.hymc as customer_name,
  53 + x.yjsj as business_date,
  54 + x.F_hkpxid as source_id,
  55 + 'lq_xh_pxmx' as source_table,
  56 + COALESCE(pm.F_ProjectNumber, 0) as item_count
  57 + FROM lq_xh_kjbsyj x
  58 + LEFT JOIN lq_xh_hyhk h ON x.glkdbh = h.F_Id
  59 + LEFT JOIN lq_xh_pxmx pm ON x.F_hkpxid = pm.F_Id
  60 + WHERE x.kjbls IS NOT NULL AND x.kjblsxm IS NOT NULL
  61 +
  62 + UNION ALL
  63 +
  64 + -- 退卡流水
  65 + SELECT
  66 + r.kjbls as teacher_id,
  67 + r.kjblsxm as teacher_name,
  68 + r.kjblszh as teacher_account,
  69 + '退卡' as business_type,
  70 + CAST(COALESCE(r.kjblsyj, 0) AS DECIMAL(10,2)) as achievement,
  71 + CAST(COALESCE(r.F_LaborCost, 0) AS DECIMAL(10,2)) as labor_cost,
  72 + CAST(COALESCE(r.F_tkpxNumber, 0) AS DECIMAL(10,2)) as project_count,
  73 + t.hy as customer_id,
  74 + t.hymc as customer_name,
  75 + r.tksj as business_date,
  76 + r.F_Id as source_id,
  77 + 'lq_hytk_kjbsyj' as source_table,
  78 + 0 as item_count
  79 + FROM lq_hytk_kjbsyj r
  80 + LEFT JOIN lq_hytk_hytk t ON r.gltkbh = t.F_Id
  81 + WHERE r.kjbls IS NOT NULL AND r.kjblsxm IS NOT NULL AND (r.F_DeleteMark IS NULL OR r.F_DeleteMark = 0)
  82 +) t;
  83 +
  84 +
[已用]创建门店耗卡业绩统计视图.sql 0 → 100644
  1 +-- =============================================
  2 +-- 门店耗卡业绩统计视图创建脚本
  3 +-- 功能:统计所有门店的目标业绩、耗卡业绩、完成率等关键指标
  4 +-- 作者:系统自动生成
  5 +-- 创建时间:2025年
  6 +-- =============================================
  7 +
  8 +-- 先删除视图(如果存在)
  9 +DROP VIEW IF EXISTS v_store_consume_performance_simple;
  10 +
  11 +-- =============================================
  12 +-- 创建门店耗卡业绩统计视图(当月数据)
  13 +-- =============================================
  14 +CREATE VIEW v_store_consume_performance_simple AS
  15 +SELECT
  16 + `md`.`F_Id` AS `store_id`,
  17 + `md`.`djmd` AS `store_code`,
  18 + `md`.`dm` AS `store_name`,
  19 + COALESCE(`md`.`xsyj`, 0) AS `target_performance`,
  20 + COALESCE(`consume`.`consume_performance`, 0) AS `actual_performance`,
  21 + (
  22 + CASE
  23 + WHEN (COALESCE(`md`.`xsyj`, 0) > 0) THEN
  24 + round(
  25 + (
  26 + (
  27 + COALESCE(`consume`.`consume_performance`, 0) / `md`.`xsyj`
  28 + ) * 100
  29 + ),
  30 + 2
  31 + ) ELSE 0
  32 + END
  33 + ) AS `completion_rate`,
  34 + COALESCE(`consume`.`consume_count`, 0) AS `order_count`
  35 + FROM
  36 + (
  37 + `lqerp`.`lq_mdxx` `md`
  38 + LEFT JOIN (
  39 + SELECT
  40 + `hk`.`md` AS `md`,
  41 + sum(COALESCE(`px`.`F_TotalPrice`, 0)) AS `consume_performance`,
  42 + count(DISTINCT `hk`.`F_Id`) AS `consume_count`
  43 + FROM
  44 + (
  45 + `lqerp`.`lq_xh_hyhk` `hk`
  46 + LEFT JOIN `lqerp`.`lq_xh_pxmx` `px` ON ((`hk`.`F_Id` = `px`.`glkdbh`))
  47 + )
  48 + WHERE
  49 + (
  50 + (
  51 + YEAR(`hk`.`hksj`) = YEAR(curdate())
  52 + )
  53 + AND (
  54 + MONTH(`hk`.`hksj`) = MONTH(curdate())
  55 + )
  56 + AND (
  57 + isnull(`px`.`F_CreateTIme`)
  58 + OR (
  59 + (
  60 + YEAR(`px`.`F_CreateTIme`) = YEAR(curdate())
  61 + )
  62 + AND (
  63 + MONTH(`px`.`F_CreateTIme`) = MONTH(curdate())
  64 + )
  65 + )
  66 + )
  67 + )
  68 + GROUP BY
  69 + `hk`.`md`
  70 + ) `consume` ON ((`md`.`F_Id` = `consume`.`md`))
  71 + )
  72 + ORDER BY
  73 + COALESCE(`consume`.`consume_performance`, 0) DESC;
  74 +
  75 +-- =============================================
  76 +-- 添加视图注释
  77 +-- =============================================
  78 +ALTER VIEW v_store_consume_performance_simple COMMENT = '门店耗卡业绩统计视图(当月数据)- 统计所有门店的目标业绩、耗卡业绩、完成率等关键指标';
  79 +
  80 +-- =============================================
  81 +-- 视图字段说明
  82 +-- =============================================
  83 +/*
  84 +字段说明:
  85 +- store_id: 门店ID(来自lq_mdxx.F_Id)
  86 +- store_code: 门店编码(来自lq_mdxx.djmd)
  87 +- store_name: 店名(来自lq_mdxx.dm)
  88 +- target_performance: 目标业绩/生命线(来自lq_mdxx.xsyj)
  89 +- actual_performance: 耗卡业绩(来自lq_xh_pxmx.F_TotalPrice的汇总)
  90 +- completion_rate: 完成率百分比(actual_performance / target_performance * 100)
  91 +- order_count: 耗卡记录数(有效耗卡记录数)
  92 +
  93 +关联说明:
  94 +- 主表:lq_mdxx(门店信息表)
  95 +- 关联表:lq_xh_hyhk(耗卡记录表)+ lq_xh_pxmx(耗卡品项表)
  96 +- 关联条件:lq_mdxx.F_Id = lq_xh_hyhk.md, lq_xh_hyhk.F_Id = lq_xh_pxmx.glkdbh
  97 +- 关联方式:LEFT JOIN(确保所有门店都显示,包括没有耗卡记录的门店)
  98 +
  99 +数据来源:
  100 +- 目标业绩:lq_mdxx.xsyj(目标-门店生命线)
  101 +- 耗卡业绩:lq_xh_pxmx.F_TotalPrice(金额合计)
  102 +- 耗卡记录数:lq_xh_hyhk表记录数统计
  103 +
  104 +时间过滤:
  105 +- 耗卡日期:lq_xh_hyhk.hksj(当月)
  106 +- 品项创建时间:lq_xh_pxmx.F_CreateTIme(当月)
  107 +
  108 +注意事项:
  109 +1. 使用金额合计(F_TotalPrice)而不是品项价格(pxjg)
  110 +2. 只统计当月的数据(耗卡日期和品项创建时间都是当月)
  111 +3. 即使目标业绩、耗卡业绩为0的门店也会显示
  112 +4. 完成率计算时,目标业绩为0的门店显示0%完成率
  113 +5. 按耗卡业绩降序排列,便于查看业绩排名
  114 +6. 确保耗卡品项也是当月创建的,避免历史数据干扰
  115 +*/
  116 +
  117 +-- =============================================
  118 +-- 使用示例
  119 +-- =============================================
  120 +/*
  121 +-- 查看所有门店当月耗卡业绩
  122 +SELECT * FROM v_store_consume_performance_simple;
  123 +
  124 +-- 查看华润店当月耗卡业绩
  125 +SELECT * FROM v_store_consume_performance_simple WHERE store_name LIKE '%华润%';
  126 +
  127 +-- 查看已完成目标的门店(当月耗卡)
  128 +SELECT * FROM v_store_consume_performance_simple WHERE completion_rate >= 100;
  129 +
  130 +-- 查看零耗卡业绩的门店(当月)
  131 +SELECT * FROM v_store_consume_performance_simple WHERE actual_performance = 0;
  132 +
  133 +-- 按完成率排名(当月耗卡)
  134 +SELECT * FROM v_store_consume_performance_simple WHERE target_performance > 0 ORDER BY completion_rate DESC;
  135 +
  136 +-- 查看当月耗卡业绩前10名门店
  137 +SELECT * FROM v_store_consume_performance_simple ORDER BY actual_performance DESC LIMIT 10;
  138 +
  139 +-- 查看当月完成率低于50%的门店
  140 +SELECT * FROM v_store_consume_performance_simple WHERE completion_rate < 50 AND target_performance > 0;
  141 +*/
netcore/src/.DS_Store
No preview for this file type
netcore/src/Application/.DS_Store
No preview for this file type
netcore/src/Application/NCC.API/.DS_Store
No preview for this file type
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqStatistics
  4 +{
  5 + /// <summary>
  6 + /// 科技部老师简化统计输出
  7 + /// </summary>
  8 + public class TechTeacherSimpleStatisticsOutput
  9 + {
  10 + /// <summary>
  11 + /// 部门名称
  12 + /// </summary>
  13 + public string DepartmentName { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 老师姓名
  17 + /// </summary>
  18 + public string TeacherName { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 消耗项目数
  22 + /// </summary>
  23 + public int ConsumeProjectCount { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 消耗业绩
  27 + /// </summary>
  28 + public decimal ConsumeAchievement { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 开卡业绩
  32 + /// </summary>
  33 + public decimal OrderAchievement { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 开卡品项次数
  37 + /// </summary>
  38 + public int OrderItemCount { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 耗卡品项次数
  42 + /// </summary>
  43 + public int ConsumeItemCount { get; set; }
  44 + }
  45 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqStatistics
  4 +{
  5 + /// <summary>
  6 + /// 科技部老师统计查询参数
  7 + /// </summary>
  8 + public class TechTeacherStatisticsInput
  9 + {
  10 + /// <summary>
  11 + /// 开始日期
  12 + /// </summary>
  13 + public DateTime? StartDate { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 结束日期
  17 + /// </summary>
  18 + public DateTime? EndDate { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 科技部老师ID
  22 + /// </summary>
  23 + public string TeacherId { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 科技部老师姓名
  27 + /// </summary>
  28 + public string TeacherName { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 部门ID
  32 + /// </summary>
  33 + public string DepartmentId { get; set; }
  34 + }
  35 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqStatistics
  5 +{
  6 + /// <summary>
  7 + /// 科技部老师统计输出结果
  8 + /// </summary>
  9 + public class TechTeacherStatisticsOutput
  10 + {
  11 + /// <summary>
  12 + /// 科技部老师ID
  13 + /// </summary>
  14 + public string TeacherId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 科技部老师姓名
  18 + /// </summary>
  19 + public string TeacherName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 科技部老师账号
  23 + /// </summary>
  24 + public string TeacherAccount { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 开卡业绩
  28 + /// </summary>
  29 + public decimal OrderAchievement { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 消耗业绩
  33 + /// </summary>
  34 + public decimal ConsumeAchievement { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 退卡业绩
  38 + /// </summary>
  39 + public decimal RefundAchievement { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 总业绩
  43 + /// </summary>
  44 + public decimal TotalAchievement { get; set; }
  45 +
  46 + /// <summary>
  47 + /// 见客数
  48 + /// </summary>
  49 + public int CustomerCount { get; set; }
  50 +
  51 + /// <summary>
  52 + /// 项目数
  53 + /// </summary>
  54 + public int ProjectCount { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 手工费
  58 + /// </summary>
  59 + public decimal LaborCost { get; set; }
  60 +
  61 + /// <summary>
  62 + /// 统计时间
  63 + /// </summary>
  64 + public DateTime StatisticsTime { get; set; }
  65 + }
  66 +
  67 + /// <summary>
  68 + /// 科技部老师统计列表输出结果
  69 + /// </summary>
  70 + public class TechTeacherStatisticsListOutput
  71 + {
  72 + /// <summary>
  73 + /// 科技部老师统计列表
  74 + /// </summary>
  75 + public List<TechTeacherStatisticsOutput> Teachers { get; set; } = new List<TechTeacherStatisticsOutput>();
  76 +
  77 + /// <summary>
  78 + /// 总记录数
  79 + /// </summary>
  80 + public int TotalCount { get; set; }
  81 +
  82 + /// <summary>
  83 + /// 总开卡业绩
  84 + /// </summary>
  85 + public decimal TotalOrderAchievement { get; set; }
  86 +
  87 + /// <summary>
  88 + /// 总消耗业绩
  89 + /// </summary>
  90 + public decimal TotalConsumeAchievement { get; set; }
  91 +
  92 + /// <summary>
  93 + /// 总退卡业绩
  94 + /// </summary>
  95 + public decimal TotalRefundAchievement { get; set; }
  96 +
  97 + /// <summary>
  98 + /// 总业绩
  99 + /// </summary>
  100 + public decimal TotalAchievement { get; set; }
  101 +
  102 + /// <summary>
  103 + /// 总见客数
  104 + /// </summary>
  105 + public int TotalCustomerCount { get; set; }
  106 +
  107 + /// <summary>
  108 + /// 总项目数
  109 + /// </summary>
  110 + public int TotalProjectCount { get; set; }
  111 +
  112 + /// <summary>
  113 + /// 总手工费
  114 + /// </summary>
  115 + public decimal TotalLaborCost { get; set; }
  116 + }
  117 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs
1 -using NCC.Common.Const; 1 +using System;
  2 +using NCC.Common.Const;
2 using SqlSugar; 3 using SqlSugar;
3 -using System;  
4 4
5 namespace NCC.Extend.Entitys.lq_kd_kjbsyj 5 namespace NCC.Extend.Entitys.lq_kd_kjbsyj
6 { 6 {
@@ -59,5 +59,10 @@ namespace NCC.Extend.Entitys.lq_kd_kjbsyj @@ -59,5 +59,10 @@ namespace NCC.Extend.Entitys.lq_kd_kjbsyj
59 [SugarColumn(ColumnName = "F_kdpxid")] 59 [SugarColumn(ColumnName = "F_kdpxid")]
60 public string Kdpxid { get; set; } 60 public string Kdpxid { get; set; }
61 61
  62 + /// <summary>
  63 + /// 手工费
  64 + /// </summary>
  65 + [SugarColumn(ColumnName = "F_LaborCost")]
  66 + public decimal? LaborCost { get; set; }
62 } 67 }
63 -}  
64 \ No newline at end of file 68 \ No newline at end of file
  69 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/v_tech_teacher_flow/VTechTeacherFlowEntity.cs 0 → 100644
  1 +using System;
  2 +using NCC.Common.Const;
  3 +using SqlSugar;
  4 +
  5 +namespace NCC.Extend.Entitys.v_tech_teacher_flow
  6 +{
  7 + /// <summary>
  8 + /// 科技部老师业绩流水视图
  9 + /// </summary>
  10 + [SugarTable("v_tech_teacher_flow")]
  11 + [Tenant(ClaimConst.TENANT_ID)]
  12 + public class VTechTeacherFlowEntity
  13 + {
  14 + /// <summary>
  15 + /// 老师ID
  16 + /// </summary>
  17 + [SugarColumn(ColumnName = "teacher_id")]
  18 + public string TeacherId { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 老师姓名
  22 + /// </summary>
  23 + [SugarColumn(ColumnName = "teacher_name")]
  24 + public string TeacherName { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 老师账号
  28 + /// </summary>
  29 + [SugarColumn(ColumnName = "teacher_account")]
  30 + public string TeacherAccount { get; set; }
  31 +
  32 + /// <summary>
  33 + /// 业务类型(开卡、耗卡、退卡)
  34 + /// </summary>
  35 + [SugarColumn(ColumnName = "business_type")]
  36 + public string BusinessType { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 业绩金额
  40 + /// </summary>
  41 + [SugarColumn(ColumnName = "achievement")]
  42 + public decimal Achievement { get; set; }
  43 +
  44 + /// <summary>
  45 + /// 手工费
  46 + /// </summary>
  47 + [SugarColumn(ColumnName = "labor_cost")]
  48 + public decimal LaborCost { get; set; }
  49 +
  50 + /// <summary>
  51 + /// 项目数
  52 + /// </summary>
  53 + [SugarColumn(ColumnName = "project_count")]
  54 + public decimal ProjectCount { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 客户ID
  58 + /// </summary>
  59 + [SugarColumn(ColumnName = "customer_id")]
  60 + public string CustomerId { get; set; }
  61 +
  62 + /// <summary>
  63 + /// 客户姓名
  64 + /// </summary>
  65 + [SugarColumn(ColumnName = "customer_name")]
  66 + public string CustomerName { get; set; }
  67 +
  68 + /// <summary>
  69 + /// 业务日期
  70 + /// </summary>
  71 + [SugarColumn(ColumnName = "business_date")]
  72 + public DateTime? BusinessDate { get; set; }
  73 +
  74 + /// <summary>
  75 + /// 源记录ID
  76 + /// </summary>
  77 + [SugarColumn(ColumnName = "source_id")]
  78 + public string SourceId { get; set; }
  79 +
  80 + /// <summary>
  81 + /// 源表名
  82 + /// </summary>
  83 + [SugarColumn(ColumnName = "source_table")]
  84 + public string SourceTable { get; set; }
  85 +
  86 + /// <summary>
  87 + /// 品项次数
  88 + /// </summary>
  89 + [SugarColumn(ColumnName = "item_count")]
  90 + public int ItemCount { get; set; }
  91 + }
  92 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/v_tech_teacher_summary/VTechTeacherSummaryEntity.cs 0 → 100644
  1 +using NCC.Common.Const;
  2 +using SqlSugar;
  3 +
  4 +namespace NCC.Extend.Entitys.v_tech_teacher_summary
  5 +{
  6 + /// <summary>
  7 + /// 科技部老师业绩汇总视图(简化版)
  8 + /// </summary>
  9 + [SugarTable("v_tech_teacher_summary")]
  10 + [Tenant(ClaimConst.TENANT_ID)]
  11 + public class VTechTeacherSummaryEntity
  12 + {
  13 + /// <summary>
  14 + /// 老师ID
  15 + /// </summary>
  16 + [SugarColumn(ColumnName = "teacher_id")]
  17 + public string TeacherId { get; set; }
  18 +
  19 + /// <summary>
  20 + /// 老师姓名
  21 + /// </summary>
  22 + [SugarColumn(ColumnName = "teacher_name")]
  23 + public string TeacherName { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 老师账号
  27 + /// </summary>
  28 + [SugarColumn(ColumnName = "teacher_account")]
  29 + public string TeacherAccount { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 开卡业绩
  33 + /// </summary>
  34 + [SugarColumn(ColumnName = "order_achievement")]
  35 + public decimal OrderAchievement { get; set; }
  36 +
  37 + /// <summary>
  38 + /// 消耗业绩
  39 + /// </summary>
  40 + [SugarColumn(ColumnName = "consume_achievement")]
  41 + public decimal ConsumeAchievement { get; set; }
  42 +
  43 + /// <summary>
  44 + /// 退卡业绩
  45 + /// </summary>
  46 + [SugarColumn(ColumnName = "refund_achievement")]
  47 + public decimal RefundAchievement { get; set; }
  48 +
  49 + /// <summary>
  50 + /// 总业绩
  51 + /// </summary>
  52 + [SugarColumn(ColumnName = "total_achievement")]
  53 + public decimal TotalAchievement { get; set; }
  54 +
  55 + /// <summary>
  56 + /// 手工费
  57 + /// </summary>
  58 + [SugarColumn(ColumnName = "labor_cost")]
  59 + public decimal LaborCost { get; set; }
  60 +
  61 + /// <summary>
  62 + /// 项目数
  63 + /// </summary>
  64 + [SugarColumn(ColumnName = "project_count")]
  65 + public decimal ProjectCount { get; set; }
  66 +
  67 + /// <summary>
  68 + /// 见客数
  69 + /// </summary>
  70 + [SugarColumn(ColumnName = "customer_count")]
  71 + public int CustomerCount { get; set; }
  72 + }
  73 +}
netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqStatistics/ILqStatisticsService.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic; @@ -2,6 +2,7 @@ using System.Collections.Generic;
2 using System.Threading.Tasks; 2 using System.Threading.Tasks;
3 using NCC.Extend.Entitys.Dto.LqMdxx; 3 using NCC.Extend.Entitys.Dto.LqMdxx;
4 using NCC.Extend.Entitys.Dto.LqStatistics; 4 using NCC.Extend.Entitys.Dto.LqStatistics;
  5 +using NCC.Extend.Entitys.v_tech_teacher_flow;
5 6
6 namespace NCC.Extend.Interfaces.LqStatistics 7 namespace NCC.Extend.Interfaces.LqStatistics
7 { 8 {
@@ -17,6 +18,12 @@ namespace NCC.Extend.Interfaces.LqStatistics @@ -17,6 +18,12 @@ namespace NCC.Extend.Interfaces.LqStatistics
17 Task<List<StorePerformanceOutput>> GetStorePerformanceList(); 18 Task<List<StorePerformanceOutput>> GetStorePerformanceList();
18 19
19 /// <summary> 20 /// <summary>
  21 + /// 获取门店开单业绩统计列表
  22 + /// </summary>
  23 + /// <returns>门店开单业绩统计列表</returns>
  24 + Task<List<StorePerformanceOutput>> GetStoreOrderPerformanceList();
  25 +
  26 + /// <summary>
20 /// 获取门店统计信息 27 /// 获取门店统计信息
21 /// </summary> 28 /// </summary>
22 /// <param name="input">查询参数</param> 29 /// <param name="input">查询参数</param>
@@ -50,5 +57,12 @@ namespace NCC.Extend.Interfaces.LqStatistics @@ -50,5 +57,12 @@ namespace NCC.Extend.Interfaces.LqStatistics
50 /// <param name="input">查询参数</param> 57 /// <param name="input">查询参数</param>
51 /// <returns>经理业绩汇总统计结果</returns> 58 /// <returns>经理业绩汇总统计结果</returns>
52 Task<ManagerSummaryStatisticsOutput> GetManagerSummaryStatistics(ManagerStatisticsInput input); 59 Task<ManagerSummaryStatisticsOutput> GetManagerSummaryStatistics(ManagerStatisticsInput input);
  60 +
  61 + /// <summary>
  62 + /// 获取科技部老师业绩统计
  63 + /// </summary>
  64 + /// <param name="input">查询参数</param>
  65 + /// <returns>科技部老师业绩统计结果</returns>
  66 + Task<List<TechTeacherSimpleStatisticsOutput>> GetTechTeacherStatistics(TechTeacherStatisticsInput input);
53 } 67 }
54 } 68 }
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -2,6 +2,7 @@ using System; @@ -2,6 +2,7 @@ using System;
2 using System.Collections.Generic; 2 using System.Collections.Generic;
3 using System.Linq; 3 using System.Linq;
4 using System.Threading.Tasks; 4 using System.Threading.Tasks;
  5 +using Microsoft.AspNetCore.Authorization;
5 using Microsoft.AspNetCore.Mvc; 6 using Microsoft.AspNetCore.Mvc;
6 using Microsoft.Extensions.Logging; 7 using Microsoft.Extensions.Logging;
7 using NCC.Common.Core.Manager; 8 using NCC.Common.Core.Manager;
@@ -12,9 +13,15 @@ using NCC.Dependency; @@ -12,9 +13,15 @@ using NCC.Dependency;
12 using NCC.DynamicApiController; 13 using NCC.DynamicApiController;
13 using NCC.Extend.Entitys.Dto.LqMdxx; 14 using NCC.Extend.Entitys.Dto.LqMdxx;
14 using NCC.Extend.Entitys.Dto.LqStatistics; 15 using NCC.Extend.Entitys.Dto.LqStatistics;
  16 +using NCC.Extend.Entitys.lq_hytk_kjbsyj;
  17 +using NCC.Extend.Entitys.lq_kd_kdjlb;
  18 +using NCC.Extend.Entitys.lq_kd_kjbsyj;
15 using NCC.Extend.Entitys.lq_mdxx; 19 using NCC.Extend.Entitys.lq_mdxx;
  20 +using NCC.Extend.Entitys.lq_xh_kjbsyj;
16 using NCC.Extend.Entitys.lq_yjmxb; 21 using NCC.Extend.Entitys.lq_yjmxb;
  22 +using NCC.Extend.Entitys.v_tech_teacher_flow;
17 using NCC.Extend.Interfaces.LqStatistics; 23 using NCC.Extend.Interfaces.LqStatistics;
  24 +using NCC.Extend.Utils;
18 using NCC.FriendlyException; 25 using NCC.FriendlyException;
19 using NCC.System.Entitys.Permission; 26 using NCC.System.Entitys.Permission;
20 using SqlSugar; 27 using SqlSugar;
@@ -32,45 +39,65 @@ namespace NCC.Extend.LqStatistics @@ -32,45 +39,65 @@ namespace NCC.Extend.LqStatistics
32 private readonly SqlSugarScope _db; 39 private readonly SqlSugarScope _db;
33 private readonly IUserManager _userManager; 40 private readonly IUserManager _userManager;
34 private readonly ILogger<LqStatisticsService> _logger; 41 private readonly ILogger<LqStatisticsService> _logger;
  42 + private readonly WeChatBotService _weChatBotService;
35 43
36 /// <summary> 44 /// <summary>
37 /// 初始化一个<see cref="LqStatisticsService"/>类型的新实例 45 /// 初始化一个<see cref="LqStatisticsService"/>类型的新实例
38 /// </summary> 46 /// </summary>
39 - public LqStatisticsService(ISqlSugarRepository<LqMdxxEntity> lqMdxxRepository, IUserManager userManager, ILogger<LqStatisticsService> logger) 47 + public LqStatisticsService(ISqlSugarRepository<LqMdxxEntity> lqMdxxRepository, IUserManager userManager, ILogger<LqStatisticsService> logger, WeChatBotService weChatBotService)
40 { 48 {
41 _lqMdxxRepository = lqMdxxRepository; 49 _lqMdxxRepository = lqMdxxRepository;
42 _db = _lqMdxxRepository.Context; 50 _db = _lqMdxxRepository.Context;
43 _userManager = userManager; 51 _userManager = userManager;
44 _logger = logger; 52 _logger = logger;
  53 + _weChatBotService = weChatBotService;
45 } 54 }
46 55
47 - #region 获取门店业绩统计列表 56 + #region 获取门店耗卡业绩统计列表
48 /// <summary> 57 /// <summary>
49 /// 获取门店业绩统计列表 58 /// 获取门店业绩统计列表
50 /// </summary> 59 /// </summary>
  60 + /// <remarks>
  61 + /// 获取所有门店的耗卡业绩统计信息,包括门店ID、名称、编码、目标业绩、耗卡业绩、完成率等关键指标
  62 + ///
  63 + /// 返回数据说明:
  64 + /// - StoreId: 门店ID
  65 + /// - StoreName: 门店名称
  66 + /// - StoreCode: 门店编码
  67 + /// - BusinessUnitId: 事业部ID(暂未关联)
  68 + /// - BusinessUnitName: 事业部名称(暂未关联)
  69 + /// - TargetPerformance: 目标业绩(门店生命线)
  70 + /// - ActualPerformance: 耗卡业绩(当月耗卡金额汇总)
  71 + /// - CompletionRate: 完成率(耗卡业绩/目标业绩*100)
  72 + /// - OrderCount: 耗卡记录数(当月耗卡记录数)
  73 + ///
  74 + /// 数据来源:v_store_consume_performance_simple 视图
  75 + /// </remarks>
51 /// <returns>门店业绩统计列表</returns> 76 /// <returns>门店业绩统计列表</returns>
52 - [HttpGet] 77 + /// <response code="200">成功返回门店业绩统计列表</response>
  78 + /// <response code="500">服务器内部错误</response>
  79 + [HttpGet("GetStorePerformanceList")]
53 public async Task<List<StorePerformanceOutput>> GetStorePerformanceList() 80 public async Task<List<StorePerformanceOutput>> GetStorePerformanceList()
54 { 81 {
55 try 82 try
56 { 83 {
57 _logger.LogInformation("开始查询门店业绩统计列表"); 84 _logger.LogInformation("开始查询门店业绩统计列表");
58 85
59 - var storeList = await _lqMdxxRepository  
60 - .AsQueryable()  
61 - .Where(x => x.Status == 1)  
62 - .Select(x => new StorePerformanceOutput  
63 - {  
64 - StoreId = x.Id,  
65 - StoreName = x.Dm,  
66 - StoreCode = x.Mdbm,  
67 - BusinessUnitId = x.Syb,  
68 - BusinessUnitName = x.Syb,  
69 - TargetPerformance = x.Xsyj ?? 0,  
70 - ActualPerformance = 0,  
71 - CompletionRate = 0,  
72 - })  
73 - .ToListAsync(); 86 + // 使用耗卡业绩统计视图
  87 + var storeList = await _db.Ado.SqlQueryAsync<StorePerformanceOutput>(
  88 + "SELECT "
  89 + + "store_id AS StoreId, "
  90 + + "store_code AS StoreCode, "
  91 + + "store_name AS StoreName, "
  92 + + "'' AS BusinessUnitId, "
  93 + + "'' AS BusinessUnitName, "
  94 + + "target_performance AS TargetPerformance, "
  95 + + "actual_performance AS ActualPerformance, "
  96 + + "completion_rate AS CompletionRate, "
  97 + + "order_count AS OrderCount "
  98 + + "FROM v_store_consume_performance_simple "
  99 + + "ORDER BY actual_performance DESC"
  100 + );
74 101
75 _logger.LogInformation("门店业绩统计列表查询完成,返回{Count}条记录", storeList.Count); 102 _logger.LogInformation("门店业绩统计列表查询完成,返回{Count}条记录", storeList.Count);
76 103
@@ -84,12 +111,101 @@ namespace NCC.Extend.LqStatistics @@ -84,12 +111,101 @@ namespace NCC.Extend.LqStatistics
84 } 111 }
85 #endregion 112 #endregion
86 113
  114 + #region 获取门店开单业绩统计列表
  115 + /// <summary>
  116 + /// 获取门店开单业绩统计列表
  117 + /// </summary>
  118 + /// <remarks>
  119 + /// 获取所有门店的开单业绩统计信息,包括门店ID、名称、编码、目标业绩、开单业绩、完成率等关键指标
  120 + ///
  121 + /// 返回数据说明:
  122 + /// - StoreId: 门店ID
  123 + /// - StoreName: 门店名称
  124 + /// - StoreCode: 门店编码
  125 + /// - BusinessUnitId: 事业部ID(暂未关联)
  126 + /// - BusinessUnitName: 事业部名称(暂未关联)
  127 + /// - TargetPerformance: 目标业绩(门店生命线)
  128 + /// - ActualPerformance: 开单业绩(当月开单金额汇总)
  129 + /// - CompletionRate: 完成率(开单业绩/目标业绩*100)
  130 + /// - OrderCount: 开单记录数(当月开单记录数)
  131 + ///
  132 + /// 数据来源:v_store_performance_simple 视图
  133 + /// </remarks>
  134 + /// <returns>门店开单业绩统计列表</returns>
  135 + /// <response code="200">成功返回门店开单业绩统计列表</response>
  136 + /// <response code="500">服务器内部错误</response>
  137 + [HttpGet("GetStoreOrderPerformanceList")]
  138 + public async Task<List<StorePerformanceOutput>> GetStoreOrderPerformanceList()
  139 + {
  140 + try
  141 + {
  142 + _logger.LogInformation("开始查询门店开单业绩统计列表");
  143 +
  144 + // 使用开单业绩统计视图
  145 + var storeList = await _db.Ado.SqlQueryAsync<StorePerformanceOutput>(
  146 + "SELECT "
  147 + + "store_id AS StoreId, "
  148 + + "store_code AS StoreCode, "
  149 + + "store_name AS StoreName, "
  150 + + "'' AS BusinessUnitId, "
  151 + + "'' AS BusinessUnitName, "
  152 + + "target_performance AS TargetPerformance, "
  153 + + "actual_performance AS ActualPerformance, "
  154 + + "completion_rate AS CompletionRate, "
  155 + + "order_count AS OrderCount "
  156 + + "FROM v_store_performance_simple "
  157 + + "ORDER BY actual_performance DESC"
  158 + );
  159 +
  160 + _logger.LogInformation("门店开单业绩统计列表查询完成,返回{Count}条记录", storeList.Count);
  161 +
  162 + return storeList ?? new List<StorePerformanceOutput>();
  163 + }
  164 + catch (Exception ex)
  165 + {
  166 + _logger.LogError(ex, "查询门店开单业绩统计列表时发生错误");
  167 + throw NCCException.Oh("查询门店开单业绩统计列表失败", ex);
  168 + }
  169 + }
  170 + #endregion
  171 +
87 #region 获取门店统计信息 172 #region 获取门店统计信息
88 /// <summary> 173 /// <summary>
89 /// 获取门店统计信息 174 /// 获取门店统计信息
90 /// </summary> 175 /// </summary>
  176 + /// <remarks>
  177 + /// 根据指定日期范围和门店ID查询门店的详细业绩统计信息,包括目标业绩、完成业绩、完成率等
  178 + ///
  179 + /// 示例请求:
  180 + /// ```json
  181 + /// {
  182 + /// "startDate": "2025-01-01T00:00:00",
  183 + /// "endDate": "2025-01-31T23:59:59",
  184 + /// "storeId": "门店ID(可选)"
  185 + /// }
  186 + /// ```
  187 + ///
  188 + /// 参数说明:
  189 + /// - startDate: 开始日期,格式:yyyy-MM-ddTHH:mm:ss
  190 + /// - endDate: 结束日期,格式:yyyy-MM-ddTHH:mm:ss
  191 + /// - storeId: 门店ID,可选参数,不传则查询所有门店
  192 + ///
  193 + /// 返回数据说明:
  194 + /// - StoreId: 门店ID
  195 + /// - StoreName: 门店名称
  196 + /// - StoreCode: 门店编码
  197 + /// - BusinessUnitId: 事业部ID
  198 + /// - BusinessUnitName: 事业部名称
  199 + /// - TargetPerformance: 目标业绩
  200 + /// - ActualPerformance: 完成业绩
  201 + /// - OrderCount: 开单数量
  202 + /// - CompletionRate: 完成率(%)
  203 + /// </remarks>
91 /// <param name="input">查询参数</param> 204 /// <param name="input">查询参数</param>
92 /// <returns>门店统计结果</returns> 205 /// <returns>门店统计结果</returns>
  206 + /// <response code="200">成功返回门店统计数据</response>
  207 + /// <response code="400">请求参数错误</response>
  208 + /// <response code="500">服务器内部错误</response>
93 [HttpPost("StoreStatistics")] 209 [HttpPost("StoreStatistics")]
94 public async Task<StoreStatisticsOutput> GetStoreStatistics(StoreStatisticsInput input) 210 public async Task<StoreStatisticsOutput> GetStoreStatistics(StoreStatisticsInput input)
95 { 211 {
@@ -101,41 +217,29 @@ namespace NCC.Extend.LqStatistics @@ -101,41 +217,29 @@ namespace NCC.Extend.LqStatistics
101 var parameters = new Dictionary<string, object> { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } }; 217 var parameters = new Dictionary<string, object> { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } };
102 218
103 // 构建WHERE条件 219 // 构建WHERE条件
104 - var whereClause = "WHERE (order_date >= @startDate AND order_date <= @endDate OR order_date IS NULL)";  
105 - 220 + var whereClause = "WHERE 1=1";
106 if (!string.IsNullOrEmpty(input.StoreId)) 221 if (!string.IsNullOrEmpty(input.StoreId))
107 { 222 {
108 whereClause += " AND store_id = @storeId"; 223 whereClause += " AND store_id = @storeId";
109 parameters.Add("@storeId", input.StoreId); 224 parameters.Add("@storeId", input.StoreId);
110 } 225 }
111 226
112 - // 构建SQL查询 227 + // 使用SQL查询门店统计信息,包含日期范围过滤
113 var sql = 228 var sql =
114 $@" 229 $@"
115 SELECT 230 SELECT
116 store_id AS StoreId, 231 store_id AS StoreId,
117 - store_name AS StoreName,  
118 store_code AS StoreCode, 232 store_code AS StoreCode,
119 - business_unit_id AS BusinessUnitId,  
120 - business_unit_name AS BusinessUnitName, 233 + store_name AS StoreName,
  234 + '' AS BusinessUnitId,
  235 + '' AS BusinessUnitName,
121 target_performance AS TargetPerformance, 236 target_performance AS TargetPerformance,
122 - SUM(COALESCE(actual_amount, 0)) AS ActualPerformance,  
123 - COUNT(order_id) AS OrderCount,  
124 - CASE  
125 - WHEN target_performance > 0  
126 - THEN ROUND((SUM(COALESCE(actual_amount, 0)) / target_performance) * 100, 2)  
127 - ELSE 0  
128 - END AS CompletionRate  
129 - FROM v_store_daily_consume_stats 237 + actual_performance AS ActualPerformance,
  238 + completion_rate AS CompletionRate,
  239 + order_count AS OrderCount
  240 + FROM v_store_performance_simple
130 {whereClause} 241 {whereClause}
131 - GROUP BY  
132 - store_id,  
133 - store_name,  
134 - store_code,  
135 - business_unit_id,  
136 - business_unit_name,  
137 - target_performance  
138 - ORDER BY ActualPerformance DESC"; 242 + ORDER BY actual_performance DESC";
139 243
140 _logger.LogInformation("执行SQL查询:{Sql}", sql); 244 _logger.LogInformation("执行SQL查询:{Sql}", sql);
141 _logger.LogInformation("查询参数:{Parameters}", string.Join(", ", parameters.Select(p => $"{p.Key}={p.Value}"))); 245 _logger.LogInformation("查询参数:{Parameters}", string.Join(", ", parameters.Select(p => $"{p.Key}={p.Value}")));
@@ -176,8 +280,38 @@ namespace NCC.Extend.LqStatistics @@ -176,8 +280,38 @@ namespace NCC.Extend.LqStatistics
176 /// <summary> 280 /// <summary>
177 /// 获取事业部业绩统计 281 /// 获取事业部业绩统计
178 /// </summary> 282 /// </summary>
  283 + /// <remarks>
  284 + /// 根据指定日期范围和事业部ID查询事业部的业绩统计信息,包括目标业绩总和、完成业绩总和、完成率等
  285 + ///
  286 + /// 示例请求:
  287 + /// ```json
  288 + /// {
  289 + /// "startDate": "2025-01-01T00:00:00",
  290 + /// "endDate": "2025-01-31T23:59:59",
  291 + /// "businessUnitId": "事业部ID(可选)"
  292 + /// }
  293 + /// ```
  294 + ///
  295 + /// 参数说明:
  296 + /// - startDate: 开始日期,格式:yyyy-MM-ddTHH:mm:ss
  297 + /// - endDate: 结束日期,格式:yyyy-MM-ddTHH:mm:ss
  298 + /// - businessUnitId: 事业部ID,可选参数,不传则查询所有事业部
  299 + ///
  300 + /// 返回数据说明:
  301 + /// - DepartmentId: 部门ID
  302 + /// - DepartmentName: 部门名称
  303 + /// - ParentId: 父部门ID
  304 + /// - ParentName: 父部门名称
  305 + /// - TotalTargetAmount: 目标业绩总和
  306 + /// - TotalActualAmount: 完成业绩总和
  307 + /// - TotalOrderCount: 开单总数量
  308 + /// - CompletionRate: 完成率(%)
  309 + /// </remarks>
179 /// <param name="input">查询参数</param> 310 /// <param name="input">查询参数</param>
180 /// <returns>事业部业绩统计结果</returns> 311 /// <returns>事业部业绩统计结果</returns>
  312 + /// <response code="200">成功返回事业部业绩统计数据</response>
  313 + /// <response code="400">请求参数错误</response>
  314 + /// <response code="500">服务器内部错误</response>
181 [HttpPost("BusinessUnitStatistics")] 315 [HttpPost("BusinessUnitStatistics")]
182 public async Task<BusinessUnitStatisticsOutput> GetBusinessUnitStatistics(BusinessUnitStatisticsInput input) 316 public async Task<BusinessUnitStatisticsOutput> GetBusinessUnitStatistics(BusinessUnitStatisticsInput input)
183 { 317 {
@@ -189,37 +323,41 @@ namespace NCC.Extend.LqStatistics @@ -189,37 +323,41 @@ namespace NCC.Extend.LqStatistics
189 var parameters = new Dictionary<string, object> { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } }; 323 var parameters = new Dictionary<string, object> { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } };
190 324
191 // 构建WHERE条件 325 // 构建WHERE条件
192 - var whereClause = "WHERE (order_date >= @startDate AND order_date <= @endDate OR order_date IS NULL)";  
193 - 326 + var whereClause = "WHERE 1=1";
194 if (!string.IsNullOrEmpty(input.BusinessUnitId)) 327 if (!string.IsNullOrEmpty(input.BusinessUnitId))
195 { 328 {
196 - whereClause += " AND department_id = @businessUnitId"; 329 + whereClause += " AND dept.F_Id = @businessUnitId";
197 parameters.Add("@businessUnitId", input.BusinessUnitId); 330 parameters.Add("@businessUnitId", input.BusinessUnitId);
198 } 331 }
199 332
200 - // 构建SQL查询 333 + // 构建SQL查询 - 先查询所有事业部,然后左连接业绩数据
201 var sql = 334 var sql =
202 $@" 335 $@"
203 SELECT 336 SELECT
204 - department_id AS DepartmentId,  
205 - department_name AS DepartmentName,  
206 - parent_id AS ParentId,  
207 - parent_name AS ParentName,  
208 - SUM(COALESCE(target_amount, 0)) AS TotalTargetAmount,  
209 - SUM(COALESCE(actual_amount, 0)) AS TotalActualAmount,  
210 - COUNT(order_id) AS TotalOrderCount, 337 + dept.F_Id AS DepartmentId,
  338 + dept.F_FullName AS DepartmentName,
  339 + dept.F_ParentId AS ParentId,
  340 + parent.F_FullName AS ParentName,
  341 + COALESCE(SUM(flow.actual_amount), 0) AS TotalActualAmount,
  342 + COALESCE(COUNT(flow.order_id), 0) AS TotalOrderCount,
211 CASE 343 CASE
212 - WHEN SUM(COALESCE(target_amount, 0)) > 0  
213 - THEN ROUND((SUM(COALESCE(actual_amount, 0)) / SUM(COALESCE(target_amount, 0))) * 100, 2) 344 + WHEN COALESCE(SUM(flow.actual_amount), 0) > 0
  345 + THEN ROUND((COALESCE(SUM(flow.actual_amount), 0) / 100000) * 100, 2)
214 ELSE 0 346 ELSE 0
215 END AS CompletionRate 347 END AS CompletionRate
216 - FROM v_department_performance_flow  
217 - {whereClause} 348 + FROM base_organize dept
  349 + LEFT JOIN base_organize parent ON dept.F_ParentId = parent.F_Id
  350 + LEFT JOIN v_department_performance_flow flow ON dept.F_Id = flow.department_id
  351 + AND (flow.order_date >= @startDate AND flow.order_date <= @endDate OR flow.order_date IS NULL)
  352 + WHERE dept.F_ParentId = (SELECT F_Id FROM base_organize WHERE F_FullName = '事业部')
  353 + AND dept.F_EnabledMark = 1
  354 + AND dept.F_DeleteMark IS NULL
  355 + {whereClause.Replace("WHERE 1=1", "")}
218 GROUP BY 356 GROUP BY
219 - department_id,  
220 - department_name,  
221 - parent_id,  
222 - parent_name 357 + dept.F_Id,
  358 + dept.F_FullName,
  359 + dept.F_ParentId,
  360 + parent.F_FullName
223 ORDER BY TotalActualAmount DESC"; 361 ORDER BY TotalActualAmount DESC";
224 362
225 _logger.LogInformation("执行SQL查询:{Sql}", sql); 363 _logger.LogInformation("执行SQL查询:{Sql}", sql);
@@ -237,7 +375,7 @@ namespace NCC.Extend.LqStatistics @@ -237,7 +375,7 @@ namespace NCC.Extend.LqStatistics
237 DepartmentName = r.DepartmentName?.ToString() ?? "", 375 DepartmentName = r.DepartmentName?.ToString() ?? "",
238 ParentId = r.ParentId?.ToString() ?? "", 376 ParentId = r.ParentId?.ToString() ?? "",
239 ParentName = r.ParentName?.ToString() ?? "", 377 ParentName = r.ParentName?.ToString() ?? "",
240 - TotalTargetAmount = Convert.ToDecimal(r.TotalTargetAmount ?? 0), 378 + TotalTargetAmount = 100000, // 默认目标业绩10万
241 TotalActualAmount = Convert.ToDecimal(r.TotalActualAmount ?? 0), 379 TotalActualAmount = Convert.ToDecimal(r.TotalActualAmount ?? 0),
242 TotalOrderCount = Convert.ToInt32(r.TotalOrderCount ?? 0), 380 TotalOrderCount = Convert.ToInt32(r.TotalOrderCount ?? 0),
243 CompletionRate = Convert.ToDecimal(r.CompletionRate ?? 0), 381 CompletionRate = Convert.ToDecimal(r.CompletionRate ?? 0),
@@ -260,8 +398,38 @@ namespace NCC.Extend.LqStatistics @@ -260,8 +398,38 @@ namespace NCC.Extend.LqStatistics
260 /// <summary> 398 /// <summary>
261 /// 获取其他部门业绩统计 399 /// 获取其他部门业绩统计
262 /// </summary> 400 /// </summary>
  401 + /// <remarks>
  402 + /// 根据指定日期范围和部门ID查询其他部门(教育部、科技部、大项目部)的业绩统计信息
  403 + ///
  404 + /// 示例请求:
  405 + /// ```json
  406 + /// {
  407 + /// "startDate": "2025-01-01T00:00:00",
  408 + /// "endDate": "2025-01-31T23:59:59",
  409 + /// "departmentId": "部门ID(可选)"
  410 + /// }
  411 + /// ```
  412 + ///
  413 + /// 参数说明:
  414 + /// - startDate: 开始日期,格式:yyyy-MM-ddTHH:mm:ss
  415 + /// - endDate: 结束日期,格式:yyyy-MM-ddTHH:mm:ss
  416 + /// - departmentId: 部门ID,可选参数,不传则查询所有其他部门
  417 + ///
  418 + /// 返回数据说明:
  419 + /// - DepartmentId: 部门ID
  420 + /// - DepartmentName: 部门名称
  421 + /// - ParentId: 父部门ID
  422 + /// - ParentName: 父部门名称
  423 + /// - TotalTargetAmount: 目标业绩总和
  424 + /// - TotalActualAmount: 完成业绩总和
  425 + /// - TotalOrderCount: 开单总数量
  426 + /// - CompletionRate: 完成率(%)
  427 + /// </remarks>
263 /// <param name="input">查询参数</param> 428 /// <param name="input">查询参数</param>
264 /// <returns>其他部门业绩统计结果</returns> 429 /// <returns>其他部门业绩统计结果</returns>
  430 + /// <response code="200">成功返回其他部门业绩统计数据</response>
  431 + /// <response code="400">请求参数错误</response>
  432 + /// <response code="500">服务器内部错误</response>
265 [HttpPost("OtherDepartmentStatistics")] 433 [HttpPost("OtherDepartmentStatistics")]
266 public async Task<OtherDepartmentStatisticsOutput> GetOtherDepartmentStatistics(OtherDepartmentStatisticsInput input) 434 public async Task<OtherDepartmentStatisticsOutput> GetOtherDepartmentStatistics(OtherDepartmentStatisticsInput input)
267 { 435 {
@@ -273,37 +441,41 @@ namespace NCC.Extend.LqStatistics @@ -273,37 +441,41 @@ namespace NCC.Extend.LqStatistics
273 var parameters = new Dictionary<string, object> { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } }; 441 var parameters = new Dictionary<string, object> { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } };
274 442
275 // 构建WHERE条件 443 // 构建WHERE条件
276 - var whereClause = "WHERE (order_date >= @startDate AND order_date <= @endDate OR order_date IS NULL)";  
277 - 444 + var whereClause = "WHERE 1=1";
278 if (!string.IsNullOrEmpty(input.DepartmentId)) 445 if (!string.IsNullOrEmpty(input.DepartmentId))
279 { 446 {
280 - whereClause += " AND department_id = @departmentId"; 447 + whereClause += " AND dept.F_Id = @departmentId";
281 parameters.Add("@departmentId", input.DepartmentId); 448 parameters.Add("@departmentId", input.DepartmentId);
282 } 449 }
283 450
284 - // 构建SQL查询 451 + // 构建SQL查询 - 先查询所有其他部门的子部门,然后左连接业绩数据
285 var sql = 452 var sql =
286 $@" 453 $@"
287 SELECT 454 SELECT
288 - department_id AS DepartmentId,  
289 - department_name AS DepartmentName,  
290 - parent_id AS ParentId,  
291 - parent_name AS ParentName,  
292 - SUM(COALESCE(target_amount, 0)) AS TotalTargetAmount,  
293 - SUM(COALESCE(actual_amount, 0)) AS TotalActualAmount,  
294 - COUNT(order_id) AS TotalOrderCount, 455 + dept.F_Id AS DepartmentId,
  456 + dept.F_FullName AS DepartmentName,
  457 + dept.F_ParentId AS ParentId,
  458 + parent.F_FullName AS ParentName,
  459 + COALESCE(SUM(flow.actual_amount), 0) AS TotalActualAmount,
  460 + COALESCE(COUNT(flow.order_id), 0) AS TotalOrderCount,
295 CASE 461 CASE
296 - WHEN SUM(COALESCE(target_amount, 0)) > 0  
297 - THEN ROUND((SUM(COALESCE(actual_amount, 0)) / SUM(COALESCE(target_amount, 0))) * 100, 2) 462 + WHEN COALESCE(SUM(flow.actual_amount), 0) > 0
  463 + THEN ROUND((COALESCE(SUM(flow.actual_amount), 0) / 50000) * 100, 2)
298 ELSE 0 464 ELSE 0
299 END AS CompletionRate 465 END AS CompletionRate
300 - FROM v_other_department_performance_flow  
301 - {whereClause} 466 + FROM base_organize dept
  467 + LEFT JOIN base_organize parent ON dept.F_ParentId = parent.F_Id
  468 + LEFT JOIN v_other_department_performance_flow flow ON dept.F_Id = flow.department_id
  469 + AND (flow.order_date >= @startDate AND flow.order_date <= @endDate OR flow.order_date IS NULL)
  470 + WHERE dept.F_ParentId IN (SELECT F_Id FROM base_organize WHERE F_FullName IN ('教育部', '科技部', '大项目部'))
  471 + AND dept.F_EnabledMark = 1
  472 + AND dept.F_DeleteMark IS NULL
  473 + {whereClause.Replace("WHERE 1=1", "")}
302 GROUP BY 474 GROUP BY
303 - department_id,  
304 - department_name,  
305 - parent_id,  
306 - parent_name 475 + dept.F_Id,
  476 + dept.F_FullName,
  477 + dept.F_ParentId,
  478 + parent.F_FullName
307 ORDER BY TotalActualAmount DESC"; 479 ORDER BY TotalActualAmount DESC";
308 480
309 _logger.LogInformation("执行SQL查询:{Sql}", sql); 481 _logger.LogInformation("执行SQL查询:{Sql}", sql);
@@ -321,7 +493,7 @@ namespace NCC.Extend.LqStatistics @@ -321,7 +493,7 @@ namespace NCC.Extend.LqStatistics
321 DepartmentName = r.DepartmentName?.ToString() ?? "", 493 DepartmentName = r.DepartmentName?.ToString() ?? "",
322 ParentId = r.ParentId?.ToString() ?? "", 494 ParentId = r.ParentId?.ToString() ?? "",
323 ParentName = r.ParentName?.ToString() ?? "", 495 ParentName = r.ParentName?.ToString() ?? "",
324 - TotalTargetAmount = Convert.ToDecimal(r.TotalTargetAmount ?? 0), 496 + TotalTargetAmount = 50000, // 默认目标业绩5万
325 TotalActualAmount = Convert.ToDecimal(r.TotalActualAmount ?? 0), 497 TotalActualAmount = Convert.ToDecimal(r.TotalActualAmount ?? 0),
326 TotalOrderCount = Convert.ToInt32(r.TotalOrderCount ?? 0), 498 TotalOrderCount = Convert.ToInt32(r.TotalOrderCount ?? 0),
327 CompletionRate = Convert.ToDecimal(r.CompletionRate ?? 0), 499 CompletionRate = Convert.ToDecimal(r.CompletionRate ?? 0),
@@ -344,8 +516,40 @@ namespace NCC.Extend.LqStatistics @@ -344,8 +516,40 @@ namespace NCC.Extend.LqStatistics
344 /// <summary> 516 /// <summary>
345 /// 获取经理业绩统计 517 /// 获取经理业绩统计
346 /// </summary> 518 /// </summary>
  519 + /// <remarks>
  520 + /// 根据指定日期范围和事业部ID查询经理的业绩统计信息,按经理和门店维度进行统计
  521 + ///
  522 + /// 示例请求:
  523 + /// ```json
  524 + /// {
  525 + /// "startDate": "2025-01-01T00:00:00",
  526 + /// "endDate": "2025-01-31T23:59:59",
  527 + /// "businessUnitId": "事业部ID(可选)"
  528 + /// }
  529 + /// ```
  530 + ///
  531 + /// 参数说明:
  532 + /// - startDate: 开始日期,格式:yyyy-MM-ddTHH:mm:ss
  533 + /// - endDate: 结束日期,格式:yyyy-MM-ddTHH:mm:ss
  534 + /// - businessUnitId: 事业部ID,可选参数,不传则查询所有事业部
  535 + ///
  536 + /// 返回数据说明:
  537 + /// - ManagerName: 经理姓名
  538 + /// - ManagerUserId: 经理用户ID
  539 + /// - BusinessUnitName: 事业部名称
  540 + /// - BusinessUnitId: 事业部ID
  541 + /// - StoreName: 门店名称
  542 + /// - StoreId: 门店ID
  543 + /// - TargetPerformance: 目标业绩
  544 + /// - ActualPerformance: 完成业绩
  545 + /// - OrderCount: 开单数量
  546 + /// - CompletionRate: 完成率(%)
  547 + /// </remarks>
347 /// <param name="input">查询参数</param> 548 /// <param name="input">查询参数</param>
348 /// <returns>经理业绩统计结果</returns> 549 /// <returns>经理业绩统计结果</returns>
  550 + /// <response code="200">成功返回经理业绩统计数据</response>
  551 + /// <response code="400">请求参数错误</response>
  552 + /// <response code="500">服务器内部错误</response>
349 [HttpPost("ManagerStatistics")] 553 [HttpPost("ManagerStatistics")]
350 public async Task<ManagerStatisticsOutput> GetManagerStatistics(ManagerStatisticsInput input) 554 public async Task<ManagerStatisticsOutput> GetManagerStatistics(ManagerStatisticsInput input)
351 { 555 {
@@ -593,6 +797,242 @@ namespace NCC.Extend.LqStatistics @@ -593,6 +797,242 @@ namespace NCC.Extend.LqStatistics
593 } 797 }
594 } 798 }
595 #endregion 799 #endregion
  800 +
  801 + #region 科技部老师业绩统计
  802 + /// <summary>
  803 + /// 获取科技部老师业绩统计
  804 + /// </summary>
  805 + /// <param name="input">查询参数</param>
  806 + /// <returns>科技部老师业绩统计结果</returns>
  807 + [HttpPost("GetTechTeacherStatistics")]
  808 + public async Task<List<TechTeacherSimpleStatisticsOutput>> GetTechTeacherStatistics(TechTeacherStatisticsInput input)
  809 + {
  810 + try
  811 + { // 1. 从用户表获取所有科技部老师
  812 + var allTeachers = await _db.Queryable<UserEntity>()
  813 + .Where(x => x.Gw == "科技老师")
  814 + .Select(x => new
  815 + {
  816 + TeacherId = x.Id,
  817 + TeacherName = x.RealName,
  818 + TeacherAccount = x.Account,
  819 + })
  820 + .ToListAsync();
  821 +
  822 + // 2. 获取业绩流水数据
  823 + var flowQuery = _db.Queryable<VTechTeacherFlowEntity>();
  824 +
  825 + // 老师过滤
  826 + if (!string.IsNullOrEmpty(input.TeacherId))
  827 + {
  828 + flowQuery = flowQuery.Where(x => x.TeacherId == input.TeacherId);
  829 + }
  830 +
  831 + if (!string.IsNullOrEmpty(input.TeacherName))
  832 + {
  833 + flowQuery = flowQuery.Where(x => x.TeacherName.Contains(input.TeacherName));
  834 + }
  835 +
  836 + // 日期过滤
  837 + if (input.StartDate.HasValue)
  838 + {
  839 + flowQuery = flowQuery.Where(x => x.BusinessDate >= input.StartDate.Value);
  840 + }
  841 +
  842 + if (input.EndDate.HasValue)
  843 + {
  844 + flowQuery = flowQuery.Where(x => x.BusinessDate <= input.EndDate.Value);
  845 + }
  846 +
  847 + var flowRecords = await flowQuery.ToListAsync();
  848 +
  849 + // 3. 按老师分组统计业绩数据
  850 + var teacherStatsDict = flowRecords
  851 + .GroupBy(x => new
  852 + {
  853 + x.TeacherId,
  854 + x.TeacherName,
  855 + x.TeacherAccount,
  856 + })
  857 + .ToDictionary(
  858 + g => g.Key,
  859 + g => new
  860 + {
  861 + ConsumeProjectCount = (int)g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ProjectCount),
  862 + ConsumeAchievement = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.Achievement),
  863 + OrderAchievement = g.Where(x => x.BusinessType == "开卡").Sum(x => x.Achievement),
  864 + OrderItemCount = g.Where(x => x.BusinessType == "开卡").Sum(x => x.ItemCount),
  865 + ConsumeItemCount = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ItemCount),
  866 + }
  867 + );
  868 +
  869 + // 4. 构建结果,包含所有老师
  870 + var result = new List<TechTeacherSimpleStatisticsOutput>();
  871 +
  872 + foreach (var teacher in allTeachers)
  873 + {
  874 + // 应用过滤条件
  875 + if (!string.IsNullOrEmpty(input.TeacherId) && teacher.TeacherId != input.TeacherId)
  876 + continue;
  877 +
  878 + if (!string.IsNullOrEmpty(input.TeacherName) && !teacher.TeacherName.Contains(input.TeacherName))
  879 + continue;
  880 +
  881 + var stats = teacherStatsDict.GetValueOrDefault(
  882 + new
  883 + {
  884 + TeacherId = teacher.TeacherId,
  885 + TeacherName = teacher.TeacherName,
  886 + TeacherAccount = teacher.TeacherAccount,
  887 + },
  888 + new
  889 + {
  890 + ConsumeProjectCount = 0,
  891 + ConsumeAchievement = 0m,
  892 + OrderAchievement = 0m,
  893 + OrderItemCount = 0,
  894 + ConsumeItemCount = 0,
  895 + }
  896 + );
  897 +
  898 + var teacherStats = new TechTeacherSimpleStatisticsOutput
  899 + {
  900 + DepartmentName = "科技部", // 固定为科技部
  901 + TeacherName = teacher.TeacherName,
  902 + ConsumeProjectCount = stats.ConsumeProjectCount,
  903 + ConsumeAchievement = stats.ConsumeAchievement,
  904 + OrderAchievement = stats.OrderAchievement,
  905 + OrderItemCount = stats.OrderItemCount,
  906 + ConsumeItemCount = stats.ConsumeItemCount,
  907 + };
  908 +
  909 + result.Add(teacherStats);
  910 + }
  911 +
  912 + return result;
  913 + }
  914 + catch (Exception ex)
  915 + {
  916 + _logger.LogError(ex, "获取科技部老师业绩统计时发生错误");
  917 + throw NCCException.Oh("获取科技部老师业绩统计失败", ex);
  918 + }
  919 + }
  920 +
  921 + #endregion
  922 +
  923 + #region 匿名接口
  924 +
  925 + /// <summary>
  926 + /// 发送每日统计消息(匿名接口)
  927 + /// </summary>
  928 + /// <returns>发送结果</returns>
  929 + [HttpPost("SendDailyReportMessage")]
  930 + [AllowAnonymous]
  931 + public async Task<object> SendDailyReportMessage()
  932 + {
  933 + try
  934 + {
  935 + // 获取本月全门店统计数据
  936 + var monthlyStats = await GetMonthlyStoreStatistics();
  937 +
  938 + // 构建包含统计数据的消息
  939 + var messageContent =
  940 + $"📊 今日日报已生成,点击链接查看\n\n"
  941 + + $"本月全门店目标业绩:{monthlyStats.TargetPerformance:N0}元\n"
  942 + + $"本月已完成业绩:{monthlyStats.ActualPerformance:N0}元\n"
  943 + + $"完成率:{monthlyStats.CompletionRate:F2}%\n\n"
  944 + + $"http://lvqian.antissoft.com/html/dailyReportnew.html";
  945 +
  946 + var result = await _weChatBotService.SendTextMessage(messageContent);
  947 + return new
  948 + {
  949 + success = result,
  950 + message = result ? "每日统计消息发送成功" : "每日统计消息发送失败",
  951 + timestamp = DateTime.Now,
  952 + statistics = monthlyStats,
  953 + };
  954 + }
  955 + catch (Exception ex)
  956 + {
  957 + _logger.LogError(ex, "发送每日统计消息时发生错误");
  958 + return new
  959 + {
  960 + success = false,
  961 + message = "发送每日统计消息时发生错误: " + ex.Message,
  962 + timestamp = DateTime.Now,
  963 + };
  964 + }
  965 + }
  966 +
  967 + /// <summary>
  968 + /// 获取本月全门店统计数据
  969 + /// </summary>
  970 + /// <returns>本月统计数据</returns>
  971 + private async Task<dynamic> GetMonthlyStoreStatistics()
  972 + {
  973 + try
  974 + {
  975 + // 获取本月开始和结束日期
  976 + var now = DateTime.Now;
  977 + var startDate = new DateTime(now.Year, now.Month, 1);
  978 + var endDate = startDate.AddMonths(1).AddDays(-1);
  979 +
  980 + // 构建查询参数
  981 + var parameters = new Dictionary<string, object> { { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") } };
  982 +
  983 + // 查询本月全门店统计数据
  984 + var sql =
  985 + @"
  986 + SELECT
  987 + SUM(target_performance) AS TotalTargetPerformance,
  988 + SUM(actual_performance) AS TotalActualPerformance,
  989 + CASE
  990 + WHEN SUM(target_performance) > 0
  991 + THEN (SUM(actual_performance) / SUM(target_performance)) * 100
  992 + ELSE 0
  993 + END AS TotalCompletionRate
  994 + FROM v_store_performance_simple";
  995 +
  996 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql, parameters);
  997 + var stats = result.FirstOrDefault();
  998 +
  999 + if (stats != null)
  1000 + {
  1001 + var targetPerformance = Convert.ToDecimal(stats.TotalTargetPerformance ?? 0);
  1002 + var actualPerformance = Convert.ToDecimal(stats.TotalActualPerformance ?? 0);
  1003 + var completionRate = Convert.ToDecimal(stats.TotalCompletionRate ?? 0);
  1004 +
  1005 + return new
  1006 + {
  1007 + TargetPerformance = targetPerformance,
  1008 + ActualPerformance = actualPerformance,
  1009 + CompletionRate = completionRate,
  1010 + Month = now.ToString("yyyy年MM月"),
  1011 + };
  1012 + }
  1013 +
  1014 + return new
  1015 + {
  1016 + TargetPerformance = 0m,
  1017 + ActualPerformance = 0m,
  1018 + CompletionRate = 0m,
  1019 + Month = now.ToString("yyyy年MM月"),
  1020 + };
  1021 + }
  1022 + catch (Exception ex)
  1023 + {
  1024 + _logger.LogError(ex, "获取本月全门店统计数据时发生错误");
  1025 + return new
  1026 + {
  1027 + TargetPerformance = 0m,
  1028 + ActualPerformance = 0m,
  1029 + CompletionRate = 0m,
  1030 + Month = DateTime.Now.ToString("yyyy年MM月"),
  1031 + };
  1032 + }
  1033 + }
  1034 +
  1035 + #endregion
596 } 1036 }
597 1037
598 /// <summary> 1038 /// <summary>
netcore/src/Modularity/Extend/NCC.Extend/Utils/WeChatBotService.cs
@@ -34,14 +34,14 @@ namespace NCC.Extend.Utils @@ -34,14 +34,14 @@ namespace NCC.Extend.Utils
34 webhookUrl = WEBHOOK_URL, 34 webhookUrl = WEBHOOK_URL,
35 content = content, 35 content = content,
36 mentionedList = (string)null, 36 mentionedList = (string)null,
37 - mentionedMobileList = (string)null 37 + mentionedMobileList = (string)null,
38 }; 38 };
39 39
40 var json = JsonConvert.SerializeObject(requestData); 40 var json = JsonConvert.SerializeObject(requestData);
41 var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); 41 var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
42 42
43 var response = await _httpClient.PostAsync(BOT_API_URL, httpContent); 43 var response = await _httpClient.PostAsync(BOT_API_URL, httpContent);
44 - 44 +
45 if (response.IsSuccessStatusCode) 45 if (response.IsSuccessStatusCode)
46 { 46 {
47 var responseContent = await response.Content.ReadAsStringAsync(); 47 var responseContent = await response.Content.ReadAsStringAsync();
@@ -77,8 +77,28 @@ namespace NCC.Extend.Utils @@ -77,8 +77,28 @@ namespace NCC.Extend.Utils
77 77
78 // 添加标题和格式化 78 // 添加标题和格式化
79 var messageContent = $"🎉 新开单记录\n\n{orderRecordString}"; 79 var messageContent = $"🎉 新开单记录\n\n{orderRecordString}";
80 -  
81 return await SendTextMessage(messageContent); 80 return await SendTextMessage(messageContent);
82 } 81 }
  82 +
  83 + /// <summary>
  84 + /// 发送每日统计消息
  85 + /// </summary>
  86 + /// <returns>发送结果</returns>
  87 + public async Task<bool> SendDailyReportMessage()
  88 + {
  89 + try
  90 + {
  91 + var reportUrl = "http://lvqian.antissoft.com/html/dailyReportnew.html";
  92 + var messageContent = $"📊 今日日报已生成,点击链接查看\n\n{reportUrl}";
  93 +
  94 + Console.WriteLine("发送每日统计消息...");
  95 + return await SendTextMessage(messageContent);
  96 + }
  97 + catch (Exception ex)
  98 + {
  99 + Console.WriteLine($"发送每日统计消息异常: {ex.Message}");
  100 + return false;
  101 + }
  102 + }
83 } 103 }
84 } 104 }
创建经理门店业绩统计视图.sql deleted
1 --- 创建经理门店业绩统计视图  
2 --- 融合经理信息、管理门店、生命线设置、业绩流水数据  
3 -  
4 -DROP VIEW IF EXISTS `v_manager_store_performance`;  
5 -  
6 -CREATE VIEW `v_manager_store_performance` AS  
7 -SELECT  
8 - -- 经理基础信息  
9 - u.F_Id AS manager_user_id, -- 经理用户ID  
10 - u.F_REALNAME AS manager_name, -- 经理姓名  
11 - u.F_GW AS manager_position, -- 经理岗位  
12 - u.F_ORGANIZEID AS manager_organize_id, -- 经理组织ID  
13 -  
14 - -- 事业部信息  
15 - o.F_Id AS business_unit_id, -- 事业部ID  
16 - o.F_FullName AS business_unit_name, -- 事业部名称  
17 - o.F_ParentId AS parent_organize_id, -- 上级组织ID  
18 - parent.F_FullName AS parent_organize_name, -- 上级组织名称  
19 -  
20 - -- 门店信息  
21 - md.F_Id AS store_id, -- 门店ID  
22 - md.mdbm AS store_code, -- 门店编号  
23 - md.dm AS store_name, -- 门店名称  
24 - md.syb AS store_business_unit_id, -- 门店所属事业部ID  
25 -  
26 - -- 生命线设置信息  
27 - COALESCE(zjl.smx1, 0) AS target_performance, -- 目标业绩(生命线1)  
28 - zjl.tcbl1 AS commission_rate1, -- 生命线1提成比例  
29 - zjl.smx2 AS target_performance2, -- 目标业绩2(生命线2)  
30 - zjl.tcbl2 AS commission_rate2, -- 生命线2提成比例  
31 - zjl.smx3 AS target_performance3, -- 目标业绩3(生命线3)  
32 - zjl.tcbl3 AS commission_rate3, -- 生命线3提成比例  
33 -  
34 - -- 业绩流水信息(从现有视图获取)  
35 - flow.order_id, -- 开单ID  
36 - flow.order_date, -- 开单时间  
37 - flow.completed_amount, -- 完成业绩(整单业绩)  
38 - flow.actual_amount, -- 实付业绩  
39 - flow.debt_amount, -- 欠款  
40 - flow.completion_rate, -- 完成率  
41 - flow.golden_triangle, -- 金三角  
42 - flow.member_id, -- 开单会员ID  
43 - flow.member_name, -- 开单会员名称  
44 - flow.member_phone, -- 开单会员手机号  
45 - flow.customer_type, -- 顾客类型  
46 - flow.payment_method, -- 付款方式  
47 - flow.customer_source, -- 客户来源  
48 -  
49 - -- 时间维度  
50 - flow.order_year, -- 开单年份  
51 - flow.order_month, -- 开单月份  
52 - flow.order_quarter, -- 开单季度  
53 - flow.order_date_only, -- 开单日期(不含时间)  
54 -  
55 - -- 统计标识  
56 - CASE  
57 - WHEN flow.order_id IS NOT NULL THEN 1  
58 - ELSE 0  
59 - END AS has_performance, -- 是否有业绩记录  
60 -  
61 - -- 创建时间  
62 - COALESCE(flow.create_time, NOW()) AS create_time,  
63 - COALESCE(flow.modify_time, NOW()) AS modify_time  
64 -  
65 -FROM base_user u  
66 --- 关联组织信息  
67 -LEFT JOIN base_organize o ON u.F_ORGANIZEID = o.F_Id  
68 -LEFT JOIN base_organize parent ON o.F_ParentId = parent.F_Id  
69 --- 关联门店信息(通过事业部关联)  
70 -LEFT JOIN lq_mdxx md ON md.syb = o.F_Id  
71 --- 关联生命线设置  
72 -LEFT JOIN lq_zjl_mdsmxsz zjl ON zjl.zjl_userid = u.F_Id  
73 - AND zjl.md_id = md.F_Id  
74 - AND zjl.deletemark = 0  
75 --- 关联业绩流水(从现有视图)  
76 -LEFT JOIN v_department_performance_flow flow ON flow.store_id = md.F_Id  
77 - AND flow.department_id = o.F_Id  
78 -  
79 -WHERE u.F_DELETEMARK IS NULL -- 用户未删除  
80 - AND u.F_GW IS NOT NULL -- 有岗位信息  
81 - AND (u.F_GW LIKE '%经理%' OR u.F_GW LIKE '%总经理%' OR u.F_GW LIKE '%主管%') -- 经理岗位  
82 - AND o.F_Category = 'department' -- 部门类型  
83 - AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1) -- 组织未删除  
84 - AND md.F_Id IS NOT NULL; -- 有门店信息  
85 -  
86 --- 添加视图注释  
87 ---ALTER VIEW `v_manager_store_performance` COMMENT = '经理门店业绩统计视图 - 融合经理信息、管理门店、生命线设置、业绩流水数据';  
创建退卡相关表.sql deleted
1 --- =============================================  
2 --- 绿纤美业ERP系统 - 退卡相关表创建脚本  
3 --- 创建时间: 2025-01-27  
4 --- 说明: 创建会员退卡主表、明细表、业绩表  
5 --- =============================================  
6 -  
7 --- 1. 创建会员退卡主表  
8 -DROP TABLE IF EXISTS `lq_hytk_hytk`;  
9 -CREATE TABLE `lq_hytk_hytk` (  
10 - `F_Id` varchar(50) NOT NULL COMMENT '退卡编号',  
11 - `md` varchar(50) DEFAULT NULL COMMENT '门店',  
12 - `mdbh` varchar(50) DEFAULT NULL COMMENT '门店编号',  
13 - `mdmc` varchar(50) DEFAULT NULL COMMENT '门店名称',  
14 - `hy` varchar(50) DEFAULT NULL COMMENT '会员',  
15 - `hyzh` varchar(50) DEFAULT NULL COMMENT '会员账号',  
16 - `hymc` varchar(50) DEFAULT NULL COMMENT '会员名称',  
17 - `gklx` varchar(50) DEFAULT NULL COMMENT '顾客类型',  
18 - `tkje` decimal(15,2) DEFAULT NULL COMMENT '退卡总金额',  
19 - `sgfy` decimal(15,2) DEFAULT NULL COMMENT '手工费用',  
20 - `bz` text COMMENT '备注',  
21 - `tksj` datetime DEFAULT NULL COMMENT '退卡时间',  
22 - `czry` varchar(50) DEFAULT NULL COMMENT '操作人员',  
23 - `tkzt` varchar(20) DEFAULT NULL COMMENT '退卡状态',  
24 - `tkyy` varchar(200) DEFAULT NULL COMMENT '退卡原因',  
25 - `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间',  
26 - `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建用户',  
27 - `F_ModifyTime` datetime DEFAULT NULL COMMENT '修改时间',  
28 - `F_ModifyUser` varchar(50) DEFAULT NULL COMMENT '修改用户',  
29 - `F_DeleteMark` int(1) DEFAULT NULL COMMENT '删除标记',  
30 - PRIMARY KEY (`F_Id`),  
31 - KEY `idx_md` (`md`),  
32 - KEY `idx_hy` (`hy`),  
33 - KEY `idx_tksj` (`tksj`),  
34 - KEY `idx_tkzt` (`tkzt`)  
35 -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退卡_信息表';  
36 -  
37 --- 2. 创建会员退卡明细表  
38 -DROP TABLE IF EXISTS `lq_hytk_mx`;  
39 -CREATE TABLE `lq_hytk_mx` (  
40 - `F_Id` varchar(50) NOT NULL COMMENT '明细编号',  
41 - `gltkbh` varchar(50) DEFAULT NULL COMMENT '关联退卡编号',  
42 - `px` varchar(50) DEFAULT NULL COMMENT '品项',  
43 - `pxmc` varchar(100) DEFAULT NULL COMMENT '品项名称',  
44 - `pxjg` decimal(15,2) DEFAULT NULL COMMENT '品项价格',  
45 - `tkje` decimal(15,2) DEFAULT NULL COMMENT '退款金额',  
46 - `F_ProjectNumber` int DEFAULT NULL COMMENT '项目次数',  
47 - `F_IsEnabled` int(1) DEFAULT NULL COMMENT '是否有效',  
48 - `F_SourceType` varchar(20) DEFAULT NULL COMMENT '来源类型',  
49 - `F_TotalPrice` decimal(15,2) DEFAULT NULL COMMENT '合计金额',  
50 - `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间',  
51 - `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建用户',  
52 - `F_ModifyTime` datetime DEFAULT NULL COMMENT '修改时间',  
53 - `F_ModifyUser` varchar(50) DEFAULT NULL COMMENT '修改用户',  
54 - `F_DeleteMark` int(1) DEFAULT NULL COMMENT '删除标记',  
55 - PRIMARY KEY (`F_Id`),  
56 - KEY `idx_gltkbh` (`gltkbh`),  
57 - KEY `idx_px` (`px`),  
58 - KEY `idx_F_SourceType` (`F_SourceType`)  
59 -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退卡_品项明细表';  
60 -  
61 --- 3. 创建退卡健康师业绩表  
62 -DROP TABLE IF EXISTS `lq_hytk_jksyj`;  
63 -CREATE TABLE `lq_hytk_jksyj` (  
64 - `F_Id` varchar(50) NOT NULL COMMENT '业绩编号',  
65 - `gltkbh` varchar(50) DEFAULT NULL COMMENT '关联退卡编号',  
66 - `jks` varchar(50) DEFAULT NULL COMMENT '健康师',  
67 - `jksxm` varchar(50) DEFAULT NULL COMMENT '健康师姓名',  
68 - `jkszh` varchar(50) DEFAULT NULL COMMENT '健康师账号',  
69 - `jksyj` decimal(15,2) DEFAULT NULL COMMENT '健康师业绩',  
70 - `tksj` datetime DEFAULT NULL COMMENT '退卡时间',  
71 - `F_jsjid` varchar(50) DEFAULT NULL COMMENT '金三角id',  
72 - `F_tkpxid` varchar(50) DEFAULT NULL COMMENT '关联项目资料表ID',  
73 - `F_LaborCost` decimal(15,2) DEFAULT NULL COMMENT '手工费',  
74 - `F_tkpxNumber` decimal(15,2) DEFAULT NULL COMMENT '退卡品项次数',  
75 - `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间',  
76 - `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建用户',  
77 - `F_ModifyTime` datetime DEFAULT NULL COMMENT '修改时间',  
78 - `F_ModifyUser` varchar(50) DEFAULT NULL COMMENT '修改用户',  
79 - `F_DeleteMark` int(1) DEFAULT NULL COMMENT '删除标记',  
80 - PRIMARY KEY (`F_Id`),  
81 - KEY `idx_gltkbh` (`gltkbh`),  
82 - KEY `idx_jks` (`jks`),  
83 - KEY `idx_F_tkpxid` (`F_tkpxid`),  
84 - KEY `idx_tksj` (`tksj`)  
85 -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退卡_健康师业绩表';  
86 -  
87 --- 4. 创建退卡科技部老师业绩表  
88 -DROP TABLE IF EXISTS `lq_hytk_kjbsyj`;  
89 -CREATE TABLE `lq_hytk_kjbsyj` (  
90 - `F_Id` varchar(50) NOT NULL COMMENT '业绩编号',  
91 - `gltkbh` varchar(50) DEFAULT NULL COMMENT '关联退卡编号',  
92 - `kjbls` varchar(50) DEFAULT NULL COMMENT '科技部老师',  
93 - `kjblsxm` varchar(50) DEFAULT NULL COMMENT '科技部老师姓名',  
94 - `kjblszh` varchar(50) DEFAULT NULL COMMENT '科技部老师账号',  
95 - `kjblsyj` decimal(15,2) DEFAULT NULL COMMENT '科技部老师业绩',  
96 - `tksj` datetime DEFAULT NULL COMMENT '退卡时间',  
97 - `F_tkpxid` varchar(50) DEFAULT NULL COMMENT '关联项目资料表ID',  
98 - `F_LaborCost` decimal(15,2) DEFAULT NULL COMMENT '手工费',  
99 - `F_tkpxNumber` decimal(15,2) DEFAULT NULL COMMENT '退卡品项次数',  
100 - `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间',  
101 - `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建用户',  
102 - `F_ModifyTime` datetime DEFAULT NULL COMMENT '修改时间',  
103 - `F_ModifyUser` varchar(50) DEFAULT NULL COMMENT '修改用户',  
104 - `F_DeleteMark` int(1) DEFAULT NULL COMMENT '删除标记',  
105 - PRIMARY KEY (`F_Id`),  
106 - KEY `idx_gltkbh` (`gltkbh`),  
107 - KEY `idx_kjbls` (`kjbls`),  
108 - KEY `idx_F_tkpxid` (`F_tkpxid`),  
109 - KEY `idx_tksj` (`tksj`)  
110 -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退卡_科技部老师业绩表';  
111 -  
112 --- =============================================  
113 --- 添加外键约束(可选)  
114 --- =============================================  
115 -  
116 --- 退卡明细表外键约束  
117 --- ALTER TABLE `lq_hytk_mx` ADD CONSTRAINT `fk_hytk_mx_gltkbh` FOREIGN KEY (`gltkbh`) REFERENCES `lq_hytk_hytk` (`F_Id`) ON DELETE CASCADE ON UPDATE CASCADE;  
118 -  
119 --- 退卡健康师业绩表外键约束  
120 --- ALTER TABLE `lq_hytk_jksyj` ADD CONSTRAINT `fk_hytk_jksyj_gltkbh` FOREIGN KEY (`gltkbh`) REFERENCES `lq_hytk_hytk` (`F_Id`) ON DELETE CASCADE ON UPDATE CASCADE;  
121 --- ALTER TABLE `lq_hytk_jksyj` ADD CONSTRAINT `fk_hytk_jksyj_tkpxid` FOREIGN KEY (`F_tkpxid`) REFERENCES `lq_xmzl` (`F_Id`) ON DELETE CASCADE ON UPDATE CASCADE;  
122 -  
123 --- 退卡科技部老师业绩表外键约束  
124 --- ALTER TABLE `lq_hytk_kjbsyj` ADD CONSTRAINT `fk_hytk_kjbsyj_gltkbh` FOREIGN KEY (`gltkbh`) REFERENCES `lq_hytk_hytk` (`F_Id`) ON DELETE CASCADE ON UPDATE CASCADE;  
125 --- ALTER TABLE `lq_hytk_kjbsyj` ADD CONSTRAINT `fk_hytk_kjbsyj_tkpxid` FOREIGN KEY (`F_tkpxid`) REFERENCES `lq_xmzl` (`F_Id`) ON DELETE CASCADE ON UPDATE CASCADE;  
126 -  
127 --- =============================================  
128 --- 创建完成提示  
129 --- =============================================  
130 -SELECT '退卡相关表创建完成!' AS '创建结果';