From 3521daebabe36a87aec380c82faa865506a9c4c9 Mon Sep 17 00:00:00 2001 From: zch Date: Mon, 21 Jul 2025 09:35:25 +0800 Subject: [PATCH] =?UTF-8?q?add(qms):=20=E6=96=B0=E5=A2=9E=E4=B8=8D?= =?UTF-8?q?=E5=90=88=E6=A0=BC=E5=93=81=E8=AF=84=E5=AE=A1=EF=BC=88=E5=86=85?= =?UTF-8?q?=E5=90=ABPDA=E6=96=B9=E6=B3=95=E5=BE=85=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QcUnqualifiedRecordController.java | 129 ++++ .../QcUnqualifiedReviewController.java | 129 ++++ .../qms/domain/QcUnqualifiedRecord.java | 47 ++ .../qms/domain/QcUnqualifiedReview.java | 126 ++++ .../qms/domain/bo/QcUnqualifiedRecordBo.java | 75 ++ .../qms/domain/bo/QcUnqualifiedReviewBo.java | 116 +++ .../domain/vo/QcInspectionItemDetailVo.java | 195 +++++ .../qms/domain/vo/QcUnqualifiedRecordVo.java | 136 ++++ .../qms/domain/vo/QcUnqualifiedReviewVo.java | 141 ++++ .../qms/mapper/QcUnqualifiedRecordMapper.java | 34 + .../qms/mapper/QcUnqualifiedReviewMapper.java | 34 + .../service/IQcUnqualifiedRecordService.java | 96 +++ .../service/IQcUnqualifiedReviewService.java | 101 +++ .../impl/QcUnqualifiedGeneratorService.java | 278 +++++++ .../impl/QcUnqualifiedRecordServiceImpl.java | 148 ++++ .../impl/QcUnqualifiedReviewServiceImpl.java | 693 ++++++++++++++++++ .../utils/QcInspectionResultCalculator.java | 368 ++++++++++ .../mapper/qms/QcUnqualifiedRecordMapper.xml | 38 + .../mapper/qms/QcUnqualifiedReviewMapper.xml | 61 ++ 19 files changed, 2945 insertions(+) create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcUnqualifiedRecordController.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcUnqualifiedReviewController.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcUnqualifiedRecord.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcUnqualifiedReview.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcUnqualifiedRecordBo.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcUnqualifiedReviewBo.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionItemDetailVo.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcUnqualifiedRecordVo.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcUnqualifiedReviewVo.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/mapper/QcUnqualifiedRecordMapper.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/mapper/QcUnqualifiedReviewMapper.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcUnqualifiedRecordService.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcUnqualifiedReviewService.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedGeneratorService.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedRecordServiceImpl.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/utils/QcInspectionResultCalculator.java create mode 100644 ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcUnqualifiedRecordMapper.xml create mode 100644 ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcUnqualifiedReviewMapper.xml diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcUnqualifiedRecordController.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcUnqualifiedRecordController.java new file mode 100644 index 0000000..c473c14 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcUnqualifiedRecordController.java @@ -0,0 +1,129 @@ +package org.dromara.qms.controller; + +import java.util.List; +import java.util.ArrayList; +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.qms.domain.vo.QcUnqualifiedRecordVo; +import org.dromara.qms.domain.bo.QcUnqualifiedRecordBo; +import org.dromara.qms.service.IQcUnqualifiedRecordService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 不合格品评审记录 + * 前端访问路由地址为:/qms/qcUnqualifiedRecord + * + * @author zch + * @date 2025-07-18 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/qcUnqualifiedRecord") +public class QcUnqualifiedRecordController extends BaseController { + + private final IQcUnqualifiedRecordService qcUnqualifiedRecordService; + + /** + * 查询不合格品评审记录列表 + */ + @SaCheckPermission("qms:qcUnqualifiedRecord:list") + @GetMapping("/list") + public TableDataInfo list(QcUnqualifiedRecordBo bo, PageQuery pageQuery) { + return qcUnqualifiedRecordService.queryPageList(bo, pageQuery); + } + + /** + * 导出不合格品评审记录列表 + */ + @SaCheckPermission("qms:qcUnqualifiedRecord:export") + @Log(title = "不合格品评审记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(QcUnqualifiedRecordBo bo, HttpServletResponse response) { + List list = qcUnqualifiedRecordService.queryList(bo); + ExcelUtil.exportExcel(list, "不合格品评审记录", QcUnqualifiedRecordVo.class, response); + } + + /** + * 导出不合格品评审记录列表模板(无任何数据) + */ + @SaCheckPermission("qms:qcUnqualifiedRecord:export") + @Log(title = "不合格品评审记录", businessType = BusinessType.EXPORT) + @PostMapping("/exportTemplate") + public void exportTemplate(QcUnqualifiedRecordBo bo, HttpServletResponse response) { + List list = new ArrayList<>(); + ExcelUtil.exportExcel(list, "不合格品评审记录", QcUnqualifiedRecordVo.class, response); + } + + + /** + * 获取不合格品评审记录详细信息 + * + * @param recordId 主键 + */ + @SaCheckPermission("qms:qcUnqualifiedRecord:query") + @GetMapping("/{recordId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long recordId) { + return R.ok(qcUnqualifiedRecordService.queryById(recordId)); + } + + /** + * 新增不合格品评审记录 + */ + @SaCheckPermission("qms:qcUnqualifiedRecord:add") + @Log(title = "不合格品评审记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody QcUnqualifiedRecordBo bo) { + return toAjax(qcUnqualifiedRecordService.insertByBo(bo)); + } + + /** + * 修改不合格品评审记录 + */ + @SaCheckPermission("qms:qcUnqualifiedRecord:edit") + @Log(title = "不合格品评审记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody QcUnqualifiedRecordBo bo) { + return toAjax(qcUnqualifiedRecordService.updateByBo(bo)); + } + + /** + * 删除不合格品评审记录 + * + * @param recordIds 主键串 + */ + @SaCheckPermission("qms:qcUnqualifiedRecord:remove") + @Log(title = "不合格品评审记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{recordIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] recordIds) { + return toAjax(qcUnqualifiedRecordService.deleteWithValidByIds(List.of(recordIds), true)); + } + + + /** + * 下拉框查询不合格品评审记录列表 + */ + + @GetMapping("/getQcUnqualifiedRecordList") + public R> getQcUnqualifiedRecordList(QcUnqualifiedRecordBo bo) { + List list = qcUnqualifiedRecordService.queryList(bo); + return R.ok(list); + } +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcUnqualifiedReviewController.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcUnqualifiedReviewController.java new file mode 100644 index 0000000..0e3c978 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcUnqualifiedReviewController.java @@ -0,0 +1,129 @@ +package org.dromara.qms.controller; + +import java.util.List; +import java.util.ArrayList; +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.qms.domain.vo.QcUnqualifiedReviewVo; +import org.dromara.qms.domain.bo.QcUnqualifiedReviewBo; +import org.dromara.qms.service.IQcUnqualifiedReviewService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 不合格品待评审 + * 前端访问路由地址为:/qms/qcUnqualifiedReview + * + * @author zch + * @date 2025-07-18 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/qcUnqualifiedReview") +public class QcUnqualifiedReviewController extends BaseController { + + private final IQcUnqualifiedReviewService qcUnqualifiedReviewService; + + /** + * 查询不合格品待评审列表 + */ + @SaCheckPermission("qms:qcUnqualifiedReview:list") + @GetMapping("/list") + public TableDataInfo list(QcUnqualifiedReviewBo bo, PageQuery pageQuery) { + return qcUnqualifiedReviewService.queryPageList(bo, pageQuery); + } + + /** + * 导出不合格品待评审列表 + */ + @SaCheckPermission("qms:qcUnqualifiedReview:export") + @Log(title = "不合格品待评审", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(QcUnqualifiedReviewBo bo, HttpServletResponse response) { + List list = qcUnqualifiedReviewService.queryList(bo); + ExcelUtil.exportExcel(list, "不合格品待评审", QcUnqualifiedReviewVo.class, response); + } + + /** + * 导出不合格品待评审列表模板(无任何数据) + */ + @SaCheckPermission("qms:qcUnqualifiedReview:export") + @Log(title = "不合格品待评审", businessType = BusinessType.EXPORT) + @PostMapping("/exportTemplate") + public void exportTemplate(QcUnqualifiedReviewBo bo, HttpServletResponse response) { + List list = new ArrayList<>(); + ExcelUtil.exportExcel(list, "不合格品待评审", QcUnqualifiedReviewVo.class, response); + } + + + /** + * 获取不合格品待评审详细信息 + * + * @param reviewId 主键 + */ + @SaCheckPermission("qms:qcUnqualifiedReview:query") + @GetMapping("/{reviewId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long reviewId) { + return R.ok(qcUnqualifiedReviewService.queryById(reviewId)); + } + + /** + * 新增不合格品待评审 + */ + @SaCheckPermission("qms:qcUnqualifiedReview:add") + @Log(title = "不合格品待评审", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody QcUnqualifiedReviewBo bo) { + return toAjax(qcUnqualifiedReviewService.insertByBo(bo)); + } + + /** + * 修改不合格品待评审 + */ + @SaCheckPermission("qms:qcUnqualifiedReview:edit") + @Log(title = "不合格品待评审", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody QcUnqualifiedReviewBo bo) { + return toAjax(qcUnqualifiedReviewService.updateByBo(bo)); + } + + /** + * 删除不合格品待评审 + * + * @param reviewIds 主键串 + */ + @SaCheckPermission("qms:qcUnqualifiedReview:remove") + @Log(title = "不合格品待评审", businessType = BusinessType.DELETE) + @DeleteMapping("/{reviewIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] reviewIds) { + return toAjax(qcUnqualifiedReviewService.deleteWithValidByIds(List.of(reviewIds), true)); + } + + + /** + * 下拉框查询不合格品待评审列表 + */ + + @GetMapping("/getQcUnqualifiedReviewList") + public R> getQcUnqualifiedReviewList(QcUnqualifiedReviewBo bo) { + List list = qcUnqualifiedReviewService.queryList(bo); + return R.ok(list); + } +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcUnqualifiedRecord.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcUnqualifiedRecord.java new file mode 100644 index 0000000..306d7fc --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcUnqualifiedRecord.java @@ -0,0 +1,47 @@ +package org.dromara.qms.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 不合格品评审记录对象 qc_unqualified_record + * + * @author zch + * @date 2025-07-18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("qc_unqualified_record") +public class QcUnqualifiedRecord extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 记录主键 + */ + @TableId(value = "record_id", type = IdType.ASSIGN_ID) + private Long recordId; + + /** + * 关联待评审表ID + */ + private Long reviewId; + + /** + * 备注 + */ + private String remark; + + /** + * 是否删除(0表示存在,2表示删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcUnqualifiedReview.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcUnqualifiedReview.java new file mode 100644 index 0000000..4881380 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcUnqualifiedReview.java @@ -0,0 +1,126 @@ +package org.dromara.qms.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 不合格品待评审对象 qc_unqualified_review + * + * @author zch + * @date 2025-07-18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("qc_unqualified_review") +public class QcUnqualifiedReview extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 评审主键 + */ + @TableId(value = "review_id", type = IdType.ASSIGN_ID) + private Long reviewId; + /** + * 不合格检测单号 + */ + private String unqualifiedNo; + + /** + * 关联质检主表ID + */ + private Long inspectionId; + + /** + * 检测类型ID + */ + private Long typeId; + + /** + * 派工单号 + */ + private String workOrder; + + /** + * 工序编码 + */ + private String processCode; + + /** + * 工序名称 + */ + private String processName; + + /** + * 批次号 + */ + private String batchNo; + + /** + * 物料名称 + */ + private String materialName; + + /** + * 物料编码 + */ + private String materialCode; + + /** + * 质检员检测结果 + */ + private String inspectorResult; + + /** + * 评审结果(0报废/1返工/2退货) + */ + private String reviewResult; + + /** + * 评审人 + */ + private String reviewer; + + /** + * 评审时间 + */ + private Date reviewTime; + + /** + * 是否删除(0表示存在,2表示删除) + */ + @TableLogic + private String delFlag; + + /** + * 检测单号/卡号 + */ + @TableField(exist = false) + private String inspectionNo;//JOIN + + /** + * 检测类型编码 + */ + @TableField(exist = false) + private String typeCode;//JOIN + + /** + * 检测类型名称 + */ + @TableField(exist = false) + private String typeName;//JOIN + + /** + * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + */ + @TableField(exist = false) + private Long qcInspectionType;//JOIN + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcUnqualifiedRecordBo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcUnqualifiedRecordBo.java new file mode 100644 index 0000000..090148f --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcUnqualifiedRecordBo.java @@ -0,0 +1,75 @@ +package org.dromara.qms.domain.bo; + +import org.dromara.qms.domain.QcUnqualifiedRecord; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.util.Date; + +/** + * 不合格品评审记录业务对象 qc_unqualified_record + * + * @author zch + * @date 2025-07-18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = QcUnqualifiedRecord.class, reverseConvertGenerate = false) +public class QcUnqualifiedRecordBo extends BaseEntity { + + /** + * 记录主键 + */ + private Long recordId; + + /** + * 关联待评审表ID + */ + private Long reviewId; + + /** + * 备注 + */ + private String remark; + + /** + * PDA查询参数:评审人(用于查询特定用户的评审记录) + */ + private String reviewer; + + /** + * PDA查询参数:开始时间(用于时间范围查询) + */ + private Date startTime; + + /** + * PDA查询参数:结束时间(用于时间范围查询) + */ + private Date endTime; + + /** + * PDA查询参数:物料编码(用于按物料筛选) + */ + private String materialCode; + + /** + * PDA查询参数:物料名称(用于按物料筛选) + */ + private String materialName; + + /** + * PDA查询参数:评审结果(用于按评审结果筛选) + */ + private String reviewResult; + + /** + * PDA查询参数:不合格检测单号(用于按单号筛选) + */ + private String unqualifiedNo; + + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcUnqualifiedReviewBo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcUnqualifiedReviewBo.java new file mode 100644 index 0000000..0aa3e79 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcUnqualifiedReviewBo.java @@ -0,0 +1,116 @@ +package org.dromara.qms.domain.bo; + +import org.dromara.qms.domain.QcUnqualifiedReview; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 不合格品待评审业务对象 qc_unqualified_review + * + * @author zch + * @date 2025-07-18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = QcUnqualifiedReview.class, reverseConvertGenerate = false) +public class QcUnqualifiedReviewBo extends BaseEntity { + + /** + * 评审主键 + */ + private Long reviewId; + + /** + * 不合格检测单号 + */ + private String unqualifiedNo; + + /** + * 关联质检主表ID + */ + private Long inspectionId; + + /** + * 检测类型ID + */ + private Long typeId; + + /** + * 派工单号 + */ + private String workOrder; + + /** + * 工序编码 + */ + private String processCode; + + /** + * 工序名称 + */ + private String processName; + + /** + * 批次号 + */ + private String batchNo; + + /** + * 物料名称 + */ + private String materialName; + + /** + * 物料编码 + */ + private String materialCode; + + /** + * 质检员检测结果 + */ + private String inspectorResult; + + /** + * 评审结果(0报废/1返工/2退货) + */ + private String reviewResult; + + /** + * 评审人 + */ + private String reviewer; + + /** + * 评审时间 + */ + private Date reviewTime; + + /** + * 检测单号/卡号 + */ + private String inspectionNo;//JOIN + + /** + * 检测类型编码 + */ + private String typeCode;//JOIN + + /** + * 检测类型名称 + */ + private String typeName;//JOIN + + /** + * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + */ + private Long qcInspectionType;//JOIN + + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionItemDetailVo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionItemDetailVo.java new file mode 100644 index 0000000..d99ca56 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionItemDetailVo.java @@ -0,0 +1,195 @@ +package org.dromara.qms.domain.vo; + +import java.math.BigDecimal; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 检测项明细视图对象 - PDA专用 + * 用于质检任务详情页面显示检测项信息和结果 + * + * @author zch + * @date 2025-07-18 + */ +@Data +@ExcelIgnoreUnannotated +public class QcInspectionItemDetailVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 检测项主键 + */ + private Long itemId; + + /** + * 检测项编码 + */ + @ExcelProperty(value = "检测项编码") + private String itemCode; + + /** + * 检测项名称 + */ + @ExcelProperty(value = "检测项名称") + private String itemName; + + /** + * 检测位置 + */ + @ExcelProperty(value = "检测位置") + private String location; + + /** + * 检测项目类别 + */ + @ExcelProperty(value = "检测项目类别") + private String category; + + /** + * 检测方法 + */ + @ExcelProperty(value = "检测方法") + private String method; + + /** + * 检测方法文本 + */ + private String methodText; + + /** + * 检测方式(0定性,1定量) + */ + @ExcelProperty(value = "检测方式", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "detect_type") + private Long detectType; + + /** + * 检测方式文本(定性/定量) + */ + private String detectTypeText; + + /** + * 检测结果 + */ + @ExcelProperty(value = "检测结果") + private String result; + + /** + * 检测值(定量检测时使用) + */ + @ExcelProperty(value = "检测值") + private BigDecimal value; + + /** + * 控制上限 + */ + @ExcelProperty(value = "控制上限") + private BigDecimal upperLimit; + + /** + * 控制下限 + */ + @ExcelProperty(value = "控制下限") + private BigDecimal lowerLimit; + + /** + * 规格质检值 + */ + @ExcelProperty(value = "规格质检值") + private BigDecimal specValue; + + /** + * 规格上限 + */ + @ExcelProperty(value = "规格上限") + private BigDecimal specUpperLimit; + + /** + * 规格下限 + */ + @ExcelProperty(value = "规格下限") + private BigDecimal specLowerLimit; + + /** + * 检测项说明 + */ + @ExcelProperty(value = "检测项说明") + private String description; + + /** + * 是否必检项 + */ + private Boolean isRequired; + + /** + * 是否已检测 + */ + private Boolean isCompleted; + + /** + * 检测结果状态(0合格/1不合格) + */ + private Long resultStatus; + + /** + * 检测结果状态文本(合格/不合格) + */ + private String resultStatusText; + + /** + * 是否超出控制限 + */ + private Boolean isOutOfControlLimit; + + /** + * 是否超出规格限 + */ + private Boolean isOutOfSpecLimit; + + /** + * 检测选项列表(定性检测时使用) + */ + private String optionCode; + + /** + * 检测选项名称 + */ + private String optionName; + + /** + * 是否默认选项 + */ + private String isDefault; + + /** + * 检测人员 + */ + private String inspector; + + /** + * 检测时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date inspectionTime; + + /** + * 排序号 + */ + private Integer sortOrder; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcUnqualifiedRecordVo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcUnqualifiedRecordVo.java new file mode 100644 index 0000000..465443a --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcUnqualifiedRecordVo.java @@ -0,0 +1,136 @@ +package org.dromara.qms.domain.vo; + +import org.dromara.qms.domain.QcUnqualifiedRecord; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 不合格品评审记录视图对象 qc_unqualified_record + * + * @author zch + * @date 2025-07-18 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = QcUnqualifiedRecord.class) +public class QcUnqualifiedRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 记录主键 + */ + @ExcelProperty(value = "记录主键") + private Long recordId; + + /** + * 关联待评审表ID + */ + @ExcelProperty(value = "关联待评审表ID") + private Long reviewId; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + // PDA需要的关联信息(来自QcUnqualifiedReview表) + /** + * 不合格检测单号 + */ + @ExcelProperty(value = "不合格检测单号") + private String unqualifiedNo; + + /** + * 关联质检主表ID + */ + @ExcelProperty(value = "关联质检主表ID") + private Long inspectionId; + + /** + * 检测单号/卡号 + */ + @ExcelProperty(value = "检测单号/卡号") + private String inspectionNo; + + /** + * 检测类型名称 + */ + @ExcelProperty(value = "检测类型名称") + private String typeName; + + /** + * 派工单号 + */ + @ExcelProperty(value = "派工单号") + private String workOrder; + + /** + * 工序名称 + */ + @ExcelProperty(value = "工序名称") + private String processName; + + /** + * 批次号 + */ + @ExcelProperty(value = "批次号") + private String batchNo; + + /** + * 物料名称 + */ + @ExcelProperty(value = "物料名称") + private String materialName; + + /** + * 物料编码 + */ + @ExcelProperty(value = "物料编码") + private String materialCode; + + /** + * 质检员检测结果 + */ + @ExcelProperty(value = "质检员检测结果") + private String inspectorResult; + + /** + * 评审结果(0报废/1返工/2退货/3让步接收/4流转) + */ + @ExcelProperty(value = "评审结果") + @ExcelDictFormat(dictType = "qc_review_result") + private String reviewResult; + + /** + * 评审人 + */ + @ExcelProperty(value = "评审人") + private String reviewer; + + /** + * 评审时间 + */ + @ExcelProperty(value = "评审时间") + private Date reviewTime; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcUnqualifiedReviewVo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcUnqualifiedReviewVo.java new file mode 100644 index 0000000..16d12ab --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcUnqualifiedReviewVo.java @@ -0,0 +1,141 @@ +package org.dromara.qms.domain.vo; + +import java.util.Date; +import java.util.List; +import java.math.BigDecimal; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.dromara.qms.domain.QcUnqualifiedReview; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 不合格品待评审视图对象 qc_unqualified_review + * + * @author zch + * @date 2025-07-18 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = QcUnqualifiedReview.class) +public class QcUnqualifiedReviewVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 评审主键 + */ + @ExcelProperty(value = "评审主键") + private Long reviewId; + + /** + * 不合格检测单号 + */ + @ExcelProperty(value = "不合格检测单号") + private String unqualifiedNo; + + /** + * 关联质检主表ID + */ + @ExcelProperty(value = "关联质检主表ID") + private Long inspectionId; + + /** + * 检测类型ID + */ + @ExcelProperty(value = "检测类型ID") + private Long typeId; + + /** + * 派工单号 + */ + @ExcelProperty(value = "派工单号") + private String workOrder; + + /** + * 工序编码 + */ + @ExcelProperty(value = "工序编码") + private String processCode; + + /** + * 工序名称 + */ + @ExcelProperty(value = "工序名称") + private String processName; + + /** + * 批次号 + */ + @ExcelProperty(value = "批次号") + private String batchNo; + + /** + * 物料名称 + */ + @ExcelProperty(value = "物料名称") + private String materialName; + + /** + * 物料编码 + */ + @ExcelProperty(value = "物料编码") + private String materialCode; + + /** + * 质检员检测结果 + */ + @ExcelProperty(value = "质检员检测结果") + private String inspectorResult; + + /** + * 评审结果(0报废/1返工/2退货) + */ + @ExcelProperty(value = "评审结果", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "review_result") + private String reviewResult; + + /** + * 评审人 + */ + @ExcelProperty(value = "评审人") + private String reviewer; + + /** + * 评审时间 + */ + @ExcelProperty(value = "评审时间") + private Date reviewTime; + + /** + * 检测单号/卡号 + */ + private String inspectionNo;//JOIN + + + /** + * 检测类型编码 + */ + private String typeCode;//JOIN + + /** + * 检测类型名称 + */ + private String typeName;//JOIN + + /** + * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + */ + private Long qcInspectionType;//JOIN + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/mapper/QcUnqualifiedRecordMapper.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/mapper/QcUnqualifiedRecordMapper.java new file mode 100644 index 0000000..e58a378 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/mapper/QcUnqualifiedRecordMapper.java @@ -0,0 +1,34 @@ +package org.dromara.qms.mapper; + +import org.dromara.qms.domain.bo.QcUnqualifiedRecordBo; +import org.dromara.qms.domain.QcUnqualifiedRecord; +import org.dromara.qms.domain.vo.QcUnqualifiedRecordVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 不合格品评审记录Mapper接口 + * + * @author zch + * @date 2025-07-18 + */ +public interface QcUnqualifiedRecordMapper extends BaseMapperPlus { + + /** + * 查询不合格品评审记录列表 + * + * @param bo 查询条件 + * @return 不合格品评审记录列表 + */ + List selectVoList(QcUnqualifiedRecordBo bo); + + /** + * 根据主键查询不合格品评审记录 + * + * @param recordId 主键 + * @return 不合格品评审记录 + */ + QcUnqualifiedRecordVo selectVoByRecordId(Long recordId); + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/mapper/QcUnqualifiedReviewMapper.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/mapper/QcUnqualifiedReviewMapper.java new file mode 100644 index 0000000..c585bdb --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/mapper/QcUnqualifiedReviewMapper.java @@ -0,0 +1,34 @@ +package org.dromara.qms.mapper; + +import org.dromara.qms.domain.bo.QcUnqualifiedReviewBo; +import org.dromara.qms.domain.QcUnqualifiedReview; +import org.dromara.qms.domain.vo.QcUnqualifiedReviewVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 不合格品待评审Mapper接口 + * + * @author zch + * @date 2025-07-18 + */ +public interface QcUnqualifiedReviewMapper extends BaseMapperPlus { + + /** + * 查询不合格品待评审列表 + * + * @param bo 查询条件 + * @return 不合格品待评审列表 + */ + List selectVoList(QcUnqualifiedReviewBo bo); + + /** + * 根据主键查询不合格品待评审 + * + * @param reviewId 主键 + * @return 不合格品待评审 + */ + QcUnqualifiedReviewVo selectVoByReviewId(Long reviewId); + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcUnqualifiedRecordService.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcUnqualifiedRecordService.java new file mode 100644 index 0000000..7e08648 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcUnqualifiedRecordService.java @@ -0,0 +1,96 @@ +package org.dromara.qms.service; + +import org.dromara.qms.domain.QcUnqualifiedRecord; +import org.dromara.qms.domain.vo.QcUnqualifiedRecordVo; +import org.dromara.qms.domain.bo.QcUnqualifiedRecordBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 不合格品评审记录Service接口 + * + * @author zch + * @date 2025-07-18 + */ +public interface IQcUnqualifiedRecordService { + + /** + * 查询不合格品评审记录 + * + * @param recordId 主键 + * @return 不合格品评审记录 + */ + QcUnqualifiedRecordVo queryById(Long recordId); + + /** + * 分页查询不合格品评审记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 不合格品评审记录分页列表 + */ + TableDataInfo queryPageList(QcUnqualifiedRecordBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的不合格品评审记录列表 + * + * @param bo 查询条件 + * @return 不合格品评审记录列表 + */ + List queryList(QcUnqualifiedRecordBo bo); + + /** + * 新增不合格品评审记录 + * + * @param bo 不合格品评审记录 + * @return 是否新增成功 + */ + Boolean insertByBo(QcUnqualifiedRecordBo bo); + + /** + * 修改不合格品评审记录 + * + * @param bo 不合格品评审记录 + * @return 是否修改成功 + */ + Boolean updateByBo(QcUnqualifiedRecordBo bo); + + /** + * 校验并批量删除不合格品评审记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * PDA专用:分页查询不合格品评审记录列表(包含关联信息) + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 不合格品评审记录分页列表(包含评审任务详细信息) + */ + TableDataInfo queryPageListWithDetails(QcUnqualifiedRecordBo bo, PageQuery pageQuery); + + /** + * PDA专用:根据用户查询评审记录历史 + * + * @param reviewer 评审人 + * @param pageQuery 分页参数 + * @return 用户的评审记录历史 + */ + TableDataInfo queryPageUserRecords(String reviewer, PageQuery pageQuery); + + /** + * PDA专用:根据时间范围查询评审记录 + * + * @param bo 查询条件(包含时间范围) + * @param pageQuery 分页参数 + * @return 指定时间范围内的评审记录 + */ + TableDataInfo queryPageRecordsByDateRange(QcUnqualifiedRecordBo bo, PageQuery pageQuery); +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcUnqualifiedReviewService.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcUnqualifiedReviewService.java new file mode 100644 index 0000000..479a041 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcUnqualifiedReviewService.java @@ -0,0 +1,101 @@ +package org.dromara.qms.service; + +import org.dromara.qms.domain.QcUnqualifiedReview; +import org.dromara.qms.domain.vo.QcUnqualifiedReviewVo; +import org.dromara.qms.domain.bo.QcUnqualifiedReviewBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 不合格品待评审Service接口 + * + * @author zch + * @date 2025-07-18 + */ +public interface IQcUnqualifiedReviewService { + + /** + * 查询不合格品待评审 + * + * @param reviewId 主键 + * @return 不合格品待评审 + */ + QcUnqualifiedReviewVo queryById(Long reviewId); + + /** + * 分页查询不合格品待评审列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 不合格品待评审分页列表 + */ + TableDataInfo queryPageList(QcUnqualifiedReviewBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的不合格品待评审列表 + * + * @param bo 查询条件 + * @return 不合格品待评审列表 + */ + List queryList(QcUnqualifiedReviewBo bo); + + /** + * 新增不合格品待评审 + * + * @param bo 不合格品待评审 + * @return 是否新增成功 + */ + Boolean insertByBo(QcUnqualifiedReviewBo bo); + + /** + * 修改不合格品待评审 + * + * @param bo 不合格品待评审 + * @return 是否修改成功 + */ + Boolean updateByBo(QcUnqualifiedReviewBo bo); + + /** + * 校验并批量删除不合格品待评审信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 分页查询待评审不合格品列表 (for PDA) + */ + TableDataInfo queryPagePendingReviews(QcUnqualifiedReviewBo bo, PageQuery pageQuery); + + /** + * 查询不合格评审详情包括质检项目 (for PDA) + */ + QcUnqualifiedReviewVo queryByIdWithItems(Long reviewId); + + /** + * 提交评审结果 (for PDA), 更新并触发流程 + */ + Boolean submitReview(QcUnqualifiedReviewBo bo); + + /** + * 获取不合格品统计汇总信息 (for PDA) + * + * @param dateRange 日期范围 + * @return 统计汇总数据 + */ + Map getUnqualifiedSummary(String dateRange); + + /** + * 触发工作流程处理 (for PDA) + * + * @param bo 不合格品评审信息 + * @return 是否触发成功 + */ + Boolean triggerWorkflowProcess(QcUnqualifiedReviewBo bo); +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedGeneratorService.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedGeneratorService.java new file mode 100644 index 0000000..8aeb6d6 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedGeneratorService.java @@ -0,0 +1,278 @@ +package org.dromara.qms.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.qms.domain.bo.QcUnqualifiedReviewBo; +import org.dromara.qms.domain.vo.QcInspectionMainVo; +import org.dromara.qms.domain.vo.QcInspectionItemDetailVo; +import org.dromara.qms.service.IQcUnqualifiedReviewService; +import org.dromara.qms.utils.QcInspectionResultCalculator; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 不合格品自动生成服务 + * 实现不合格品自动生成逻辑 + * + * @author zch + * @date 2025-07-18 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class QcUnqualifiedGeneratorService { + + private final IQcUnqualifiedReviewService qcUnqualifiedReviewService; + + /** + * 根据质检结果自动生成不合格品评审任务 + * 实现质检不合格时自动生成不合格品评审任务 + * + * @param inspectionVo 质检主表数据 + * @param inspectionItems 检测项列表 + * @return 是否生成成功 + */ + public Boolean autoGenerateUnqualifiedReview(QcInspectionMainVo inspectionVo, List inspectionItems) { + if (inspectionVo == null) { + log.error("质检主表数据为空,无法生成不合格品评审任务"); + return false; + } + + // 检查是否存在不合格项 + List unqualifiedItems = getUnqualifiedItems(inspectionItems); + if (unqualifiedItems.isEmpty()) { + log.info("质检任务全部合格,无需生成不合格品评审任务: {}", inspectionVo.getInspectionNo()); + return true; + } + + try { + // 生成不合格品评审任务 + QcUnqualifiedReviewBo reviewBo = createUnqualifiedReviewBo(inspectionVo, unqualifiedItems); + + // 保存不合格品评审任务 + Boolean result = qcUnqualifiedReviewService.insertByBo(reviewBo); + + if (result) { + log.info("成功生成不合格品评审任务: 质检单号={}, 不合格项数量={}", + inspectionVo.getInspectionNo(), unqualifiedItems.size()); + } else { + log.error("生成不合格品评审任务失败: {}", inspectionVo.getInspectionNo()); + } + + return result; + } catch (Exception e) { + log.error("生成不合格品评审任务异常: {}", inspectionVo.getInspectionNo(), e); + return false; + } + } + + /** + * 获取不合格的检测项 + * + * @param inspectionItems 检测项列表 + * @return 不合格检测项列表 + */ + private List getUnqualifiedItems(List inspectionItems) { + if (inspectionItems == null || inspectionItems.isEmpty()) { + return List.of(); + } + + return inspectionItems.stream() + .filter(item -> { + Long resultStatus = QcInspectionResultCalculator.calculateItemResultStatus(item); + return QcInspectionResultCalculator.RESULT_STATUS_UNQUALIFIED.equals(resultStatus); + }) + .collect(Collectors.toList()); + } + + /** + * 创建不合格品评审业务对象 + * + * @param inspectionVo 质检主表数据 + * @param unqualifiedItems 不合格检测项列表 + * @return 不合格品评审业务对象 + */ + private QcUnqualifiedReviewBo createUnqualifiedReviewBo(QcInspectionMainVo inspectionVo, List unqualifiedItems) { + QcUnqualifiedReviewBo reviewBo = new QcUnqualifiedReviewBo(); + + // 生成不合格检测单号 + String unqualifiedNo = generateUnqualifiedNo(inspectionVo.getInspectionNo()); + reviewBo.setUnqualifiedNo(unqualifiedNo); + + // 设置关联的质检主表ID + reviewBo.setInspectionId(inspectionVo.getInspectionId()); + + // 设置检测类型 + reviewBo.setTypeId(inspectionVo.getTypeId()); + + // 复制质检主表的基本信息 +// reviewBo.setWorkOrder(inspectionVo.getWorkOrder()); +// reviewBo.setProcessCode(inspectionVo.getProcessCode()); + reviewBo.setProcessName(inspectionVo.getProcessName()); + reviewBo.setBatchNo(inspectionVo.getBatchNo()); + reviewBo.setMaterialName(inspectionVo.getMaterialName()); + reviewBo.setMaterialCode(inspectionVo.getMaterialCode()); + + // 设置质检员检测结果描述 + String inspectorResult = buildInspectorResultDescription(unqualifiedItems); + reviewBo.setInspectorResult(inspectorResult); + + // 初始状态:待评审(reviewResult为空) + reviewBo.setReviewResult(null); + reviewBo.setReviewer(null); + reviewBo.setReviewTime(null); + + return reviewBo; + } + + /** + * 生成不合格检测单号 + * 格式:UQ + 原检测单号 + 时间戳后4位 + * + * @param inspectionNo 原检测单号 + * @return 不合格检测单号 + */ + private String generateUnqualifiedNo(String inspectionNo) { + String timestamp = String.valueOf(System.currentTimeMillis()); + String suffix = timestamp.substring(timestamp.length() - 4); + + if (StringUtils.isNotBlank(inspectionNo)) { + return "UQ" + inspectionNo + suffix; + } else { + return "UQ" + System.currentTimeMillis(); + } + } + + /** + * 构建质检员检测结果描述 + * + * @param unqualifiedItems 不合格检测项列表 + * @return 检测结果描述 + */ + private String buildInspectorResultDescription(List unqualifiedItems) { + if (unqualifiedItems == null || unqualifiedItems.isEmpty()) { + return "无不合格项"; + } + + StringBuilder description = new StringBuilder(); + description.append("不合格项目:"); + + for (int i = 0; i < unqualifiedItems.size(); i++) { + QcInspectionItemDetailVo item = unqualifiedItems.get(i); + + if (i > 0) { + description.append(";"); + } + + description.append(item.getItemName()); + + // 添加具体的不合格原因 + if (QcInspectionResultCalculator.DETECT_TYPE_QUANTITATIVE.equals(item.getDetectType())) { + // 定量检测:显示检测值和限值 + if (item.getValue() != null) { + description.append("(检测值:").append(item.getValue()); + + if (Boolean.TRUE.equals(item.getIsOutOfControlLimit())) { + description.append(", 超出控制限"); + if (item.getUpperLimit() != null) { + description.append(", 上限:").append(item.getUpperLimit()); + } + if (item.getLowerLimit() != null) { + description.append(", 下限:").append(item.getLowerLimit()); + } + } + + description.append(")"); + } + } else { + // 定性检测:显示检测结果 + if (StringUtils.isNotBlank(item.getResult())) { + description.append("(结果:").append(item.getResult()).append(")"); + } + } + } + + // 限制描述长度,避免过长 + String result = description.toString(); + if (result.length() > 500) { + result = result.substring(0, 497) + "..."; + } + + return result; + } + + /** + * 检查是否需要生成不合格品评审任务 + * + * @param inspectionVo 质检主表数据 + * @param overallResult 整体质检结果 + * @return 是否需要生成 + */ + public Boolean shouldGenerateUnqualifiedReview(QcInspectionMainVo inspectionVo, Long overallResult) { + // 如果整体结果合格,不需要生成 + if (QcInspectionResultCalculator.RESULT_STATUS_QUALIFIED.equals(overallResult)) { + return false; + } + + // 检查是否已经存在不合格品评审任务 + if (hasExistingUnqualifiedReview(inspectionVo.getInspectionId())) { + log.info("质检任务已存在不合格品评审任务,跳过生成: {}", inspectionVo.getInspectionNo()); + return false; + } + + return true; + } + + /** + * 检查是否已存在不合格品评审任务 + * + * @param inspectionId 质检主表ID + * @return 是否已存在 + */ + private Boolean hasExistingUnqualifiedReview(Long inspectionId) { + try { + // 查询是否已存在该质检任务的不合格品评审记录 + QcUnqualifiedReviewBo queryBo = new QcUnqualifiedReviewBo(); + queryBo.setInspectionId(inspectionId); + + return !qcUnqualifiedReviewService.queryList(queryBo).isEmpty(); + } catch (Exception e) { + log.error("检查不合格品评审任务是否存在时发生异常: inspectionId={}", inspectionId, e); + return false; + } + } + + /** + * 批量生成不合格品评审任务 + * + * @param inspectionList 质检任务列表 + * @return 成功生成的数量 + */ + public Integer batchGenerateUnqualifiedReviews(List inspectionList) { + if (inspectionList == null || inspectionList.isEmpty()) { + return 0; + } + + int successCount = 0; + for (QcInspectionMainVo inspection : inspectionList) { + try { + // 这里需要获取检测项数据,实际实现中可能需要调用其他服务 + // List items = getInspectionItems(inspection.getInspectionId()); + // Boolean result = autoGenerateUnqualifiedReview(inspection, items); + // if (Boolean.TRUE.equals(result)) { + // successCount++; + // } + } catch (Exception e) { + log.error("批量生成不合格品评审任务失败: {}", inspection.getInspectionNo(), e); + } + } + + log.info("批量生成不合格品评审任务完成,总数: {}, 成功: {}", inspectionList.size(), successCount); + return successCount; + } +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedRecordServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedRecordServiceImpl.java new file mode 100644 index 0000000..f473051 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedRecordServiceImpl.java @@ -0,0 +1,148 @@ +package org.dromara.qms.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.dromara.qms.domain.bo.QcUnqualifiedRecordBo; +import org.dromara.qms.domain.vo.QcUnqualifiedRecordVo; +import org.dromara.qms.domain.QcUnqualifiedRecord; +import org.dromara.qms.mapper.QcUnqualifiedRecordMapper; +import org.dromara.qms.service.IQcUnqualifiedRecordService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 不合格品评审记录Service业务层处理 + * + * @author zch + * @date 2025-07-18 + */ +@RequiredArgsConstructor +@Service +public class QcUnqualifiedRecordServiceImpl implements IQcUnqualifiedRecordService { + + private final QcUnqualifiedRecordMapper baseMapper; + + /** + * 查询不合格品评审记录 + * + * @param recordId 主键 + * @return 不合格品评审记录 + */ + @Override + public QcUnqualifiedRecordVo queryById(Long recordId){ + return baseMapper.selectVoById(recordId); + } + + /** + * 分页查询不合格品评审记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 不合格品评审记录分页列表 + */ + @Override + public TableDataInfo queryPageList(QcUnqualifiedRecordBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的不合格品评审记录列表 + * + * @param bo 查询条件 + * @return 不合格品评审记录列表 + */ + @Override + public List queryList(QcUnqualifiedRecordBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(QcUnqualifiedRecordBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(QcUnqualifiedRecord.class) + .selectAll(QcUnqualifiedRecord.class) + .eq(bo.getRecordId() != null, QcUnqualifiedRecord::getRecordId, bo.getRecordId()) + .eq(bo.getReviewId() != null, QcUnqualifiedRecord::getReviewId, bo.getReviewId()) + .orderByDesc(QcUnqualifiedRecord::getCreateTime); + return lqw; + } + + /** + * 新增不合格品评审记录 + * + * @param bo 不合格品评审记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(QcUnqualifiedRecordBo bo) { + QcUnqualifiedRecord add = MapstructUtils.convert(bo, QcUnqualifiedRecord.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setRecordId(add.getRecordId()); + } + return flag; + } + + /** + * 修改不合格品评审记录 + * + * @param bo 不合格品评审记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(QcUnqualifiedRecordBo bo) { + QcUnqualifiedRecord update = MapstructUtils.convert(bo, QcUnqualifiedRecord.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(QcUnqualifiedRecord entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除不合格品评审记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public TableDataInfo queryPageListWithDetails(QcUnqualifiedRecordBo bo, PageQuery pageQuery) { + return null; + } + + @Override + public TableDataInfo queryPageUserRecords(String reviewer, PageQuery pageQuery) { + return null; + } + + @Override + public TableDataInfo queryPageRecordsByDateRange(QcUnqualifiedRecordBo bo, PageQuery pageQuery) { + return null; + } +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java new file mode 100644 index 0000000..5b79035 --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java @@ -0,0 +1,693 @@ +package org.dromara.qms.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.qms.domain.QcInspectionMain; +import org.dromara.qms.domain.QcInspectionType; +import org.dromara.qms.domain.bo.QcInspectionMainBo; +import org.dromara.qms.domain.bo.QcInspectionResultBo; +import org.dromara.qms.domain.vo.QcInspectionResultVo; +import org.dromara.qms.service.IQcInspectionResultService; +import org.springframework.stereotype.Service; +import org.dromara.qms.domain.bo.QcUnqualifiedReviewBo; +import org.dromara.qms.domain.vo.QcUnqualifiedReviewVo; +import org.dromara.qms.domain.QcUnqualifiedReview; +import org.dromara.qms.mapper.QcUnqualifiedReviewMapper; +import org.dromara.qms.service.IQcUnqualifiedReviewService; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 不合格品待评审Service业务层处理 + * + * @author zch + * @date 2025-07-18 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class QcUnqualifiedReviewServiceImpl implements IQcUnqualifiedReviewService { + + private final QcUnqualifiedReviewMapper baseMapper; + private final IQcInspectionResultService qcInspectionResultService; + + /** + * 查询不合格品待评审 + * + * @param reviewId 主键 + * @return 不合格品待评审 + */ + @Override + public QcUnqualifiedReviewVo queryById(Long reviewId){ + return baseMapper.selectVoById(reviewId); + } + + /** + * 分页查询不合格品待评审列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 不合格品待评审分页列表 + */ + @Override + public TableDataInfo queryPageList(QcUnqualifiedReviewBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的不合格品待评审列表 + * + * @param bo 查询条件 + * @return 不合格品待评审列表 + */ + @Override + public List queryList(QcUnqualifiedReviewBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 分页查询待评审不合格品列表 (for PDA) + * 只查询reviewResult为空的记录,表示待评审状态 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 待评审不合格品分页列表 + */ + @Override + public TableDataInfo queryPagePendingReviews(QcUnqualifiedReviewBo bo, PageQuery pageQuery) { + // 设置查询条件:只查询待评审的记录(reviewResult为空) + QcUnqualifiedReviewBo pendingBo = new QcUnqualifiedReviewBo(); + // 复制原有查询条件 + if (bo != null) { + pendingBo.setUnqualifiedNo(bo.getUnqualifiedNo()); + pendingBo.setInspectionId(bo.getInspectionId()); + pendingBo.setTypeId(bo.getTypeId()); + pendingBo.setWorkOrder(bo.getWorkOrder()); + pendingBo.setProcessCode(bo.getProcessCode()); + pendingBo.setProcessName(bo.getProcessName()); + pendingBo.setBatchNo(bo.getBatchNo()); + pendingBo.setMaterialName(bo.getMaterialName()); + pendingBo.setMaterialCode(bo.getMaterialCode()); + pendingBo.setInspectorResult(bo.getInspectorResult()); + pendingBo.setReviewer(bo.getReviewer()); + pendingBo.setParams(bo.getParams()); + } + // 强制设置reviewResult为null,查询待评审记录 + pendingBo.setReviewResult(null); + + MPJLambdaWrapper lqw = buildQueryWrapper(pendingBo); + // 添加条件:reviewResult为空或null + lqw.and(wrapper -> wrapper.isNull(QcUnqualifiedReview::getReviewResult) + .or().eq(QcUnqualifiedReview::getReviewResult, "")); + + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询不合格评审详情包括质检项目 (for PDA) + * 关联查询不合格品的检测项明细 + * + * @param reviewId 主键 + * @return 不合格品待评审详情,包含检测项明细 + */ + @Override + public QcUnqualifiedReviewVo queryByIdWithItems(Long reviewId) { + QcUnqualifiedReviewVo reviewVo = queryById(reviewId); + if (reviewVo != null && reviewVo.getInspectionId() != null) { + // 查询关联的检测项结果明细 + QcInspectionResultBo resultBo = new QcInspectionResultBo(); + resultBo.setInspectionId(reviewVo.getInspectionId()); + List inspectionResults = qcInspectionResultService.queryList(resultBo); + + // 可以将检测项结果添加到reviewVo中,但由于QcUnqualifiedReviewVo没有相应字段 + // 这里暂时通过日志记录或者扩展VO类来处理 + // 实际项目中可能需要扩展QcUnqualifiedReviewVo类添加inspectionResults字段 + } + return reviewVo; + } + + /** + * 提交评审结果 (for PDA) + * 处理评审结果并触发后续流程 + * + * @param bo 不合格品待评审 + * @return 是否提交成功 + */ + @Override + public Boolean submitReview(QcUnqualifiedReviewBo bo) { + // 设置评审时间 + bo.setReviewTime(new Date()); + + // 更新评审结果 + Boolean updated = updateByBo(bo); + + if (updated) { + // 根据评审结果触发后续流程 + triggerWorkflowProcess(bo); + } + + return updated; + } + + /** + * 获取不合格品统计汇总信息 (for PDA) + * + * @param dateRange 日期范围,格式如 "2025-01-01,2025-01-31" + * @return 统计汇总数据 + */ + @Override + public Map getUnqualifiedSummary(String dateRange) { + Map summary = new HashMap<>(); + + // 解析日期范围 + LocalDate startDate = null; + LocalDate endDate = null; + if (StringUtils.isNotBlank(dateRange)) { + String[] dates = dateRange.split(","); + if (dates.length == 2) { + try { + startDate = LocalDate.parse(dates[0].trim(), DateTimeFormatter.ISO_LOCAL_DATE); + endDate = LocalDate.parse(dates[1].trim(), DateTimeFormatter.ISO_LOCAL_DATE); + } catch (Exception e) { + // 日期解析失败,使用默认范围(当月) + startDate = LocalDate.now().withDayOfMonth(1); + endDate = LocalDate.now(); + } + } + } else { + // 默认查询当月数据 + startDate = LocalDate.now().withDayOfMonth(1); + endDate = LocalDate.now(); + } + + // 构建查询条件 + QcUnqualifiedReviewBo bo = new QcUnqualifiedReviewBo(); + Map params = new HashMap<>(); + params.put("beginTime", startDate.toString()); + params.put("endTime", endDate.toString()); + bo.setParams(params); + + // 查询所有不合格品记录 + List allRecords = queryList(bo); + + // 统计总数 + summary.put("totalCount", allRecords.size()); + + // 统计待评审数量 + long pendingCount = allRecords.stream() + .filter(record -> StringUtils.isBlank(record.getReviewResult())) + .count(); + summary.put("pendingCount", pendingCount); + + // 统计已评审数量 + long reviewedCount = allRecords.size() - pendingCount; + summary.put("reviewedCount", reviewedCount); + + // 按评审结果分组统计 + Map reviewResultStats = allRecords.stream() + .filter(record -> StringUtils.isNotBlank(record.getReviewResult())) + .collect(Collectors.groupingBy( + QcUnqualifiedReviewVo::getReviewResult, + Collectors.counting() + )); + + summary.put("scrapCount", reviewResultStats.getOrDefault("0", 0L)); // 报废 + summary.put("reworkCount", reviewResultStats.getOrDefault("1", 0L)); // 返工 + summary.put("returnCount", reviewResultStats.getOrDefault("2", 0L)); // 退货 + summary.put("acceptCount", reviewResultStats.getOrDefault("3", 0L)); // 让步接收 + summary.put("transferCount", reviewResultStats.getOrDefault("4", 0L)); // 流转 + + // 按物料分组统计 + Map materialStats = allRecords.stream() + .filter(record -> StringUtils.isNotBlank(record.getMaterialName())) + .collect(Collectors.groupingBy( + QcUnqualifiedReviewVo::getMaterialName, + Collectors.counting() + )); + summary.put("materialStats", materialStats); + + // 按工序分组统计 + Map processStats = allRecords.stream() + .filter(record -> StringUtils.isNotBlank(record.getProcessName())) + .collect(Collectors.groupingBy( + QcUnqualifiedReviewVo::getProcessName, + Collectors.counting() + )); + summary.put("processStats", processStats); + + return summary; + } + + /** + * 触发工作流程处理 (for PDA) + * 实现评审完成后的状态更新和流程触发 + * + * @param bo 不合格品评审信息 + * @return 是否触发成功 + */ + @Override + public Boolean triggerWorkflowProcess(QcUnqualifiedReviewBo bo) { + if (bo == null || StringUtils.isBlank(bo.getReviewResult())) { + log.error("评审结果为空,无法触发工作流程"); + return false; + } + + try { + log.info("开始触发工作流程: 评审ID={}, 评审结果={}", bo.getReviewId(), bo.getReviewResult()); + + // 根据评审结果触发不同的后续流程 + switch (bo.getReviewResult()) { + case "0": // 报废 + return handleScrapProcess(bo); + case "1": // 返工 + return handleReworkProcess(bo); + case "2": // 退货 + return handleReturnProcess(bo); + case "3": // 让步接收 + return handleAcceptProcess(bo); + case "4": // 流转 + return handleTransferProcess(bo); + default: + log.warn("未知的评审结果: {}", bo.getReviewResult()); + return false; + } + } catch (Exception e) { + log.error("触发工作流程异常: 评审ID={}", bo.getReviewId(), e); + return false; + } + } + + /** + * 处理报废流程 + * 实现评审结果的五种处置方式:报废 + */ + private Boolean handleScrapProcess(QcUnqualifiedReviewBo bo) { + try { + log.info("执行报废流程: 评审ID={}", bo.getReviewId()); + + // 1. 更新库存状态为报废 + updateInventoryStatus(bo, "SCRAPPED"); + + // 2. 生成报废单据 + generateScrapDocument(bo); + + // 3. 更新质检主表状态 + updateInspectionMainStatus(bo, "SCRAPPED"); + + // 4. 记录处理日志 + recordProcessLog(bo, "报废流程执行完成"); + + log.info("报废流程执行成功: 评审ID={}", bo.getReviewId()); + return true; + } catch (Exception e) { + log.error("报废流程执行失败: 评审ID={}", bo.getReviewId(), e); + return false; + } + } + + /** + * 处理返工流程 + * 实现评审结果的五种处置方式:返工 + */ + private Boolean handleReworkProcess(QcUnqualifiedReviewBo bo) { + try { + log.info("执行返工流程: 评审ID={}", bo.getReviewId()); + + // 1. 生成返工工单 + generateReworkOrder(bo); + + // 2. 分派返工任务给相关人员 + assignReworkTask(bo); + + // 3. 更新质检主表状态为返工中 + updateInspectionMainStatus(bo, "REWORKING"); + + // 4. 更新库存状态 + updateInventoryStatus(bo, "REWORKING"); + + // 5. 记录处理日志 + recordProcessLog(bo, "返工流程执行完成,已生成返工工单"); + + log.info("返工流程执行成功: 评审ID={}", bo.getReviewId()); + return true; + } catch (Exception e) { + log.error("返工流程执行失败: 评审ID={}", bo.getReviewId(), e); + return false; + } + } + + /** + * 处理退货流程 + * 实现评审结果的五种处置方式:退货 + */ + private Boolean handleReturnProcess(QcUnqualifiedReviewBo bo) { + try { + log.info("执行退货流程: 评审ID={}", bo.getReviewId()); + + // 1. 生成退货单据 + generateReturnDocument(bo); + + // 2. 通知供应商 + notifySupplier(bo); + + // 3. 更新库存状态为待退货 + updateInventoryStatus(bo, "RETURNING"); + + // 4. 更新质检主表状态 + updateInspectionMainStatus(bo, "RETURNED"); + + // 5. 记录处理日志 + recordProcessLog(bo, "退货流程执行完成,已通知供应商"); + + log.info("退货流程执行成功: 评审ID={}", bo.getReviewId()); + return true; + } catch (Exception e) { + log.error("退货流程执行失败: 评审ID={}", bo.getReviewId(), e); + return false; + } + } + + /** + * 处理让步接收流程 + * 实现评审结果的五种处置方式:让步接收 + */ + private Boolean handleAcceptProcess(QcUnqualifiedReviewBo bo) { + try { + log.info("执行让步接收流程: 评审ID={}", bo.getReviewId()); + + // 1. 更新质检状态为让步合格 + updateInspectionMainStatus(bo, "CONDITIONALLY_ACCEPTED"); + + // 2. 更新库存状态为可用 + updateInventoryStatus(bo, "AVAILABLE"); + + // 3. 生成让步接收单据 + generateAcceptanceDocument(bo); + + // 4. 触发入库流程 + triggerInboundProcess(bo); + + // 5. 记录处理日志 + recordProcessLog(bo, "让步接收流程执行完成,产品已入库"); + + log.info("让步接收流程执行成功: 评审ID={}", bo.getReviewId()); + return true; + } catch (Exception e) { + log.error("让步接收流程执行失败: 评审ID={}", bo.getReviewId(), e); + return false; + } + } + + /** + * 处理流转流程 + * 实现评审结果的五种处置方式:流转 + */ + private Boolean handleTransferProcess(QcUnqualifiedReviewBo bo) { + try { + log.info("执行流转流程: 评审ID={}", bo.getReviewId()); + + // 1. 确定流转目标部门或工序 + String transferTarget = determineTransferTarget(bo); + + // 2. 生成流转单据 + generateTransferDocument(bo, transferTarget); + + // 3. 通知目标部门 + notifyTargetDepartment(bo, transferTarget); + + // 4. 更新质检主表状态 + updateInspectionMainStatus(bo, "TRANSFERRED"); + + // 5. 更新库存状态 + updateInventoryStatus(bo, "TRANSFERRED"); + + // 6. 记录处理日志 + recordProcessLog(bo, "流转流程执行完成,已转移至: " + transferTarget); + + log.info("流转流程执行成功: 评审ID={}, 流转目标={}", bo.getReviewId(), transferTarget); + return true; + } catch (Exception e) { + log.error("流转流程执行失败: 评审ID={}", bo.getReviewId(), e); + return false; + } + } + + // 以下是辅助方法的实现 + + /** + * 更新库存状态 + */ + private void updateInventoryStatus(QcUnqualifiedReviewBo bo, String status) { + try { + // TODO: 实现库存状态更新逻辑 + // 这里需要调用库存管理模块的接口 + log.info("更新库存状态: 评审ID={}, 状态={}", bo.getReviewId(), status); + } catch (Exception e) { + log.error("更新库存状态失败: 评审ID={}, 状态={}", bo.getReviewId(), status, e); + } + } + + /** + * 更新质检主表状态 + */ + private void updateInspectionMainStatus(QcUnqualifiedReviewBo bo, String status) { + try { + // TODO: 实现质检主表状态更新逻辑 + // 这里需要调用质检主表服务的更新方法 + log.info("更新质检主表状态: 质检ID={}, 状态={}", bo.getInspectionId(), status); + } catch (Exception e) { + log.error("更新质检主表状态失败: 质检ID={}, 状态={}", bo.getInspectionId(), status, e); + } + } + + /** + * 生成报废单据 + */ + private void generateScrapDocument(QcUnqualifiedReviewBo bo) { + try { + // TODO: 实现报废单据生成逻辑 + log.info("生成报废单据: 评审ID={}", bo.getReviewId()); + } catch (Exception e) { + log.error("生成报废单据失败: 评审ID={}", bo.getReviewId(), e); + } + } + + /** + * 生成返工工单 + */ + private void generateReworkOrder(QcUnqualifiedReviewBo bo) { + try { + // TODO: 实现返工工单生成逻辑 + log.info("生成返工工单: 评审ID={}", bo.getReviewId()); + } catch (Exception e) { + log.error("生成返工工单失败: 评审ID={}", bo.getReviewId(), e); + } + } + + /** + * 分派返工任务 + */ + private void assignReworkTask(QcUnqualifiedReviewBo bo) { + try { + // TODO: 实现返工任务分派逻辑 + log.info("分派返工任务: 评审ID={}", bo.getReviewId()); + } catch (Exception e) { + log.error("分派返工任务失败: 评审ID={}", bo.getReviewId(), e); + } + } + + /** + * 生成退货单据 + */ + private void generateReturnDocument(QcUnqualifiedReviewBo bo) { + try { + // TODO: 实现退货单据生成逻辑 + log.info("生成退货单据: 评审ID={}", bo.getReviewId()); + } catch (Exception e) { + log.error("生成退货单据失败: 评审ID={}", bo.getReviewId(), e); + } + } + + /** + * 通知供应商 + */ + private void notifySupplier(QcUnqualifiedReviewBo bo) { + try { + // TODO: 实现供应商通知逻辑 + log.info("通知供应商: 评审ID={}", bo.getReviewId()); + } catch (Exception e) { + log.error("通知供应商失败: 评审ID={}", bo.getReviewId(), e); + } + } + + /** + * 生成让步接收单据 + */ + private void generateAcceptanceDocument(QcUnqualifiedReviewBo bo) { + try { + // TODO: 实现让步接收单据生成逻辑 + log.info("生成让步接收单据: 评审ID={}", bo.getReviewId()); + } catch (Exception e) { + log.error("生成让步接收单据失败: 评审ID={}", bo.getReviewId(), e); + } + } + + /** + * 触发入库流程 + */ + private void triggerInboundProcess(QcUnqualifiedReviewBo bo) { + try { + // TODO: 实现入库流程触发逻辑 + log.info("触发入库流程: 评审ID={}", bo.getReviewId()); + } catch (Exception e) { + log.error("触发入库流程失败: 评审ID={}", bo.getReviewId(), e); + } + } + + /** + * 确定流转目标 + */ + private String determineTransferTarget(QcUnqualifiedReviewBo bo) { + try { + // TODO: 实现流转目标确定逻辑 + // 这里可以根据业务规则或配置来确定流转目标 + return "质量管理部"; // 默认流转目标 + } catch (Exception e) { + log.error("确定流转目标失败: 评审ID={}", bo.getReviewId(), e); + return "质量管理部"; + } + } + + /** + * 生成流转单据 + */ + private void generateTransferDocument(QcUnqualifiedReviewBo bo, String transferTarget) { + try { + // TODO: 实现流转单据生成逻辑 + log.info("生成流转单据: 评审ID={}, 目标={}", bo.getReviewId(), transferTarget); + } catch (Exception e) { + log.error("生成流转单据失败: 评审ID={}, 目标={}", bo.getReviewId(), transferTarget, e); + } + } + + /** + * 通知目标部门 + */ + private void notifyTargetDepartment(QcUnqualifiedReviewBo bo, String transferTarget) { + try { + // TODO: 实现目标部门通知逻辑 + log.info("通知目标部门: 评审ID={}, 目标={}", bo.getReviewId(), transferTarget); + } catch (Exception e) { + log.error("通知目标部门失败: 评审ID={}, 目标={}", bo.getReviewId(), transferTarget, e); + } + } + + /** + * 记录处理日志 + */ + private void recordProcessLog(QcUnqualifiedReviewBo bo, String message) { + try { + // TODO: 实现处理日志记录逻辑 + log.info("记录处理日志: 评审ID={}, 消息={}", bo.getReviewId(), message); + } catch (Exception e) { + log.error("记录处理日志失败: 评审ID={}, 消息={}", bo.getReviewId(), message, e); + } + } + + private MPJLambdaWrapper buildQueryWrapper(QcUnqualifiedReviewBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(QcUnqualifiedReview.class) + .selectAll(QcUnqualifiedReview.class) + + //关联质检主表 + .select(QcInspectionMain::getInspectionNo) + .leftJoin(QcInspectionMain.class, QcInspectionMain::getInspectionId, QcUnqualifiedReview::getInspectionId) + //关联检测类型 + .select(QcInspectionType::getTypeName, QcInspectionType::getQcInspectionType, QcInspectionType::getTypeCode, QcInspectionType::getQcInspectionType) + .leftJoin(QcInspectionType.class, QcInspectionType::getTypeId, QcInspectionMain::getTypeId) + + .eq(bo.getReviewId() != null, QcUnqualifiedReview::getReviewId, bo.getReviewId()) + .eq(StringUtils.isNotBlank(bo.getUnqualifiedNo()), QcUnqualifiedReview::getUnqualifiedNo, bo.getUnqualifiedNo()) + .eq(bo.getInspectionId() != null, QcUnqualifiedReview::getInspectionId, bo.getInspectionId()) + .eq(bo.getTypeId() != null, QcUnqualifiedReview::getTypeId, bo.getTypeId()) + .eq(StringUtils.isNotBlank(bo.getWorkOrder()), QcUnqualifiedReview::getWorkOrder, bo.getWorkOrder()) + .eq(StringUtils.isNotBlank(bo.getProcessCode()), QcUnqualifiedReview::getProcessCode, bo.getProcessCode()) + .like(StringUtils.isNotBlank(bo.getProcessName()), QcUnqualifiedReview::getProcessName, bo.getProcessName()) + .eq(StringUtils.isNotBlank(bo.getBatchNo()), QcUnqualifiedReview::getBatchNo, bo.getBatchNo()) + .like(StringUtils.isNotBlank(bo.getMaterialName()), QcUnqualifiedReview::getMaterialName, bo.getMaterialName()) + .eq(StringUtils.isNotBlank(bo.getMaterialCode()), QcUnqualifiedReview::getMaterialCode, bo.getMaterialCode()) + .eq(StringUtils.isNotBlank(bo.getInspectorResult()), QcUnqualifiedReview::getInspectorResult, bo.getInspectorResult()) + .eq(StringUtils.isNotBlank(bo.getReviewResult()), QcUnqualifiedReview::getReviewResult, bo.getReviewResult()) + .eq(StringUtils.isNotBlank(bo.getReviewer()), QcUnqualifiedReview::getReviewer, bo.getReviewer()) + .eq(bo.getReviewTime() != null, QcUnqualifiedReview::getReviewTime, bo.getReviewTime()) + .orderByDesc(QcUnqualifiedReview::getCreateTime); + return lqw; + } + + /** + * 新增不合格品待评审 + * + * @param bo 不合格品待评审 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(QcUnqualifiedReviewBo bo) { + QcUnqualifiedReview add = MapstructUtils.convert(bo, QcUnqualifiedReview.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setReviewId(add.getReviewId()); + } + return flag; + } + + /** + * 修改不合格品待评审 + * + * @param bo 不合格品待评审 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(QcUnqualifiedReviewBo bo) { + QcUnqualifiedReview update = MapstructUtils.convert(bo, QcUnqualifiedReview.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(QcUnqualifiedReview entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除不合格品待评审信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/utils/QcInspectionResultCalculator.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/utils/QcInspectionResultCalculator.java new file mode 100644 index 0000000..f181fda --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/utils/QcInspectionResultCalculator.java @@ -0,0 +1,368 @@ +package org.dromara.qms.utils; + +import org.dromara.qms.domain.vo.QcInspectionItemDetailVo; +import org.dromara.qms.domain.bo.QcInspectionMainBo; +import org.dromara.common.core.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Objects; + +/** + * 质检结果计算工具类 + * 实现质检结果自动判定逻辑 + * + * @author zch + * @date 2025-07-18 + */ +@Slf4j +public class QcInspectionResultCalculator { + + /** + * 检测方式:定性检测 + */ + public static final Long DETECT_TYPE_QUALITATIVE = 0L; + + /** + * 检测方式:定量检测 + */ + public static final Long DETECT_TYPE_QUANTITATIVE = 1L; + + /** + * 检测结果:合格 + */ + public static final Long RESULT_STATUS_QUALIFIED = 0L; + + /** + * 检测结果:不合格 + */ + public static final Long RESULT_STATUS_UNQUALIFIED = 1L; + + /** + * 计算单个检测项的结果状态 + * 实现定量检测的自动判定:根据控制上下限判断合格/不合格 + * + * @param itemDetail 检测项明细 + * @return 检测结果状态(0合格/1不合格) + */ + public static Long calculateItemResultStatus(QcInspectionItemDetailVo itemDetail) { + if (itemDetail == null) { + log.warn("检测项明细为空,无法计算结果状态"); + return RESULT_STATUS_UNQUALIFIED; + } + + // 定性检测:需要手动选择结果,验证是否已选择 + if (DETECT_TYPE_QUALITATIVE.equals(itemDetail.getDetectType())) { + return calculateQualitativeResult(itemDetail); + } + + // 定量检测:根据控制上下限自动判定 + if (DETECT_TYPE_QUANTITATIVE.equals(itemDetail.getDetectType())) { + return calculateQuantitativeResult(itemDetail); + } + + log.warn("未知的检测方式: {}, 检测项: {}", itemDetail.getDetectType(), itemDetail.getItemName()); + return RESULT_STATUS_UNQUALIFIED; + } + + /** + * 计算定性检测结果 + * 定性检测需要用户手动选择合格/不合格状态 + * + * @param itemDetail 检测项明细 + * @return 检测结果状态 + */ + private static Long calculateQualitativeResult(QcInspectionItemDetailVo itemDetail) { + // 检查是否已手动选择结果 + if (itemDetail.getResultStatus() != null) { + return itemDetail.getResultStatus(); + } + + // 如果有检测结果字符串,尝试解析 + if (StringUtils.isNotBlank(itemDetail.getResult())) { + String result = itemDetail.getResult().trim().toLowerCase(); + if ("合格".equals(result) || "qualified".equals(result) || "pass".equals(result) || "0".equals(result)) { + return RESULT_STATUS_QUALIFIED; + } + if ("不合格".equals(result) || "unqualified".equals(result) || "fail".equals(result) || "1".equals(result)) { + return RESULT_STATUS_UNQUALIFIED; + } + } + + log.warn("定性检测项未选择结果状态: {}", itemDetail.getItemName()); + return RESULT_STATUS_UNQUALIFIED; + } + + /** + * 计算定量检测结果 + * 根据控制上下限自动判定合格/不合格 + * + * @param itemDetail 检测项明细 + * @return 检测结果状态 + */ + private static Long calculateQuantitativeResult(QcInspectionItemDetailVo itemDetail) { + BigDecimal value = itemDetail.getValue(); + BigDecimal upperLimit = itemDetail.getUpperLimit(); + BigDecimal lowerLimit = itemDetail.getLowerLimit(); + + // 检查检测值是否存在 + if (value == null) { + log.warn("定量检测项缺少检测值: {}", itemDetail.getItemName()); + return RESULT_STATUS_UNQUALIFIED; + } + + // 检查控制限是否设置 + if (upperLimit == null && lowerLimit == null) { + log.warn("定量检测项未设置控制限: {}", itemDetail.getItemName()); + // 如果没有设置控制限,默认为合格 + return RESULT_STATUS_QUALIFIED; + } + + // 检查是否超出控制上限 + if (upperLimit != null && value.compareTo(upperLimit) > 0) { + log.info("检测值超出控制上限: {} > {}, 检测项: {}", value, upperLimit, itemDetail.getItemName()); + itemDetail.setIsOutOfControlLimit(true); + return RESULT_STATUS_UNQUALIFIED; + } + + // 检查是否低于控制下限 + if (lowerLimit != null && value.compareTo(lowerLimit) < 0) { + log.info("检测值低于控制下限: {} < {}, 检测项: {}", value, lowerLimit, itemDetail.getItemName()); + itemDetail.setIsOutOfControlLimit(true); + return RESULT_STATUS_UNQUALIFIED; + } + + // 检查规格限(如果设置了规格限) + if (isOutOfSpecLimit(itemDetail)) { + itemDetail.setIsOutOfSpecLimit(true); + log.info("检测值超出规格限,但在控制限内: {}, 检测项: {}", value, itemDetail.getItemName()); + } + + itemDetail.setIsOutOfControlLimit(false); + return RESULT_STATUS_QUALIFIED; + } + + /** + * 检查是否超出规格限 + * + * @param itemDetail 检测项明细 + * @return 是否超出规格限 + */ + private static boolean isOutOfSpecLimit(QcInspectionItemDetailVo itemDetail) { + BigDecimal value = itemDetail.getValue(); + BigDecimal specUpperLimit = itemDetail.getSpecUpperLimit(); + BigDecimal specLowerLimit = itemDetail.getSpecLowerLimit(); + + if (value == null) { + return false; + } + + // 检查规格上限 + if (specUpperLimit != null && value.compareTo(specUpperLimit) > 0) { + return true; + } + + // 检查规格下限 + if (specLowerLimit != null && value.compareTo(specLowerLimit) < 0) { + return true; + } + + return false; + } + + /** + * 计算整体质检结果 + * 实现整体质检结果计算:所有检测项都合格才算整体合格 + * + * @param inspectionItems 检测项列表 + * @return 整体质检结果状态(0合格/1不合格) + */ + public static Long calculateOverallResult(List inspectionItems) { + if (inspectionItems == null || inspectionItems.isEmpty()) { + log.warn("检测项列表为空,整体结果判定为不合格"); + return RESULT_STATUS_UNQUALIFIED; + } + + // 检查是否所有必检项都已完成 + boolean hasUncompletedRequiredItems = inspectionItems.stream() + .filter(item -> Boolean.TRUE.equals(item.getIsRequired())) + .anyMatch(item -> !Boolean.TRUE.equals(item.getIsCompleted())); + + if (hasUncompletedRequiredItems) { + log.warn("存在未完成的必检项,整体结果判定为不合格"); + return RESULT_STATUS_UNQUALIFIED; + } + + // 计算每个检测项的结果状态 + for (QcInspectionItemDetailVo item : inspectionItems) { + Long itemResult = calculateItemResultStatus(item); + item.setResultStatus(itemResult); + + // 如果任何一个检测项不合格,整体结果就是不合格 + if (RESULT_STATUS_UNQUALIFIED.equals(itemResult)) { + log.info("检测项不合格,整体结果判定为不合格: {}", item.getItemName()); + return RESULT_STATUS_UNQUALIFIED; + } + } + + log.info("所有检测项都合格,整体结果判定为合格"); + return RESULT_STATUS_QUALIFIED; + } + + /** + * 验证检测数据的完整性 + * 添加检测值范围验证和异常处理 + * + * @param bo 质检主表业务对象 + * @return 验证结果和错误信息 + */ + public static ValidationResult validateInspectionData(QcInspectionMainBo bo) { + ValidationResult result = new ValidationResult(); + + if (bo == null) { + result.setValid(false); + result.addError("质检数据不能为空"); + return result; + } + + // 验证基本信息 + if (bo.getInspectionId() == null) { + result.addError("质检任务ID不能为空"); + } + + if (StringUtils.isBlank(bo.getInspector())) { + result.addError("检测人员不能为空"); + } + + // 验证检测项数据(这里假设检测项数据在bo中,实际可能需要从其他地方获取) + // 实际实现中可能需要根据inspectionId查询检测项数据 + + return result; + } + + /** + * 验证检测项数据的完整性 + * + * @param inspectionItems 检测项列表 + * @return 验证结果 + */ + public static ValidationResult validateInspectionItems(List inspectionItems) { + ValidationResult result = new ValidationResult(); + + if (inspectionItems == null || inspectionItems.isEmpty()) { + result.setValid(false); + result.addError("检测项列表不能为空"); + return result; + } + + for (QcInspectionItemDetailVo item : inspectionItems) { + validateSingleInspectionItem(item, result); + } + + return result; + } + + /** + * 验证单个检测项数据 + * + * @param item 检测项 + * @param result 验证结果 + */ + private static void validateSingleInspectionItem(QcInspectionItemDetailVo item, ValidationResult result) { + if (item == null) { + result.addError("检测项不能为空"); + return; + } + + // 验证基本信息 + if (StringUtils.isBlank(item.getItemName())) { + result.addError("检测项名称不能为空"); + } + + if (item.getDetectType() == null) { + result.addError("检测方式不能为空: " + item.getItemName()); + } + + // 验证定量检测数据 + if (DETECT_TYPE_QUANTITATIVE.equals(item.getDetectType())) { + validateQuantitativeItem(item, result); + } + + // 验证定性检测数据 + if (DETECT_TYPE_QUALITATIVE.equals(item.getDetectType())) { + validateQualitativeItem(item, result); + } + + // 验证必检项是否完成 + if (Boolean.TRUE.equals(item.getIsRequired()) && !Boolean.TRUE.equals(item.getIsCompleted())) { + result.addError("必检项未完成: " + item.getItemName()); + } + } + + /** + * 验证定量检测项数据 + */ + private static void validateQuantitativeItem(QcInspectionItemDetailVo item, ValidationResult result) { + if (item.getValue() == null) { + result.addError("定量检测项缺少检测值: " + item.getItemName()); + return; + } + + // 验证检测值的合理性 + BigDecimal value = item.getValue(); + if (value.compareTo(BigDecimal.ZERO) < 0) { + // 根据实际业务需求,某些检测项可能允许负值 + log.warn("检测值为负数: {}, 检测项: {}", value, item.getItemName()); + } + + // 验证检测值是否在合理范围内(可以根据业务需求设置极值检查) + BigDecimal maxReasonableValue = new BigDecimal("999999999"); + BigDecimal minReasonableValue = new BigDecimal("-999999999"); + + if (value.compareTo(maxReasonableValue) > 0 || value.compareTo(minReasonableValue) < 0) { + result.addError("检测值超出合理范围: " + value + ", 检测项: " + item.getItemName()); + } + } + + /** + * 验证定性检测项数据 + */ + private static void validateQualitativeItem(QcInspectionItemDetailVo item, ValidationResult result) { + if (item.getResultStatus() == null && StringUtils.isBlank(item.getResult())) { + result.addError("定性检测项未选择结果: " + item.getItemName()); + } + } + + /** + * 验证结果类 + */ + public static class ValidationResult { + private boolean valid = true; + private StringBuilder errors = new StringBuilder(); + + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + public String getErrors() { + return errors.toString(); + } + + public void addError(String error) { + this.valid = false; + if (errors.length() > 0) { + errors.append("; "); + } + errors.append(error); + } + + public boolean hasErrors() { + return !valid; + } + } +} \ No newline at end of file diff --git a/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcUnqualifiedRecordMapper.xml b/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcUnqualifiedRecordMapper.xml new file mode 100644 index 0000000..a33090a --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcUnqualifiedRecordMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + select record_id, tenant_id, review_id, remark, create_dept, create_by, create_time, update_by, update_time, del_flag + from qc_unqualified_record + + + + + + + diff --git a/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcUnqualifiedReviewMapper.xml b/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcUnqualifiedReviewMapper.xml new file mode 100644 index 0000000..439bf0f --- /dev/null +++ b/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcUnqualifiedReviewMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select review_id, tenant_id, unqualified_no, inspection_id, type_id, work_order, process_code, process_name, batch_no, material_name, material_code, inspector_result, review_result, reviewer, review_time, create_dept, create_by, create_time, update_by, update_time, del_flag + from qc_unqualified_review + + + + + + +