PDI部件图形绑定

This commit is contained in:
yf001217 2026-06-08 14:16:56 +08:00
parent 713ce0aefb
commit a0aa1ee169
8 changed files with 620 additions and 0 deletions

View File

@ -0,0 +1,40 @@
package com.nflg.qms.admin.controller;
import com.nflg.qms.admin.service.QmsPdiComponentBindingControllerService;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.qo.QmsPdiComponentBindingSaveQO;
import com.nflg.wms.common.pojo.vo.QmsPdiComponentBindingSearchVO;
import com.nflg.wms.starter.BaseController;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.springframework.web.bind.annotation.*;
/**
* PDI部件绑定
*/
@RestController
@RequestMapping("/pdiComponentBinding")
public class QmsPdiComponentBindingController extends BaseController {
@Resource
private QmsPdiComponentBindingControllerService componentBindingControllerService;
/**
* 保存绑定
*/
@PostMapping("/save")
public ApiResult<Void> save(@Valid @RequestBody QmsPdiComponentBindingSaveQO request) {
componentBindingControllerService.save(request);
return ApiResult.success();
}
/**
* 查询绑定
*/
@GetMapping("/search")
public ApiResult<QmsPdiComponentBindingSearchVO> search(
@Valid @NotNull(message = "PDI标准检测规则ID不能为空") @RequestParam Long pdiDetectionRulesId) {
return ApiResult.success(componentBindingControllerService.search(pdiDetectionRulesId));
}
}

View File

