Commit 88a30b7ae3b8c8ca690b6dc898f6baf6fe09ef07

Authored by 李宇
2 parents 0df75a77 2a3f0169

Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP

netcore/src/Modularity/Extend/NCC.Extend.Entitys.Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqTechTeacherSalary
  4 +{
  5 + /// <summary>
  6 + /// 科技部老师统计数据输出
  7 + /// </summary>
  8 + public class TechTeacherStatisticsOutput
  9 + {
  10 + /// <summary>
  11 + /// 员工ID
  12 + /// </summary>
  13 + public string EmployeeId { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 员工姓名
  17 + /// </summary>
  18 + public string EmployeeName { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 开单业绩
  22 + /// </summary>
  23 + public decimal OrderAchievement { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 消耗业绩
  27 + /// </summary>
  28 + public decimal ConsumeAchievement { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 退卡业绩
  32 + /// </summary>
  33 + public decimal RefundAchievement { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 人头(按月份+客户去重统计)
  37 + /// </summary>
  38 + public int PersonCount { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 人次(按日期+客户去重统计)
  42 + /// </summary>
  43 + public decimal PersonTimes { get; set; }
  44 +
  45 + /// <summary>
  46 + /// 手工费
  47 + /// </summary>
  48 + public decimal LaborCost { get; set; }
  49 + }
  50 +}
  51 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsageApplication/LqInventoryUsageApplicationListOutput.cs
... ... @@ -89,6 +89,11 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsageApplication
89 89 public List<CurrentApproverInfo> currentApprovers { get; set; }
90 90  
91 91 /// <summary>
  92 + /// 当前审批人ID列表(用于快速获取审批人ID)
  93 + /// </summary>
  94 + public List<string> approverIds { get; set; }
  95 +
  96 + /// <summary>
92 97 /// 使用记录统计信息(批次总数量、总金额等)
93 98 /// </summary>
94 99 public BatchUsageInfo batchUsageInfo { get; set; }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTechTeacherSalary/TechTeacherStatisticsInput.cs 0 → 100644
  1 +namespace NCC.Extend.Entitys.Dto.LqTechTeacherSalary
  2 +{
  3 + /// <summary>
  4 + /// 科技部老师统计数据查询参数
  5 + /// </summary>
  6 + public class TechTeacherStatisticsInput
  7 + {
  8 + /// <summary>
  9 + /// 年份
  10 + /// </summary>
  11 + public int Year { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 月份(1-12)
  15 + /// </summary>
  16 + public int Month { get; set; }
  17 + }
  18 +}
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqTechTeacherSalary
  4 +{
  5 + /// <summary>
  6 + /// 科技部老师统计数据输出
  7 + /// </summary>
  8 + public class TechTeacherStatisticsOutput
  9 + {
  10 + /// <summary>
  11 + /// 员工ID
  12 + /// </summary>
  13 + public string EmployeeId { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 员工姓名
  17 + /// </summary>
  18 + public string EmployeeName { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 开单业绩
  22 + /// </summary>
  23 + public decimal OrderAchievement { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 消耗业绩
  27 + /// </summary>
  28 + public decimal ConsumeAchievement { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 退卡业绩
  32 + /// </summary>
  33 + public decimal RefundAchievement { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 人头(按月份+客户去重统计)
  37 + /// </summary>
  38 + public int PersonCount { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 人次(按日期+客户去重统计)
  42 + /// </summary>
  43 + public decimal PersonTimes { get; set; }
  44 +
  45 + /// <summary>
  46 + /// 手工费(仅统计耗卡中的手工费)
  47 + /// </summary>
  48 + public decimal LaborCost { get; set; }
  49 + }
  50 +}
  51 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs
... ... @@ -64,3 +64,4 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_application_node
64 64  
65 65  
66 66  
  67 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs
... ... @@ -82,3 +82,4 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_approval_record
82 82  
83 83  
84 84  
  85 +
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
... ... @@ -428,7 +428,7 @@ namespace NCC.Extend
428 428 CreateTime = DateTime.Now,
429 429 IsEffective = StatusEnum.有效.GetHashCode()
430 430 };
431   -
  431 +
432 432 // 设置申请总金额(该批次所有商品的总价)
433 433 // 使用反射设置,避免服务未重启时找不到属性的问题
434 434 try
... ... @@ -450,6 +450,27 @@ namespace NCC.Extend
450 450 {
451 451 throw NCCException.Oh("创建申请记录失败");
452 452 }
  453 +
  454 + // 创建审批记录(当前节点的审批人)
  455 + var approvalRecord = new LqInventoryUsageApprovalRecordEntity
  456 + {
  457 + Id = YitIdHelper.NextId().ToString(),
  458 + ApplicationId = applicationEntity.Id,
  459 + NodeId = applicationEntity.CurrentNodeId,
  460 + NodeOrder = 1,
  461 + ApproverId = approverUser.Id,
  462 + ApproverName = approverUser.RealName ?? "",
  463 + ApprovalResult = "待审批",
  464 + ApprovalOpinion = "",
  465 + ApprovalTime = DateTime.Now,
  466 + IsCurrentNode = 1
  467 + };
  468 +
  469 + var approvalInsertCount = await _db.Insertable(approvalRecord).ExecuteCommandAsync();
  470 + if (approvalInsertCount <= 0)
  471 + {
  472 + throw NCCException.Oh("创建审批记录失败");
  473 + }
453 474 }
454 475  
455 476 _db.Ado.CommitTran();
... ... @@ -779,7 +800,7 @@ namespace NCC.Extend
779 800 ReceiveUser = application.ReceiveUser,
780 801 Remarks = application.Remarks
781 802 };
782   -
  803 +
783 804 // 设置申请总金额(该批次所有商品的总价)
784 805 // 使用反射设置,避免服务未重启时找不到属性的问题
785 806 try
... ... @@ -1595,8 +1616,8 @@ namespace NCC.Extend
1595 1616 // 关键字搜索(申请编号、申请人姓名、门店名称)
1596 1617 if (!string.IsNullOrWhiteSpace(input.keyword))
1597 1618 {
1598   - query = query.Where((app, store) =>
1599   - app.Id.Contains(input.keyword) ||
  1619 + query = query.Where((app, store) =>
  1620 + app.Id.Contains(input.keyword) ||
1600 1621 app.ApplicationUserName.Contains(input.keyword) ||
1601 1622 store.Dm.Contains(input.keyword));
1602 1623 }
... ... @@ -1651,17 +1672,20 @@ namespace NCC.Extend
1651 1672 // 获取所有申请ID,用于查询审批人和使用记录统计
1652 1673 var applicationIds = pageList.list.Select(x => x.id).ToList();
1653 1674  
  1675 + // 初始化审批人相关数据
  1676 + var approvalRecords = new List<LqInventoryUsageApprovalRecordEntity>();
  1677 + var approverDict = new Dictionary<string, string>();
  1678 +
1654 1679 if (applicationIds.Any())
1655 1680 {
1656 1681 // 查询当前审批人信息(通过审批记录表,查找当前节点的审批人)
1657   - var approvalRecords = await _db.Queryable<LqInventoryUsageApprovalRecordEntity>()
  1682 + approvalRecords = await _db.Queryable<LqInventoryUsageApprovalRecordEntity>()
1658 1683 .Where(x => applicationIds.Contains(x.ApplicationId) && x.IsCurrentNode == 1)
1659 1684 .Where(x => x.ApprovalResult == "待审批" || x.ApprovalResult == "")
1660 1685 .ToListAsync();
1661 1686  
1662 1687 // 获取审批人ID列表
1663   - var approverIds = approvalRecords.Select(x => x.ApproverId).Distinct().ToList();
1664   - var approverDict = new Dictionary<string, string>();
  1688 + var approverIds = approvalRecords.Select(x => x.ApproverId).Distinct().Where(x => !string.IsNullOrEmpty(x)).ToList();
1665 1689 if (approverIds.Any())
1666 1690 {
1667 1691 var approvers = await _db.Queryable<UserEntity>()
... ... @@ -1670,9 +1694,13 @@ namespace NCC.Extend
1670 1694 .ToListAsync();
1671 1695 approverDict = approvers.ToDictionary(k => k.Id, v => v.RealName ?? "");
1672 1696 }
  1697 + }
1673 1698  
1674   - // 查询使用记录统计(按批次ID分组)
1675   - var batchIds = pageList.list.Select(x => x.usageBatchId).Distinct().ToList();
  1699 + // 查询使用记录统计(按批次ID分组)
  1700 + var batchIds = pageList.list.Select(x => x.usageBatchId).Distinct().ToList();
  1701 + var usageDict = new Dictionary<string, dynamic>();
  1702 + if (batchIds.Any())
  1703 + {
1676 1704 var usageRecords = await _db.Queryable<LqInventoryUsageEntity>()
1677 1705 .Where(x => batchIds.Contains(x.UsageBatchId))
1678 1706 .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
... ... @@ -1686,64 +1714,70 @@ namespace NCC.Extend
1686 1714 })
1687 1715 .ToListAsync();
1688 1716  
1689   - var usageDict = usageRecords.ToDictionary(k => k.UsageBatchId, v => v);
  1717 + usageDict = usageRecords.ToDictionary(k => k.UsageBatchId, v => (dynamic)v);
  1718 + }
1690 1719  
1691   - // 查询领取人信息
1692   - var receiveUserIds = pageList.list.Where(x => !string.IsNullOrWhiteSpace(x.receiveUser))
1693   - .Select(x => x.receiveUser)
  1720 + // 查询领取人信息
  1721 + var receiveUserIds = pageList.list.Where(x => !string.IsNullOrWhiteSpace(x.receiveUser))
  1722 + .Select(x => x.receiveUser)
  1723 + .Distinct()
  1724 + .ToList();
  1725 + var receiveUserDict = new Dictionary<string, string>();
  1726 + if (receiveUserIds.Any())
  1727 + {
  1728 + var receiveUsers = await _db.Queryable<UserEntity>()
  1729 + .Where(x => receiveUserIds.Contains(x.Id))
  1730 + .Select(x => new { x.Id, x.RealName })
  1731 + .ToListAsync();
  1732 + receiveUserDict = receiveUsers.ToDictionary(k => k.Id, v => v.RealName ?? "");
  1733 + }
  1734 +
  1735 + // 填充审批人和使用记录统计信息
  1736 + foreach (var item in pageList.list)
  1737 + {
  1738 + // 当前审批人信息
  1739 + var currentApprovers = approvalRecords
  1740 + .Where(x => x.ApplicationId == item.id)
  1741 + .Select(x => new CurrentApproverInfo
  1742 + {
  1743 + approverId = x.ApproverId,
  1744 + approverName = approverDict.ContainsKey(x.ApproverId) ? approverDict[x.ApproverId] : x.ApproverName,
  1745 + approvalResult = x.ApprovalResult
  1746 + })
  1747 + .ToList();
  1748 + item.currentApprovers = currentApprovers ?? new List<CurrentApproverInfo>();
  1749 + // 审批人ID列表(从审批人信息中提取)
  1750 + item.approverIds = currentApprovers
  1751 + .Where(x => !string.IsNullOrEmpty(x.approverId))
  1752 + .Select(x => x.approverId)
1694 1753 .Distinct()
1695 1754 .ToList();
1696   - var receiveUserDict = new Dictionary<string, string>();
1697   - if (receiveUserIds.Any())
1698   - {
1699   - var receiveUsers = await _db.Queryable<UserEntity>()
1700   - .Where(x => receiveUserIds.Contains(x.Id))
1701   - .Select(x => new { x.Id, x.RealName })
1702   - .ToListAsync();
1703   - receiveUserDict = receiveUsers.ToDictionary(k => k.Id, v => v.RealName ?? "");
1704   - }
1705 1755  
1706   - // 填充审批人和使用记录统计信息
1707   - foreach (var item in pageList.list)
  1756 + // 使用记录统计信息
  1757 + if (usageDict.ContainsKey(item.usageBatchId))
1708 1758 {
1709   - // 当前审批人信息
1710   - var currentApprovers = approvalRecords
1711   - .Where(x => x.ApplicationId == item.id)
1712   - .Select(x => new CurrentApproverInfo
1713   - {
1714   - approverId = x.ApproverId,
1715   - approverName = approverDict.ContainsKey(x.ApproverId) ? approverDict[x.ApproverId] : x.ApproverName,
1716   - approvalResult = x.ApprovalResult
1717   - })
1718   - .ToList();
1719   - item.currentApprovers = currentApprovers;
1720   -
1721   - // 使用记录统计信息
1722   - if (usageDict.ContainsKey(item.usageBatchId))
  1759 + var usage = usageDict[item.usageBatchId];
  1760 + item.batchUsageInfo = new BatchUsageInfo
1723 1761 {
1724   - var usage = usageDict[item.usageBatchId];
1725   - item.batchUsageInfo = new BatchUsageInfo
1726   - {
1727   - totalCount = usage.TotalCount,
1728   - totalQuantity = usage.TotalQuantity,
1729   - totalAmount = usage.TotalAmount
1730   - };
1731   - }
1732   - else
  1762 + totalCount = usage.TotalCount,
  1763 + totalQuantity = usage.TotalQuantity,
  1764 + totalAmount = usage.TotalAmount
  1765 + };
  1766 + }
  1767 + else
  1768 + {
  1769 + item.batchUsageInfo = new BatchUsageInfo
1733 1770 {
1734   - item.batchUsageInfo = new BatchUsageInfo
1735   - {
1736   - totalCount = 0,
1737   - totalQuantity = 0,
1738   - totalAmount = 0
1739   - };
1740   - }
  1771 + totalCount = 0,
  1772 + totalQuantity = 0,
  1773 + totalAmount = 0
  1774 + };
  1775 + }
1741 1776  
1742   - // 领取人姓名
1743   - if (!string.IsNullOrWhiteSpace(item.receiveUser) && receiveUserDict.ContainsKey(item.receiveUser))
1744   - {
1745   - item.receiveUserName = receiveUserDict[item.receiveUser];
1746   - }
  1777 + // 领取人姓名
  1778 + if (!string.IsNullOrWhiteSpace(item.receiveUser) && receiveUserDict.ContainsKey(item.receiveUser))
  1779 + {
  1780 + item.receiveUserName = receiveUserDict[item.receiveUser];
1747 1781 }
1748 1782 }
1749 1783  
... ... @@ -1803,7 +1837,7 @@ namespace NCC.Extend
1803 1837 // 由于审批记录在审批时才创建,这里需要通过审批记录表查找当前用户已审批的记录
1804 1838 // 然后排除这些申请,找出状态为"审批中"但当前用户还未审批的申请
1805 1839 // 注意:这里需要根据实际业务逻辑调整,如果审批人信息存储在节点审批人表中,应该通过那个表查询
1806   -
  1840 +
1807 1841 // 先查询当前用户已经审批过的申请ID(排除这些)
1808 1842 var approvedApplicationIds = await _db.Queryable<LqInventoryUsageApprovalRecordEntity>()
1809 1843 .Where(x => x.ApproverId == currentUserId)
... ... @@ -1811,13 +1845,13 @@ namespace NCC.Extend
1811 1845 .Select(x => x.ApplicationId)
1812 1846 .Distinct()
1813 1847 .ToListAsync();
1814   -
  1848 +
1815 1849 // 查询所有状态为"审批中"的申请ID
1816 1850 var allPendingApplicationIds = await _db.Queryable<LqInventoryUsageApplicationEntity>()
1817 1851 .Where(x => x.ApprovalStatus == "审批中" && x.IsEffective == StatusEnum.有效.GetHashCode())
1818 1852 .Select(x => x.Id)
1819 1853 .ToListAsync();
1820   -
  1854 +
1821 1855 // 找出当前用户需要审批的申请(状态为"审批中"且当前用户还未审批的)
1822 1856 // 注意:这里简化处理,实际应该通过审批人配置表来确定审批人
1823 1857 // 如果审批人信息没有单独存储,可能需要通过其他方式确定
... ... @@ -1859,8 +1893,8 @@ namespace NCC.Extend
1859 1893 // 关键字搜索
1860 1894 if (!string.IsNullOrWhiteSpace(input.keyword))
1861 1895 {
1862   - query = query.Where((app, store) =>
1863   - app.Id.Contains(input.keyword) ||
  1896 + query = query.Where((app, store) =>
  1897 + app.Id.Contains(input.keyword) ||
1864 1898 app.ApplicationUserName.Contains(input.keyword) ||
1865 1899 store.Dm.Contains(input.keyword));
1866 1900 }
... ... @@ -1965,5 +1999,106 @@ namespace NCC.Extend
1965 1999  
1966 2000 #endregion
1967 2001  
  2002 + #region 测试接口:为现有申请记录添加审批记录
  2003 + /// <summary>
  2004 + /// 为现有申请记录添加审批记录(测试用)
  2005 + /// </summary>
  2006 + /// <remarks>
  2007 + /// 为"审批中"状态的申请记录添加当前节点的审批记录,用于测试审批人信息返回
  2008 + /// </remarks>
  2009 + /// <param name="applicationId">申请ID(可选,不传则处理所有"审批中"状态的申请)</param>
  2010 + /// <param name="approverId">审批人ID(可选,不传则使用admin)</param>
  2011 + /// <returns>处理结果</returns>
  2012 + [HttpPost("Test/AddApprovalRecord")]
  2013 + public async Task<dynamic> AddApprovalRecordForTest(string applicationId = null, string approverId = null)
  2014 + {
  2015 + try
  2016 + {
  2017 + _db.Ado.BeginTran();
  2018 +
  2019 + // 获取审批人信息(默认使用admin)
  2020 + var defaultApproverId = approverId ?? "admin";
  2021 + var approverUser = await _db.Queryable<UserEntity>()
  2022 + .Where(x => x.Id == defaultApproverId)
  2023 + .Select(x => new { x.Id, x.RealName })
  2024 + .FirstAsync();
  2025 +
  2026 + if (approverUser == null)
  2027 + {
  2028 + throw NCCException.Oh("审批人不存在");
  2029 + }
  2030 +
  2031 + // 查询需要处理的申请记录
  2032 + var query = _db.Queryable<LqInventoryUsageApplicationEntity>()
  2033 + .Where(x => x.ApprovalStatus == "审批中");
  2034 +
  2035 + if (!string.IsNullOrWhiteSpace(applicationId))
  2036 + {
  2037 + query = query.Where(x => x.Id == applicationId);
  2038 + }
  2039 +
  2040 + var applications = await query.ToListAsync();
  2041 +
  2042 + if (!applications.Any())
  2043 + {
  2044 + return new { success = true, message = "没有需要处理的申请记录", count = 0 };
  2045 + }
  2046 +
  2047 + var successCount = 0;
  2048 + var skipCount = 0;
  2049 +
  2050 + foreach (var application in applications)
  2051 + {
  2052 + // 检查是否已存在当前节点的审批记录
  2053 + var existingRecord = await _db.Queryable<LqInventoryUsageApprovalRecordEntity>()
  2054 + .Where(x => x.ApplicationId == application.Id && x.IsCurrentNode == 1)
  2055 + .Where(x => x.ApprovalResult == "待审批" || x.ApprovalResult == "")
  2056 + .FirstAsync();
  2057 +
  2058 + if (existingRecord != null)
  2059 + {
  2060 + skipCount++;
  2061 + continue;
  2062 + }
  2063 +
  2064 + // 创建审批记录
  2065 + var approvalRecord = new LqInventoryUsageApprovalRecordEntity
  2066 + {
  2067 + Id = YitIdHelper.NextId().ToString(),
  2068 + ApplicationId = application.Id,
  2069 + NodeId = application.CurrentNodeId ?? application.Id, // 使用申请ID作为节点ID(如果CurrentNodeId为空)
  2070 + NodeOrder = 1,
  2071 + ApproverId = approverUser.Id,
  2072 + ApproverName = approverUser.RealName ?? "",
  2073 + ApprovalResult = "待审批",
  2074 + ApprovalOpinion = "",
  2075 + ApprovalTime = DateTime.Now,
  2076 + IsCurrentNode = 1
  2077 + };
  2078 +
  2079 + await _db.Insertable(approvalRecord).ExecuteCommandAsync();
  2080 + successCount++;
  2081 + }
  2082 +
  2083 + _db.Ado.CommitTran();
  2084 +
  2085 + return new
  2086 + {
  2087 + success = true,
  2088 + message = $"处理完成:成功添加 {successCount} 条审批记录,跳过 {skipCount} 条(已存在)",
  2089 + total = applications.Count,
  2090 + successCount = successCount,
  2091 + skipCount = skipCount
  2092 + };
  2093 + }
  2094 + catch (Exception ex)
  2095 + {
  2096 + _db.Ado.RollbackTran();
  2097 + _logger.LogError(ex, "添加审批记录失败");
  2098 + throw NCCException.Oh($"添加失败:{ex.Message}");
  2099 + }
  2100 + }
  2101 + #endregion
  2102 +
1968 2103 }
1969 2104 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
... ... @@ -619,6 +619,7 @@ namespace NCC.Extend
619 619 salary.DailyAverageConsumption,
620 620 salary.DailyAverageProjectCount,
621 621 daysInMonth,
  622 + salary.WorkingDays,
622 623 isNewStore);
623 624  
624 625 // 4.2 提成计算
... ... @@ -732,7 +733,7 @@ namespace NCC.Extend
732 733 /// <summary>
733 734 /// 计算底薪
734 735 /// </summary>
735   - private decimal CalculateBaseSalary(decimal dailyAvgConsumption, decimal dailyAvgProjectCount, int daysInMonth, bool isNewStore)
  736 + private decimal CalculateBaseSalary(decimal dailyAvgConsumption, decimal dailyAvgProjectCount, int daysInMonth, decimal workingDays, bool isNewStore)
736 737 {
737 738 // 规则调整:按日均计算
738 739 // 一星:月消耗 10000 / 当月天数,项目数 96 / 当月天数
... ... @@ -788,7 +789,13 @@ namespace NCC.Extend
788 789 baseSalary = 2000;
789 790 }
790 791  
791   - return baseSalary;
  792 + // 最终计算:(底薪 / 当月天数) * 在店天数
  793 + if (daysInMonth > 0)
  794 + {
  795 + baseSalary = (baseSalary / daysInMonth) * workingDays;
  796 + }
  797 +
  798 + return Math.Round(baseSalary, 2);
792 799 }
793 800  
794 801 /// <summary>
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqTechTeacherSalaryService.cs
... ... @@ -13,6 +13,7 @@ using NCC.Extend.Entitys.lq_attendance_summary;
13 13 using NCC.Extend.Entitys.lq_tech_teacher_salary_statistics;
14 14 using NCC.Extend.Entitys.lq_xh_hyhk;
15 15 using NCC.Extend.Entitys.lq_xh_kjbsyj;
  16 +using NCC.Extend.Entitys.lq_person_times_record;
16 17 using NCC.System.Entitys.Permission;
17 18 using SqlSugar;
18 19 using System;
... ... @@ -509,6 +510,199 @@ namespace NCC.Extend
509 510 return (1m, consumeAchievement * 0.01m);
510 511 }
511 512 }
  513 +
  514 + /// <summary>
  515 + /// 统计科技部老师某个月的数据
  516 + /// </summary>
  517 + /// <remarks>
  518 + /// 实时统计科技部老师某个月的数据,包括:
  519 + /// - 开单业绩:从 lq_kd_kjbsyj 表统计
  520 + /// - 消耗业绩:从 lq_xh_kjbsyj 表统计(关联 lq_xh_hyhk 获取时间)
  521 + /// - 退卡业绩:从 lq_hytk_kjbsyj 表统计
  522 + /// - 手工费:从耗卡表(lq_xh_kjbsyj)的 F_LaborCost 字段统计
  523 + /// - 人头:从 lq_person_times_record 表统计(按月份+客户去重)
  524 + /// - 人次:从 lq_person_times_record 表统计(按日期+客户去重)
  525 + ///
  526 + /// 示例请求:
  527 + /// ```json
  528 + /// {
  529 + /// "year": 2025,
  530 + /// "month": 11
  531 + /// }
  532 + /// ```
  533 + /// </remarks>
  534 + /// <param name="input">查询参数(年份、月份)</param>
  535 + /// <returns>科技部老师统计数据列表</returns>
  536 + [HttpGet("statistics")]
  537 + public async Task<List<TechTeacherStatisticsOutput>> GetTechTeacherStatistics([FromQuery] TechTeacherStatisticsInput input)
  538 + {
  539 + // 1. 参数验证
  540 + if (input.Year < 2020 || input.Year > 2100)
  541 + {
  542 + throw new Exception("年份范围不正确");
  543 + }
  544 + if (input.Month < 1 || input.Month > 12)
  545 + {
  546 + throw new Exception("月份范围不正确(1-12)");
  547 + }
  548 +
  549 + // 2. 计算时间范围
  550 + var startDate = new DateTime(input.Year, input.Month, 1);
  551 + var endDate = startDate.AddMonths(1).AddDays(-1);
  552 + var monthStr = $"{input.Year}{input.Month:D2}";
  553 +
  554 + // 3. 获取所有科技部老师(岗位为"科技老师")
  555 + var techTeacherList = await _db.Queryable<UserEntity>()
  556 + .Where(x => x.Gw == "科技老师")
  557 + .Select(x => new
  558 + {
  559 + EmployeeId = x.Id,
  560 + EmployeeName = x.RealName,
  561 + EmployeeAccount = x.Account
  562 + })
  563 + .ToListAsync();
  564 +
  565 + if (techTeacherList == null || !techTeacherList.Any())
  566 + {
  567 + return new List<TechTeacherStatisticsOutput>();
  568 + }
  569 +
  570 + var teacherIds = techTeacherList.Select(x => x.EmployeeId).ToList();
  571 + var teacherAccounts = techTeacherList.Where(x => !string.IsNullOrEmpty(x.EmployeeAccount)).Select(x => x.EmployeeAccount).ToList();
  572 +
  573 + // 4. 使用聚合查询统计开单业绩和手工费(优化性能)
  574 + // 注意:kjblsyj字段是varchar类型,需要转换
  575 + var orderStatsList = await _db.Queryable<LqKdKjbsyjEntity>()
  576 + .Where(x => x.Yjsj >= startDate && x.Yjsj <= endDate.AddDays(1) && x.IsEffective == 1)
  577 + .Where(x => teacherIds.Contains(x.Kjbls) || teacherAccounts.Contains(x.Kjblszh))
  578 + .ToListAsync();
  579 +
  580 + var orderStats = orderStatsList
  581 + .Where(x => !string.IsNullOrEmpty(x.Kjbls))
  582 + .GroupBy(x => x.Kjbls)
  583 + .Select(g => new
  584 + {
  585 + TeacherId = g.Key,
  586 + OrderAchievement = g.Sum(x => decimal.TryParse(x.Kjblsyj, out var val) ? val : 0m)
  587 + })
  588 + .ToList();
  589 +
  590 + // 5. 使用聚合查询统计消耗业绩和手工费(关联耗卡主表获取时间)
  591 + var consumeStatsList = await _db.Queryable<LqXhKjbsyjEntity, LqXhHyhkEntity>(
  592 + (kjbsyj, hyhk) => kjbsyj.Glkdbh == hyhk.Id && hyhk.IsEffective == 1)
  593 + .Where((kjbsyj, hyhk) => kjbsyj.IsEffective == 1
  594 + && hyhk.Hksj >= startDate && hyhk.Hksj <= endDate.AddDays(1))
  595 + .Where((kjbsyj, hyhk) => teacherIds.Contains(kjbsyj.Kjbls) || teacherAccounts.Contains(kjbsyj.Kjblszh))
  596 + .Select((kjbsyj, hyhk) => new
  597 + {
  598 + TeacherId = kjbsyj.Kjbls,
  599 + ConsumeAchievement = kjbsyj.Kjblsyj,
  600 + LaborCost = kjbsyj.LaborCost
  601 + })
  602 + .ToListAsync();
  603 +
  604 + var consumeStats = consumeStatsList
  605 + .Where(x => !string.IsNullOrEmpty(x.TeacherId))
  606 + .GroupBy(x => x.TeacherId)
  607 + .Select(g => new
  608 + {
  609 + TeacherId = g.Key,
  610 + ConsumeAchievement = g.Sum(x => x.ConsumeAchievement ?? 0m),
  611 + LaborCost = g.Sum(x => x.LaborCost ?? 0m)
  612 + })
  613 + .ToList();
  614 +
  615 + // 6. 使用聚合查询统计退卡业绩和手工费
  616 + var refundStatsList = await _db.Queryable<LqHytkKjbsyjEntity>()
  617 + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1)
  618 + .Where(x => teacherIds.Contains(x.Kjbls) || teacherAccounts.Contains(x.Kjblszh))
  619 + .ToListAsync();
  620 +
  621 + var refundStats = refundStatsList
  622 + .Where(x => !string.IsNullOrEmpty(x.Kjbls))
  623 + .GroupBy(x => x.Kjbls)
  624 + .Select(g => new
  625 + {
  626 + TeacherId = g.Key,
  627 + RefundAchievement = g.Sum(x => x.Kjblsyj ?? 0m)
  628 + })
  629 + .ToList();
  630 +
  631 + // 7. 统计人头(按月份+客户去重)
  632 + var personCountRecords = await _db.Queryable<LqPersonTimesRecordEntity>()
  633 + .Where(x => x.PersonType == "科技老师"
  634 + && x.WorkMonth == monthStr
  635 + && x.IsEffective == 1
  636 + && teacherIds.Contains(x.PersonId))
  637 + .ToListAsync();
  638 +
  639 + var personCountStats = personCountRecords
  640 + .GroupBy(x => new { x.PersonId, x.MemberId })
  641 + .Select(g => new { TeacherId = g.Key.PersonId })
  642 + .GroupBy(x => x.TeacherId)
  643 + .Select(g => new
  644 + {
  645 + TeacherId = g.Key,
  646 + PersonCount = g.Count()
  647 + })
  648 + .ToList();
  649 +
  650 + // 8. 统计人次(按日期+客户去重,汇总数量)
  651 + var personTimesRecords = await _db.Queryable<LqPersonTimesRecordEntity>()
  652 + .Where(x => x.PersonType == "科技老师"
  653 + && x.WorkMonth == monthStr
  654 + && x.IsEffective == 1
  655 + && teacherIds.Contains(x.PersonId))
  656 + .ToListAsync();
  657 +
  658 + // 先按日期+客户去重,取最大数量,然后按老师汇总
  659 + var personTimesStats = personTimesRecords
  660 + .GroupBy(x => new { x.PersonId, x.WorkDate, x.MemberId })
  661 + .Select(g => new
  662 + {
  663 + TeacherId = g.Key.PersonId,
  664 + Quantity = g.Max(x => x.Quantity ?? 0m) // 按日期+客户去重,取最大数量
  665 + })
  666 + .GroupBy(x => x.TeacherId)
  667 + .Select(g => new
  668 + {
  669 + TeacherId = g.Key,
  670 + PersonTimes = g.Sum(x => x.Quantity) // 汇总所有去重后的数量
  671 + })
  672 + .ToList();
  673 +
  674 + // 9. 构建结果字典(优化查找性能)
  675 + var orderDict = orderStats.ToDictionary(x => x.TeacherId, x => x);
  676 + var consumeDict = consumeStats.ToDictionary(x => x.TeacherId, x => x);
  677 + var refundDict = refundStats.ToDictionary(x => x.TeacherId, x => x);
  678 + var personCountDict = personCountStats.ToDictionary(x => x.TeacherId, x => x);
  679 + var personTimesDict = personTimesStats.ToDictionary(x => x.TeacherId, x => x);
  680 +
  681 + // 10. 组装结果
  682 + var result = new List<TechTeacherStatisticsOutput>();
  683 + foreach (var teacher in techTeacherList)
  684 + {
  685 + var orderStat = orderDict.ContainsKey(teacher.EmployeeId) ? orderDict[teacher.EmployeeId] : null;
  686 + var consumeStat = consumeDict.ContainsKey(teacher.EmployeeId) ? consumeDict[teacher.EmployeeId] : null;
  687 + var refundStat = refundDict.ContainsKey(teacher.EmployeeId) ? refundDict[teacher.EmployeeId] : null;
  688 + var personCountStat = personCountDict.ContainsKey(teacher.EmployeeId) ? personCountDict[teacher.EmployeeId] : null;
  689 + var personTimesStat = personTimesDict.ContainsKey(teacher.EmployeeId) ? personTimesDict[teacher.EmployeeId] : null;
  690 +
  691 + result.Add(new TechTeacherStatisticsOutput
  692 + {
  693 + EmployeeId = teacher.EmployeeId,
  694 + EmployeeName = teacher.EmployeeName,
  695 + OrderAchievement = orderStat?.OrderAchievement ?? 0m,
  696 + ConsumeAchievement = consumeStat?.ConsumeAchievement ?? 0m,
  697 + RefundAchievement = refundStat?.RefundAchievement ?? 0m,
  698 + PersonCount = personCountStat?.PersonCount ?? 0,
  699 + PersonTimes = personTimesStat?.PersonTimes ?? 0m,
  700 + LaborCost = consumeStat?.LaborCost ?? 0m // 手工费只统计耗卡中的手工费
  701 + });
  702 + }
  703 +
  704 + return result;
  705 + }
512 706 }
513 707 }
514 708  
... ...
sql/创建库存使用申请审批流程表.sql
... ... @@ -108,3 +108,4 @@ WHERE u.`F_UnitPrice` = 0 OR u.`F_UnitPrice` IS NULL;
108 108  
109 109  
110 110  
  111 +
... ...