package cn.iocoder.yudao.module.mes.service.pro.feedback;
|
|
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryDO;
|
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.task.MesProTaskDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.dv.machinery.MesDvMachineryMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskMapper;
|
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
|
import cn.iocoder.yudao.module.mes.service.pro.task.schedule.MesProScheduleGuardService;
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
import java.math.BigDecimal;
|
import java.time.*;
|
import java.util.*;
|
|
import static org.junit.jupiter.api.Assertions.*;
|
import static org.mockito.ArgumentMatchers.*;
|
import static org.mockito.Mockito.*;
|
|
class MesProFeedbackEquipmentServiceTest {
|
private final MesProScheduleGuardService guard = mock(MesProScheduleGuardService.class);
|
private final MesProTaskMapper tasks = mock(MesProTaskMapper.class);
|
private final MesProFeedbackMapper feedbacks = mock(MesProFeedbackMapper.class);
|
private final MesDvMachineryMapper machines = mock(MesDvMachineryMapper.class);
|
private final MesMdWorkstationService stations = mock(MesMdWorkstationService.class);
|
private MesProFeedbackEquipmentService service;
|
private MesProTaskDO task;
|
private MesDvMachineryDO machine;
|
private final LocalDateTime now = LocalDateTime.of(2026,10,15,14,0);
|
|
@BeforeEach void setup() {
|
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), "test"), MesDvMachineryDO.class);
|
service = new MesProFeedbackEquipmentService();
|
ReflectionTestUtils.setField(service,"guard",guard);
|
ReflectionTestUtils.setField(service,"taskMapper",tasks);
|
ReflectionTestUtils.setField(service,"feedbackMapper",feedbacks);
|
ReflectionTestUtils.setField(service,"machineryMapper",machines);
|
ReflectionTestUtils.setField(service,"workstationService",stations);
|
ReflectionTestUtils.setField(service,"clock",Clock.fixed(now.atZone(ZoneId.of("Asia/Shanghai")).toInstant(),ZoneId.of("Asia/Shanghai")));
|
task=new MesProTaskDO().setId(1L).setWorkOrderId(2L).setWorkstationId(3L).setProcessId(4L).setRouteId(5L)
|
.setQuantity(BigDecimal.TEN).setProducedQuantity(BigDecimal.ZERO).setStatus(0);
|
machine=new MesDvMachineryDO().setId(10L).setName("冷热罐").setStatus(1);
|
when(tasks.selectById(1L)).thenReturn(task);
|
when(tasks.selectSchedulingTasks()).thenReturn(List.of(task));
|
when(tasks.updateById(any(MesProTaskDO.class))).thenAnswer(call->{MesProTaskDO update=call.getArgument(0);if(update.getStatus()!=null)task.setStatus(update.getStatus());return 1;});
|
when(guard.machineIdsByStation()).thenReturn(Map.of(3L,Set.of(10L),6L,Set.of(10L)));
|
when(stations.getWorkstation(3L)).thenReturn(new MesMdWorkstationDO().setId(3L).setProcessId(4L).setStatus(0));
|
when(machines.selectByIds(anyCollection())).thenReturn(List.of(machine));
|
when(machines.update(isNull(),any(Wrapper.class))).thenReturn(1);
|
when(feedbacks.selectListByTaskId(anyLong())).thenReturn(List.of());
|
when(feedbacks.selectListByTaskIds(anyCollection())).thenReturn(List.of());
|
}
|
private MesProFeedbackDO report() {
|
return new MesProFeedbackDO().setId(7L).setTaskId(1L).setWorkOrderId(2L).setWorkstationId(3L)
|
.setProcessId(4L).setRouteId(5L).setFeedbackTime(now).setStatus(2);
|
}
|
private MesProTaskDO other() {
|
return new MesProTaskDO().setId(8L).setWorkstationId(6L).setStatus(2)
|
.setQuantity(BigDecimal.TEN).setProducedQuantity(BigDecimal.ONE);
|
}
|
@Test void submitCapturesStoppedEquipmentWithoutStartingIt() {
|
var f=report();service.validateSubmission(f);
|
assertEquals("10",f.getMachineryIds());
|
verify(machines,never()).update(isNull(),any(Wrapper.class));
|
}
|
@Test void maintenanceBlocksCurrentSubmission() {
|
machine.setStatus(3);
|
assertThrows(ServiceException.class,()->service.validateSubmission(report()));
|
}
|
@Test void pastDayReportNeitherUsesCurrentMaintenanceNorChangesEquipment() {
|
var f=report().setFeedbackTime(now.minusDays(1));machine.setStatus(3);
|
assertDoesNotThrow(()->service.validateSubmission(f));service.afterApproved(f);
|
assertNull(f.getMachineryIds());verifyNoInteractions(machines);
|
}
|
@Test void futureTimeAndPausedTaskAreRejected() {
|
assertThrows(ServiceException.class,()->service.validateSubmission(report().setFeedbackTime(now.plusHours(1))));
|
task.setStatus(9);assertThrows(ServiceException.class,()->service.validateSubmission(report()));
|
}
|
@Test void missingStationEquipmentAndChangedTaskAreRejected() {
|
when(guard.machineIdsByStation()).thenReturn(Map.of());
|
assertThrows(ServiceException.class,()->service.validateSubmission(report()));
|
assertThrows(ServiceException.class,()->service.validateSubmission(report().setProcessId(999L)));
|
}
|
@Test void delayedApprovalDoesNotInvalidateAlreadySubmittedProduction() {
|
machine.setStatus(3);
|
assertDoesNotThrow(()->service.validateApproval(report().setMachineryIds("10")));
|
service.afterApproved(report().setMachineryIds("10"));
|
verify(machines,never()).update(isNull(),any(Wrapper.class));
|
}
|
@Test void partialApprovedProductionStartsEquipment() {
|
task.setProducedQuantity(BigDecimal.ONE);
|
service.afterApproved(report().setMachineryIds("10"));
|
assertEquals(2,task.getStatus());verify(machines).update(isNull(),any(Wrapper.class));
|
}
|
@Test void finishedQuantityStopsEquipment() {
|
task.setProducedQuantity(BigDecimal.TEN);machine.setStatus(2);
|
service.afterApproved(report().setMachineryIds("10"));
|
assertEquals(4, task.getStatus());
|
verify(machines).update(isNull(),any(Wrapper.class));
|
}
|
@Test void historicalCompletedTaskClosesWithoutChangingLiveMachineStatus() {
|
task.setProducedQuantity(BigDecimal.TEN);machine.setStatus(2);
|
service.afterApproved(report().setFeedbackTime(now.minusDays(1)));
|
assertEquals(4,task.getStatus());verifyNoInteractions(machines);
|
}
|
@Test void resumeChecksMaintenanceAndSharedOccupancy() {
|
task.setStatus(2).setProducedQuantity(BigDecimal.ONE);machine.setStatus(3);
|
assertThrows(ServiceException.class,()->service.validateResume(task));
|
machine.setStatus(1);
|
when(tasks.selectSchedulingTasks()).thenReturn(List.of(task,other()));
|
assertThrows(ServiceException.class,()->service.validateResume(task));
|
}
|
@Test void anotherActiveTaskKeepsSharedEquipmentRunning() {
|
task.setProducedQuantity(BigDecimal.TEN);machine.setStatus(2);
|
when(tasks.selectSchedulingTasks()).thenReturn(List.of(task,other()));
|
service.afterApproved(report().setMachineryIds("10"));
|
verify(machines,never()).update(isNull(),any(Wrapper.class));
|
}
|
@Test void otherRunningTaskWithZeroReportedQuantityStillBlocksSubmission() {
|
when(tasks.selectSchedulingTasks()).thenReturn(List.of(task,other().setStatus(1).setProducedQuantity(BigDecimal.ZERO)));
|
assertThrows(ServiceException.class,()->service.validateSubmission(report()));
|
}
|
@Test void submittedReportReservesEquipmentWithoutStartingIt() {
|
var pendingTask=other().setStatus(0).setProducedQuantity(BigDecimal.ZERO);
|
when(tasks.selectSchedulingTasks()).thenReturn(List.of(task,pendingTask));
|
when(feedbacks.selectListByTaskIds(anyCollection())).thenReturn(List.of(
|
report().setId(9L).setTaskId(8L).setMachineryIds("10").setStatus(2)));
|
assertThrows(ServiceException.class,()->service.validateSubmission(report()));
|
task.setProducedQuantity(BigDecimal.TEN);machine.setStatus(2);
|
service.afterApproved(report().setMachineryIds("10"));
|
verify(machines).update(isNull(),any(Wrapper.class));
|
}
|
@Test void pausedTaskReleasesButFutureDraftDoesNotOccupyEquipment() {
|
task.setStatus(9).setProducedQuantity(BigDecimal.ONE).setFirstFeedbackTime(now);machine.setStatus(2);
|
when(tasks.selectSchedulingTasks()).thenReturn(List.of(task,other().setStatus(0).setProducedQuantity(BigDecimal.ZERO)));
|
service.afterTaskStateChanged(List.of(task));verify(machines).update(isNull(),any(Wrapper.class));
|
}
|
@Test void cancelingUnstartedDraftDoesNotStopUntrackedEquipment() {
|
machine.setStatus(2);service.afterTaskStateChanged(List.of(task));
|
verify(machines,never()).selectByIds(anyCollection());
|
}
|
@Test void existingSnapshotSurvivesWorkstationRebinding() {
|
when(guard.machineIdsByStation()).thenReturn(Map.of(3L,Set.of(99L)));
|
when(feedbacks.selectListByTaskId(1L)).thenReturn(List.of(report().setMachineryIds("10").setStatus(4)));
|
var f=report();service.validateSubmission(f);assertEquals("10",f.getMachineryIds());
|
}
|
@Test void newTaskCanUseAvailableSubsetButCannotSwapExistingMaintenanceSnapshot() {
|
var maintenance=new MesDvMachineryDO().setId(11L).setStatus(3).setName("维护设备");
|
when(guard.machineIdsByStation()).thenReturn(Map.of(3L,Set.of(10L,11L)));
|
when(machines.selectByIds(anyCollection())).thenAnswer(call->{Collection<Long> ids=call.getArgument(0);return List.of(machine,maintenance).stream().filter(m->ids.contains(m.getId())).toList();});
|
var f=report();service.validateSubmission(f);assertEquals("10",f.getMachineryIds());
|
when(feedbacks.selectListByTaskId(1L)).thenReturn(List.of(report().setMachineryIds("11").setStatus(4)));
|
assertThrows(ServiceException.class,()->service.validateSubmission(report()));
|
}
|
@Test void maintenanceIsNeverOverwrittenWhenTaskStops() {
|
task.setStatus(9).setProducedQuantity(BigDecimal.ONE).setFirstFeedbackTime(now);machine.setStatus(3);
|
service.afterTaskStateChanged(List.of(task));verify(machines,never()).update(isNull(),any(Wrapper.class));
|
}
|
@Test void stoppingReboundStationTouchesSnapshotMachinesOnly() {
|
task.setStatus(9).setProducedQuantity(BigDecimal.ONE).setFirstFeedbackTime(now);
|
when(guard.machineIdsByStation()).thenReturn(Map.of(3L,Set.of(99L)));
|
when(feedbacks.selectListByTaskId(1L)).thenReturn(List.of(report().setMachineryIds("10").setStatus(4)));
|
service.afterTaskStateChanged(List.of(task));
|
verify(machines).selectByIds(eq(Set.of(10L)));
|
}
|
@Test void concurrentMaintenanceRollsBackStartingProduction() {
|
task.setProducedQuantity(BigDecimal.ONE);
|
when(machines.update(isNull(),any(Wrapper.class))).thenReturn(0);
|
assertThrows(ServiceException.class,()->service.afterApproved(report().setMachineryIds("10")));
|
}
|
@Test void historicalAutoFinishDoesNotTouchLiveMachinesAndScopeIsReleased() {
|
task.setStatus(2).setProducedQuantity(BigDecimal.ONE);
|
try (var scope=service.beginApproval(report().setFeedbackTime(now.minusDays(1)))) {
|
service.afterTaskStateChanged(List.of(task));
|
verifyNoInteractions(machines);
|
}
|
service.afterTaskStateChanged(List.of(task));
|
verify(machines).selectByIds(anyCollection());
|
}
|
}
|