员工统计新增总检统计

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,
@Param("startDate") String startDate,
@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.system.api.dict.DictDataApi;
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.service.DlRepairTicketsService;
import cn.iocoder.yudao.module.tickets.vo.DlRepairTicketsRespVO;
import cn.iocoder.yudao.util.StringUtils;
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.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -33,6 +35,7 @@ import cn.hutool.core.util.ObjectUtil;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.common.RepairCons.DICT_REPAIR_WORK_TYPE;
@ -69,6 +72,9 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
@Resource
private RepairWorkerService workerService;
@Resource
private cn.iocoder.yudao.module.system.api.permission.RoleApi roleApi;
/**
* 将统计结果列表转换为Map格式
* @param list 统计结果列表每个元素包含key和value字段
@ -250,7 +256,7 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
/**
* @Author
* @Description 统计最近业务车辆或客户
* @Description 统计最近业务车辆或客户
* @Date 15:03 2025/8/25
* @Param [reqVO, page]
* @return com.baomidou.mybatisplus.core.metadata.IPage<cn.iocoder.yudao.module.base.vo.QueryBusinessResp>
@ -259,9 +265,9 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
public IPage<QueryBusinessResp> listBusinessByCustomerOrCar(QueryBusinessReqVO reqVO, Page<QueryBusinessResp> page) {
IPage<QueryBusinessResp> queryBusinessResps = null;
if ("customer".equals(reqVO.getType())) {
queryBusinessResps = statisticsMapper.listBusinessByCustomer(reqVO,page);
}else if ("car".equals(reqVO.getType())) {
queryBusinessResps = statisticsMapper.listBusinessByCar(reqVO,page);
queryBusinessResps = statisticsMapper.listBusinessByCustomer(reqVO, page);
} else if ("car".equals(reqVO.getType())) {
queryBusinessResps = statisticsMapper.listBusinessByCar(reqVO, page);
}
return queryBusinessResps;
}
@ -275,7 +281,7 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
*/
@Override
public IPage<QueryTableResp> pageByCustomerOrCar(QueryBusinessReqVO reqVO, Page<QueryTableResp> page) {
IPage<QueryTableResp> queryTableResps = statisticsMapper.pageByCustomerOrCar(reqVO,page);
IPage<QueryTableResp> queryTableResps = statisticsMapper.pageByCustomerOrCar(reqVO, page);
return queryTableResps;
}
@ -304,7 +310,7 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
// 查询所有渠道
List<DlBusinessChannel> channels = businessChannelService.list(Wrappers.<DlBusinessChannel>lambdaQuery()
.eq(DlBusinessChannel::getSystemCode, "repair")
.eq(StringUtils.isEmpty(reqVO.getSearch()),DlBusinessChannel::getType, 0)
.eq(StringUtils.isEmpty(reqVO.getSearch()), DlBusinessChannel::getType, 0)
.eq(StringUtils.isNotEmpty(reqVO.getSearch()), DlBusinessChannel::getPid, reqVO.getSearch()));
List<Map<String, Object>> resp = new ArrayList<>();
@ -324,7 +330,7 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
try {
// 查询渠道排名
Map<String, Object> bossNumStatistics = ticketsService.getBossNumStatistics(
startDate, endDate, StringUtils.isNotEmpty(reqVO.getSearch()) ? null :channel.getName(), StringUtils.isNotEmpty(reqVO.getSearch()) ? channel.getName() :null, false);
startDate, endDate, StringUtils.isNotEmpty(reqVO.getSearch()) ? null : channel.getName(), StringUtils.isNotEmpty(reqVO.getSearch()) ? channel.getName() : null, false);
respMap.put("name", channel.getName());
respMap.put("id", channel.getId());
@ -402,8 +408,8 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
List<DlRepairTickets> tickets = ticketsService.list(Wrappers.<DlRepairTickets>lambdaQuery()
.between("insurance".equals(reqVO.getType()) && CollUtil.isNotEmpty(reqVO.getDateRange()), DlRepairTickets::getInsuranceExpiryDate, reqVO.getDateRange().get(0), reqVO.getDateRange().get(1))
.between("inspection".equals(reqVO.getType()) && CollUtil.isNotEmpty(reqVO.getDateRange()), DlRepairTickets::getNextInspectionDate, reqVO.getDateRange().get(0), reqVO.getDateRange().get(1))
.orderByDesc("insurance".equals(reqVO.getType()),DlRepairTickets::getInsuranceExpiryDate)
.orderByDesc("inspection".equals(reqVO.getType()),DlRepairTickets::getNextInspectionDate));
.orderByDesc("insurance".equals(reqVO.getType()), DlRepairTickets::getInsuranceExpiryDate)
.orderByDesc("inspection".equals(reqVO.getType()), DlRepairTickets::getNextInspectionDate));
return tickets;
}
@ -557,14 +563,14 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
//查询记录表 zj的数量
long zjCount = repairRecordsService.count(Wrappers.<RepairRecords>lambdaQuery()
.eq(RepairRecords::getType, RecordTypeEnum.ZJ.getCode())
.between(StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate), RepairRecords::getCreateTime, startDate, endDate));
.eq(RepairRecords::getType, RecordTypeEnum.ZJ.getCode())
.between(StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate), RepairRecords::getCreateTime, startDate, endDate));
resp.add(new HashMap<String, Object>() {{
put("name", "总检");
put("id", "zj");
put("count", nfpgCount);
put("count2", zjCount);
put("nfpgCount", nfpgCount);
put("zjCount", zjCount);
}});
return resp;
@ -692,74 +698,276 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
List<Map<String, Object>> resp = new ArrayList<>();
// 根据workType查询该工种下的所有员工
List<RepairWorkerRespVO> workers = workerService.listByWorkType(workType);
// 如果没有员工直接返回空列表
if (workers == null || workers.isEmpty()) {
return resp;
}
// 对每个员工进行统计
for (RepairWorkerRespVO worker : workers) {
Long userId = worker.getUserId();
if (userId == null) {
continue;
}
List<Long> userIds = Collections.singletonList(userId);
// 特殊处理当workType等于fwgw时查询角色为fwgw的员工
if ("fwgw".equals(workType)) {
try {
// 查询员工的统计数据
List<Map<String, Object>> workerStats = statisticsMapper.selectWorkerStatisticsByUserIds(userIds, startDate, endDate);
if (workerStats != null && !workerStats.isEmpty()) {
Map<String, Object> workerStat = workerStats.get(0);
// 获取当前租户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");
Map<String, Object> respMap = new HashMap<>();
respMap.put("name", worker.getUserName());
respMap.put("id", userId);
// 如果没有员工直接返回空列表
if (advisors == null || advisors.isEmpty()) {
return resp;
}
// 构造与workTypeStatistics相同格式的数据
Map<String, Object> dataMap = new HashMap<>();
List<Map<String, Object>> statsList = new ArrayList<>();
// 对每个服务顾问进行统计
for (cn.iocoder.yudao.module.system.api.user.dto.UserDTO advisor : advisors) {
Long userId = advisor.getId();
if (userId == null) {
continue;
}
// 添加进厂数统计
Map<String, Object> newOrderNode = new HashMap<>();
newOrderNode.put("code", "newOrderNum");
newOrderNode.put("name", "订单(进厂数)");
newOrderNode.put("selectType", "jinchang");
newOrderNode.put("total", workerStat.get("ticketCount"));
statsList.add(newOrderNode);
respMap.put("count", workerStat.get("ticketCount"));
try {
Map<String, Object> respMap = new HashMap<>();
respMap.put("name", advisor.getNickname());
respMap.put("id", userId);
// 添加维修中统计
Map<String, Object> workingNode = new HashMap<>();
workingNode.put("code", "workingNum");
workingNode.put("name", "维修中");
workingNode.put("selectType", "weixiuzhong");
workingNode.put("total", workerStat.get("workingCount"));
statsList.add(workingNode);
// 构造与workTypeStatistics相同格式的数据
Map<String, Object> dataMap = new HashMap<>();
List<Map<String, Object>> statsList = new ArrayList<>();
// 添加已竣工统计
Map<String, Object> completedNode = new HashMap<>();
completedNode.put("code", "overNum");
completedNode.put("name", "已竣工");
completedNode.put("selectType", "yijungong");
completedNode.put("total", workerStat.get("completedCount"));
statsList.add(completedNode);
// 初始化统计数据
// Integer ticketCount = 0;
// Integer workingCount = 0;
// Integer completedCount = 0;
BigDecimal totalAmount = BigDecimal.ZERO;
BigDecimal grossProfit = BigDecimal.ZERO;
dataMap.put("stats", statsList);
respMap.put("data", dataMap);
// 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;
// }
resp.add(respMap);
// 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.warn("获取员工统计信息时发生异常员工ID: {}", userId, e);
// 添加一个默认的统计对象避免前端出现空值
Map<String, Object> defaultStats = new HashMap<>();
defaultStats.put("name", worker.getUserName());
defaultStats.put("id", userId);
resp.add(defaultStats);
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);
// 如果没有员工直接返回空列表
if (workers == null || workers.isEmpty()) {
return resp;
}
// 对每个员工进行统计
for (RepairWorkerRespVO worker : workers) {
Long userId = worker.getUserId();
if (userId == null) {
continue;
}
List<Long> userIds = Collections.singletonList(userId);
try {
// 查询员工的统计数据
List<Map<String, Object>> workerStats = statisticsMapper.selectWorkerStatisticsByUserIds(userIds, startDate, endDate);
if (workerStats != null && !workerStats.isEmpty()) {
Map<String, Object> workerStat = workerStats.get(0);
Map<String, Object> respMap = new HashMap<>();
respMap.put("name", worker.getUserName());
respMap.put("id", userId);
// 构造与workTypeStatistics相同格式的数据
Map<String, Object> dataMap = new HashMap<>();
List<Map<String, Object>> statsList = new ArrayList<>();
// 添加进厂数统计
Map<String, Object> newOrderNode = new HashMap<>();
newOrderNode.put("code", "newOrderNum");
newOrderNode.put("name", "订单(进厂数)");
newOrderNode.put("selectType", "jinchang");
newOrderNode.put("total", workerStat.get("ticketCount"));
statsList.add(newOrderNode);
respMap.put("count", workerStat.get("ticketCount"));
// 添加维修中统计
Map<String, Object> workingNode = new HashMap<>();
workingNode.put("code", "workingNum");
workingNode.put("name", "维修中");
workingNode.put("selectType", "weixiuzhong");
workingNode.put("total", workerStat.get("workingCount"));
statsList.add(workingNode);
// 添加已竣工统计
Map<String, Object> completedNode = new HashMap<>();
completedNode.put("code", "overNum");
completedNode.put("name", "已竣工");
completedNode.put("selectType", "yijungong");
completedNode.put("total", workerStat.get("completedCount"));
statsList.add(completedNode);
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", worker.getUserName());
defaultStats.put("id", userId);
resp.add(defaultStats);
}
}
}
@ -771,9 +979,13 @@ public class RepairStatisticsServiceImpl implements RepairStatisticsService {
if (countA == null) return 1;
if (countB == null) return -1;
int valA = Integer.parseInt(countA.toString());
int valB = Integer.parseInt(countB.toString());
return Integer.compare(valB, valA); // 降序
// 处理BigDecimal和其他类型
BigDecimal valA = (countA instanceof BigDecimal) ? (BigDecimal) countA :
(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;
}

View File

@ -687,4 +687,20 @@
GROUP BY drw.user_id, drw.user_name
ORDER BY ticketCount DESC
</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>