工序设备表数据处理

parent fb073845
...@@ -82,7 +82,7 @@ public class ScheduleController { ...@@ -82,7 +82,7 @@ public class ScheduleController {
} }
// 提取工序ID用于后续查询 // 提取工序ID用于后续查询
Set<Integer> operationIds = operations.stream() Set<Long> operationIds = operations.stream()
.map(RoutingDetail::getId) .map(RoutingDetail::getId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
...@@ -96,7 +96,7 @@ public class ScheduleController { ...@@ -96,7 +96,7 @@ public class ScheduleController {
} }
// 提取设备ID用于后续查询 // 提取设备ID用于后续查询
Set<Integer> equipmentIds = operationEquipments.stream() Set<Long> equipmentIds = operationEquipments.stream()
.map(RoutingDetailEquip::getEquipId) .map(RoutingDetailEquip::getEquipId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
......
...@@ -90,7 +90,7 @@ public class ScheduleResultController { ...@@ -90,7 +90,7 @@ public class ScheduleResultController {
} }
// 提取工序ID用于后续查询 // 提取工序ID用于后续查询
Set<Integer> operationIds = operations.stream() Set<Long> operationIds = operations.stream()
.map(RoutingDetail::getId) .map(RoutingDetail::getId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
...@@ -104,7 +104,7 @@ public class ScheduleResultController { ...@@ -104,7 +104,7 @@ public class ScheduleResultController {
} }
// 提取设备ID用于后续查询 // 提取设备ID用于后续查询
Set<Integer> equipmentIds = operationEquipments.stream() Set<Long> equipmentIds = operationEquipments.stream()
.map(RoutingDetailEquip::getEquipId) .map(RoutingDetailEquip::getEquipId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
......
...@@ -105,7 +105,7 @@ public class ScheduleResultController1 { ...@@ -105,7 +105,7 @@ public class ScheduleResultController1 {
} }
// 提取工序ID用于后续查询 // 提取工序ID用于后续查询
Set<Integer> operationIds = operations.stream() Set<Long> operationIds = operations.stream()
.map(RoutingDetail::getId) .map(RoutingDetail::getId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
...@@ -119,7 +119,7 @@ public class ScheduleResultController1 { ...@@ -119,7 +119,7 @@ public class ScheduleResultController1 {
} }
// 提取设备ID用于后续查询 // 提取设备ID用于后续查询
Set<Integer> equipmentIds = operationEquipments.stream() Set<Long> equipmentIds = operationEquipments.stream()
.map(RoutingDetailEquip::getEquipId) .map(RoutingDetailEquip::getEquipId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
......
...@@ -30,10 +30,10 @@ private String mmid; ...@@ -30,10 +30,10 @@ private String mmid;
private String mmcode; private String mmcode;
private String mmname; private String mmname;
private String unit; private String unit;
private BigDecimal quantity; private double quantity;
private BigDecimal meetquantity; private double meetquantity;
private BigDecimal meetrate; private double meetrate;
private BigDecimal price; private double price;
private LocalDateTime deliverytime; private LocalDateTime deliverytime;
private Long stockid; private Long stockid;
private String stock; private String stock;
......
package com.aps.entity;
import lombok.Data;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
@Data
@TableName("prod_equipment")
public class ProdEquipment implements Serializable {
private String sceneId;
private Long equipId;
private String equipCode;
private String equipName;
private Long resourceId;
private String execId;
private Double speed;
}
\ No newline at end of file
...@@ -26,7 +26,7 @@ public class ProdLaunchOrder { ...@@ -26,7 +26,7 @@ public class ProdLaunchOrder {
private Integer orderPriority; private Integer orderPriority;
private String groupCode; private String groupCode;
private String colorCode; private String colorCode;
private BigDecimal quantity; private double quantity;
private String orderStatus;//S未启动E执行中C已完成 private String orderStatus;//S未启动E执行中C已完成
private String materialId; private String materialId;
private String routingCode; private String routingCode;
......
...@@ -9,4 +9,6 @@ private String orderId; ...@@ -9,4 +9,6 @@ private String orderId;
private String taskSeq; private String taskSeq;
private String targetOrderId; private String targetOrderId;
private String targetTaskSeq; private String targetTaskSeq;
private String execId;
private String targetExecId;
} }
\ No newline at end of file
package com.aps.entity; package com.aps.entity;
import lombok.Data; import lombok.Data;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Data @Data
@TableName("prod_process_exec")
public class ProdProcessExec { public class ProdProcessExec {
private String execId;
private String sceneId; private String execId;
private String orderId; private String sceneId;
private String routingDetailId; private String orderId;
private String taskSeq; private Long routingDetailId;
private String routingDetailName; private Long taskSeq;
private Long machineId; private String routingDetailName;
private String resourceGroup; private Long machineId;
private BigDecimal planQty; private String resourceGroup;
private BigDecimal actualQty; private Double planQty;
private Long planDuration; private Double actualQty;
private Long usedDuration; private Long planDuration;
private Long remainDuration; private Long usedDuration;
private String processStatus; private Long remainDuration;
private Long standardCapacity; private String processStatus;
private Long minCapacity; private Long standardCapacity;
private Long maxCapacity; private Long minCapacity;
private Long prepDuration; private Long maxCapacity;
private Long changeDuration; private Long prepDuration;
private Short logicalOrder; private Long changeDuration;
private LocalDateTime targetStartDate; private Long logicalOrder;
private LocalDateTime targetEndDate; private LocalDateTime targetStartDate;
private LocalDateTime targetEndDate;
/** /**
* 工序准备工时(小时) * 工序准备工时(小时)
...@@ -66,5 +70,5 @@ private LocalDateTime targetEndDate; ...@@ -66,5 +70,5 @@ private LocalDateTime targetEndDate;
private Long postprocessingTime; private Long postprocessingTime;
//速度 //速度
private BigDecimal speed; private Double speed;
} }
\ No newline at end of file
...@@ -8,7 +8,7 @@ import lombok.Data; ...@@ -8,7 +8,7 @@ import lombok.Data;
@Data @Data
public class RoutingDetail { public class RoutingDetail {
private Integer id; private Long id;
private LocalDateTime creationTime; private LocalDateTime creationTime;
private BigDecimal creatorUserId; private BigDecimal creatorUserId;
private LocalDateTime lastModificationTime; private LocalDateTime lastModificationTime;
...@@ -19,7 +19,7 @@ private LocalDateTime deletionTime; ...@@ -19,7 +19,7 @@ private LocalDateTime deletionTime;
private Long classId; private Long classId;
private Long routingHeaderId; private Long routingHeaderId;
private String name; private String name;
private Short taskSeq; private Long taskSeq;
private String description; private String description;
private String taskContent; private String taskContent;
private Long resourceId; private Long resourceId;
......
...@@ -8,13 +8,13 @@ import java.time.LocalDateTime; ...@@ -8,13 +8,13 @@ import java.time.LocalDateTime;
public class RoutingDetailConnect { public class RoutingDetailConnect {
private Long id; private Long id;
private LocalDateTime creationtime; private LocalDateTime creationtime;
private Long creatoruserid; private Long creatoruserid;
private LocalDateTime lastmodificationtime; private LocalDateTime lastmodificationtime;
private Long lastmodifieruserid; private Integer lastmodifieruserid;
private Long isdeleted; private Integer isdeleted;
private LocalDateTime deletiontime; private LocalDateTime deletiontime;
private Long deleteruserid; private Long deleteruserid;
private Long destoperationid; private Long destoperationid;
private Long sourceoperationid; private Long sourceoperationid;
private String destoperation; private String destoperation;
private String sourceoperation; private String sourceoperation;
......
...@@ -45,7 +45,7 @@ public class RoutingDetailEquip implements Serializable { ...@@ -45,7 +45,7 @@ public class RoutingDetailEquip implements Serializable {
/** /**
* 更新人 * 更新人
*/ */
private Integer lastmodifieruserid; private Long lastmodifieruserid;
/** /**
* 删除人 * 删除人
...@@ -65,7 +65,7 @@ public class RoutingDetailEquip implements Serializable { ...@@ -65,7 +65,7 @@ public class RoutingDetailEquip implements Serializable {
/** /**
* 设备类型 * 设备类型
*/ */
private Integer type1; private Long type1;
/** /**
* 设备类型名 * 设备类型名
...@@ -111,17 +111,17 @@ public class RoutingDetailEquip implements Serializable { ...@@ -111,17 +111,17 @@ public class RoutingDetailEquip implements Serializable {
/** /**
* 工序id * 工序id
*/ */
private Integer routingDetailId; private Long routingDetailId;
/** /**
* 设备id * 设备id
*/ */
private Integer equipId; private Long equipId;
/** /**
* 工艺id * 工艺id
*/ */
private Integer routingHeaderId; private Long routingHeaderId;
/** /**
* 单次批量产出 * 单次批量产出
......
...@@ -9,8 +9,8 @@ import java.time.LocalDateTime; ...@@ -9,8 +9,8 @@ import java.time.LocalDateTime;
@Data @Data
public class ScheduledTask { public class ScheduledTask {
private String orderId; // 工单ID private String orderId; // 工单ID
private Integer operationId; // 工序ID private Long operationId; // 工序ID
private Integer equipId; // 设备ID private Long equipId; // 设备ID
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime; // 实际开始时间 private LocalDateTime startTime; // 实际开始时间
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
......
package com.aps.mapper;
import com.aps.entity.ProdEquipment;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-11-27
*/
public interface ProdEquipmentMapper extends BaseMapper<ProdEquipment> {
}
package com.aps.service;
import com.aps.entity.ProdEquipment;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-11-27
*/
public interface ProdEquipmentService extends IService<ProdEquipment> {
}
...@@ -18,21 +18,20 @@ import java.math.RoundingMode; ...@@ -18,21 +18,20 @@ import java.math.RoundingMode;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.util.List; import java.util.*;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 设备日历服务实现类 • 设备日历服务实现类
* 负责生成设备日历数据、处理订单数据转换等功能
• 负责生成设备日历数据、处理订单数据转换等功能
*/ */
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor // 使用构造器注入替代@Autowired @RequiredArgsConstructor
public class LanuchServiceImpl implements LanuchService { public class LanuchServiceImpl implements LanuchService {
@Autowired @Autowired
ProdEquipSpecialCalService prodEquipSpecialCalService; ProdEquipSpecialCalService prodEquipSpecialCalService;
@Autowired @Autowired
...@@ -56,13 +55,22 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -56,13 +55,22 @@ public class LanuchServiceImpl implements LanuchService {
RoutingHeaderMapper routingHeaderMapper; RoutingHeaderMapper routingHeaderMapper;
@Autowired @Autowired
ProdSceneConfigService prodSceneConfigService; ProdSceneConfigService prodSceneConfigService;
@Autowired
ProdEquipmentService prodEquipmentService;
@Autowired
RoutingDetailEquipService routingDetailEquipService;
/** /**
* 生成场景数据 ◦ 生成场景数据
* *
* *
* @param sceneName 场景名称 ◦ @param sceneName 场景名称
* @param username 操作用户
* @return 处理结果 ◦ @param username 操作用户
◦ @return 处理结果
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
...@@ -72,9 +80,9 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -72,9 +80,9 @@ public class LanuchServiceImpl implements LanuchService {
if (sceneName == null || sceneName.trim().isEmpty()) { if (sceneName == null || sceneName.trim().isEmpty()) {
return R.failed("场景名称不能为空"); return R.failed("场景名称不能为空");
} }
// 1. 创建场景 // 1. 创建场景
String sceneId = createScene(sceneName); String sceneId = createScene(sceneName);
if (sceneId == null) { if (sceneId == null) {
...@@ -97,9 +105,10 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -97,9 +105,10 @@ public class LanuchServiceImpl implements LanuchService {
// 5. 转换工单于工序执行表 // 5. 转换工单于工序执行表
convertToProcessExecBatch(prodLaunchOrders, sceneId); convertToProcessExecBatch(prodLaunchOrders, sceneId);
// 6. 生成工序关联关系 // 6. 生成工序关联关系
generateProcessRelationsBatch(prodLaunchOrders, sceneId); generateProcessRelationsBatch(prodLaunchOrders, sceneId);
log.info("场景数据生成完成,场景ID:{}", sceneId); log.info("场景数据生成完成,场景ID:{}", sceneId);
return R.ok("场景数据生成成功"); return R.ok("场景数据生成成功");
} catch (Exception e) { } catch (Exception e) {
...@@ -107,15 +116,17 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -107,15 +116,17 @@ public class LanuchServiceImpl implements LanuchService {
return R.failed("生成场景数据失败: " + e.getMessage()); return R.failed("生成场景数据失败: " + e.getMessage());
} }
} }
/** /**
* 处理订单数据:批量转换ProdOrderMain到ProdLaunchOrder ◦ 处理订单数据:批量转换ProdOrderMain到ProdLaunchOrder
* *
* @param sceneId 场景ID ◦ @param sceneId 场景ID
*/ */
private List<ProdLaunchOrder> processOrderData(String sceneId, List<ApsOrder> prodOrderMainList) { private List<ProdLaunchOrder> processOrderData(String sceneId, List<ApsOrder> prodOrderMainList) {
...@@ -126,11 +137,9 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -126,11 +137,9 @@ public class LanuchServiceImpl implements LanuchService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
// 在您的 LanuchServiceImpl 中修改这一行:
// 原来的问题代码:
// prodLaunchOrderService.saveBatch(launchOrders);
// 修改为:
// 批量插入
if (!launchOrderList .isEmpty()) { if (!launchOrderList .isEmpty()) {
for (ProdLaunchOrder order : launchOrderList ) { for (ProdLaunchOrder order : launchOrderList ) {
try { try {
...@@ -148,11 +157,15 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -148,11 +157,15 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 将ProdOrderMain转换为ProdLaunchOrder ◦ 将ProdOrderMain转换为ProdLaunchOrder
* *
* @param order 源订单 ◦ @param order 源订单
* @param sceneId 场景ID
* @return 转换后的ProdLaunchOrder ◦ @param sceneId 场景ID
◦ @return 转换后的ProdLaunchOrder
*/ */
private ProdLaunchOrder convertToLaunchOrder(ApsOrder order, String sceneId) { private ProdLaunchOrder convertToLaunchOrder(ApsOrder order, String sceneId) {
try { try {
...@@ -168,7 +181,7 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -168,7 +181,7 @@ public class LanuchServiceImpl implements LanuchService {
launchOrder.setOrderPriority(1); launchOrder.setOrderPriority(1);
launchOrder.setQuantity(order.getQuantity()); launchOrder.setQuantity(order.getQuantity());
launchOrder.setMaterialId(order.getMmid()); launchOrder.setMaterialId(order.getMmid());
String mmid = order.getMmid(); String mmid = order.getMmid();
// 通过mmid查找对应的工艺 // 通过mmid查找对应的工艺
if (mmid != null && !mmid.isEmpty()) { if (mmid != null && !mmid.isEmpty()) {
...@@ -180,7 +193,7 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -180,7 +193,7 @@ public class LanuchServiceImpl implements LanuchService {
if (!CollectionUtils.isEmpty(routingHeaders)) { if (!CollectionUtils.isEmpty(routingHeaders)) {
routingHeader = routingHeaders.get(0); routingHeader = routingHeaders.get(0);
} }
if (routingHeader != null) { if (routingHeader != null) {
launchOrder.setRoutingId(routingHeader.getId().toString()); launchOrder.setRoutingId(routingHeader.getId().toString());
launchOrder.setRoutingCode(defaultIfBlank(routingHeader.getCode(), "")); launchOrder.setRoutingCode(defaultIfBlank(routingHeader.getCode(), ""));
...@@ -195,10 +208,13 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -195,10 +208,13 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 创建场景 ◦ 创建场景
* *
* @param sceneName 场景名称 ◦ @param sceneName 场景名称
* @return 场景ID,如果场景已存在则返回null
◦ @return 场景ID,如果场景已存在则返回null
*/ */
private String createScene(String sceneName) { private String createScene(String sceneName) {
// 检查场景名称是否已存在 // 检查场景名称是否已存在
...@@ -226,26 +242,34 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -226,26 +242,34 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 生成场景ID ◦ 生成场景ID
* *
* @return 格式化的场景ID ◦ @return 格式化的场景ID
*/ */
private String generateSceneId() { private String generateSceneId() {
return UUID.randomUUID().toString().replace("-", "").toUpperCase(); return UUID.randomUUID().toString().replace("-", "").toUpperCase();
} }
/** /**
* 生成日历数据 ◦ 生成日历数据
* *
* @param sceneId 场景ID ◦ @param sceneId 场景ID
* @param planResource 设备信息
* @param shiftWorkSchedList 班次信息 ◦ @param planResource 设备信息
* @param startTime 开始时间
* @param endTime 结束时间 ◦ @param shiftWorkSchedList 班次信息
◦ @param startTime 开始时间
◦ @param endTime 结束时间
*/ */
private void generateCalendarData(String sceneId, PlanResource planResource, private void generateCalendarData(String sceneId, PlanResource planResource,
List<MesShiftWorkSched> shiftWorkSchedList, List<MesShiftWorkSched> shiftWorkSchedList,
LocalDateTime startTime, LocalDateTime endTime) { LocalDateTime startTime, LocalDateTime endTime) {
LocalDate currentDate = startTime.toLocalDate(); LocalDate currentDate = startTime.toLocalDate();
LocalDate endDate = endTime.toLocalDate(); LocalDate endDate = endTime.toLocalDate();
...@@ -258,7 +282,7 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -258,7 +282,7 @@ public class LanuchServiceImpl implements LanuchService {
for (MesShiftWorkSched shiftWorkSched : shiftWorkSchedList) { for (MesShiftWorkSched shiftWorkSched : shiftWorkSchedList) {
if (shiftWorkSched.getStartWeekDay() == dayOfWeekValue) { if (shiftWorkSched.getStartWeekDay() == dayOfWeekValue) {
processSingleShift(sceneId, planResource, shiftWorkSched, processSingleShift(sceneId, planResource, shiftWorkSched,
currentDate, startTime, endTime, calendarList); currentDate, startTime, endTime, calendarList);
} }
} }
...@@ -273,20 +297,28 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -273,20 +297,28 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 处理单个班次 ◦ 处理单个班次
* *
* @param sceneId 场景ID ◦ @param sceneId 场景ID
* @param planResource 设备信息
* @param shiftWorkSched 班次信息 ◦ @param planResource 设备信息
* @param currentDate 当前日期
* @param startTime 开始时间 ◦ @param shiftWorkSched 班次信息
* @param endTime 结束时间
* @param calendarList 日历列表 ◦ @param currentDate 当前日期
◦ @param startTime 开始时间
◦ @param endTime 结束时间
◦ @param calendarList 日历列表
*/ */
private void processSingleShift(String sceneId, PlanResource planResource, private void processSingleShift(String sceneId, PlanResource planResource,
MesShiftWorkSched shiftWorkSched, LocalDate currentDate, MesShiftWorkSched shiftWorkSched, LocalDate currentDate,
LocalDateTime startTime, LocalDateTime endTime, LocalDateTime startTime, LocalDateTime endTime,
List<ProdEquipSpecialCal> calendarList) { List<ProdEquipSpecialCal> calendarList) {
LocalTime shiftStartTimeRaw = shiftWorkSched.getShiftStart().toLocalTime(); LocalTime shiftStartTimeRaw = shiftWorkSched.getShiftStart().toLocalTime();
LocalTime shiftEndTimeRaw = shiftWorkSched.getShiftEnd().toLocalTime(); LocalTime shiftEndTimeRaw = shiftWorkSched.getShiftEnd().toLocalTime();
...@@ -309,24 +341,31 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -309,24 +341,31 @@ public class LanuchServiceImpl implements LanuchService {
if (actualStartTime.isBefore(actualEndTime)) { if (actualStartTime.isBefore(actualEndTime)) {
ProdEquipSpecialCal equipSpecialCal = createCalendarRecord(sceneId, planResource, ProdEquipSpecialCal equipSpecialCal = createCalendarRecord(sceneId, planResource,
shiftWorkSched, actualStartTime, actualEndTime); shiftWorkSched, actualStartTime, actualEndTime);
calendarList.add(equipSpecialCal); calendarList.add(equipSpecialCal);
} }
} }
/** /**
* 创建日历记录 ◦ 创建日历记录
* *
* @param sceneId 场景ID ◦ @param sceneId 场景ID
* @param planResource 设备信息
* @param shiftWorkSched 班次信息 ◦ @param planResource 设备信息
* @param startTime 开始时间
* @param endTime 结束时间 ◦ @param shiftWorkSched 班次信息
* @return 日历记录
◦ @param startTime 开始时间
◦ @param endTime 结束时间
◦ @return 日历记录
*/ */
private ProdEquipSpecialCal createCalendarRecord(String sceneId, PlanResource planResource, private ProdEquipSpecialCal createCalendarRecord(String sceneId, PlanResource planResource,
MesShiftWorkSched shiftWorkSched, MesShiftWorkSched shiftWorkSched,
LocalDateTime startTime, LocalDateTime endTime) { LocalDateTime startTime, LocalDateTime endTime) {
ProdEquipSpecialCal equipSpecialCal = new ProdEquipSpecialCal(); ProdEquipSpecialCal equipSpecialCal = new ProdEquipSpecialCal();
equipSpecialCal.setSceneId(sceneId); equipSpecialCal.setSceneId(sceneId);
equipSpecialCal.setEquipId(planResource.getReferenceId()); equipSpecialCal.setEquipId(planResource.getReferenceId());
...@@ -340,10 +379,13 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -340,10 +379,13 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 批量转换工单于工序执行表 ◦ 批量转换工单于工序执行表
* *
* @param order 工单列表 ◦ @param order 工单列表
* @param sceneId 场景ID
◦ @param sceneId 场景ID
*/ */
private void convertToProcessExecBatch(List<ProdLaunchOrder> order, String sceneId) { private void convertToProcessExecBatch(List<ProdLaunchOrder> order, String sceneId) {
if (CollectionUtils.isEmpty(order)) { if (CollectionUtils.isEmpty(order)) {
...@@ -365,11 +407,15 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -365,11 +407,15 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 转换工单到工序执行表 ◦ 转换工单到工序执行表
* *
* @param prodOrderMain 工单ID ◦ @param prodOrderMain 工单ID
* @param sceneId 场景ID
* @return 转换结果 ◦ @param sceneId 场景ID
◦ @return 转换结果
*/ */
public boolean convertToProcessExec(ProdLaunchOrder prodOrderMain, String sceneId) { public boolean convertToProcessExec(ProdLaunchOrder prodOrderMain, String sceneId) {
...@@ -381,9 +427,11 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -381,9 +427,11 @@ public class LanuchServiceImpl implements LanuchService {
List<RoutingDetail> routingDetails = getRoutingDetails(routingHeader.getId()); List<RoutingDetail> routingDetails = getRoutingDetails(routingHeader.getId());
List<RoutingDetailEquip> routingDetailEquip = getRoutingDetailEquip(routingHeader.getId());
// 批量插入新的工序工单记录 // 批量插入新的工序工单记录
batchInsertProcessExec(prodOrderMain, routingDetails, sceneId); batchInsertProcessExec(prodOrderMain, routingDetails, sceneId,routingDetailEquip);
...@@ -391,15 +439,18 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -391,15 +439,18 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 获取工艺信息 ◦ 获取工艺信息
* *
* @param routingId 工艺ID ◦ @param routingId 工艺ID
* @return 工艺信息
◦ @return 工艺信息
*/ */
private RoutingHeader getRoutingHeader(String routingId) { private RoutingHeader getRoutingHeader(String routingId) {
LambdaQueryWrapper<RoutingHeader> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<RoutingHeader> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoutingHeader::getId, routingId) wrapper.eq(RoutingHeader::getId, routingId)
.eq(RoutingHeader::getIsDeleted, 0); // 添加 is_deleted=0 过滤条件 .eq(RoutingHeader::getIsDeleted, 0); // 添加 is_deleted=0 过滤条件
RoutingHeader routingHeader = routingHeaderMapper.selectOne(wrapper); RoutingHeader routingHeader = routingHeaderMapper.selectOne(wrapper);
if (routingHeader == null) { if (routingHeader == null) {
...@@ -411,16 +462,19 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -411,16 +462,19 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 获取工艺工序列表 ◦ 获取工艺工序列表
* *
* @param routingHeaderId 工艺头ID ◦ @param routingHeaderId 工艺头ID
* @return 工序列表
◦ @return 工序列表
*/ */
private List<RoutingDetail> getRoutingDetails(Integer routingHeaderId) { private List<RoutingDetail> getRoutingDetails(Integer routingHeaderId) {
LambdaQueryWrapper<RoutingDetail> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<RoutingDetail> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoutingDetail::getRoutingHeaderId, routingHeaderId) wrapper.eq(RoutingDetail::getRoutingHeaderId, routingHeaderId)
.eq(RoutingDetail::getIsDeleted, 0) // 添加 is_deleted=0 过滤条件 .eq(RoutingDetail::getIsDeleted, 0) // 添加 is_deleted=0 过滤条件
.orderByAsc(RoutingDetail::getTaskSeq); .orderByAsc(RoutingDetail::getTaskSeq);
List<RoutingDetail> routingDetails = routingDetailMapper.selectList(wrapper); List<RoutingDetail> routingDetails = routingDetailMapper.selectList(wrapper);
...@@ -434,41 +488,60 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -434,41 +488,60 @@ public class LanuchServiceImpl implements LanuchService {
/** /**
* 批量插入工序执行记录 ◦ 批量插入工序执行记录
* *
* @param prodOrderMain 工单信息 ◦ @param prodOrderMain 工单信息
* @param routingDetails 工序列表
* @param sceneId 场景ID ◦ @param routingDetails 工序列表
◦ @param sceneId 场景ID
*/ */
private void batchInsertProcessExec(ProdLaunchOrder prodOrderMain, private void batchInsertProcessExec(ProdLaunchOrder prodOrderMain,
List<RoutingDetail> routingDetails, List<RoutingDetail> routingDetails,
String sceneId) { String sceneId, List<RoutingDetailEquip> routingDetailEquip) {
List<ProdProcessExec> processExecList = routingDetails.stream() List<ProdProcessExec> processExecList = routingDetails.stream()
.map(detail -> createProcessExec(prodOrderMain, detail, sceneId)) .map(detail -> createProcessExec(prodOrderMain, detail, sceneId))
.collect(Collectors.toList()); .collect(Collectors.toList());
batchInsertEquipMent(routingDetailEquip, sceneId,processExecList);
// 批量插入 // 批量插入
if (!CollectionUtils.isEmpty(processExecList)) { if (!CollectionUtils.isEmpty(processExecList)) {
prodProcessExecService.saveBatch(processExecList); // 假设MyBatis-Plus支持批量插入 prodProcessExecService.saveBatch(processExecList); // 假设MyBatis-Plus支持批量插入
} }
} }
// 添加一个方法来更新execId映射
/*
private void updateExecIdMapping(List<ProdProcessExec> processExecList) {
// 这里可以将processExecList中的数据添加到一个全局映射中,供其他方法使用
// 由于当前设计限制,暂不实现全局映射,但保留此方法以备将来扩展
}
*/
/** /**
* 创建工序执行记录 ◦ 创建工序执行记录
* *
* @param prodOrderMain 工单信息 ◦ @param prodOrderMain 工单信息
* @param detail 工序详情
* @param sceneId 场景ID ◦ @param detail 工序详情
* @return 工序执行记录
◦ @param sceneId 场景ID
◦ @return 工序执行记录
*/ */
private ProdProcessExec createProcessExec(ProdLaunchOrder prodOrderMain, private ProdProcessExec createProcessExec(ProdLaunchOrder prodOrderMain,
RoutingDetail detail, RoutingDetail detail,
String sceneId) { String sceneId) {
ProdProcessExec prodProcessExec = new ProdProcessExec(); ProdProcessExec prodProcessExec = new ProdProcessExec();
prodProcessExec.setExecId(UUID.randomUUID().toString().replace("-", "")); prodProcessExec.setExecId(UUID.randomUUID().toString().replace("-", ""));
prodProcessExec.setOrderId(prodOrderMain.getOrderId()); prodProcessExec.setOrderId(prodOrderMain.getOrderId());
prodProcessExec.setRoutingDetailId(String.valueOf(detail.getId())); prodProcessExec.setRoutingDetailId(detail.getId());
prodProcessExec.setTaskSeq(String.valueOf(detail.getTaskSeq())); prodProcessExec.setTaskSeq(detail.getTaskSeq());
prodProcessExec.setRoutingDetailName(detail.getName()); prodProcessExec.setRoutingDetailName(detail.getName());
prodProcessExec.setMachineId(detail.getEquipTypeId()); prodProcessExec.setMachineId(detail.getEquipTypeId());
// prodProcessExec.setResourceGroup(String.valueOf(detail.getResourceId())); // prodProcessExec.setResourceGroup(String.valueOf(detail.getResourceId()));
...@@ -487,20 +560,24 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -487,20 +560,24 @@ public class LanuchServiceImpl implements LanuchService {
Optional.ofNullable(detail.getSingleOut()) Optional.ofNullable(detail.getSingleOut())
.filter(out -> detail.getRuntime() != null) .filter(out -> detail.getRuntime() != null)
.filter(out -> detail.getRuntime().compareTo(BigDecimal.ZERO) != 0) .filter(out -> detail.getRuntime().compareTo(BigDecimal.ZERO) != 0)
.map(out -> out.divide(detail.getRuntime(), 4, RoundingMode.HALF_UP)) .map(out -> out.divide(detail.getRuntime(), 6, RoundingMode.HALF_UP))
.orElse(BigDecimal.ZERO) .map(BigDecimal::doubleValue) // 关键转换
.orElse(0.0)
); );
System.out.println(prodProcessExec.getSpeed());
return prodProcessExec; return prodProcessExec;
} }
/** /**
* 批量生成工序关联关系 ◦ 批量生成工序关联关系
* *
* @param prodOrderMains 工单ID列表 ◦ @param prodOrderMains 工单ID列表
* @param sceneId 场景ID
◦ @param sceneId 场景ID
*/ */
private void generateProcessRelationsBatch(List<ProdLaunchOrder> prodOrderMains, String sceneId) { private void generateProcessRelationsBatch(List<ProdLaunchOrder> prodOrderMains, String sceneId) {
if (CollectionUtils.isEmpty(prodOrderMains)) { if (CollectionUtils.isEmpty(prodOrderMains)) {
...@@ -508,18 +585,47 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -508,18 +585,47 @@ public class LanuchServiceImpl implements LanuchService {
return; return;
} }
generateProcessRelations(prodOrderMains, sceneId); // 先批量获取所有需要的ProdProcessExec对象
Set<Long> routingDetailIds = new HashSet<>();
for (ProdLaunchOrder order : prodOrderMains) {
List<RoutingDetailConnect> connections = routingDetailConnectService.lambdaQuery()
.eq(RoutingDetailConnect::getRoutingHeaderId, order.getRoutingId())
.eq(RoutingDetailConnect::getIsdeleted, 0)
.list();
for (RoutingDetailConnect connection : connections) {
routingDetailIds.add(connection.getSourceoperationid());
routingDetailIds.add(connection.getDestoperationid());
}
}
List<ProdProcessExec> processExecList = prodProcessExecService.lambdaQuery()
.eq(ProdProcessExec::getSceneId, sceneId)
.in(ProdProcessExec::getRoutingDetailId, new ArrayList<>(routingDetailIds))
.list();
// 构建routingDetailId到execId的映射
Map<Long, String> routingDetailIdToExecIdMap = processExecList.stream()
.collect(Collectors.toMap(
ProdProcessExec::getRoutingDetailId,
ProdProcessExec::getExecId,
(existing, replacement) -> existing)); // 处理重复key的情况
generateProcessRelations(prodOrderMains, sceneId, routingDetailIdToExecIdMap);
log.info("完成{}个工单的工序关系生成", prodOrderMains.size()); log.info("完成{}个工单的工序关系生成", prodOrderMains.size());
} }
/** /**
* 生成工序关联关系 ◦ 生成工序关联关系
* *
* @param prodOrderMains 工单列表 ◦ @param prodOrderMains 工单列表
* @param sceneId 场景ID
◦ @param sceneId 场景ID
*/ */
public void generateProcessRelations(List<ProdLaunchOrder> prodOrderMains, String sceneId) { public void generateProcessRelations(List<ProdLaunchOrder> prodOrderMains, String sceneId, Map<Long, String> routingDetailIdToExecIdMap) {
if (CollectionUtils.isEmpty(prodOrderMains)) { if (CollectionUtils.isEmpty(prodOrderMains)) {
return; return;
} }
...@@ -528,7 +634,7 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -528,7 +634,7 @@ public class LanuchServiceImpl implements LanuchService {
for (ProdLaunchOrder prodOrderMain : prodOrderMains) { for (ProdLaunchOrder prodOrderMain : prodOrderMains) {
try { try {
List<ProdOrderProcess> relations = createProcessRelations(prodOrderMain, sceneId); List<ProdOrderProcess> relations = createProcessRelations(prodOrderMain, sceneId, routingDetailIdToExecIdMap);
processRelations.addAll(relations); processRelations.addAll(relations);
} catch (Exception e) { } catch (Exception e) {
log.error("生成工单[{}]工序关系失败", prodOrderMain.getOrderId(), e); log.error("生成工单[{}]工序关系失败", prodOrderMain.getOrderId(), e);
...@@ -543,35 +649,45 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -543,35 +649,45 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 创建工单工序关系 ◦ 创建工单工序关系
* *
* @param prodOrderMain 工单信息 ◦ @param prodOrderMain 工单信息
* @param sceneId 场景ID
* @return 工序关系列表 ◦ @param sceneId 场景ID
◦ @return 工序关系列表
*/ */
private List<ProdOrderProcess> createProcessRelations(ProdLaunchOrder prodOrderMain, String sceneId) { private List<ProdOrderProcess> createProcessRelations(ProdLaunchOrder prodOrderMain, String sceneId, Map<Long, String> routingDetailIdToExecIdMap) {
LambdaQueryWrapper<RoutingDetailConnect> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<RoutingDetailConnect> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoutingDetailConnect::getRoutingHeaderId, prodOrderMain.getRoutingId()) wrapper.eq(RoutingDetailConnect::getRoutingHeaderId, prodOrderMain.getRoutingId())
.eq(RoutingDetailConnect::getIsdeleted, 0); // 添加 isdeleted=0 过滤条件 .eq(RoutingDetailConnect::getIsdeleted, 0); // 添加 isdeleted=0 过滤条件
List<RoutingDetailConnect> connections = routingDetailConnectService.list(wrapper); List<RoutingDetailConnect> connections = routingDetailConnectService.list(wrapper);
return connections.stream() return connections.stream()
.map(connection -> createProcessRelation(prodOrderMain, connection, sceneId)) .map(connection -> createProcessRelation(prodOrderMain, connection, sceneId, routingDetailIdToExecIdMap))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/** /**
* 创建单个工序关系 ◦ 创建单个工序关系
* *
* @param prodOrderMain 工单信息 ◦ @param prodOrderMain 工单信息
* @param connection 工序连接
* @param sceneId 场景ID ◦ @param connection 工序连接
* @return 工序关系
◦ @param sceneId 场景ID
◦ @return 工序关系
*/ */
private ProdOrderProcess createProcessRelation(ProdLaunchOrder prodOrderMain, private ProdOrderProcess createProcessRelation(ProdLaunchOrder prodOrderMain,
RoutingDetailConnect connection, RoutingDetailConnect connection,
String sceneId) { String sceneId,
Map<Long, String> routingDetailIdToExecIdMap) {
ProdOrderProcess prodOrderProcess = new ProdOrderProcess(); ProdOrderProcess prodOrderProcess = new ProdOrderProcess();
prodOrderProcess.setSceneId(sceneId); prodOrderProcess.setSceneId(sceneId);
prodOrderProcess.setOrderId(prodOrderMain.getOrderId()); prodOrderProcess.setOrderId(prodOrderMain.getOrderId());
...@@ -579,16 +695,32 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -579,16 +695,32 @@ public class LanuchServiceImpl implements LanuchService {
prodOrderProcess.setTargetOrderId(prodOrderMain.getOrderId()); prodOrderProcess.setTargetOrderId(prodOrderMain.getOrderId());
prodOrderProcess.setTargetTaskSeq(connection.getDestoperation()); prodOrderProcess.setTargetTaskSeq(connection.getDestoperation());
String execId = routingDetailIdToExecIdMap.get(connection.getSourceoperationid());
String targetExecId = routingDetailIdToExecIdMap.get(connection.getDestoperationid());
if (execId != null) {
prodOrderProcess.setExecId(execId);
}
if (targetExecId != null) {
prodOrderProcess.setTargetExecId(targetExecId);
}
return prodOrderProcess; return prodOrderProcess;
} }
/** /**
* 如果值为null或空白,返回默认值 ◦ 如果值为null或空白,返回默认值
* *
* @param value 原始值 ◦ @param value 原始值
* @param defaultValue 默认值
* @param <T> 类型 ◦ @param defaultValue 默认值
* @return 处理后的值
◦ @param <T> 类型
◦ @return 处理后的值
*/ */
private <T> T defaultIfBlank(T value, T defaultValue) { private <T> T defaultIfBlank(T value, T defaultValue) {
if (value == null) { if (value == null) {
...@@ -603,14 +735,98 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -603,14 +735,98 @@ public class LanuchServiceImpl implements LanuchService {
} }
/** /**
* 如果值为null,返回默认值 ◦ 设备对照表
*/
private void batchInsertEquipMent(
List<RoutingDetailEquip> routingDetailEquips,
String sceneId,List<ProdProcessExec> processExecList) {
if (CollectionUtils.isEmpty(routingDetailEquips)) {
log.warn("工艺设备列表为空,跳过设备对照表生成");
return;
}
// 创建ProdEquipment列表
List<ProdEquipment> prodEquipments = new java.util.ArrayList<>();
// 遍历RoutingDetailEquip列表,转换为ProdEquipment对象
for (RoutingDetailEquip routingDetailEquip : routingDetailEquips) {
ProdEquipment prodEquipment = new ProdEquipment();
prodEquipment.setSceneId(sceneId);
prodEquipment.setEquipId(routingDetailEquip.getEquipId().longValue());
// prodEquipment.setEquipCode(routingDetailEquip.getName());
prodEquipment.setEquipName(routingDetailEquip.getName());
prodEquipment.setResourceId(routingDetailEquip.getType1());
double duration = routingDetailEquip.getDuration() != null ? routingDetailEquip.getDuration().doubleValue() : 0.0;
double outputQuantity = routingDetailEquip.getOutputQuantity() != null ? routingDetailEquip.getOutputQuantity().doubleValue() : 0.0;
double speed = (outputQuantity > 0) ? duration / outputQuantity : 0.0;
prodEquipment.setSpeed(speed);
// 从映射中获取execId,避免额外的数据库查询
// 修复:使用equals比较,并处理null值
String execId = processExecList.stream()
.filter(exec -> {
Long execRoutingDetailId = exec.getRoutingDetailId();
Long equipRoutingDetailId = routingDetailEquip.getRoutingDetailId();
// 处理null值
if (execRoutingDetailId == null || equipRoutingDetailId == null) {
return false;
}
// 使用equals比较
return execRoutingDetailId.equals(equipRoutingDetailId);
})
.findFirst()
.map(ProdProcessExec::getExecId)
.orElse(null);
if (execId != null) {
prodEquipment.setExecId(execId);
} else {
log.warn("未找到routingDetailId={}对应的execId", routingDetailEquip.getRoutingDetailId());
}
prodEquipments.add(prodEquipment);
}
// 批量保存
if (!prodEquipments.isEmpty()) {
prodEquipmentService.saveBatch(prodEquipments);
}
}
/**
◦ 获取工艺工序列表
* *
* @param value 原始值 ◦ @param routingHeaderId 工艺头ID
* @param defaultValue 默认值
* @param <T> 类型 ◦ @return 工序列表
* @return 处理后的值
*/ */
private <T> T defaultIfNull(T value, T defaultValue) { private List<RoutingDetailEquip> getRoutingDetailEquip(Integer routingHeaderId) {
return value != null ? value : defaultValue;
List<RoutingDetailEquip> detailEquips = routingDetailEquipService.lambdaQuery()
.eq(RoutingDetailEquip::getRoutingHeaderId, routingHeaderId)
.eq(RoutingDetailEquip::getIsdeleted, 0)
.list();// 添加 is_deleted=0 过滤条件
if (CollectionUtils.isEmpty(detailEquips)) {
log.error("工艺下无设备信息: {}", routingHeaderId);
throw new RuntimeException("工艺下无设备信息: " + routingHeaderId);
}
return detailEquips;
} }
} }
\ No newline at end of file
package com.aps.service.impl;
import com.aps.entity.ProdEquipment;
import com.aps.mapper.ProdEquipmentMapper;
import com.aps.service.ProdEquipmentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-11-27
*/
@Service
public class ProdEquipmentServiceImpl extends ServiceImpl<ProdEquipmentMapper, ProdEquipment> implements ProdEquipmentService {
}
...@@ -41,7 +41,7 @@ public class ScheduleServiceImpl implements ScheduleService { ...@@ -41,7 +41,7 @@ public class ScheduleServiceImpl implements ScheduleService {
)); ));
// 2. 构建工序-设备关系映射 // 2. 构建工序-设备关系映射
Map<Integer, List<RoutingDetailEquip>> operationToEquipments = operationEquipments.stream() Map<Long, List<RoutingDetailEquip>> operationToEquipments = operationEquipments.stream()
.collect(Collectors.groupingBy(RoutingDetailEquip::getRoutingDetailId)); .collect(Collectors.groupingBy(RoutingDetailEquip::getRoutingDetailId));
// 3. 按优先级和截止时间排序工单 // 3. 按优先级和截止时间排序工单
...@@ -69,7 +69,7 @@ public class ScheduleServiceImpl implements ScheduleService { ...@@ -69,7 +69,7 @@ public class ScheduleServiceImpl implements ScheduleService {
private void processOrder(Mpsplannedorder order, private void processOrder(Mpsplannedorder order,
List<RoutingHeader> processes, List<RoutingHeader> processes,
List<RoutingDetail> operations, List<RoutingDetail> operations,
Map<Integer, List<RoutingDetailEquip>> operationToEquipments, Map<Long, List<RoutingDetailEquip>> operationToEquipments,
Map<Integer, EquipmentScheduleState> equipmentStates, Map<Integer, EquipmentScheduleState> equipmentStates,
List<ScheduledTask> scheduledTasks) { List<ScheduledTask> scheduledTasks) {
...@@ -103,7 +103,7 @@ public class ScheduleServiceImpl implements ScheduleService { ...@@ -103,7 +103,7 @@ public class ScheduleServiceImpl implements ScheduleService {
private ScheduledTask scheduleOperation(Mpsplannedorder order, private ScheduledTask scheduleOperation(Mpsplannedorder order,
RoutingDetail operation, RoutingDetail operation,
Map<Integer, List<RoutingDetailEquip>> operationToEquipments, Map<Long, List<RoutingDetailEquip>> operationToEquipments,
Map<Integer, EquipmentScheduleState> equipmentStates, Map<Integer, EquipmentScheduleState> equipmentStates,
LocalDateTime lastOperationEndTime) { LocalDateTime lastOperationEndTime) {
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.ProdEquipmentMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.ProdEquipment">
<id column="SCENE_ID" property="sceneId" />
<id column="EQUIP_ID" property="equipId" />
<id column="RESOURCE_ID" property="resourceId" />
<result column="EQUIP_CODE" property="equipCode" />
<result column="EQUIP_NAME" property="equipName" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
SCENE_ID, EQUIP_ID, EQUIP_CODE, EQUIP_NAME, RESOURCE_ID
</sql>
</mapper>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment