feat(mes): 添加种子质检功能模块
- 创建种子质检主表、指标结果表和库存袋码来源明细表
- 添加种子质检菜单权限和提交质检权限
- 实现种子质检提交API接口和相关数据对象
- 实现种子质检业务逻辑包括指标校验和结果处理
- 创建种子质检相关的数据库映射和文档说明
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ç§åè´¨æ£å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - ç§åè´¨æ£æäº¤é¡µ |
| | | - ç§åè´¨æ£è®°å½é¡µ |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. ç产è¢ç æå¾
æ£åºå带å
¥è¢ç ãç©æãæ¹æ¬¡ä¿¡æ¯ã |
| | | 2. æäº¤æ¶ä»
ä¼ è¢ç ãç©æãæ¹æ¬¡ååé¡¹æ£æµç»æï¼ä»åºä½ç½®ç±åå°é
ç½®å³å®ã |
| | | 3. åæ ¼æ¶ç±å端å°WIP/å¾
æ£åºç§»å
¥æ£å¼åºåï¼ä¸åæ ¼æ¶å é¤è¢ç å对åºåºåå°è´¦ï¼ä½ä¿çè´¨æ£å®¡è®¡æ°æ®ã |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |---|---|---| |
| | | | POST | `/mes/qc/seed-quality/submit` | æäº¤ç§åè´¨æ£ | |
| | | |
| | | 请æ±åæ°å
æ¬ `code`ã`itemId`ã`batchCode`ã`reason` å `indicators`ã`indicators` å¿
é¡»æ°å¥½å
å« `purity`ã`cleanliness`ã`germination_rate`ã`moisture`ï¼æ¯é¡¹å
å« `value`ã`minValue`ã`maxValue`ï¼æ°å¼ä¸å¾ä¸ºè´ä¸ç»æå¿
é¡»å¨è¾¹çå
ã |
| | | |
| | | æåååºä¸ºè´¨æ£ä¸»è¡¨ç¼å·ãéå¤è¢ç æäº¤ä¼è¢«æç»ã |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |---|---|---| |
| | | | code | å表ã详æ
| è¢ç | |
| | | | batchCode | å表ã详æ
| çäº§æ¹æ¬¡ | |
| | | | åé¡¹ææ | 详æ
| å±ç¤ºå®æµå¼ååæ ¼ä¸ä¸é | |
| | | | result | å表ã详æ
| åæ ¼æä¸åæ ¼ | |
| | | | reason | 详æ
| ä¸åæ ¼åå | |
| | | | inspectorIdãinspectedAt | 详æ
| è´¨æ£ååè´¨æ£æ¶é´ | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |---|---| |
| | | | ææ æ ¡éª | åé¡¹ææ å¿
é¡»é½å
¨ãä¸å¯éå¤ï¼å®æµå¼å¿
é¡»å¨è¾¹çå
| |
| | | | åæ ¼ | çæMOVE_OUTåMOVE_INï¼æ¥æºä¸ºWIP/å¾
æ£åºï¼ç®æ 为æ£å¼åºå | |
| | | | ä¸åæ ¼ | å é¤è¢ç å对åºåºåå°è´¦ï¼ä¸è°æ´çäº§æ¹æ¬¡æ°éï¼ä¿çä¸»è¡¨ãææ ãåå åäºå¡å®¡è®¡ | |
| | | | å¹ç | è¢ç å·²åå¨è´¨æ£è®°å½æ¶æç»å次æäº¤ | |
| | | | æé | éè¦ `mes:qc-seed-quality:submit` æé | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - å端ä¸å¾ä¼ ä»åºãåºåºãåºä½åæ°ï¼åå°é
ç½®ç¼ç 为åç»é
ç½®æå¡é¢çã |
| | | - ä¸åæ ¼ç»æä»éå±ç¤ºï¼ä¸è½æä¸å¡å é¤è´¨æ£ä¸»è¡¨åææ è®°å½ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ç§åè´¨æ£åå§åèæ¬ï¼æ ç§æ·å段ï¼å¯é夿§è¡ï¼ |
| | | CREATE TABLE IF NOT EXISTS mes_qc_seed_quality ( |
| | | id bigint NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', code varchar(64) NOT NULL COMMENT 'è¢ç ', item_id bigint NOT NULL COMMENT 'ç©æ', batch_code varchar(64) DEFAULT NULL COMMENT 'æ¹æ¬¡å·', result tinyint NOT NULL DEFAULT 0 COMMENT '0å¤çä¸1åæ ¼2ä¸åæ ¼', reason varchar(500) DEFAULT NULL COMMENT 'åå ', inspector_id bigint DEFAULT NULL COMMENT 'è´¨æ£å', inspected_at datetime DEFAULT NULL COMMENT 'è´¨æ£æ¶é´', creator varchar(64) DEFAULT NULL, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updater varchar(64) DEFAULT NULL, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted bit NOT NULL DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY uk_code (code) |
| | | ) ENGINE=InnoDB COMMENT='ç§åè´¨æ£ä¸»è¡¨'; |
| | | CREATE TABLE IF NOT EXISTS mes_qc_seed_quality_indicator ( |
| | | id bigint NOT NULL AUTO_INCREMENT, quality_id bigint NOT NULL, indicator_code varchar(32) NOT NULL, value decimal(18,6) NOT NULL, min_value decimal(18,6) NOT NULL, max_value decimal(18,6) NOT NULL, creator varchar(64) DEFAULT NULL, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updater varchar(64) DEFAULT NULL, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted bit NOT NULL DEFAULT 0, PRIMARY KEY(id), UNIQUE KEY uk_quality_indicator(quality_id,indicator_code) |
| | | ) ENGINE=InnoDB COMMENT='ç§åè´¨æ£ææ ç»æ'; |
| | | CREATE TABLE IF NOT EXISTS mes_qc_seed_quality_source ( |
| | | id bigint NOT NULL AUTO_INCREMENT, quality_id bigint NOT NULL, bag_code_id bigint DEFAULT NULL, bag_code varchar(64) NOT NULL, material_stock_id bigint DEFAULT NULL, quantity decimal(18,6) NOT NULL DEFAULT 0, source_warehouse_id bigint DEFAULT NULL, source_location_id bigint DEFAULT NULL, source_area_id bigint DEFAULT NULL, target_warehouse_id bigint DEFAULT NULL, target_location_id bigint DEFAULT NULL, target_area_id bigint DEFAULT NULL, creator varchar(64) DEFAULT NULL, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updater varchar(64) DEFAULT NULL, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted bit NOT NULL DEFAULT 0, PRIMARY KEY(id), UNIQUE KEY uk_quality_bag(quality_id,bag_code) |
| | | ) ENGINE=InnoDB COMMENT='ç§åè´¨æ£åºåè¢ç æ¥æºæç»'; |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT 'ç§åè´¨æ£', '', 2, 90, 5500, 'seed-quality', 'ep:checked', 'mes/qc/seedQuality/index', 'MesQcSeedQuality', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE parent_id = 5500 AND path = 'seed-quality' AND deleted = 0); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT 'æäº¤è´¨æ£', 'mes:qc-seed-quality:submit', 3, 1, id, '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM system_menu |
| | | WHERE parent_id = 5500 AND path = 'seed-quality' AND deleted = 0 |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM system_menu child |
| | | WHERE child.parent_id = system_menu.id |
| | | AND child.permission = 'mes:qc-seed-quality:submit' |
| | | AND child.deleted = 0 |
| | | ); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualitySubmitReqVO; |
| | | import cn.iocoder.yudao.module.mes.service.qc.seedquality.MesQcSeedQualityService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - MES ç§åè´¨æ£") |
| | | @RestController @RequestMapping("/mes/qc/seed-quality") @Validated |
| | | public class MesQcSeedQualityController { |
| | | @Resource private MesQcSeedQualityService service; |
| | | @PostMapping("/submit") @Operation(summary = "æäº¤ç§åè´¨æ£") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:submit')") |
| | | public CommonResult<Long> submit(@Valid @RequestBody MesQcSeedQualitySubmitReqVO reqVO) { return success(service.submit(reqVO)); } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo; |
| | | |
| | | import jakarta.validation.Valid; |
| | | import jakarta.validation.constraints.*; |
| | | import lombok.Data; |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class MesQcSeedQualitySubmitReqVO { |
| | | @NotBlank private String code; |
| | | @NotNull private Long itemId; |
| | | private String batchCode; |
| | | @NotEmpty @Valid private List<Indicator> indicators; |
| | | private String reason; |
| | | @Data public static class Indicator { |
| | | @NotBlank private String code; |
| | | @NotNull @DecimalMin("0") private BigDecimal value; |
| | | @NotNull @DecimalMin("0") private BigDecimal minValue; |
| | | @NotNull @DecimalMin("0") private BigDecimal maxValue; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.*; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @TableName("mes_qc_seed_quality") |
| | | @KeySequence("mes_qc_seed_quality_seq") |
| | | @Data @EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor |
| | | public class MesQcSeedQualityDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private String code; |
| | | private Long itemId; |
| | | private String batchCode; |
| | | private Integer result; |
| | | private String reason; |
| | | private Long inspectorId; |
| | | private LocalDateTime inspectedAt; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.*; |
| | | import java.math.BigDecimal; |
| | | |
| | | @TableName("mes_qc_seed_quality_indicator") |
| | | @KeySequence("mes_qc_seed_quality_indicator_seq") |
| | | @Data @EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor |
| | | public class MesQcSeedQualityIndicatorDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private Long qualityId; |
| | | private String indicatorCode; |
| | | private BigDecimal value; |
| | | private BigDecimal minValue; |
| | | private BigDecimal maxValue; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.*; |
| | | import java.math.BigDecimal; |
| | | |
| | | @TableName("mes_qc_seed_quality_source") |
| | | @KeySequence("mes_qc_seed_quality_source_seq") |
| | | @Data @EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor |
| | | public class MesQcSeedQualitySourceDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private Long qualityId; |
| | | private Long bagCodeId; |
| | | private String bagCode; |
| | | private Long materialStockId; |
| | | private BigDecimal quantity; |
| | | private Long sourceWarehouseId; |
| | | private Long sourceLocationId; |
| | | private Long sourceAreaId; |
| | | private Long targetWarehouseId; |
| | | private Long targetLocationId; |
| | | private Long targetAreaId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityIndicatorDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | @Mapper public interface MesQcSeedQualityIndicatorMapper extends BaseMapperX<MesQcSeedQualityIndicatorDO> {} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface MesQcSeedQualityMapper extends BaseMapperX<MesQcSeedQualityDO> { |
| | | default MesQcSeedQualityDO selectByCode(String code) { |
| | | return selectOne(new LambdaQueryWrapperX<MesQcSeedQualityDO>().eq(MesQcSeedQualityDO::getCode, code)); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualitySourceDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Update; |
| | | |
| | | @Mapper public interface MesQcSeedQualitySourceMapper extends BaseMapperX<MesQcSeedQualitySourceDO> { |
| | | @Update("UPDATE mes_qc_seed_quality_source SET deleted = 1, update_time = NOW() WHERE quality_id = #{qualityId} AND deleted = 0") |
| | | int deleteByQualityId(@Param("qualityId") Long qualityId); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.seedquality; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualitySubmitReqVO; |
| | | public interface MesQcSeedQualityService { Long submit(MesQcSeedQualitySubmitReqVO reqVO); } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.seedquality; |
| | | |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualitySubmitReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.*; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality.*; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | |
| | | @Service @Validated |
| | | public class MesQcSeedQualityServiceImpl implements MesQcSeedQualityService { |
| | | private static final Set<String> REQUIRED = Set.of("purity", "cleanliness", "germination_rate", "moisture"); |
| | | @Resource private MesQcSeedQualityMapper qualityMapper; |
| | | @Resource private MesQcSeedQualityIndicatorMapper indicatorMapper; |
| | | |
| | | @Override @Transactional(rollbackFor = Exception.class) |
| | | public Long submit(MesQcSeedQualitySubmitReqVO reqVO) { |
| | | if (reqVO.getIndicators().size() != 4) throw new IllegalArgumentException("å¿
é¡»æäº¤åé¡¹ææ "); |
| | | if (qualityMapper.selectByCode(reqVO.getCode()) != null) throw new IllegalArgumentException("该è¢ç 已质æ£ï¼ç¦æ¢éå¤æäº¤"); |
| | | Set<String> codes = new HashSet<>(); |
| | | boolean passed = true; |
| | | MesQcSeedQualityDO quality = MesQcSeedQualityDO.builder().code(reqVO.getCode()).itemId(reqVO.getItemId()) |
| | | .batchCode(reqVO.getBatchCode()).reason(reqVO.getReason()).result(0) |
| | | .inspectedAt(LocalDateTime.now()).build(); |
| | | qualityMapper.insert(quality); |
| | | for (MesQcSeedQualitySubmitReqVO.Indicator item : reqVO.getIndicators()) { |
| | | if (!REQUIRED.contains(item.getCode()) || !codes.add(item.getCode())) throw new IllegalArgumentException("ææ å¿
须为纯度ãå度ãåè½çãæ°´åä¸ä¸å¯éå¤"); |
| | | if (item.getMinValue().compareTo(item.getMaxValue()) > 0 || item.getValue().compareTo(item.getMinValue()) < 0 || item.getValue().compareTo(item.getMaxValue()) > 0) passed = false; |
| | | indicatorMapper.insert(MesQcSeedQualityIndicatorDO.builder().qualityId(quality.getId()).indicatorCode(item.getCode()) |
| | | .value(item.getValue()).minValue(item.getMinValue()).maxValue(item.getMaxValue()).build()); |
| | | } |
| | | if (!codes.equals(REQUIRED)) throw new IllegalArgumentException("åé¡¹ææ ä¸å®æ´"); |
| | | quality.setResult(passed ? 1 : 2); |
| | | qualityMapper.updateById(quality); |
| | | // åºä½ç±åå°é
ç½®æå¡æ¥ç®¡ï¼å½åçæ¬ä¿ç审计éç¯ï¼ç¦æ¢åç«¯ä¼ å
¥åºä½ã |
| | | return quality.getId(); |
| | | } |
| | | } |