gaoluyang
昨天 8c65f898ed6e5b0c7a94c792c4562f6ec831dccd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package cn.iocoder.yudao.module.mes.service.pro.worktime;
 
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.mes.controller.admin.pro.worktime.vo.MesProWorktimeReportReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.worktime.vo.MesProWorktimeReportRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
 
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
 
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORKTIME_REPORT_DIM_INVALID;
 
/**
 * MES 工时考核报表 Service 实现类
 *
 * @author 超级管理员
 */
@Service
@Validated
public class MesProWorktimeReportServiceImpl implements MesProWorktimeReportService {
 
    private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);
 
    @Resource
    private MesProFeedbackMapper feedbackMapper;
    @Resource
    private MesProProcessService processService;
    @Resource
    private MesMdWorkstationService workstationService;
    @Resource
    private AdminUserApi adminUserApi;
 
    @Override
    public MesProWorktimeReportRespVO getReport(MesProWorktimeReportReqVO reqVO) {
        Integer dimType = reqVO.getDimType();
        if (dimType == null || dimType < 1 || dimType > 4) {
            throw exception(PRO_WORKTIME_REPORT_DIM_INVALID, dimType);
        }
        List<MesProFeedbackDO> list = feedbackMapper.selectWorktimeReportList(reqVO.getBeginTime(), reqVO.getEndTime(),
                reqVO.getFeedbackUserId(), reqVO.getProcessId(), reqVO.getWorkstationId());
 
        // 1. 按维度分组累加
        Map<String, Accumulator> accumulatorMap = new LinkedHashMap<>();
        for (MesProFeedbackDO feedback : list) {
            String key = buildDimKey(feedback, dimType);
            accumulatorMap.computeIfAbsent(key, k -> new Accumulator()).add(feedback);
        }
 
        // 2. 解析维度名称
        Map<String, String> nameMap = buildDimNameMap(accumulatorMap.keySet(), dimType);
 
        // 3. 输出行 + 合计
        MesProWorktimeReportRespVO resp = new MesProWorktimeReportRespVO();
        Accumulator total = new Accumulator();
        list.forEach(total::add);
        List<MesProWorktimeReportRespVO.Row> rows = new ArrayList<>();
        accumulatorMap.forEach((key, accumulator) -> {
            MesProWorktimeReportRespVO.Row row = accumulator.toRow(key);
            row.setDimName(nameMap.getOrDefault(key, key));
            rows.add(row);
        });
        rows.sort(buildComparator(dimType));
        resp.setRows(rows);
        resp.setSummary(total.toRow("ALL"));
        resp.getSummary().setDimName("合计");
        return resp;
    }
 
    private static String buildDimKey(MesProFeedbackDO feedback, int dimType) {
        return switch (dimType) {
            case 1 -> String.valueOf(feedback.getFeedbackUserId());
            case 2 -> String.valueOf(feedback.getProcessId());
            case 3 -> feedback.getWorkstationId() == null ? "0" : String.valueOf(feedback.getWorkstationId());
            default -> feedback.getFeedbackTime() == null ? "" : feedback.getFeedbackTime().toLocalDate().toString();
        };
    }
 
    private Map<String, String> buildDimNameMap(java.util.Set<String> keys, int dimType) {
        Map<String, String> nameMap = new LinkedHashMap<>();
        if (CollUtil.isEmpty(keys)) {
            return nameMap;
        }
        switch (dimType) {
            case 1 -> {
                Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                        keys.stream().map(Long::valueOf).toList());
                keys.forEach(key -> nameMap.put(key,
                        userMap.containsKey(Long.valueOf(key)) ? userMap.get(Long.valueOf(key)).getNickname() : "ID=" + key));
            }
            case 2 -> {
                Map<Long, MesProProcessDO> processMap = processService.getProcessMap(
                        keys.stream().map(Long::valueOf).toList());
                keys.forEach(key -> nameMap.put(key,
                        processMap.containsKey(Long.valueOf(key)) ? processMap.get(Long.valueOf(key)).getName() : "ID=" + key));
            }
            case 3 -> {
                List<Long> workstationIds = keys.stream().filter(key -> !"0".equals(key)).map(Long::valueOf).toList();
                Map<Long, MesMdWorkstationDO> workstationMap = workstationService.getWorkstationMap(workstationIds);
                keys.forEach(key -> {
                    if ("0".equals(key)) {
                        nameMap.put(key, "未指定机台");
                    } else {
                        nameMap.put(key, workstationMap.containsKey(Long.valueOf(key))
                                ? workstationMap.get(Long.valueOf(key)).getName() : "ID=" + key);
                    }
                });
            }
            default -> keys.forEach(key -> nameMap.put(key, key));
        }
        return nameMap;
    }
 
    private static Comparator<MesProWorktimeReportRespVO.Row> buildComparator(int dimType) {
        if (dimType == 4) {
            return Comparator.comparing(MesProWorktimeReportRespVO.Row::getDimKey);
        }
        return Comparator.comparing(row -> {
            try {
                return Long.parseLong(row.getDimKey());
            } catch (NumberFormatException ignored) {
                return Long.MAX_VALUE;
            }
        });
    }
 
    private static class Accumulator {
 
        private long count;
        private BigDecimal productionLength = BigDecimal.ZERO;
        private BigDecimal actualHours = BigDecimal.ZERO;
        private BigDecimal standardHours = BigDecimal.ZERO;
        private BigDecimal wageAmount = BigDecimal.ZERO;
 
        void add(MesProFeedbackDO feedback) {
            count++;
            productionLength = productionLength.add(nvl(feedback.getProductionLength()));
            actualHours = actualHours.add(nvl(feedback.getWorkHours()));
            standardHours = standardHours.add(nvl(feedback.getStandardWorkHours()));
            wageAmount = wageAmount.add(nvl(feedback.getWageAmount()));
        }
 
        MesProWorktimeReportRespVO.Row toRow(String key) {
            MesProWorktimeReportRespVO.Row row = new MesProWorktimeReportRespVO.Row();
            row.setDimKey(key);
            row.setFeedbackCount(count);
            row.setProductionLength(productionLength.setScale(2, RoundingMode.HALF_UP));
            row.setActualHours(actualHours.setScale(2, RoundingMode.HALF_UP));
            row.setStandardHours(standardHours.setScale(2, RoundingMode.HALF_UP));
            BigDecimal diff = actualHours.subtract(standardHours).setScale(2, RoundingMode.HALF_UP);
            row.setWorkHoursDiff(diff);
            row.setWorkHoursDiffRate(standardHours.signum() > 0
                    ? diff.multiply(HUNDRED).divide(standardHours, 2, RoundingMode.HALF_UP) : null);
            row.setWageAmount(wageAmount.setScale(2, RoundingMode.HALF_UP));
            return row;
        }
 
        private static BigDecimal nvl(BigDecimal value) {
            return value != null ? value : BigDecimal.ZERO;
        }
 
    }
 
}