package cn.iocoder.yudao.module.mes.service.energy;
|
|
import cn.iocoder.yudao.module.mes.controller.admin.energy.vo.*;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.energy.EnergyRecordDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.energy.EnergyRecordMapper;
|
import jakarta.validation.Validation;
|
import org.apache.ibatis.session.Configuration;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.mockito.InjectMocks;
|
import org.mockito.Mock;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
import java.math.BigDecimal;
|
import java.time.LocalDate;
|
import java.util.List;
|
import java.util.Map;
|
import static org.junit.jupiter.api.Assertions.*;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.Mockito.*;
|
|
@ExtendWith(MockitoExtension.class)
|
class EnergyRecordServiceTest {
|
@Mock
|
private EnergyRecordMapper mapper;
|
@InjectMocks
|
private EnergyRecordService service;
|
|
@Test
|
void calculatesMoneyWithDecimalRoundingAndClearsRemark() {
|
EnergyRecordSaveReqVO req = request();
|
req.setQuantity(new BigDecimal("12.345"));
|
req.setUnitPrice(new BigDecimal("0.6789"));
|
EnergyRecordDO record = EnergyRecordService.toRecord(req);
|
assertEquals(new BigDecimal("8.38"), record.getAmount());
|
assertEquals("燕窝生产区", record.getUsageArea());
|
assertEquals("", record.getRemark());
|
req.setUnitPrice(BigDecimal.ZERO);
|
assertEquals(new BigDecimal("0.00"), EnergyRecordService.toRecord(req).getAmount());
|
}
|
|
@Test
|
void createIgnoresClientIdAndUpdateRequiresExistingRecord() {
|
EnergyRecordSaveReqVO req = request();
|
req.setId(77L);
|
doAnswer(invocation -> {
|
EnergyRecordDO record = invocation.getArgument(0);
|
assertNull(record.getId());
|
record.setId(9L);
|
return 1;
|
}).when(mapper).insert(any(EnergyRecordDO.class));
|
assertEquals(9L, service.create(req));
|
assertThrows(RuntimeException.class, () -> service.update(req));
|
verify(mapper, never()).updateById(any(EnergyRecordDO.class));
|
req.setId(null);
|
assertThrows(RuntimeException.class, () -> service.update(req));
|
}
|
|
@Test
|
void deleteMissingRecordDoesNotExecuteDelete() {
|
assertThrows(RuntimeException.class, () -> service.delete(8L));
|
verify(mapper, never()).deleteById(any(Long.class));
|
}
|
|
@Test
|
void aggregatesSeparateUnitsAndMergesDatesAreasAndCosts() {
|
EnergyAnalysisRespVO result = EnergyRecordService.aggregate(List.of(
|
row("WATER", "2026-09-01", "燕窝生产区", "10", "30", 1),
|
row("WATER", "2026-09-01", "原浆生产区", "5", "15", 2),
|
row("ELECTRICITY", "2026-09-01", "燕窝生产区", "100", "80", 1),
|
row("WATER", "2026-09-02", "燕窝生产区", "2", "0", 1)));
|
assertEquals(new BigDecimal("125"), result.getTotalAmount());
|
assertEquals(5, result.getRecordCount());
|
assertEquals(new BigDecimal("17"), result.getSummary().get(0).getQuantity());
|
assertEquals(new BigDecimal("100"), result.getSummary().get(1).getQuantity());
|
assertEquals(BigDecimal.ZERO, result.getSummary().get(2).getQuantity());
|
assertEquals(3, result.getTrend().size());
|
assertEquals(new BigDecimal("15"), result.getTrend().get(0).getQuantity());
|
assertEquals(3, result.getAreas().size());
|
assertEquals(new BigDecimal("12"), result.getAreas().get(0).getQuantity());
|
}
|
|
@Test
|
void emptyAnalysisReturnsZeroTotalsAndAllThreeEnergies() {
|
EnergyAnalysisRespVO result = EnergyRecordService.aggregate(List.of());
|
assertEquals(3, result.getSummary().size());
|
assertEquals(BigDecimal.ZERO, result.getTotalAmount());
|
assertTrue(result.getTrend().isEmpty());
|
assertTrue(result.getAreas().isEmpty());
|
}
|
|
@Test
|
void rejectsInvalidTypeNegativePriceZeroQuantityFutureDateAndExcessPrecision() {
|
try (var factory = Validation.buildDefaultValidatorFactory()) {
|
var validator = factory.getValidator();
|
assertTrue(validator.validate(request()).isEmpty());
|
EnergyRecordSaveReqVO req = request();
|
req.setEnergyType("GAS");
|
req.setUnitPrice(new BigDecimal("-1"));
|
req.setQuantity(BigDecimal.ZERO);
|
req.setRecordDate(LocalDate.now().plusDays(1));
|
assertTrue(validator.validate(req).size() >= 4);
|
req = request();
|
req.setQuantity(new BigDecimal("1.0001"));
|
assertFalse(validator.validate(req).isEmpty());
|
EnergyAnalysisReqVO analysis = new EnergyAnalysisReqVO();
|
analysis.setStartDate(LocalDate.of(2026, 1, 1));
|
analysis.setEndDate(LocalDate.of(2025, 1, 1));
|
assertFalse(validator.validate(analysis).isEmpty());
|
analysis.setEndDate(LocalDate.of(2029, 1, 1));
|
assertFalse(validator.validate(analysis).isEmpty());
|
}
|
}
|
|
@Test
|
void analysisSqlBindsFiltersAndSelectsDayOrMonthSafely() {
|
Configuration config = new Configuration();
|
config.addMapper(EnergyRecordMapper.class);
|
var statement = config.getMappedStatement(EnergyRecordMapper.class.getName() + ".selectAnalysis");
|
EnergyAnalysisReqVO req = new EnergyAnalysisReqVO();
|
req.setStartDate(LocalDate.of(2026, 1, 1));
|
req.setEndDate(LocalDate.of(2026, 1, 31));
|
String daily = statement.getBoundSql(Map.of("req", req)).getSql();
|
assertTrue(daily.contains("'%Y-%m-%d'"));
|
assertTrue(daily.contains("deleted = 0"));
|
req.setGroupBy("MONTH");
|
req.setEnergyType("STEAM");
|
req.setUsageArea("' OR 1=1 --");
|
req.setProductCategory("燕窝");
|
String monthly = statement.getBoundSql(Map.of("req", req)).getSql();
|
assertTrue(monthly.contains("'%Y-%m'"));
|
assertTrue(monthly.contains("energy_type = ?"));
|
assertTrue(monthly.contains("product_category = ?"));
|
assertFalse(monthly.contains(req.getUsageArea()));
|
}
|
|
private static EnergyRecordSaveReqVO request() {
|
EnergyRecordSaveReqVO req = new EnergyRecordSaveReqVO();
|
req.setRecordDate(LocalDate.now());
|
req.setEnergyType("WATER");
|
req.setUsageArea(" 燕窝生产区 ");
|
req.setProductCategory("燕窝");
|
req.setUsagePurpose("设备清洗");
|
req.setQuantity(BigDecimal.ONE);
|
req.setUnitPrice(BigDecimal.ONE);
|
req.setRecorder("张三");
|
return req;
|
}
|
|
private static EnergyRecordMapper.Aggregate row(String type, String period, String area,
|
String quantity, String amount, long count) {
|
EnergyRecordMapper.Aggregate row = new EnergyRecordMapper.Aggregate();
|
row.setEnergyType(type);
|
row.setPeriod(period);
|
row.setUsageArea(area);
|
row.setQuantity(new BigDecimal(quantity));
|
row.setAmount(new BigDecimal(amount));
|
row.setRecordCount(count);
|
return row;
|
}
|
}
|