diff --git a/.DS_Store b/.DS_Store index 7966048..0812e3d 100644 --- a/.DS_Store +++ b/.DS_Store diff --git a/[已用]创建科技部老师业绩统计视图.sql b/[已用]创建科技部老师业绩统计视图.sql new file mode 100644 index 0000000..9125ee9 --- /dev/null +++ b/[已用]创建科技部老师业绩统计视图.sql @@ -0,0 +1,84 @@ +-- 创建科技部老师业绩流水视图 +-- 包含所有业务类型的流水记录,并包含对应的信息表ID和品项次数 + +CREATE OR REPLACE VIEW v_tech_teacher_flow AS +SELECT + teacher_id, + teacher_name, + teacher_account, + business_type, + achievement, + labor_cost, + project_count, + customer_id, + customer_name, + business_date, + source_id, + source_table, + item_count +FROM ( + -- 开卡流水 + SELECT + k.kjbls as teacher_id, + k.kjblsxm as teacher_name, + k.kjblszh as teacher_account, + '开卡' as business_type, + CAST(COALESCE(k.kjblsyj, 0) AS DECIMAL(10,2)) as achievement, + CAST(COALESCE(k.F_LaborCost, 0) AS DECIMAL(10,2)) as labor_cost, + CAST(COALESCE(pm.F_ProjectNumber, 0) AS DECIMAL(10,2)) as project_count, + o.kdhy as customer_id, + o.kdhyc as customer_name, + k.yjsj as business_date, + k.F_kdpxid as source_id, + 'lq_kd_pxmx' as source_table, + COALESCE(pm.F_ProjectNumber, 0) as item_count + FROM lq_kd_kjbsyj k + LEFT JOIN lq_kd_kdjlb o ON k.glkdbh = o.F_Id + LEFT JOIN lq_kd_pxmx pm ON k.F_kdpxid = pm.F_Id + WHERE k.kjbls IS NOT NULL AND k.kjblsxm IS NOT NULL + + UNION ALL + + -- 耗卡流水 + SELECT + x.kjbls as teacher_id, + x.kjblsxm as teacher_name, + x.kjblszh as teacher_account, + '耗卡' as business_type, + CAST(COALESCE(x.kjblsyj, 0) AS DECIMAL(10,2)) as achievement, + CAST(COALESCE(x.F_LaborCost, 0) AS DECIMAL(10,2)) as labor_cost, + CAST(COALESCE(pm.F_ProjectNumber, 0) AS DECIMAL(10,2)) as project_count, + h.hy as customer_id, + h.hymc as customer_name, + x.yjsj as business_date, + x.F_hkpxid as source_id, + 'lq_xh_pxmx' as source_table, + COALESCE(pm.F_ProjectNumber, 0) as item_count + FROM lq_xh_kjbsyj x + LEFT JOIN lq_xh_hyhk h ON x.glkdbh = h.F_Id + LEFT JOIN lq_xh_pxmx pm ON x.F_hkpxid = pm.F_Id + WHERE x.kjbls IS NOT NULL AND x.kjblsxm IS NOT NULL + + UNION ALL + + -- 退卡流水 + SELECT + r.kjbls as teacher_id, + r.kjblsxm as teacher_name, + r.kjblszh as teacher_account, + '退卡' as business_type, + CAST(COALESCE(r.kjblsyj, 0) AS DECIMAL(10,2)) as achievement, + CAST(COALESCE(r.F_LaborCost, 0) AS DECIMAL(10,2)) as labor_cost, + CAST(COALESCE(r.F_tkpxNumber, 0) AS DECIMAL(10,2)) as project_count, + t.hy as customer_id, + t.hymc as customer_name, + r.tksj as business_date, + r.F_Id as source_id, + 'lq_hytk_kjbsyj' as source_table, + 0 as item_count + FROM lq_hytk_kjbsyj r + LEFT JOIN lq_hytk_hytk t ON r.gltkbh = t.F_Id + WHERE r.kjbls IS NOT NULL AND r.kjblsxm IS NOT NULL AND (r.F_DeleteMark IS NULL OR r.F_DeleteMark = 0) +) t; + + diff --git a/[已用]创建门店耗卡业绩统计视图.sql b/[已用]创建门店耗卡业绩统计视图.sql new file mode 100644 index 0000000..0162fa9 --- /dev/null +++ b/[已用]创建门店耗卡业绩统计视图.sql @@ -0,0 +1,141 @@ +-- ============================================= +-- 门店耗卡业绩统计视图创建脚本 +-- 功能:统计所有门店的目标业绩、耗卡业绩、完成率等关键指标 +-- 作者:系统自动生成 +-- 创建时间:2025年 +-- ============================================= + +-- 先删除视图(如果存在) +DROP VIEW IF EXISTS v_store_consume_performance_simple; + +-- ============================================= +-- 创建门店耗卡业绩统计视图(当月数据) +-- ============================================= +CREATE VIEW v_store_consume_performance_simple AS +SELECT + `md`.`F_Id` AS `store_id`, + `md`.`djmd` AS `store_code`, + `md`.`dm` AS `store_name`, + COALESCE(`md`.`xsyj`, 0) AS `target_performance`, + COALESCE(`consume`.`consume_performance`, 0) AS `actual_performance`, + ( + CASE + WHEN (COALESCE(`md`.`xsyj`, 0) > 0) THEN + round( + ( + ( + COALESCE(`consume`.`consume_performance`, 0) / `md`.`xsyj` + ) * 100 + ), + 2 + ) ELSE 0 + END + ) AS `completion_rate`, + COALESCE(`consume`.`consume_count`, 0) AS `order_count` + FROM + ( + `lqerp`.`lq_mdxx` `md` + LEFT JOIN ( + SELECT + `hk`.`md` AS `md`, + sum(COALESCE(`px`.`F_TotalPrice`, 0)) AS `consume_performance`, + count(DISTINCT `hk`.`F_Id`) AS `consume_count` + FROM + ( + `lqerp`.`lq_xh_hyhk` `hk` + LEFT JOIN `lqerp`.`lq_xh_pxmx` `px` ON ((`hk`.`F_Id` = `px`.`glkdbh`)) + ) + WHERE + ( + ( + YEAR(`hk`.`hksj`) = YEAR(curdate()) + ) + AND ( + MONTH(`hk`.`hksj`) = MONTH(curdate()) + ) + AND ( + isnull(`px`.`F_CreateTIme`) + OR ( + ( + YEAR(`px`.`F_CreateTIme`) = YEAR(curdate()) + ) + AND ( + MONTH(`px`.`F_CreateTIme`) = MONTH(curdate()) + ) + ) + ) + ) + GROUP BY + `hk`.`md` + ) `consume` ON ((`md`.`F_Id` = `consume`.`md`)) + ) + ORDER BY + COALESCE(`consume`.`consume_performance`, 0) DESC; + +-- ============================================= +-- 添加视图注释 +-- ============================================= +ALTER VIEW v_store_consume_performance_simple COMMENT = '门店耗卡业绩统计视图(当月数据)- 统计所有门店的目标业绩、耗卡业绩、完成率等关键指标'; + +-- ============================================= +-- 视图字段说明 +-- ============================================= +/* +字段说明: +- store_id: 门店ID(来自lq_mdxx.F_Id) +- store_code: 门店编码(来自lq_mdxx.djmd) +- store_name: 店名(来自lq_mdxx.dm) +- target_performance: 目标业绩/生命线(来自lq_mdxx.xsyj) +- actual_performance: 耗卡业绩(来自lq_xh_pxmx.F_TotalPrice的汇总) +- completion_rate: 完成率百分比(actual_performance / target_performance * 100) +- order_count: 耗卡记录数(有效耗卡记录数) + +关联说明: +- 主表:lq_mdxx(门店信息表) +- 关联表:lq_xh_hyhk(耗卡记录表)+ lq_xh_pxmx(耗卡品项表) +- 关联条件:lq_mdxx.F_Id = lq_xh_hyhk.md, lq_xh_hyhk.F_Id = lq_xh_pxmx.glkdbh +- 关联方式:LEFT JOIN(确保所有门店都显示,包括没有耗卡记录的门店) + +数据来源: +- 目标业绩:lq_mdxx.xsyj(目标-门店生命线) +- 耗卡业绩:lq_xh_pxmx.F_TotalPrice(金额合计) +- 耗卡记录数:lq_xh_hyhk表记录数统计 + +时间过滤: +- 耗卡日期:lq_xh_hyhk.hksj(当月) +- 品项创建时间:lq_xh_pxmx.F_CreateTIme(当月) + +注意事项: +1. 使用金额合计(F_TotalPrice)而不是品项价格(pxjg) +2. 只统计当月的数据(耗卡日期和品项创建时间都是当月) +3. 即使目标业绩、耗卡业绩为0的门店也会显示 +4. 完成率计算时,目标业绩为0的门店显示0%完成率 +5. 按耗卡业绩降序排列,便于查看业绩排名 +6. 确保耗卡品项也是当月创建的,避免历史数据干扰 +*/ + +-- ============================================= +-- 使用示例 +-- ============================================= +/* +-- 查看所有门店当月耗卡业绩 +SELECT * FROM v_store_consume_performance_simple; + +-- 查看华润店当月耗卡业绩 +SELECT * FROM v_store_consume_performance_simple WHERE store_name LIKE '%华润%'; + +-- 查看已完成目标的门店(当月耗卡) +SELECT * FROM v_store_consume_performance_simple WHERE completion_rate >= 100; + +-- 查看零耗卡业绩的门店(当月) +SELECT * FROM v_store_consume_performance_simple WHERE actual_performance = 0; + +-- 按完成率排名(当月耗卡) +SELECT * FROM v_store_consume_performance_simple WHERE target_performance > 0 ORDER BY completion_rate DESC; + +-- 查看当月耗卡业绩前10名门店 +SELECT * FROM v_store_consume_performance_simple ORDER BY actual_performance DESC LIMIT 10; + +-- 查看当月完成率低于50%的门店 +SELECT * FROM v_store_consume_performance_simple WHERE completion_rate < 50 AND target_performance > 0; +*/ diff --git a/netcore/src/.DS_Store b/netcore/src/.DS_Store index 141eeaa..a997e15 100644 --- a/netcore/src/.DS_Store +++ b/netcore/src/.DS_Store diff --git a/netcore/src/Application/.DS_Store b/netcore/src/Application/.DS_Store index 1f866c2..58d28c4 100644 --- a/netcore/src/Application/.DS_Store +++ b/netcore/src/Application/.DS_Store diff --git a/netcore/src/Application/NCC.API/.DS_Store b/netcore/src/Application/NCC.API/.DS_Store index 76012f3..08c8cba 100644 --- a/netcore/src/Application/NCC.API/.DS_Store +++ b/netcore/src/Application/NCC.API/.DS_Store diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs new file mode 100644 index 0000000..e8ac726 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherSimpleStatisticsOutput.cs @@ -0,0 +1,45 @@ +using System; + +namespace NCC.Extend.Entitys.Dto.LqStatistics +{ + /// + /// 科技部老师简化统计输出 + /// + public class TechTeacherSimpleStatisticsOutput + { + /// + /// 部门名称 + /// + public string DepartmentName { get; set; } + + /// + /// 老师姓名 + /// + public string TeacherName { get; set; } + + /// + /// 消耗项目数 + /// + public int ConsumeProjectCount { get; set; } + + /// + /// 消耗业绩 + /// + public decimal ConsumeAchievement { get; set; } + + /// + /// 开卡业绩 + /// + public decimal OrderAchievement { get; set; } + + /// + /// 开卡品项次数 + /// + public int OrderItemCount { get; set; } + + /// + /// 耗卡品项次数 + /// + public int ConsumeItemCount { get; set; } + } +} diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherStatisticsInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherStatisticsInput.cs new file mode 100644 index 0000000..369a322 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherStatisticsInput.cs @@ -0,0 +1,35 @@ +using System; + +namespace NCC.Extend.Entitys.Dto.LqStatistics +{ + /// + /// 科技部老师统计查询参数 + /// + public class TechTeacherStatisticsInput + { + /// + /// 开始日期 + /// + public DateTime? StartDate { get; set; } + + /// + /// 结束日期 + /// + public DateTime? EndDate { get; set; } + + /// + /// 科技部老师ID + /// + public string TeacherId { get; set; } + + /// + /// 科技部老师姓名 + /// + public string TeacherName { get; set; } + + /// + /// 部门ID + /// + public string DepartmentId { get; set; } + } +} diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherStatisticsOutput.cs new file mode 100644 index 0000000..a3dc47f --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/TechTeacherStatisticsOutput.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; + +namespace NCC.Extend.Entitys.Dto.LqStatistics +{ + /// + /// 科技部老师统计输出结果 + /// + public class TechTeacherStatisticsOutput + { + /// + /// 科技部老师ID + /// + public string TeacherId { get; set; } + + /// + /// 科技部老师姓名 + /// + public string TeacherName { get; set; } + + /// + /// 科技部老师账号 + /// + public string TeacherAccount { get; set; } + + /// + /// 开卡业绩 + /// + public decimal OrderAchievement { get; set; } + + /// + /// 消耗业绩 + /// + public decimal ConsumeAchievement { get; set; } + + /// + /// 退卡业绩 + /// + public decimal RefundAchievement { get; set; } + + /// + /// 总业绩 + /// + public decimal TotalAchievement { get; set; } + + /// + /// 见客数 + /// + public int CustomerCount { get; set; } + + /// + /// 项目数 + /// + public int ProjectCount { get; set; } + + /// + /// 手工费 + /// + public decimal LaborCost { get; set; } + + /// + /// 统计时间 + /// + public DateTime StatisticsTime { get; set; } + } + + /// + /// 科技部老师统计列表输出结果 + /// + public class TechTeacherStatisticsListOutput + { + /// + /// 科技部老师统计列表 + /// + public List Teachers { get; set; } = new List(); + + /// + /// 总记录数 + /// + public int TotalCount { get; set; } + + /// + /// 总开卡业绩 + /// + public decimal TotalOrderAchievement { get; set; } + + /// + /// 总消耗业绩 + /// + public decimal TotalConsumeAchievement { get; set; } + + /// + /// 总退卡业绩 + /// + public decimal TotalRefundAchievement { get; set; } + + /// + /// 总业绩 + /// + public decimal TotalAchievement { get; set; } + + /// + /// 总见客数 + /// + public int TotalCustomerCount { get; set; } + + /// + /// 总项目数 + /// + public int TotalProjectCount { get; set; } + + /// + /// 总手工费 + /// + public decimal TotalLaborCost { get; set; } + } +} diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs index 1b23940..4efc633 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs @@ -1,6 +1,6 @@ -using NCC.Common.Const; +using System; +using NCC.Common.Const; using SqlSugar; -using System; namespace NCC.Extend.Entitys.lq_kd_kjbsyj { @@ -59,5 +59,10 @@ namespace NCC.Extend.Entitys.lq_kd_kjbsyj [SugarColumn(ColumnName = "F_kdpxid")] public string Kdpxid { get; set; } + /// + /// 手工费 + /// + [SugarColumn(ColumnName = "F_LaborCost")] + public decimal? LaborCost { get; set; } } -} \ No newline at end of file +} diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/v_tech_teacher_flow/VTechTeacherFlowEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/v_tech_teacher_flow/VTechTeacherFlowEntity.cs new file mode 100644 index 0000000..4058174 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/v_tech_teacher_flow/VTechTeacherFlowEntity.cs @@ -0,0 +1,92 @@ +using System; +using NCC.Common.Const; +using SqlSugar; + +namespace NCC.Extend.Entitys.v_tech_teacher_flow +{ + /// + /// 科技部老师业绩流水视图 + /// + [SugarTable("v_tech_teacher_flow")] + [Tenant(ClaimConst.TENANT_ID)] + public class VTechTeacherFlowEntity + { + /// + /// 老师ID + /// + [SugarColumn(ColumnName = "teacher_id")] + public string TeacherId { get; set; } + + /// + /// 老师姓名 + /// + [SugarColumn(ColumnName = "teacher_name")] + public string TeacherName { get; set; } + + /// + /// 老师账号 + /// + [SugarColumn(ColumnName = "teacher_account")] + public string TeacherAccount { get; set; } + + /// + /// 业务类型(开卡、耗卡、退卡) + /// + [SugarColumn(ColumnName = "business_type")] + public string BusinessType { get; set; } + + /// + /// 业绩金额 + /// + [SugarColumn(ColumnName = "achievement")] + public decimal Achievement { get; set; } + + /// + /// 手工费 + /// + [SugarColumn(ColumnName = "labor_cost")] + public decimal LaborCost { get; set; } + + /// + /// 项目数 + /// + [SugarColumn(ColumnName = "project_count")] + public decimal ProjectCount { get; set; } + + /// + /// 客户ID + /// + [SugarColumn(ColumnName = "customer_id")] + public string CustomerId { get; set; } + + /// + /// 客户姓名 + /// + [SugarColumn(ColumnName = "customer_name")] + public string CustomerName { get; set; } + + /// + /// 业务日期 + /// + [SugarColumn(ColumnName = "business_date")] + public DateTime? BusinessDate { get; set; } + + /// + /// 源记录ID + /// + [SugarColumn(ColumnName = "source_id")] + public string SourceId { get; set; } + + /// + /// 源表名 + /// + [SugarColumn(ColumnName = "source_table")] + public string SourceTable { get; set; } + + /// + /// 品项次数 + /// + [SugarColumn(ColumnName = "item_count")] + public int ItemCount { get; set; } + } +} diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/v_tech_teacher_summary/VTechTeacherSummaryEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/v_tech_teacher_summary/VTechTeacherSummaryEntity.cs new file mode 100644 index 0000000..ea7d811 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/v_tech_teacher_summary/VTechTeacherSummaryEntity.cs @@ -0,0 +1,73 @@ +using NCC.Common.Const; +using SqlSugar; + +namespace NCC.Extend.Entitys.v_tech_teacher_summary +{ + /// + /// 科技部老师业绩汇总视图(简化版) + /// + [SugarTable("v_tech_teacher_summary")] + [Tenant(ClaimConst.TENANT_ID)] + public class VTechTeacherSummaryEntity + { + /// + /// 老师ID + /// + [SugarColumn(ColumnName = "teacher_id")] + public string TeacherId { get; set; } + + /// + /// 老师姓名 + /// + [SugarColumn(ColumnName = "teacher_name")] + public string TeacherName { get; set; } + + /// + /// 老师账号 + /// + [SugarColumn(ColumnName = "teacher_account")] + public string TeacherAccount { get; set; } + + /// + /// 开卡业绩 + /// + [SugarColumn(ColumnName = "order_achievement")] + public decimal OrderAchievement { get; set; } + + /// + /// 消耗业绩 + /// + [SugarColumn(ColumnName = "consume_achievement")] + public decimal ConsumeAchievement { get; set; } + + /// + /// 退卡业绩 + /// + [SugarColumn(ColumnName = "refund_achievement")] + public decimal RefundAchievement { get; set; } + + /// + /// 总业绩 + /// + [SugarColumn(ColumnName = "total_achievement")] + public decimal TotalAchievement { get; set; } + + /// + /// 手工费 + /// + [SugarColumn(ColumnName = "labor_cost")] + public decimal LaborCost { get; set; } + + /// + /// 项目数 + /// + [SugarColumn(ColumnName = "project_count")] + public decimal ProjectCount { get; set; } + + /// + /// 见客数 + /// + [SugarColumn(ColumnName = "customer_count")] + public int CustomerCount { get; set; } + } +} diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqStatistics/ILqStatisticsService.cs b/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqStatistics/ILqStatisticsService.cs index 04fd1bc..d752a24 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqStatistics/ILqStatisticsService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqStatistics/ILqStatisticsService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using NCC.Extend.Entitys.Dto.LqMdxx; using NCC.Extend.Entitys.Dto.LqStatistics; +using NCC.Extend.Entitys.v_tech_teacher_flow; namespace NCC.Extend.Interfaces.LqStatistics { @@ -17,6 +18,12 @@ namespace NCC.Extend.Interfaces.LqStatistics Task> GetStorePerformanceList(); /// + /// 获取门店开单业绩统计列表 + /// + /// 门店开单业绩统计列表 + Task> GetStoreOrderPerformanceList(); + + /// /// 获取门店统计信息 /// /// 查询参数 @@ -50,5 +57,12 @@ namespace NCC.Extend.Interfaces.LqStatistics /// 查询参数 /// 经理业绩汇总统计结果 Task GetManagerSummaryStatistics(ManagerStatisticsInput input); + + /// + /// 获取科技部老师业绩统计 + /// + /// 查询参数 + /// 科技部老师业绩统计结果 + Task> GetTechTeacherStatistics(TechTeacherStatisticsInput input); } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs index 8aa9c86..eedfb41 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NCC.Common.Core.Manager; @@ -12,9 +13,15 @@ using NCC.Dependency; using NCC.DynamicApiController; using NCC.Extend.Entitys.Dto.LqMdxx; using NCC.Extend.Entitys.Dto.LqStatistics; +using NCC.Extend.Entitys.lq_hytk_kjbsyj; +using NCC.Extend.Entitys.lq_kd_kdjlb; +using NCC.Extend.Entitys.lq_kd_kjbsyj; using NCC.Extend.Entitys.lq_mdxx; +using NCC.Extend.Entitys.lq_xh_kjbsyj; using NCC.Extend.Entitys.lq_yjmxb; +using NCC.Extend.Entitys.v_tech_teacher_flow; using NCC.Extend.Interfaces.LqStatistics; +using NCC.Extend.Utils; using NCC.FriendlyException; using NCC.System.Entitys.Permission; using SqlSugar; @@ -32,45 +39,65 @@ namespace NCC.Extend.LqStatistics private readonly SqlSugarScope _db; private readonly IUserManager _userManager; private readonly ILogger _logger; + private readonly WeChatBotService _weChatBotService; /// /// 初始化一个类型的新实例 /// - public LqStatisticsService(ISqlSugarRepository lqMdxxRepository, IUserManager userManager, ILogger logger) + public LqStatisticsService(ISqlSugarRepository lqMdxxRepository, IUserManager userManager, ILogger logger, WeChatBotService weChatBotService) { _lqMdxxRepository = lqMdxxRepository; _db = _lqMdxxRepository.Context; _userManager = userManager; _logger = logger; + _weChatBotService = weChatBotService; } - #region 获取门店业绩统计列表 + #region 获取门店耗卡业绩统计列表 /// /// 获取门店业绩统计列表 /// + /// + /// 获取所有门店的耗卡业绩统计信息,包括门店ID、名称、编码、目标业绩、耗卡业绩、完成率等关键指标 + /// + /// 返回数据说明: + /// - StoreId: 门店ID + /// - StoreName: 门店名称 + /// - StoreCode: 门店编码 + /// - BusinessUnitId: 事业部ID(暂未关联) + /// - BusinessUnitName: 事业部名称(暂未关联) + /// - TargetPerformance: 目标业绩(门店生命线) + /// - ActualPerformance: 耗卡业绩(当月耗卡金额汇总) + /// - CompletionRate: 完成率(耗卡业绩/目标业绩*100) + /// - OrderCount: 耗卡记录数(当月耗卡记录数) + /// + /// 数据来源:v_store_consume_performance_simple 视图 + /// /// 门店业绩统计列表 - [HttpGet] + /// 成功返回门店业绩统计列表 + /// 服务器内部错误 + [HttpGet("GetStorePerformanceList")] public async Task> GetStorePerformanceList() { try { _logger.LogInformation("开始查询门店业绩统计列表"); - var storeList = await _lqMdxxRepository - .AsQueryable() - .Where(x => x.Status == 1) - .Select(x => new StorePerformanceOutput - { - StoreId = x.Id, - StoreName = x.Dm, - StoreCode = x.Mdbm, - BusinessUnitId = x.Syb, - BusinessUnitName = x.Syb, - TargetPerformance = x.Xsyj ?? 0, - ActualPerformance = 0, - CompletionRate = 0, - }) - .ToListAsync(); + // 使用耗卡业绩统计视图 + var storeList = await _db.Ado.SqlQueryAsync( + "SELECT " + + "store_id AS StoreId, " + + "store_code AS StoreCode, " + + "store_name AS StoreName, " + + "'' AS BusinessUnitId, " + + "'' AS BusinessUnitName, " + + "target_performance AS TargetPerformance, " + + "actual_performance AS ActualPerformance, " + + "completion_rate AS CompletionRate, " + + "order_count AS OrderCount " + + "FROM v_store_consume_performance_simple " + + "ORDER BY actual_performance DESC" + ); _logger.LogInformation("门店业绩统计列表查询完成,返回{Count}条记录", storeList.Count); @@ -84,12 +111,101 @@ namespace NCC.Extend.LqStatistics } #endregion + #region 获取门店开单业绩统计列表 + /// + /// 获取门店开单业绩统计列表 + /// + /// + /// 获取所有门店的开单业绩统计信息,包括门店ID、名称、编码、目标业绩、开单业绩、完成率等关键指标 + /// + /// 返回数据说明: + /// - StoreId: 门店ID + /// - StoreName: 门店名称 + /// - StoreCode: 门店编码 + /// - BusinessUnitId: 事业部ID(暂未关联) + /// - BusinessUnitName: 事业部名称(暂未关联) + /// - TargetPerformance: 目标业绩(门店生命线) + /// - ActualPerformance: 开单业绩(当月开单金额汇总) + /// - CompletionRate: 完成率(开单业绩/目标业绩*100) + /// - OrderCount: 开单记录数(当月开单记录数) + /// + /// 数据来源:v_store_performance_simple 视图 + /// + /// 门店开单业绩统计列表 + /// 成功返回门店开单业绩统计列表 + /// 服务器内部错误 + [HttpGet("GetStoreOrderPerformanceList")] + public async Task> GetStoreOrderPerformanceList() + { + try + { + _logger.LogInformation("开始查询门店开单业绩统计列表"); + + // 使用开单业绩统计视图 + var storeList = await _db.Ado.SqlQueryAsync( + "SELECT " + + "store_id AS StoreId, " + + "store_code AS StoreCode, " + + "store_name AS StoreName, " + + "'' AS BusinessUnitId, " + + "'' AS BusinessUnitName, " + + "target_performance AS TargetPerformance, " + + "actual_performance AS ActualPerformance, " + + "completion_rate AS CompletionRate, " + + "order_count AS OrderCount " + + "FROM v_store_performance_simple " + + "ORDER BY actual_performance DESC" + ); + + _logger.LogInformation("门店开单业绩统计列表查询完成,返回{Count}条记录", storeList.Count); + + return storeList ?? new List(); + } + catch (Exception ex) + { + _logger.LogError(ex, "查询门店开单业绩统计列表时发生错误"); + throw NCCException.Oh("查询门店开单业绩统计列表失败", ex); + } + } + #endregion + #region 获取门店统计信息 /// /// 获取门店统计信息 /// + /// + /// 根据指定日期范围和门店ID查询门店的详细业绩统计信息,包括目标业绩、完成业绩、完成率等 + /// + /// 示例请求: + /// ```json + /// { + /// "startDate": "2025-01-01T00:00:00", + /// "endDate": "2025-01-31T23:59:59", + /// "storeId": "门店ID(可选)" + /// } + /// ``` + /// + /// 参数说明: + /// - startDate: 开始日期,格式:yyyy-MM-ddTHH:mm:ss + /// - endDate: 结束日期,格式:yyyy-MM-ddTHH:mm:ss + /// - storeId: 门店ID,可选参数,不传则查询所有门店 + /// + /// 返回数据说明: + /// - StoreId: 门店ID + /// - StoreName: 门店名称 + /// - StoreCode: 门店编码 + /// - BusinessUnitId: 事业部ID + /// - BusinessUnitName: 事业部名称 + /// - TargetPerformance: 目标业绩 + /// - ActualPerformance: 完成业绩 + /// - OrderCount: 开单数量 + /// - CompletionRate: 完成率(%) + /// /// 查询参数 /// 门店统计结果 + /// 成功返回门店统计数据 + /// 请求参数错误 + /// 服务器内部错误 [HttpPost("StoreStatistics")] public async Task GetStoreStatistics(StoreStatisticsInput input) { @@ -101,41 +217,29 @@ namespace NCC.Extend.LqStatistics var parameters = new Dictionary { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } }; // 构建WHERE条件 - var whereClause = "WHERE (order_date >= @startDate AND order_date <= @endDate OR order_date IS NULL)"; - + var whereClause = "WHERE 1=1"; if (!string.IsNullOrEmpty(input.StoreId)) { whereClause += " AND store_id = @storeId"; parameters.Add("@storeId", input.StoreId); } - // 构建SQL查询 + // 使用SQL查询门店统计信息,包含日期范围过滤 var sql = $@" SELECT store_id AS StoreId, - store_name AS StoreName, store_code AS StoreCode, - business_unit_id AS BusinessUnitId, - business_unit_name AS BusinessUnitName, + store_name AS StoreName, + '' AS BusinessUnitId, + '' AS BusinessUnitName, target_performance AS TargetPerformance, - SUM(COALESCE(actual_amount, 0)) AS ActualPerformance, - COUNT(order_id) AS OrderCount, - CASE - WHEN target_performance > 0 - THEN ROUND((SUM(COALESCE(actual_amount, 0)) / target_performance) * 100, 2) - ELSE 0 - END AS CompletionRate - FROM v_store_daily_consume_stats + actual_performance AS ActualPerformance, + completion_rate AS CompletionRate, + order_count AS OrderCount + FROM v_store_performance_simple {whereClause} - GROUP BY - store_id, - store_name, - store_code, - business_unit_id, - business_unit_name, - target_performance - ORDER BY ActualPerformance DESC"; + ORDER BY actual_performance DESC"; _logger.LogInformation("执行SQL查询:{Sql}", sql); _logger.LogInformation("查询参数:{Parameters}", string.Join(", ", parameters.Select(p => $"{p.Key}={p.Value}"))); @@ -176,8 +280,38 @@ namespace NCC.Extend.LqStatistics /// /// 获取事业部业绩统计 /// + /// + /// 根据指定日期范围和事业部ID查询事业部的业绩统计信息,包括目标业绩总和、完成业绩总和、完成率等 + /// + /// 示例请求: + /// ```json + /// { + /// "startDate": "2025-01-01T00:00:00", + /// "endDate": "2025-01-31T23:59:59", + /// "businessUnitId": "事业部ID(可选)" + /// } + /// ``` + /// + /// 参数说明: + /// - startDate: 开始日期,格式:yyyy-MM-ddTHH:mm:ss + /// - endDate: 结束日期,格式:yyyy-MM-ddTHH:mm:ss + /// - businessUnitId: 事业部ID,可选参数,不传则查询所有事业部 + /// + /// 返回数据说明: + /// - DepartmentId: 部门ID + /// - DepartmentName: 部门名称 + /// - ParentId: 父部门ID + /// - ParentName: 父部门名称 + /// - TotalTargetAmount: 目标业绩总和 + /// - TotalActualAmount: 完成业绩总和 + /// - TotalOrderCount: 开单总数量 + /// - CompletionRate: 完成率(%) + /// /// 查询参数 /// 事业部业绩统计结果 + /// 成功返回事业部业绩统计数据 + /// 请求参数错误 + /// 服务器内部错误 [HttpPost("BusinessUnitStatistics")] public async Task GetBusinessUnitStatistics(BusinessUnitStatisticsInput input) { @@ -189,37 +323,41 @@ namespace NCC.Extend.LqStatistics var parameters = new Dictionary { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } }; // 构建WHERE条件 - var whereClause = "WHERE (order_date >= @startDate AND order_date <= @endDate OR order_date IS NULL)"; - + var whereClause = "WHERE 1=1"; if (!string.IsNullOrEmpty(input.BusinessUnitId)) { - whereClause += " AND department_id = @businessUnitId"; + whereClause += " AND dept.F_Id = @businessUnitId"; parameters.Add("@businessUnitId", input.BusinessUnitId); } - // 构建SQL查询 + // 构建SQL查询 - 先查询所有事业部,然后左连接业绩数据 var sql = $@" SELECT - department_id AS DepartmentId, - department_name AS DepartmentName, - parent_id AS ParentId, - parent_name AS ParentName, - SUM(COALESCE(target_amount, 0)) AS TotalTargetAmount, - SUM(COALESCE(actual_amount, 0)) AS TotalActualAmount, - COUNT(order_id) AS TotalOrderCount, + dept.F_Id AS DepartmentId, + dept.F_FullName AS DepartmentName, + dept.F_ParentId AS ParentId, + parent.F_FullName AS ParentName, + COALESCE(SUM(flow.actual_amount), 0) AS TotalActualAmount, + COALESCE(COUNT(flow.order_id), 0) AS TotalOrderCount, CASE - WHEN SUM(COALESCE(target_amount, 0)) > 0 - THEN ROUND((SUM(COALESCE(actual_amount, 0)) / SUM(COALESCE(target_amount, 0))) * 100, 2) + WHEN COALESCE(SUM(flow.actual_amount), 0) > 0 + THEN ROUND((COALESCE(SUM(flow.actual_amount), 0) / 100000) * 100, 2) ELSE 0 END AS CompletionRate - FROM v_department_performance_flow - {whereClause} + FROM base_organize dept + LEFT JOIN base_organize parent ON dept.F_ParentId = parent.F_Id + LEFT JOIN v_department_performance_flow flow ON dept.F_Id = flow.department_id + AND (flow.order_date >= @startDate AND flow.order_date <= @endDate OR flow.order_date IS NULL) + WHERE dept.F_ParentId = (SELECT F_Id FROM base_organize WHERE F_FullName = '事业部') + AND dept.F_EnabledMark = 1 + AND dept.F_DeleteMark IS NULL + {whereClause.Replace("WHERE 1=1", "")} GROUP BY - department_id, - department_name, - parent_id, - parent_name + dept.F_Id, + dept.F_FullName, + dept.F_ParentId, + parent.F_FullName ORDER BY TotalActualAmount DESC"; _logger.LogInformation("执行SQL查询:{Sql}", sql); @@ -237,7 +375,7 @@ namespace NCC.Extend.LqStatistics DepartmentName = r.DepartmentName?.ToString() ?? "", ParentId = r.ParentId?.ToString() ?? "", ParentName = r.ParentName?.ToString() ?? "", - TotalTargetAmount = Convert.ToDecimal(r.TotalTargetAmount ?? 0), + TotalTargetAmount = 100000, // 默认目标业绩10万 TotalActualAmount = Convert.ToDecimal(r.TotalActualAmount ?? 0), TotalOrderCount = Convert.ToInt32(r.TotalOrderCount ?? 0), CompletionRate = Convert.ToDecimal(r.CompletionRate ?? 0), @@ -260,8 +398,38 @@ namespace NCC.Extend.LqStatistics /// /// 获取其他部门业绩统计 /// + /// + /// 根据指定日期范围和部门ID查询其他部门(教育部、科技部、大项目部)的业绩统计信息 + /// + /// 示例请求: + /// ```json + /// { + /// "startDate": "2025-01-01T00:00:00", + /// "endDate": "2025-01-31T23:59:59", + /// "departmentId": "部门ID(可选)" + /// } + /// ``` + /// + /// 参数说明: + /// - startDate: 开始日期,格式:yyyy-MM-ddTHH:mm:ss + /// - endDate: 结束日期,格式:yyyy-MM-ddTHH:mm:ss + /// - departmentId: 部门ID,可选参数,不传则查询所有其他部门 + /// + /// 返回数据说明: + /// - DepartmentId: 部门ID + /// - DepartmentName: 部门名称 + /// - ParentId: 父部门ID + /// - ParentName: 父部门名称 + /// - TotalTargetAmount: 目标业绩总和 + /// - TotalActualAmount: 完成业绩总和 + /// - TotalOrderCount: 开单总数量 + /// - CompletionRate: 完成率(%) + /// /// 查询参数 /// 其他部门业绩统计结果 + /// 成功返回其他部门业绩统计数据 + /// 请求参数错误 + /// 服务器内部错误 [HttpPost("OtherDepartmentStatistics")] public async Task GetOtherDepartmentStatistics(OtherDepartmentStatisticsInput input) { @@ -273,37 +441,41 @@ namespace NCC.Extend.LqStatistics var parameters = new Dictionary { { "@startDate", input.StartDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", input.EndDate.ToString("yyyy-MM-dd 23:59:59") } }; // 构建WHERE条件 - var whereClause = "WHERE (order_date >= @startDate AND order_date <= @endDate OR order_date IS NULL)"; - + var whereClause = "WHERE 1=1"; if (!string.IsNullOrEmpty(input.DepartmentId)) { - whereClause += " AND department_id = @departmentId"; + whereClause += " AND dept.F_Id = @departmentId"; parameters.Add("@departmentId", input.DepartmentId); } - // 构建SQL查询 + // 构建SQL查询 - 先查询所有其他部门的子部门,然后左连接业绩数据 var sql = $@" SELECT - department_id AS DepartmentId, - department_name AS DepartmentName, - parent_id AS ParentId, - parent_name AS ParentName, - SUM(COALESCE(target_amount, 0)) AS TotalTargetAmount, - SUM(COALESCE(actual_amount, 0)) AS TotalActualAmount, - COUNT(order_id) AS TotalOrderCount, + dept.F_Id AS DepartmentId, + dept.F_FullName AS DepartmentName, + dept.F_ParentId AS ParentId, + parent.F_FullName AS ParentName, + COALESCE(SUM(flow.actual_amount), 0) AS TotalActualAmount, + COALESCE(COUNT(flow.order_id), 0) AS TotalOrderCount, CASE - WHEN SUM(COALESCE(target_amount, 0)) > 0 - THEN ROUND((SUM(COALESCE(actual_amount, 0)) / SUM(COALESCE(target_amount, 0))) * 100, 2) + WHEN COALESCE(SUM(flow.actual_amount), 0) > 0 + THEN ROUND((COALESCE(SUM(flow.actual_amount), 0) / 50000) * 100, 2) ELSE 0 END AS CompletionRate - FROM v_other_department_performance_flow - {whereClause} + FROM base_organize dept + LEFT JOIN base_organize parent ON dept.F_ParentId = parent.F_Id + LEFT JOIN v_other_department_performance_flow flow ON dept.F_Id = flow.department_id + AND (flow.order_date >= @startDate AND flow.order_date <= @endDate OR flow.order_date IS NULL) + WHERE dept.F_ParentId IN (SELECT F_Id FROM base_organize WHERE F_FullName IN ('教育部', '科技部', '大项目部')) + AND dept.F_EnabledMark = 1 + AND dept.F_DeleteMark IS NULL + {whereClause.Replace("WHERE 1=1", "")} GROUP BY - department_id, - department_name, - parent_id, - parent_name + dept.F_Id, + dept.F_FullName, + dept.F_ParentId, + parent.F_FullName ORDER BY TotalActualAmount DESC"; _logger.LogInformation("执行SQL查询:{Sql}", sql); @@ -321,7 +493,7 @@ namespace NCC.Extend.LqStatistics DepartmentName = r.DepartmentName?.ToString() ?? "", ParentId = r.ParentId?.ToString() ?? "", ParentName = r.ParentName?.ToString() ?? "", - TotalTargetAmount = Convert.ToDecimal(r.TotalTargetAmount ?? 0), + TotalTargetAmount = 50000, // 默认目标业绩5万 TotalActualAmount = Convert.ToDecimal(r.TotalActualAmount ?? 0), TotalOrderCount = Convert.ToInt32(r.TotalOrderCount ?? 0), CompletionRate = Convert.ToDecimal(r.CompletionRate ?? 0), @@ -344,8 +516,40 @@ namespace NCC.Extend.LqStatistics /// /// 获取经理业绩统计 /// + /// + /// 根据指定日期范围和事业部ID查询经理的业绩统计信息,按经理和门店维度进行统计 + /// + /// 示例请求: + /// ```json + /// { + /// "startDate": "2025-01-01T00:00:00", + /// "endDate": "2025-01-31T23:59:59", + /// "businessUnitId": "事业部ID(可选)" + /// } + /// ``` + /// + /// 参数说明: + /// - startDate: 开始日期,格式:yyyy-MM-ddTHH:mm:ss + /// - endDate: 结束日期,格式:yyyy-MM-ddTHH:mm:ss + /// - businessUnitId: 事业部ID,可选参数,不传则查询所有事业部 + /// + /// 返回数据说明: + /// - ManagerName: 经理姓名 + /// - ManagerUserId: 经理用户ID + /// - BusinessUnitName: 事业部名称 + /// - BusinessUnitId: 事业部ID + /// - StoreName: 门店名称 + /// - StoreId: 门店ID + /// - TargetPerformance: 目标业绩 + /// - ActualPerformance: 完成业绩 + /// - OrderCount: 开单数量 + /// - CompletionRate: 完成率(%) + /// /// 查询参数 /// 经理业绩统计结果 + /// 成功返回经理业绩统计数据 + /// 请求参数错误 + /// 服务器内部错误 [HttpPost("ManagerStatistics")] public async Task GetManagerStatistics(ManagerStatisticsInput input) { @@ -593,6 +797,242 @@ namespace NCC.Extend.LqStatistics } } #endregion + + #region 科技部老师业绩统计 + /// + /// 获取科技部老师业绩统计 + /// + /// 查询参数 + /// 科技部老师业绩统计结果 + [HttpPost("GetTechTeacherStatistics")] + public async Task> GetTechTeacherStatistics(TechTeacherStatisticsInput input) + { + try + { // 1. 从用户表获取所有科技部老师 + var allTeachers = await _db.Queryable() + .Where(x => x.Gw == "科技老师") + .Select(x => new + { + TeacherId = x.Id, + TeacherName = x.RealName, + TeacherAccount = x.Account, + }) + .ToListAsync(); + + // 2. 获取业绩流水数据 + var flowQuery = _db.Queryable(); + + // 老师过滤 + if (!string.IsNullOrEmpty(input.TeacherId)) + { + flowQuery = flowQuery.Where(x => x.TeacherId == input.TeacherId); + } + + if (!string.IsNullOrEmpty(input.TeacherName)) + { + flowQuery = flowQuery.Where(x => x.TeacherName.Contains(input.TeacherName)); + } + + // 日期过滤 + if (input.StartDate.HasValue) + { + flowQuery = flowQuery.Where(x => x.BusinessDate >= input.StartDate.Value); + } + + if (input.EndDate.HasValue) + { + flowQuery = flowQuery.Where(x => x.BusinessDate <= input.EndDate.Value); + } + + var flowRecords = await flowQuery.ToListAsync(); + + // 3. 按老师分组统计业绩数据 + var teacherStatsDict = flowRecords + .GroupBy(x => new + { + x.TeacherId, + x.TeacherName, + x.TeacherAccount, + }) + .ToDictionary( + g => g.Key, + g => new + { + ConsumeProjectCount = (int)g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ProjectCount), + ConsumeAchievement = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.Achievement), + OrderAchievement = g.Where(x => x.BusinessType == "开卡").Sum(x => x.Achievement), + OrderItemCount = g.Where(x => x.BusinessType == "开卡").Sum(x => x.ItemCount), + ConsumeItemCount = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ItemCount), + } + ); + + // 4. 构建结果,包含所有老师 + var result = new List(); + + foreach (var teacher in allTeachers) + { + // 应用过滤条件 + if (!string.IsNullOrEmpty(input.TeacherId) && teacher.TeacherId != input.TeacherId) + continue; + + if (!string.IsNullOrEmpty(input.TeacherName) && !teacher.TeacherName.Contains(input.TeacherName)) + continue; + + var stats = teacherStatsDict.GetValueOrDefault( + new + { + TeacherId = teacher.TeacherId, + TeacherName = teacher.TeacherName, + TeacherAccount = teacher.TeacherAccount, + }, + new + { + ConsumeProjectCount = 0, + ConsumeAchievement = 0m, + OrderAchievement = 0m, + OrderItemCount = 0, + ConsumeItemCount = 0, + } + ); + + var teacherStats = new TechTeacherSimpleStatisticsOutput + { + DepartmentName = "科技部", // 固定为科技部 + TeacherName = teacher.TeacherName, + ConsumeProjectCount = stats.ConsumeProjectCount, + ConsumeAchievement = stats.ConsumeAchievement, + OrderAchievement = stats.OrderAchievement, + OrderItemCount = stats.OrderItemCount, + ConsumeItemCount = stats.ConsumeItemCount, + }; + + result.Add(teacherStats); + } + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取科技部老师业绩统计时发生错误"); + throw NCCException.Oh("获取科技部老师业绩统计失败", ex); + } + } + + #endregion + + #region 匿名接口 + + /// + /// 发送每日统计消息(匿名接口) + /// + /// 发送结果 + [HttpPost("SendDailyReportMessage")] + [AllowAnonymous] + public async Task SendDailyReportMessage() + { + try + { + // 获取本月全门店统计数据 + var monthlyStats = await GetMonthlyStoreStatistics(); + + // 构建包含统计数据的消息 + var messageContent = + $"📊 今日日报已生成,点击链接查看\n\n" + + $"本月全门店目标业绩:{monthlyStats.TargetPerformance:N0}元\n" + + $"本月已完成业绩:{monthlyStats.ActualPerformance:N0}元\n" + + $"完成率:{monthlyStats.CompletionRate:F2}%\n\n" + + $"http://lvqian.antissoft.com/html/dailyReportnew.html"; + + var result = await _weChatBotService.SendTextMessage(messageContent); + return new + { + success = result, + message = result ? "每日统计消息发送成功" : "每日统计消息发送失败", + timestamp = DateTime.Now, + statistics = monthlyStats, + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "发送每日统计消息时发生错误"); + return new + { + success = false, + message = "发送每日统计消息时发生错误: " + ex.Message, + timestamp = DateTime.Now, + }; + } + } + + /// + /// 获取本月全门店统计数据 + /// + /// 本月统计数据 + private async Task GetMonthlyStoreStatistics() + { + try + { + // 获取本月开始和结束日期 + var now = DateTime.Now; + var startDate = new DateTime(now.Year, now.Month, 1); + var endDate = startDate.AddMonths(1).AddDays(-1); + + // 构建查询参数 + var parameters = new Dictionary { { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") } }; + + // 查询本月全门店统计数据 + var sql = + @" + SELECT + SUM(target_performance) AS TotalTargetPerformance, + SUM(actual_performance) AS TotalActualPerformance, + CASE + WHEN SUM(target_performance) > 0 + THEN (SUM(actual_performance) / SUM(target_performance)) * 100 + ELSE 0 + END AS TotalCompletionRate + FROM v_store_performance_simple"; + + var result = await _db.Ado.SqlQueryAsync(sql, parameters); + var stats = result.FirstOrDefault(); + + if (stats != null) + { + var targetPerformance = Convert.ToDecimal(stats.TotalTargetPerformance ?? 0); + var actualPerformance = Convert.ToDecimal(stats.TotalActualPerformance ?? 0); + var completionRate = Convert.ToDecimal(stats.TotalCompletionRate ?? 0); + + return new + { + TargetPerformance = targetPerformance, + ActualPerformance = actualPerformance, + CompletionRate = completionRate, + Month = now.ToString("yyyy年MM月"), + }; + } + + return new + { + TargetPerformance = 0m, + ActualPerformance = 0m, + CompletionRate = 0m, + Month = now.ToString("yyyy年MM月"), + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取本月全门店统计数据时发生错误"); + return new + { + TargetPerformance = 0m, + ActualPerformance = 0m, + CompletionRate = 0m, + Month = DateTime.Now.ToString("yyyy年MM月"), + }; + } + } + + #endregion } /// diff --git a/netcore/src/Modularity/Extend/NCC.Extend/Utils/WeChatBotService.cs b/netcore/src/Modularity/Extend/NCC.Extend/Utils/WeChatBotService.cs index ce4f174..ac4b177 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/Utils/WeChatBotService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/Utils/WeChatBotService.cs @@ -34,14 +34,14 @@ namespace NCC.Extend.Utils webhookUrl = WEBHOOK_URL, content = content, mentionedList = (string)null, - mentionedMobileList = (string)null + mentionedMobileList = (string)null, }; var json = JsonConvert.SerializeObject(requestData); var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(BOT_API_URL, httpContent); - + if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); @@ -77,8 +77,28 @@ namespace NCC.Extend.Utils // 添加标题和格式化 var messageContent = $"🎉 新开单记录\n\n{orderRecordString}"; - return await SendTextMessage(messageContent); } + + /// + /// 发送每日统计消息 + /// + /// 发送结果 + public async Task SendDailyReportMessage() + { + try + { + var reportUrl = "http://lvqian.antissoft.com/html/dailyReportnew.html"; + var messageContent = $"📊 今日日报已生成,点击链接查看\n\n{reportUrl}"; + + Console.WriteLine("发送每日统计消息..."); + return await SendTextMessage(messageContent); + } + catch (Exception ex) + { + Console.WriteLine($"发送每日统计消息异常: {ex.Message}"); + return false; + } + } } } diff --git a/创建经理门店业绩统计视图.sql b/创建经理门店业绩统计视图.sql deleted file mode 100644 index c26f5ce..0000000 --- a/创建经理门店业绩统计视图.sql +++ /dev/null @@ -1,87 +0,0 @@ --- 创建经理门店业绩统计视图 --- 融合经理信息、管理门店、生命线设置、业绩流水数据 - -DROP VIEW IF EXISTS `v_manager_store_performance`; - -CREATE VIEW `v_manager_store_performance` AS -SELECT - -- 经理基础信息 - u.F_Id AS manager_user_id, -- 经理用户ID - u.F_REALNAME AS manager_name, -- 经理姓名 - u.F_GW AS manager_position, -- 经理岗位 - u.F_ORGANIZEID AS manager_organize_id, -- 经理组织ID - - -- 事业部信息 - o.F_Id AS business_unit_id, -- 事业部ID - o.F_FullName AS business_unit_name, -- 事业部名称 - o.F_ParentId AS parent_organize_id, -- 上级组织ID - parent.F_FullName AS parent_organize_name, -- 上级组织名称 - - -- 门店信息 - md.F_Id AS store_id, -- 门店ID - md.mdbm AS store_code, -- 门店编号 - md.dm AS store_name, -- 门店名称 - md.syb AS store_business_unit_id, -- 门店所属事业部ID - - -- 生命线设置信息 - COALESCE(zjl.smx1, 0) AS target_performance, -- 目标业绩(生命线1) - zjl.tcbl1 AS commission_rate1, -- 生命线1提成比例 - zjl.smx2 AS target_performance2, -- 目标业绩2(生命线2) - zjl.tcbl2 AS commission_rate2, -- 生命线2提成比例 - zjl.smx3 AS target_performance3, -- 目标业绩3(生命线3) - zjl.tcbl3 AS commission_rate3, -- 生命线3提成比例 - - -- 业绩流水信息(从现有视图获取) - flow.order_id, -- 开单ID - flow.order_date, -- 开单时间 - flow.completed_amount, -- 完成业绩(整单业绩) - flow.actual_amount, -- 实付业绩 - flow.debt_amount, -- 欠款 - flow.completion_rate, -- 完成率 - flow.golden_triangle, -- 金三角 - flow.member_id, -- 开单会员ID - flow.member_name, -- 开单会员名称 - flow.member_phone, -- 开单会员手机号 - flow.customer_type, -- 顾客类型 - flow.payment_method, -- 付款方式 - flow.customer_source, -- 客户来源 - - -- 时间维度 - flow.order_year, -- 开单年份 - flow.order_month, -- 开单月份 - flow.order_quarter, -- 开单季度 - flow.order_date_only, -- 开单日期(不含时间) - - -- 统计标识 - CASE - WHEN flow.order_id IS NOT NULL THEN 1 - ELSE 0 - END AS has_performance, -- 是否有业绩记录 - - -- 创建时间 - COALESCE(flow.create_time, NOW()) AS create_time, - COALESCE(flow.modify_time, NOW()) AS modify_time - -FROM base_user u --- 关联组织信息 -LEFT JOIN base_organize o ON u.F_ORGANIZEID = o.F_Id -LEFT JOIN base_organize parent ON o.F_ParentId = parent.F_Id --- 关联门店信息(通过事业部关联) -LEFT JOIN lq_mdxx md ON md.syb = o.F_Id --- 关联生命线设置 -LEFT JOIN lq_zjl_mdsmxsz zjl ON zjl.zjl_userid = u.F_Id - AND zjl.md_id = md.F_Id - AND zjl.deletemark = 0 --- 关联业绩流水(从现有视图) -LEFT JOIN v_department_performance_flow flow ON flow.store_id = md.F_Id - AND flow.department_id = o.F_Id - -WHERE u.F_DELETEMARK IS NULL -- 用户未删除 - AND u.F_GW IS NOT NULL -- 有岗位信息 - AND (u.F_GW LIKE '%经理%' OR u.F_GW LIKE '%总经理%' OR u.F_GW LIKE '%主管%') -- 经理岗位 - AND o.F_Category = 'department' -- 部门类型 - AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1) -- 组织未删除 - AND md.F_Id IS NOT NULL; -- 有门店信息 - --- 添加视图注释 ---ALTER VIEW `v_manager_store_performance` COMMENT = '经理门店业绩统计视图 - 融合经理信息、管理门店、生命线设置、业绩流水数据'; diff --git a/创建退卡相关表.sql b/创建退卡相关表.sql deleted file mode 100644 index 43bf951..0000000 --- a/创建退卡相关表.sql +++ /dev/null @@ -1,130 +0,0 @@ --- ============================================= --- 绿纤美业ERP系统 - 退卡相关表创建脚本 --- 创建时间: 2025-01-27 --- 说明: 创建会员退卡主表、明细表、业绩表 --- ============================================= - --- 1. 创建会员退卡主表 -DROP TABLE IF EXISTS `lq_hytk_hytk`; -CREATE TABLE `lq_hytk_hytk` ( - `F_Id` varchar(50) NOT NULL COMMENT '退卡编号', - `md` varchar(50) DEFAULT NULL COMMENT '门店', - `mdbh` varchar(50) DEFAULT NULL COMMENT '门店编号', - `mdmc` varchar(50) DEFAULT NULL COMMENT '门店名称', - `hy` varchar(50) DEFAULT NULL COMMENT '会员', - `hyzh` varchar(50) DEFAULT NULL COMMENT '会员账号', - `hymc` varchar(50) DEFAULT NULL COMMENT '会员名称', - `gklx` varchar(50) DEFAULT NULL COMMENT '顾客类型', - `tkje` decimal(15,2) DEFAULT NULL COMMENT '退卡总金额', - `sgfy` decimal(15,2) DEFAULT NULL COMMENT '手工费用', - `bz` text COMMENT '备注', - `tksj` datetime DEFAULT NULL COMMENT '退卡时间', - `czry` varchar(50) DEFAULT NULL COMMENT '操作人员', - `tkzt` varchar(20) DEFAULT NULL COMMENT '退卡状态', - `tkyy` varchar(200) DEFAULT NULL COMMENT '退卡原因', - `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', - `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建用户', - `F_ModifyTime` datetime DEFAULT NULL COMMENT '修改时间', - `F_ModifyUser` varchar(50) DEFAULT NULL COMMENT '修改用户', - `F_DeleteMark` int(1) DEFAULT NULL COMMENT '删除标记', - PRIMARY KEY (`F_Id`), - KEY `idx_md` (`md`), - KEY `idx_hy` (`hy`), - KEY `idx_tksj` (`tksj`), - KEY `idx_tkzt` (`tkzt`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退卡_信息表'; - --- 2. 创建会员退卡明细表 -DROP TABLE IF EXISTS `lq_hytk_mx`; -CREATE TABLE `lq_hytk_mx` ( - `F_Id` varchar(50) NOT NULL COMMENT '明细编号', - `gltkbh` varchar(50) DEFAULT NULL COMMENT '关联退卡编号', - `px` varchar(50) DEFAULT NULL COMMENT '品项', - `pxmc` varchar(100) DEFAULT NULL COMMENT '品项名称', - `pxjg` decimal(15,2) DEFAULT NULL COMMENT '品项价格', - `tkje` decimal(15,2) DEFAULT NULL COMMENT '退款金额', - `F_ProjectNumber` int DEFAULT NULL COMMENT '项目次数', - `F_IsEnabled` int(1) DEFAULT NULL COMMENT '是否有效', - `F_SourceType` varchar(20) DEFAULT NULL COMMENT '来源类型', - `F_TotalPrice` decimal(15,2) DEFAULT NULL COMMENT '合计金额', - `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', - `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建用户', - `F_ModifyTime` datetime DEFAULT NULL COMMENT '修改时间', - `F_ModifyUser` varchar(50) DEFAULT NULL COMMENT '修改用户', - `F_DeleteMark` int(1) DEFAULT NULL COMMENT '删除标记', - PRIMARY KEY (`F_Id`), - KEY `idx_gltkbh` (`gltkbh`), - KEY `idx_px` (`px`), - KEY `idx_F_SourceType` (`F_SourceType`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退卡_品项明细表'; - --- 3. 创建退卡健康师业绩表 -DROP TABLE IF EXISTS `lq_hytk_jksyj`; -CREATE TABLE `lq_hytk_jksyj` ( - `F_Id` varchar(50) NOT NULL COMMENT '业绩编号', - `gltkbh` varchar(50) DEFAULT NULL COMMENT '关联退卡编号', - `jks` varchar(50) DEFAULT NULL COMMENT '健康师', - `jksxm` varchar(50) DEFAULT NULL COMMENT '健康师姓名', - `jkszh` varchar(50) DEFAULT NULL COMMENT '健康师账号', - `jksyj` decimal(15,2) DEFAULT NULL COMMENT '健康师业绩', - `tksj` datetime DEFAULT NULL COMMENT '退卡时间', - `F_jsjid` varchar(50) DEFAULT NULL COMMENT '金三角id', - `F_tkpxid` varchar(50) DEFAULT NULL COMMENT '关联项目资料表ID', - `F_LaborCost` decimal(15,2) DEFAULT NULL COMMENT '手工费', - `F_tkpxNumber` decimal(15,2) DEFAULT NULL COMMENT '退卡品项次数', - `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', - `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建用户', - `F_ModifyTime` datetime DEFAULT NULL COMMENT '修改时间', - `F_ModifyUser` varchar(50) DEFAULT NULL COMMENT '修改用户', - `F_DeleteMark` int(1) DEFAULT NULL COMMENT '删除标记', - PRIMARY KEY (`F_Id`), - KEY `idx_gltkbh` (`gltkbh`), - KEY `idx_jks` (`jks`), - KEY `idx_F_tkpxid` (`F_tkpxid`), - KEY `idx_tksj` (`tksj`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退卡_健康师业绩表'; - --- 4. 创建退卡科技部老师业绩表 -DROP TABLE IF EXISTS `lq_hytk_kjbsyj`; -CREATE TABLE `lq_hytk_kjbsyj` ( - `F_Id` varchar(50) NOT NULL COMMENT '业绩编号', - `gltkbh` varchar(50) DEFAULT NULL COMMENT '关联退卡编号', - `kjbls` varchar(50) DEFAULT NULL COMMENT '科技部老师', - `kjblsxm` varchar(50) DEFAULT NULL COMMENT '科技部老师姓名', - `kjblszh` varchar(50) DEFAULT NULL COMMENT '科技部老师账号', - `kjblsyj` decimal(15,2) DEFAULT NULL COMMENT '科技部老师业绩', - `tksj` datetime DEFAULT NULL COMMENT '退卡时间', - `F_tkpxid` varchar(50) DEFAULT NULL COMMENT '关联项目资料表ID', - `F_LaborCost` decimal(15,2) DEFAULT NULL COMMENT '手工费', - `F_tkpxNumber` decimal(15,2) DEFAULT NULL COMMENT '退卡品项次数', - `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', - `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建用户', - `F_ModifyTime` datetime DEFAULT NULL COMMENT '修改时间', - `F_ModifyUser` varchar(50) DEFAULT NULL COMMENT '修改用户', - `F_DeleteMark` int(1) DEFAULT NULL COMMENT '删除标记', - PRIMARY KEY (`F_Id`), - KEY `idx_gltkbh` (`gltkbh`), - KEY `idx_kjbls` (`kjbls`), - KEY `idx_F_tkpxid` (`F_tkpxid`), - KEY `idx_tksj` (`tksj`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='退卡_科技部老师业绩表'; - --- ============================================= --- 添加外键约束(可选) --- ============================================= - --- 退卡明细表外键约束 --- 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; - --- 退卡健康师业绩表外键约束 --- 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; --- 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; - --- 退卡科技部老师业绩表外键约束 --- 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; --- 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; - --- ============================================= --- 创建完成提示 --- ============================================= -SELECT '退卡相关表创建完成!' AS '创建结果';