员工统计新增总检统计

This commit is contained in:
xuyuncong 2025-11-04 18:40:05 +08:00
parent 9f0f577b53
commit 8819caa791
3 changed files with 318 additions and 81 deletions

View File

@ -286,4 +286,13 @@ public interface RepairStatisticsMapper {
List<Map<String, Object>> selectWorkerStatisticsByUserIds(@Param("userIds") List<Long> userIds, List<Map<String, Object>> selectWorkerStatisticsByUserIds(@Param("userIds") List<Long> userIds,
@Param("startDate") String startDate, @Param("startDate") String startDate,
@Param("endDate") String endDate); @Param("endDate") String endDate);
/**
* 查询指定服务顾问的统计数据产值和毛利
* @param advisorId 服务顾问ID
* @param startDate 开始日期
* @param endDate 结束日期
* @return 包含产值和毛利的统计数据
*/
Map<String, Object> selectAdvisorStatistics(@Param("advisorId") Long advisorId, @Param("startDate") String startDate, @Param("endDate") String endDate);
} }

View File

@ -14,11 +14,13 @@ import cn.iocoder.yudao.module.business.entity.DlBusinessChannel;
import cn.iocoder.yudao.module.business.service.BusinessChannelService; import cn.iocoder.yudao.module.business.service.BusinessChannelService;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.UserDTO;
import cn.iocoder.yudao.module.tickets.entity.DlRepairTickets; import cn.iocoder.yudao.module.tickets.entity.DlRepairTickets;
import cn.iocoder.yudao.module.tickets.service.DlRepairTicketsService; import cn.iocoder.yudao.module.tickets.service.DlRepairTicketsService;
import cn.iocoder.yudao.module.tickets.vo.DlRepairTicketsRespVO; import cn.iocoder.yudao.module.tickets.vo.DlRepairTicketsRespVO;
import cn.iocoder.yudao.util.StringUtils; import cn.iocoder.yudao.util.StringUtils;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -33,6 +35,7 @@ import cn.hutool.core.util.ObjectUtil;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cn.iocoder.yudao.common.RepairCons.DICT_REPAIR_WORK_TYPE; import static cn.iocoder.yudao.common.RepairCons.DICT_REPAIR_WORK_TYPE;
@ -69,6 +72,9 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
@Resource @Resource
private RepairWorkerService workerService; private RepairWorkerService workerService;
@Resource
private cn.iocoder.yudao.module.system.api.permission.RoleApi roleApi;
/** /**
* 将统计结果列表转换为Map格式 * 将统计结果列表转换为Map格式
* @param list 统计结果列表每个元素包含key和value字段 * @param list 统计结果列表每个元素包含key和value字段
@ -563,8 +569,8 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
resp.add(new HashMap<String, Object>() {{ resp.add(new HashMap<String, Object>() {{
put("name", "总检"); put("name", "总检");
put("id", "zj"); put("id", "zj");
put("count", nfpgCount); put("nfpgCount", nfpgCount);
put("count2", zjCount); put("zjCount", zjCount);
}}); }});
return resp; return resp;
@ -692,7 +698,208 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
List<Map<String, Object>> resp = new ArrayList<>(); List<Map<String, Object>> resp = new ArrayList<>();
// 根据workType查询该工种下的所有员工 // 特殊处理当workType等于fwgw时查询角色为fwgw的员工
if ("fwgw".equals(workType)) {
try {
// 获取当前租户ID
Long tenantId = cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.getRequiredTenantId();
// 查询角色为fwgw的员工列表服务顾问
List<cn.iocoder.yudao.module.system.api.user.dto.UserDTO> advisors = roleApi.selectUserListByRoleCode(tenantId, "service_advisor");
// 如果没有员工直接返回空列表
if (advisors == null || advisors.isEmpty()) {
return resp;
}
// 对每个服务顾问进行统计
for (cn.iocoder.yudao.module.system.api.user.dto.UserDTO advisor : advisors) {
Long userId = advisor.getId();
if (userId == null) {
continue;
}
try {
Map<String, Object> respMap = new HashMap<>();
respMap.put("name", advisor.getNickname());
respMap.put("id", userId);
// 构造与workTypeStatistics相同格式的数据
Map<String, Object> dataMap = new HashMap<>();
List<Map<String, Object>> statsList = new ArrayList<>();
// 初始化统计数据
// Integer ticketCount = 0;
// Integer workingCount = 0;
// Integer completedCount = 0;
BigDecimal totalAmount = BigDecimal.ZERO;
BigDecimal grossProfit = BigDecimal.ZERO;
// 1. 查询该服务顾问的工单统计数据接单量维修中已竣工
// List<Long> userIds = Collections.singletonList(userId);
// List<Map<String, Object>> workerStats = statisticsMapper.selectWorkerStatisticsByUserIds(userIds, startDate, endDate);
//
// if (workerStats != null && !workerStats.isEmpty()) {
// Map<String, Object> workerStat = workerStats.get(0);
// ticketCount = workerStat.get("ticketCount") instanceof Number ? ((Number) workerStat.get("ticketCount")).intValue() : 0;
// workingCount = workerStat.get("workingCount") instanceof Number ? ((Number) workerStat.get("workingCount")).intValue() : 0;
// completedCount = workerStat.get("completedCount") instanceof Number ? ((Number) workerStat.get("completedCount")).intValue() : 0;
// }
// 2. 使用selectAdvisorStatistics方法统计产值和毛利润
Map<String, Object> statistics = statisticsMapper.selectAdvisorStatistics(userId, startDate, endDate);
if (statistics != null) {
// 获取产值总金额
totalAmount = statistics.get("totalOutput") instanceof Number ?
new BigDecimal(statistics.get("totalOutput").toString()) : BigDecimal.ZERO;
// 获取毛利配件毛利
grossProfit = statistics.get("totalProfit") instanceof Number ?
new BigDecimal(statistics.get("totalProfit").toString()) : BigDecimal.ZERO;
}
// 添加产值统计
Map<String, Object> amountNode = new HashMap<>();
amountNode.put("code", "totalAmount");
amountNode.put("name", "产值");
amountNode.put("selectType", "chanzhi");
amountNode.put("total", totalAmount);
statsList.add(amountNode);
respMap.put("amount", totalAmount);
respMap.put("count", totalAmount);
// 添加毛利润统计
Map<String, Object> profitNode = new HashMap<>();
profitNode.put("code", "grossProfit");
profitNode.put("name", "毛利润");
profitNode.put("selectType", "maolirun");
profitNode.put("total", grossProfit);
statsList.add(profitNode);
respMap.put("profit", grossProfit);
dataMap.put("stats", statsList);
respMap.put("data", dataMap);
resp.add(respMap);
} catch (Exception e) {
// 记录异常但不中断整个流程
log.warn("获取服务顾问统计信息时发生异常员工ID: {}", userId, e);
// 添加一个默认的统计对象避免前端出现空值
Map<String, Object> defaultStats = new HashMap<>();
defaultStats.put("name", advisor.getNickname());
defaultStats.put("id", userId);
resp.add(defaultStats);
}
}
} catch (Exception e) {
log.error("查询服务顾问列表时发生异常", e);
return resp;
}
} else if ("zj".equals(workType)) {
try {
// 获取当前租户ID
Long tenantId = cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.getRequiredTenantId();
// 查询角色为 general_inspection总检的员工列表
List<cn.iocoder.yudao.module.system.api.user.dto.UserDTO> advisors =
roleApi.selectUserListByRoleCode(tenantId, "general_inspection");
if (advisors == null || advisors.isEmpty()) {
return resp;
}
// 查询记录表根据时间筛选
List<RepairRecords> rescueRefuelRecords = repairRecordsService.list(
new LambdaQueryWrapper<RepairRecords>()
.in(RepairRecords::getType, RecordTypeEnum.NFPG.getCode(), RecordTypeEnum.ZJ.getCode())
.between(StringUtils.isNotEmpty(startDate), RepairRecords::getCreateTime, startDate, endDate)
);
// 按员工ID分组注意 null-safe
Map<Long, List<RepairRecords>> recordsByUserId = (rescueRefuelRecords == null)
? Collections.emptyMap()
: rescueRefuelRecords.stream().filter(Objects::nonNull)
.collect(Collectors.groupingBy(RepairRecords::getDealUserId));
// 遍历每个总检员工并组装返回值
for (UserDTO advisor : advisors) {
Long userId = advisor.getId();
if (userId == null) {
// 跳过没有 id 的记录
continue;
}
try {
Map<String, Object> respMap = new HashMap<>();
respMap.put("name", advisor.getNickname());
respMap.put("id", userId);
Map<String, Object> dataMap = new HashMap<>();
List<Map<String, Object>> statsList = new ArrayList<>();
// 取出该员工对应的记录列表可能为 null
List<RepairRecords> repairRecords = recordsByUserId.get(userId);
if (repairRecords == null) {
repairRecords = Collections.emptyList();
}
// 统计总检类型记录数ZJ
long zjCount = repairRecords.stream()
.filter(Objects::nonNull)
.filter(record -> RecordTypeEnum.ZJ.getCode().equals(record.getType()))
.count();
// 统计内排返工类型记录数NFPG
long nfpgCount = repairRecords.stream()
.filter(Objects::nonNull)
.filter(record -> RecordTypeEnum.NFPG.getCode().equals(record.getType()))
.count();
// 如果还需要统计金额等可以在这里聚合例如
// BigDecimal totalAmount = repairRecords.stream()
// .map(RepairRecords::getAmount)
// .filter(Objects::nonNull)
// .reduce(BigDecimal.ZERO, BigDecimal::add);
// 封装 ZJ 统计节点
Map<String, Object> zjNode = new HashMap<>();
zjNode.put("code", "zjCount");
zjNode.put("name", "总检次数");
zjNode.put("selectType", "zj");
zjNode.put("total", zjCount);
statsList.add(zjNode);
// 封装 NFPG 统计节点
Map<String, Object> nfpgNode = new HashMap<>();
nfpgNode.put("code", "nfpgCount");
nfpgNode.put("name", "内排返工次数");
nfpgNode.put("selectType", "nfpg");
nfpgNode.put("total", nfpgCount);
statsList.add(nfpgNode);
// 可选把合计次数放在 resp 顶层前端如果需要也能直接取
respMap.put("count", zjCount);
// 可选如果有金额产值等也可以放到 respMap示例注释
// respMap.put("amount", totalAmount);
dataMap.put("stats", statsList);
respMap.put("data", dataMap);
resp.add(respMap);
} catch (Exception e) {
// 记录异常但不中断整个流程
log.warn("获取总检统计信息时发生异常员工ID: {}", userId, e);
Map<String, Object> defaultStats = new HashMap<>();
defaultStats.put("name", advisor.getNickname());
defaultStats.put("id", userId);
resp.add(defaultStats);
}
}
} catch (Exception e) {
log.error("查询总检列表或记录时发生异常", e);
return resp;
}
}
else {
// 原有逻辑根据workType查询该工种下的所有员工
List<RepairWorkerRespVO> workers = workerService.listByWorkType(workType); List<RepairWorkerRespVO> workers = workerService.listByWorkType(workType);
// 如果没有员工直接返回空列表 // 如果没有员工直接返回空列表
@ -762,6 +969,7 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
resp.add(defaultStats); resp.add(defaultStats);
} }
} }
}
// 排序根据 count 字段降序 // 排序根据 count 字段降序
resp.sort((a, b) -> { resp.sort((a, b) -> {
@ -771,9 +979,13 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
if (countA == null) return 1; if (countA == null) return 1;
if (countB == null) return -1; if (countB == null) return -1;
int valA = Integer.parseInt(countA.toString()); // 处理BigDecimal和其他类型
int valB = Integer.parseInt(countB.toString()); BigDecimal valA = (countA instanceof BigDecimal) ? (BigDecimal) countA :
return Integer.compare(valB, valA); // 降序 (countA instanceof Number) ? new BigDecimal(countA.toString()) : BigDecimal.ZERO;
BigDecimal valB = (countB instanceof BigDecimal) ? (BigDecimal) countB :
(countB instanceof Number) ? new BigDecimal(countB.toString()) : BigDecimal.ZERO;
return valB.compareTo(valA); // 降序
}); });
return resp; return resp;
} }

View File

@ -687,4 +687,20 @@
GROUP BY drw.user_id, drw.user_name GROUP BY drw.user_id, drw.user_name
ORDER BY ticketCount DESC ORDER BY ticketCount DESC
</select> </select>
<!-- 查询指定服务顾问的统计数据(产值和毛利) -->
<select id="selectAdvisorStatistics" resultType="java.util.Map">
SELECT
SUM(drt.total_price) AS totalOutput, -- 工单总金额(产值)
-- 直接使用表中已有的item_profit字段计算总毛利
SUM(COALESCE((SELECT SUM(dri.item_profit)
FROM dl_repair_titem dri
WHERE dri.ticket_id = drt.id AND dri.deleted = 0), 0)) AS totalProfit -- 总毛利
FROM dl_repair_tickets drt
WHERE drt.deleted = 0
AND drt.adviser_id = #{advisorId}
<if test="startDate != null and endDate != null">
AND drt.create_time BETWEEN CONCAT(#{startDate}, ' 00:00:00') AND CONCAT(#{endDate}, ' 23:59:59')
</if>
</select>
</mapper> </mapper>