@ -0,0 +1,305 @@
package com.nflg.qms.admin.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.exception.NflgException;
import com.nflg.wms.common.pojo.qo.QmsPdiComponentBindingSaveQO;
import com.nflg.wms.common.pojo.vo.QmsPdiComponentBindingSearchVO;
import com.nflg.wms.repository.entity.FileUploadRecord;
import com.nflg.wms.repository.entity.QmsPdiComponentAnagement;
import com.nflg.wms.repository.entity.QmsPdiComponentBinding;
import com.nflg.wms.repository.entity.QmsPdiDetectionRulesStatusItem;
import com.nflg.wms.repository.service.IFileUploadRecordService;
import com.nflg.wms.repository.service.IQmsPdiComponentAnagementService;
import com.nflg.wms.repository.service.IQmsPdiComponentBindingService;
import com.nflg.wms.repository.service.IQmsPdiDetectionRulesStatusItemService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* PDI部件绑定
*/
@Component
@RequiredArgsConstructor
public class QmsPdiComponentBindingControllerService {
private static final short STATIC_STATUS = 0;
private static final short DYNAMIC_STATUS = 1;
private final IQmsPdiComponentBindingService componentBindingService;
private final IQmsPdiComponentAnagementService componentService;
private final IQmsPdiDetectionRulesStatusItemService statusItemService;
private final IFileUploadRecordService fileUploadRecordService;
/**
* 保存PDI部件绑定按PDI标准检测规则整单覆盖
*/
@Transactional
public void save(QmsPdiComponentBindingSaveQO request) {
validateSaveRequest(request);
componentBindingService.lambdaUpdate()
.eq(QmsPdiComponentBinding::getPdiDetectionRulesId, request.getPdiDetectionRulesId())
.remove();
List<QmsPdiComponentBinding> bindings = new ArrayList<>();
for (QmsPdiComponentBindingSaveQO.ImageBindingQO image : request.getItems()) {
if (CollectionUtil.isEmpty(image.getComponents())) {
bindings.add(new QmsPdiComponentBinding()
.setPdiDetectionRulesId(request.getPdiDetectionRulesId())
.setUploadImageId(image.getUploadImageId()));
continue;
}
for (QmsPdiComponentBindingSaveQO.ComponentBindingQO component : image.getComponents()) {
bindings.add(new QmsPdiComponentBinding()
.setPdiDetectionRulesId(request.getPdiDetectionRulesId())
.setUploadImageId(image.getUploadImageId())
.setPdiComponentId(component.getPdiComponentId())
.setXCoordinatePoint(component.getXCoordinatePoint())
.setYCoordinatePoint(component.getYCoordinatePoint())
.setStatus(component.getStatus()));
}
}
if (CollectionUtil.isNotEmpty(bindings)) {
componentBindingService.saveBatch(bindings);
}
}
/**
* 查询PDI部件绑定
*/
public QmsPdiComponentBindingSearchVO search(Long pdiDetectionRulesId) {
QmsPdiComponentBindingSearchVO vo = new QmsPdiComponentBindingSearchVO();
List<QmsPdiComponentBinding> bindings = componentBindingService.lambdaQuery()
.eq(QmsPdiComponentBinding::getPdiDetectionRulesId, pdiDetectionRulesId)
.list();
Map<Long, FileUploadRecord> imageMap = getImageMap(bindings);
Map<Long, QmsPdiComponentAnagement> componentMap = componentService.lambdaQuery()
.eq(QmsPdiComponentAnagement::getDetectionRulesId, pdiDetectionRulesId)
.list()
.stream()
.collect(Collectors.toMap(QmsPdiComponentAnagement::getId, Function.identity(), (a, b) -> a));
Map<String, List<QmsPdiComponentBindingSearchVO.InspectionItemVO>> inspectionItemMap =
getInspectionItemMap(pdiDetectionRulesId);
Set<String> boundComponentStatusKeys = new HashSet<>();
Map<Long, QmsPdiComponentBindingSearchVO.ImageBindingVO> boundImageMap = new LinkedHashMap<>();
for (QmsPdiComponentBinding binding : bindings) {
if (binding.getUploadImageId() == null) {
continue;
}
QmsPdiComponentBindingSearchVO.ImageBindingVO imageVO = boundImageMap.computeIfAbsent(
binding.getUploadImageId(), uploadImageId -> buildImageVO(uploadImageId, imageMap.get(uploadImageId)));
if (!isEffectiveBinding(binding)) {
continue;
}
QmsPdiComponentAnagement component = componentMap.get(binding.getPdiComponentId());
QmsPdiComponentBindingSearchVO.ComponentBindingVO componentVO =
buildComponentVO(binding, component, inspectionItemMap.get(componentStatusKey(binding.getPdiComponentId(), binding.getStatus())));
if (binding.getStatus() == STATIC_STATUS) {
imageVO.getStaticComponents().add(componentVO);
} else {
imageVO.getDynamicComponents().add(componentVO);
}
boundComponentStatusKeys.add(componentStatusKey(binding.getPdiComponentId(), binding.getStatus()));
}
vo.setBoundList(new ArrayList<>(boundImageMap.values()));
inspectionItemMap.forEach((key, inspectionItems) -> {
if (boundComponentStatusKeys.contains(key)) {
return;
}
ComponentStatus componentStatus = parseComponentStatusKey(key);
QmsPdiComponentAnagement component = componentMap.get(componentStatus.componentId);
QmsPdiComponentBindingSearchVO.ComponentBindingVO componentVO =
buildUnboundComponentVO(componentStatus.componentId, component, componentStatus.status, inspectionItems);
if (componentStatus.status == STATIC_STATUS) {
vo.getUnbound().getStaticComponents().add(componentVO);
} else {
vo.getUnbound().getDynamicComponents().add(componentVO);
}
});
return vo;
}
private void validateSaveRequest(QmsPdiComponentBindingSaveQO request) {
Set<Long> imageIds = request.getItems().stream()
.peek(image -> {
if (image == null) {
throw new NflgException(STATE.BusinessError, "图片绑定对象不能为空");
}
})
.map(QmsPdiComponentBindingSaveQO.ImageBindingQO::getUploadImageId)
.collect(Collectors.toSet());
List<FileUploadRecord> images = fileUploadRecordService.listByIds(imageIds);
if (images.size() != imageIds.size()) {
throw new NflgException(STATE.BusinessError, "上传图片不存在");
}
Set<Long> componentIds = new HashSet<>();
Map<String, Set<Short>> coordinateStatusMap = new HashMap<>();
for (QmsPdiComponentBindingSaveQO.ImageBindingQO image : request.getItems()) {
if (CollectionUtil.isEmpty(image.getComponents())) {
continue;
}
for (QmsPdiComponentBindingSaveQO.ComponentBindingQO component : image.getComponents()) {
if (component == null) {
throw new NflgException(STATE.BusinessError, "部件绑定对象不能为空");
}
if (component.getPdiComponentId() == null) {
throw new NflgException(STATE.BusinessError, "部件ID不能为空");
}
if (StrUtil.isBlank(component.getXCoordinatePoint()) || StrUtil.isBlank(component.getYCoordinatePoint())) {
throw new NflgException(STATE.BusinessError, "绑定部件时x轴坐标和y轴坐标不能为空");
}
if (!isAllowedStatus(component.getStatus())) {
throw new NflgException(STATE.BusinessError, "状态只能为0静态或1动态");
}
componentIds.add(component.getPdiComponentId());
String coordinateKey = coordinateKey(image.getUploadImageId(), component.getXCoordinatePoint(), component.getYCoordinatePoint());
Set<Short> statuses = coordinateStatusMap.computeIfAbsent(coordinateKey, key -> new HashSet<>());
if (!statuses.add(component.getStatus())) {
throw new NflgException(STATE.BusinessError, "同一图片同一坐标不能重复绑定相同状态");
}
if (statuses.size() > 2) {
throw new NflgException(STATE.BusinessError, "同一图片同一坐标最多绑定一个静态和一个动态部件");
}
}
}
if (CollectionUtil.isEmpty(componentIds)) {
return;
}
Set<Long> validComponentIds = componentService.lambdaQuery()
.select(QmsPdiComponentAnagement::getId)
.eq(QmsPdiComponentAnagement::getDetectionRulesId, request.getPdiDetectionRulesId())
.in(QmsPdiComponentAnagement::getId, componentIds)
.list()
.stream()
.map(QmsPdiComponentAnagement::getId)
.collect(Collectors.toSet());
if (validComponentIds.size() != componentIds.size()) {
throw new NflgException(STATE.BusinessError, "存在不属于当前PDI标准检测规则的部件");
}
}
private Map<Long, FileUploadRecord> getImageMap(List<QmsPdiComponentBinding> bindings) {
Set<Long> imageIds = bindings.stream()
.map(QmsPdiComponentBinding::getUploadImageId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (CollectionUtil.isEmpty(imageIds)) {
return Collections.emptyMap();
}
return fileUploadRecordService.listByIds(imageIds).stream()
.collect(Collectors.toMap(FileUploadRecord::getId, Function.identity(), (a, b) -> a));
}
private Map<String, List<QmsPdiComponentBindingSearchVO.InspectionItemVO>> getInspectionItemMap(Long pdiDetectionRulesId) {
List<QmsPdiDetectionRulesStatusItem> statusItems = statusItemService.lambdaQuery()
.eq(QmsPdiDetectionRulesStatusItem::getDetectionRulesId, pdiDetectionRulesId)
.in(QmsPdiDetectionRulesStatusItem::getStatus, List.of((int) STATIC_STATUS, (int) DYNAMIC_STATUS))
.isNotNull(QmsPdiDetectionRulesStatusItem::getComponentsId)
.orderByAsc(QmsPdiDetectionRulesStatusItem::getSort)
.list();
Map<String, List<QmsPdiComponentBindingSearchVO.InspectionItemVO>> itemMap = new LinkedHashMap<>();
for (QmsPdiDetectionRulesStatusItem item : statusItems) {
Short status = item.getStatus().shortValue();
String key = componentStatusKey(item.getComponentsId(), status);
itemMap.computeIfAbsent(key, ignored -> new ArrayList<>()).add(buildInspectionItemVO(item));
}
return itemMap;
}
private QmsPdiComponentBindingSearchVO.ImageBindingVO buildImageVO(Long uploadImageId, FileUploadRecord image) {
QmsPdiComponentBindingSearchVO.ImageBindingVO imageVO = new QmsPdiComponentBindingSearchVO.ImageBindingVO();
imageVO.setUploadImageId(uploadImageId);
imageVO.setUploadImageUrl(image == null ? null : image.getUrl());
return imageVO;
}
private QmsPdiComponentBindingSearchVO.ComponentBindingVO buildComponentVO(
QmsPdiComponentBinding binding,
QmsPdiComponentAnagement component,
List<QmsPdiComponentBindingSearchVO.InspectionItemVO> inspectionItems) {
QmsPdiComponentBindingSearchVO.ComponentBindingVO componentVO = buildUnboundComponentVO(
binding.getPdiComponentId(), component, binding.getStatus(), inspectionItems);
componentVO.setXCoordinatePoint(binding.getXCoordinatePoint());
componentVO.setYCoordinatePoint(binding.getYCoordinatePoint());
return componentVO;
}
private QmsPdiComponentBindingSearchVO.ComponentBindingVO buildUnboundComponentVO(
Long componentId,
QmsPdiComponentAnagement component,
Short status,
List<QmsPdiComponentBindingSearchVO.InspectionItemVO> inspectionItems) {
QmsPdiComponentBindingSearchVO.ComponentBindingVO componentVO = new QmsPdiComponentBindingSearchVO.ComponentBindingVO();
componentVO.setPdiComponentId(componentId);
componentVO.setPdiComponentName(component == null ? null : component.getComponentName());
componentVO.setStatus(status);
componentVO.setInspectionItems(inspectionItems == null ? new ArrayList<>() : new ArrayList<>(inspectionItems));
return componentVO;
}
private QmsPdiComponentBindingSearchVO.InspectionItemVO buildInspectionItemVO(QmsPdiDetectionRulesStatusItem item) {
QmsPdiComponentBindingSearchVO.InspectionItemVO itemVO = new QmsPdiComponentBindingSearchVO.InspectionItemVO();
itemVO.setInspectionItemId(item.getId());
itemVO.setInspectionContent(item.getInspectionContent());
return itemVO;
}
private boolean isEffectiveBinding(QmsPdiComponentBinding binding) {
return binding.getPdiComponentId() != null
&& StrUtil.isNotBlank(binding.getXCoordinatePoint())
&& StrUtil.isNotBlank(binding.getYCoordinatePoint())
&& isAllowedStatus(binding.getStatus());
}
private boolean isAllowedStatus(Short status) {
return status != null && (status == STATIC_STATUS || status == DYNAMIC_STATUS);
}
private String coordinateKey(Long uploadImageId, String xCoordinatePoint, String yCoordinatePoint) {
return uploadImageId + "|" + xCoordinatePoint + "|" + yCoordinatePoint;
}
private String componentStatusKey(Long componentId, Short status) {
return componentId + "|" + status;
}
private ComponentStatus parseComponentStatusKey(String key) {
String[] parts = key.split("\\|", 2);
return new ComponentStatus(Long.valueOf(parts[0]), Short.valueOf(parts[1]));
}
private static class ComponentStatus {
private final Long componentId;
private final Short status;
private ComponentStatus(Long componentId, Short status) {
this.componentId = componentId;
this.status = status;
}
}
}

View File

@ -0,0 +1,68 @@
package com.nflg.wms.common.pojo.qo;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
/**
* PDI部件绑定 保存请求参数
*/
@Data
public class QmsPdiComponentBindingSaveQO {
/**
* PDI标准检测规则ID
*/
@NotNull(message = "PDI标准检测规则ID不能为空")
private Long pdiDetectionRulesId;
/**
* 图片绑定列表
*/
@Valid
@NotEmpty(message = "图片绑定列表不能为空")
private List<ImageBindingQO> items;
@Data
public static class ImageBindingQO {
/**
* 上传图片的ID
*/
@NotNull(message = "上传图片ID不能为空")
private Long uploadImageId;
/**
* 部件绑定列表
*/
@Valid
private List<ComponentBindingQO> components;
}
@Data
public static class ComponentBindingQO {
/**
* 部件ID
*/
private Long pdiComponentId;
/**
* x轴坐标
*/
private String xCoordinatePoint;
/**
* y轴坐标
*/
private String yCoordinatePoint;
/**
* 状态0为静态1为动态
*/
private Short status;
}
}

View File

@ -0,0 +1,109 @@
package com.nflg.wms.common.pojo.vo;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* PDI部件绑定 查询返回值
*/
@Data
public class QmsPdiComponentBindingSearchVO {
/**
* 已绑定图片列表
*/
private List<ImageBindingVO> boundList = new ArrayList<>();
/**
* 未绑定部件
*/
private UnboundVO unbound = new UnboundVO();
@Data
public static class ImageBindingVO {
/**
* 上传图片的ID
*/
private Long uploadImageId;
/**
* 上传图片URL
*/
private String uploadImageUrl;
/**
* 静态部件列表
*/
private List<ComponentBindingVO> staticComponents = new ArrayList<>();
/**
* 动态部件列表
*/
private List<ComponentBindingVO> dynamicComponents = new ArrayList<>();
}
@Data
public static class UnboundVO {
/**
* 未绑定静态部件列表
*/
private List<ComponentBindingVO> staticComponents = new ArrayList<>();
/**
* 未绑定动态部件列表
*/
private List<ComponentBindingVO> dynamicComponents = new ArrayList<>();
}
@Data
public static class ComponentBindingVO {
/**
* 部件ID
*/
private Long pdiComponentId;
/**
* 部件名称
*/
private String pdiComponentName;
/**
* x轴坐标
*/
private String xCoordinatePoint;
/**
* y轴坐标
*/
private String yCoordinatePoint;
/**
* 状态0为静态1为动态
*/
private Short status;
/**
* 检测项列表
*/
private List<InspectionItemVO> inspectionItems = new ArrayList<>();
}
@Data
public static class InspectionItemVO {
/**
* 检测项ID
*/
private Long inspectionItemId;
/**
* 检查核实内容
*/
private String inspectionContent;
}
}

View File

@ -0,0 +1,60 @@
package com.nflg.wms.repository.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* PDI部件绑定
*/
@Getter
@Setter
@ToString
@Accessors(chain = true)
@TableName("qms_pdi_component_binding")
public class QmsPdiComponentBinding implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**
* 上传图片的id
*/
private Long uploadImageId;
/**
* PDI标准检测规则id
*/
private Long pdiDetectionRulesId;
/**
* 部件id
*/
private Long pdiComponentId;
/**
* x轴坐标
*/
private String xCoordinatePoint;
/**
* y轴坐标
*/
private String yCoordinatePoint;
/**
* 状态0为静态1为动态
*/
private Short status;
}

View File

@ -0,0 +1,12 @@
package com.nflg.wms.repository.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nflg.wms.repository.entity.QmsPdiComponentBinding;
import org.apache.ibatis.annotations.Mapper;
/**
* PDI部件绑定 Mapper
*/
@Mapper
public interface QmsPdiComponentBindingMapper extends BaseMapper<QmsPdiComponentBinding> {
}

View File

@ -0,0 +1,10 @@
package com.nflg.wms.repository.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.wms.repository.entity.QmsPdiComponentBinding;
/**
* PDI部件绑定 Service
*/
public interface IQmsPdiComponentBindingService extends IService<QmsPdiComponentBinding> {
}

View File

@ -0,0 +1,16 @@
package com.nflg.wms.repository.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.wms.repository.entity.QmsPdiComponentBinding;
import com.nflg.wms.repository.mapper.QmsPdiComponentBindingMapper;
import com.nflg.wms.repository.service.IQmsPdiComponentBindingService;
import org.springframework.stereotype.Service;
/**
* PDI部件绑定 ServiceImpl
*/
@Service
public class QmsPdiComponentBindingServiceImpl
extends ServiceImpl<QmsPdiComponentBindingMapper, QmsPdiComponentBinding>
implements IQmsPdiComponentBindingService {
}