tuqin
2026-10-28 b142c7f4774c550a1e417557fbf206435916bc93
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
package cn.iocoder.yudao.module.mes.service.em.record;
 
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.em.record.vo.MesEmLastReadingRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.em.record.vo.MesEmRecordPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.em.record.vo.MesEmRecordRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.em.record.vo.MesEmRecordSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.em.meter.MesEmMeterDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.em.record.MesEmRecordDO;
import cn.iocoder.yudao.module.mes.dal.mysql.em.record.MesEmRecordMapper;
import cn.iocoder.yudao.module.mes.enums.em.MesEmEnergyTypeEnum;
import cn.iocoder.yudao.module.mes.enums.em.MesEmRecordSourceEnum;
import cn.iocoder.yudao.module.mes.service.em.alert.MesEmAlertService;
import cn.iocoder.yudao.module.mes.service.em.meter.MesEmMeterService;
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.time.LocalDate;
import java.util.Collections;
import java.util.List;
 
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
/**
 * MES 能耗抄表记录 Service 实现类
 *
 * @author 超级管理员
 */
@Service
@Validated
public class MesEmRecordServiceImpl implements MesEmRecordService {
 
    @Resource
    private MesEmRecordMapper recordMapper;
    @Resource
    private MesEmMeterService meterService;
    @Resource
    private MesEmAlertService alertService;
 
    @Override
    public Long createRecord(MesEmRecordSaveReqVO createReqVO) {
        MesEmMeterDO meter = meterService.getMeter(createReqVO.getMeterId());
        if (meter == null) {
            throw exception(EM_METER_NOT_EXISTS);
        }
        validateRecordDateUnique(null, createReqVO.getMeterId(), createReqVO.getRecordDate());
 
        MesEmRecordDO record = buildRecord(createReqVO, meter, null);
        recordMapper.insert(record);
        // 写入后按定额、环比、同比、跑冒滴漏规则检测预警
        alertService.checkRecord(record);
        return record.getId();
    }
 
    @Override
    public void updateRecord(MesEmRecordSaveReqVO updateReqVO) {
        validateRecordExists(updateReqVO.getId());
        MesEmMeterDO meter = meterService.getMeter(updateReqVO.getMeterId());
        if (meter == null) {
            throw exception(EM_METER_NOT_EXISTS);
        }
        validateRecordDateUnique(updateReqVO.getId(), updateReqVO.getMeterId(), updateReqVO.getRecordDate());
 
        MesEmRecordDO record = buildRecord(updateReqVO, meter, updateReqVO.getId());
        recordMapper.updateById(record);
        alertService.checkRecord(record);
    }
 
    @Override
    public void deleteRecord(Long id) {
        MesEmRecordDO record = validateRecordExists(id);
        recordMapper.deleteById(id);
        // 同步清理该记录产生的环比/同比/跑冒滴漏预警
        alertService.deleteMeterAlerts(record.getMeterId(), record.getRecordDate());
    }
 
    @Override
    public MesEmRecordDO getRecord(Long id) {
        return recordMapper.selectById(id);
    }
 
    private MesEmRecordDO validateRecordExists(Long id) {
        MesEmRecordDO record = recordMapper.selectById(id);
        if (record == null) {
            throw exception(EM_RECORD_NOT_EXISTS);
        }
        return record;
    }
 
    private void validateRecordDateUnique(Long id, Long meterId, LocalDate recordDate) {
        MesEmRecordDO record = recordMapper.selectByMeterIdAndDate(meterId, recordDate);
        if (record != null && ObjUtil.notEqual(record.getId(), id)) {
            throw exception(EM_RECORD_DATE_DUPLICATE);
        }
    }
 
    /**
     * 构建抄表记录:自动计算用量与费用,并冗余计量点/车间/产线/设备快照
     */
    private MesEmRecordDO buildRecord(MesEmRecordSaveReqVO reqVO, MesEmMeterDO meter, Long id) {
        // 期初读数:未填写时取上一条记录读数,没有历史记录则取计量点初始读数
        BigDecimal prevReading = reqVO.getPrevReading();
        if (prevReading == null) {
            MesEmRecordDO lastRecord = recordMapper.selectLastByMeterId(
                    reqVO.getMeterId(), reqVO.getRecordDate(), id);
            prevReading = lastRecord != null ? lastRecord.getReading() : meter.getInitialReading();
        }
        if (prevReading == null) {
            prevReading = BigDecimal.ZERO;
        }
        if (reqVO.getReading().compareTo(prevReading) < 0) {
            throw exception(EM_RECORD_READING_INVALID);
        }
        BigDecimal ratio = meter.getRatio() == null ? BigDecimal.ONE : meter.getRatio();
        BigDecimal usage = reqVO.getReading().subtract(prevReading).multiply(ratio);
        BigDecimal price = reqVO.getPrice() == null ? BigDecimal.ZERO : reqVO.getPrice();
        BigDecimal cost = usage.multiply(price).setScale(2, RoundingMode.HALF_UP);
 
        MesEmRecordDO record = BeanUtils.toBean(reqVO, MesEmRecordDO.class);
        record.setId(id);
        record.setMeterCode(meter.getCode());
        record.setMeterName(meter.getName());
        record.setEnergyType(meter.getEnergyType());
        record.setUnit(meter.getUnit());
        record.setRatio(ratio);
        record.setCoalFactor(meter.getCoalFactor());
        record.setWorkshopId(meter.getWorkshopId());
        record.setWorkshopName(meter.getWorkshopName());
        record.setLineId(meter.getLineId());
        record.setLineName(meter.getLineName());
        record.setMachineryId(meter.getMachineryId());
        record.setMachineryName(meter.getMachineryName());
        record.setPrevReading(prevReading);
        record.setUsageAmount(usage);
        record.setPrice(price);
        record.setCost(cost);
        record.setSource(reqVO.getSource() == null ? MesEmRecordSourceEnum.MANUAL.getSource() : reqVO.getSource());
        return record;
    }
 
    @Override
    public PageResult<MesEmRecordDO> getRecordPage(MesEmRecordPageReqVO pageReqVO) {
        return recordMapper.selectPage(pageReqVO);
    }
 
    @Override
    public List<MesEmRecordRespVO> getRecordRespVOList(List<MesEmRecordDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        return BeanUtils.toBean(list, MesEmRecordRespVO.class, vo -> {
            vo.setEnergyTypeName(MesEmEnergyTypeEnum.getName(vo.getEnergyType()));
            vo.setSourceName(MesEmRecordSourceEnum.getName(vo.getSource()));
        });
    }
 
    @Override
    public MesEmLastReadingRespVO getLastReading(Long meterId, LocalDate recordDate, Long excludeId) {
        MesEmRecordDO lastRecord = recordMapper.selectLastByMeterId(meterId, recordDate, excludeId);
        if (lastRecord != null) {
            return new MesEmLastReadingRespVO(lastRecord.getReading(), lastRecord.getRecordDate());
        }
        MesEmMeterDO meter = meterService.getMeter(meterId);
        if (meter == null) {
            throw exception(EM_METER_NOT_EXISTS);
        }
        return new MesEmLastReadingRespVO(
                meter.getInitialReading() == null ? BigDecimal.ZERO : meter.getInitialReading(), null);
    }
 
}