feat: 新增质量评审流程全量功能

- 新增质量评审相关的实体、Mapper、Service、Controller接口与实现
- 新增前端页面与API接口,支持评审单增删改查、提交送审、审批驳回、改判执行
- 新增数据库初始化脚本与字典数据
- 修复vue.config热加载监视系统文件导致的EBUSY错误
- 清理HRM模块API导出注释
This commit is contained in:
王文昊
2026-06-30 17:58:21 +08:00
parent c997a4a1a0
commit 450fca0c45
25 changed files with 2816 additions and 1 deletions

View File

@@ -0,0 +1,159 @@
package com.klp.mes.qc.controller;
import com.klp.common.annotation.Log;
import com.klp.common.annotation.RepeatSubmit;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.mes.qc.domain.bo.QcQualityReviewApproveBo;
import com.klp.mes.qc.domain.bo.QcQualityReviewBo;
import com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewLogVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewVo;
import com.klp.mes.qc.service.IQcQualityReviewService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 质量评审单Controller
*
* @author klp
* @date 2026-06-30
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/qc/qualityReview")
public class QcQualityReviewController extends BaseController {
private final IQcQualityReviewService iQcQualityReviewService;
/**
* 查询质量评审单列表
*/
@GetMapping("/list")
public TableDataInfo<QcQualityReviewVo> list(QcQualityReviewBo bo, PageQuery pageQuery) {
return iQcQualityReviewService.queryPageList(bo, pageQuery);
}
/**
* 导出质量评审单列表
*/
@Log(title = "质量评审单", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(QcQualityReviewBo bo, HttpServletResponse response) {
List<QcQualityReviewVo> list = iQcQualityReviewService.queryList(bo);
ExcelUtil.exportExcel(list, "质量评审单", QcQualityReviewVo.class, response);
}
/**
* 获取质量评审单详细信息
*/
@GetMapping("/{reviewId}")
public R<QcQualityReviewVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return R.ok(iQcQualityReviewService.queryById(reviewId));
}
/**
* 新增质量评审单
*/
@Log(title = "质量评审单", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody QcQualityReviewBo bo) {
return toAjax(iQcQualityReviewService.insertByBo(bo));
}
/**
* 修改质量评审单
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody QcQualityReviewBo bo) {
return toAjax(iQcQualityReviewService.updateByBo(bo));
}
/**
* 删除质量评审单
*/
@Log(title = "质量评审单", businessType = BusinessType.DELETE)
@DeleteMapping("/{reviewIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] reviewIds) {
return toAjax(iQcQualityReviewService.deleteWithValidByIds(Arrays.asList(reviewIds), true));
}
// ==================== 业务操作 ====================
/**
* 提交送审
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@PostMapping("/submit/{reviewId}")
public R<Void> submit(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return toAjax(iQcQualityReviewService.submit(reviewId));
}
/**
* 审批通过
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@PostMapping("/approve")
public R<Void> approve(@Validated @RequestBody QcQualityReviewApproveBo bo) {
return toAjax(iQcQualityReviewService.approve(bo));
}
/**
* 驳回
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@PostMapping("/reject/{reviewId}")
public R<Void> reject(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId,
@RequestParam String reason) {
return toAjax(iQcQualityReviewService.reject(reviewId, reason));
}
/**
* 执行改判
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@PostMapping("/execute/{reviewId}")
public R<Void> execute(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return toAjax(iQcQualityReviewService.execute(reviewId));
}
/**
* 查询钢卷明细列表
*/
@GetMapping("/coilList/{reviewId}")
public R<List<QcQualityReviewCoilVo>> coilList(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return R.ok(iQcQualityReviewService.queryCoilListByReviewId(reviewId));
}
/**
* 查询审批日志列表
*/
@GetMapping("/logList/{reviewId}")
public R<List<QcQualityReviewLogVo>> logList(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return R.ok(iQcQualityReviewService.queryLogListByReviewId(reviewId));
}
}

View File

@@ -0,0 +1,76 @@
package com.klp.mes.qc.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 质量评审单对象 qc_quality_review
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("qc_quality_review")
public class QcQualityReview extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 评审单主键 */
@TableId(value = "review_id")
private Long reviewId;
/** 评审单编号 */
private String reviewNo;
/** 产品名称 */
private String productName;
/** 传递部门 */
private String transmitDept;
/** 传递人 */
private String transmitUser;
/** 传递日期 */
private Date transmitDate;
/** 生产日期范围 */
private String prodDateRange;
/** 流程状态1=待提交 2=待审批 3=已通过 4=已驳回 */
private Long flowStatus;
/** 品质部评审意见 */
private String deptOpinion;
/** 品质部签字人 */
private String deptSign;
/** 品质部签字日期 */
private Date deptSignDate;
/** 领导审批意见 */
private String leaderOpinion;
/** 领导签字人 */
private String leaderSign;
/** 领导签字日期 */
private Date leaderSignDate;
/** 驳回原因 */
private String rejectReason;
/** 备注 */
private String remark;
/** 删除标志0=正常1=已删除) */
@TableLogic
private Integer delFlag;
}

View File

@@ -0,0 +1,74 @@
package com.klp.mes.qc.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
/**
* 质量评审钢卷明细对象 qc_quality_review_coil
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("qc_quality_review_coil")
public class QcQualityReviewCoil extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 明细主键 */
@TableId(value = "detail_id")
private Long detailId;
/** 关联评审单ID */
private Long reviewId;
/** 序号 */
private Integer groupSeq;
/** 组备注 */
private String groupRemark;
/** 钢卷ID */
private Long coilId;
/** 产品卷号 */
private String currentCoilNo;
/** 原料卷号 */
private String supplierCoilNo;
/** 规格 */
private String spec;
/** 卷重(t) */
private BigDecimal netWeight;
/** 缺陷描述 */
private String defectDesc;
/** 改判前质量等级 */
private String beforeQuality;
/** 改判后质量状态字典regrade_quality_type */
private String regradeQuality;
/** 执行状态0=待执行 1=已执行 */
private Long executeStatus;
/** 执行时间 */
private Date executeTime;
/** 备注 */
private String remark;
/** 删除标志0=正常1=已删除) */
@TableLogic
private Integer delFlag;
}

View File

@@ -0,0 +1,45 @@
package com.klp.mes.qc.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* 质量评审审批日志对象 qc_quality_review_log
*
* @author klp
* @date 2026-06-30
*/
@Data
@TableName("qc_quality_review_log")
public class QcQualityReviewLog {
private static final long serialVersionUID = 1L;
/** 日志主键 */
@TableId(value = "log_id")
private Long logId;
/** 关联评审单ID */
private Long reviewId;
/** 动作submit=提交 approve=通过 reject=驳回 */
private String action;
/** 操作人 */
private String operator;
/** 审批意见 */
private String opinion;
/** 操作时间 */
private Date operateTime;
/** 创建人 */
private String createBy;
/** 创建时间 */
private Date createTime;
}

View File

@@ -0,0 +1,41 @@
package com.klp.mes.qc.domain.bo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 质量评审审批参数(领导审批时使用)
*
* @author klp
* @date 2026-06-30
*/
@Data
public class QcQualityReviewApproveBo {
/** 评审单ID */
@NotNull(message = "评审单ID不能为空")
private Long reviewId;
/** 领导审批意见 */
private String leaderOpinion;
/** 领导签字人 */
private String leaderSign;
/** 各钢卷的改判状态 */
private List<CoilRegradeBo> coilRegradeList;
@Data
public static class CoilRegradeBo {
/** 明细ID */
@NotNull(message = "钢卷明细ID不能为空")
private Long detailId;
/** 改判后质量状态 */
@NotBlank(message = "改判后质量状态不能为空")
private String regradeQuality;
}
}

View File

@@ -0,0 +1,85 @@
package com.klp.mes.qc.domain.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
/**
* 质量评审单业务对象 qc_quality_review
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class QcQualityReviewBo extends BaseEntity {
/** 评审单主键 */
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
private Long reviewId;
/** 评审单编号 */
private String reviewNo;
/** 产品名称 */
@NotBlank(message = "产品名称不能为空", groups = {AddGroup.class})
private String productName;
/** 传递部门 */
private String transmitDept;
/** 传递人 */
private String transmitUser;
/** 传递日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date transmitDate;
/** 生产日期范围 */
private String prodDateRange;
/** 流程状态 */
private Long flowStatus;
/** 品质部评审意见 */
private String deptOpinion;
/** 品质部签字人 */
private String deptSign;
/** 品质部签字日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date deptSignDate;
/** 领导审批意见 */
private String leaderOpinion;
/** 领导签字人 */
private String leaderSign;
/** 领导签字日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date leaderSignDate;
/** 驳回原因 */
private String rejectReason;
/** 备注 */
private String remark;
/** 钢卷明细列表 */
private List<QcQualityReviewCoilBo> coilList;
}

View File

@@ -0,0 +1,67 @@
package com.klp.mes.qc.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 质量评审钢卷明细业务对象 qc_quality_review_coil
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class QcQualityReviewCoilBo extends BaseEntity {
/** 明细主键 */
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
private Long detailId;
/** 关联评审单ID */
private Long reviewId;
/** 序号 */
private Integer groupSeq;
/** 组备注 */
private String groupRemark;
/** 钢卷ID */
@NotNull(message = "钢卷不能为空", groups = {AddGroup.class})
private Long coilId;
/** 产品卷号 */
private String currentCoilNo;
/** 原料卷号 */
private String supplierCoilNo;
/** 规格 */
private String spec;
/** 卷重(t) */
private BigDecimal netWeight;
/** 缺陷描述 */
private String defectDesc;
/** 改判前质量等级 */
private String beforeQuality;
/** 改判后质量状态 */
private String regradeQuality;
/** 执行状态 */
private Long executeStatus;
/** 备注 */
private String remark;
}

View File

@@ -0,0 +1,72 @@
package com.klp.mes.qc.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
/**
* 质量评审钢卷明细视图对象 qc_quality_review_coil
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class QcQualityReviewCoilVo extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 明细主键 */
private Long detailId;
/** 关联评审单ID */
private Long reviewId;
/** 序号 */
private Integer groupSeq;
/** 组备注 */
private String groupRemark;
/** 钢卷ID */
private Long coilId;
/** 产品卷号 */
private String currentCoilNo;
/** 原料卷号 */
private String supplierCoilNo;
/** 规格 */
private String spec;
/** 卷重(t) */
private BigDecimal netWeight;
/** 缺陷描述 */
private String defectDesc;
/** 改判前质量等级 */
private String beforeQuality;
/** 改判后质量状态 */
private String regradeQuality;
/** 执行状态 */
private Long executeStatus;
/** 执行时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date executeTime;
/** 备注 */
private String remark;
/** 删除标志 */
private Integer delFlag;
}

View File

@@ -0,0 +1,40 @@
package com.klp.mes.qc.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* 质量评审审批日志视图对象
*
* @author klp
* @date 2026-06-30
*/
@Data
public class QcQualityReviewLogVo {
/** 日志主键 */
private Long logId;
/** 关联评审单ID */
private Long reviewId;
/** 动作submit=提交 approve=通过 reject=驳回 */
private String action;
/** 操作人 */
private String operator;
/** 审批意见 */
private String opinion;
/** 操作时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date operateTime;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}

View File

@@ -0,0 +1,83 @@
package com.klp.mes.qc.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import java.util.List;
/**
* 质量评审单视图对象 qc_quality_review
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class QcQualityReviewVo extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 评审单主键 */
private Long reviewId;
/** 评审单编号 */
private String reviewNo;
/** 产品名称 */
private String productName;
/** 传递部门 */
private String transmitDept;
/** 传递人 */
private String transmitUser;
/** 传递日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date transmitDate;
/** 生产日期范围 */
private String prodDateRange;
/** 流程状态 */
private Long flowStatus;
/** 品质部评审意见 */
private String deptOpinion;
/** 品质部签字人 */
private String deptSign;
/** 品质部签字日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date deptSignDate;
/** 领导审批意见 */
private String leaderOpinion;
/** 领导签字人 */
private String leaderSign;
/** 领导签字日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date leaderSignDate;
/** 驳回原因 */
private String rejectReason;
/** 备注 */
private String remark;
/** 删除标志 */
private Integer delFlag;
/** 钢卷明细列表 */
private List<QcQualityReviewCoilVo> coilList;
/** 审批日志列表 */
private List<QcQualityReviewLogVo> logList;
}

View File

@@ -0,0 +1,22 @@
package com.klp.mes.qc.mapper;
import com.klp.mes.qc.domain.QcQualityReviewCoil;
import com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* 质量评审钢卷明细Mapper接口
*
* @author klp
* @date 2026-06-30
*/
public interface QcQualityReviewCoilMapper extends BaseMapper<QcQualityReviewCoil> {
/**
* 根据评审单ID查询钢卷明细列表
*/
List<QcQualityReviewCoilVo> selectVoListByReviewId(Long reviewId);
}

View File

@@ -0,0 +1,22 @@
package com.klp.mes.qc.mapper;
import com.klp.mes.qc.domain.QcQualityReviewLog;
import com.klp.mes.qc.domain.vo.QcQualityReviewLogVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* 质量评审审批日志Mapper接口
*
* @author klp
* @date 2026-06-30
*/
public interface QcQualityReviewLogMapper extends BaseMapper<QcQualityReviewLog> {
/**
* 根据评审单ID查询审批日志列表
*/
List<QcQualityReviewLogVo> selectVoListByReviewId(Long reviewId);
}

View File

@@ -0,0 +1,35 @@
package com.klp.mes.qc.mapper;
import com.klp.mes.qc.domain.QcQualityReview;
import com.klp.mes.qc.domain.vo.QcQualityReviewVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 质量评审单Mapper接口
*
* @author klp
* @date 2026-06-30
*/
public interface QcQualityReviewMapper extends BaseMapper<QcQualityReview> {
/**
* 分页查询评审单列表(含状态字典值)
*/
Page<QcQualityReviewVo> selectVoPage(IPage<?> page, @Param("vo") QcQualityReviewVo vo);
/**
* 查询评审单详情
*/
QcQualityReviewVo selectVoById(@Param("reviewId") Long reviewId);
/**
* 查询评审单列表
*/
List<QcQualityReviewVo> selectVoList(@Param("vo") QcQualityReviewVo vo);
}

View File

@@ -0,0 +1,82 @@
package com.klp.mes.qc.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.mes.qc.domain.bo.QcQualityReviewApproveBo;
import com.klp.mes.qc.domain.bo.QcQualityReviewBo;
import com.klp.mes.qc.domain.vo.QcQualityReviewVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewLogVo;
import java.util.Collection;
import java.util.List;
/**
* 质量评审单Service接口
*
* @author klp
* @date 2026-06-30
*/
public interface IQcQualityReviewService {
/**
* 查询评审单详情
*/
QcQualityReviewVo queryById(Long reviewId);
/**
* 分页查询评审单列表
*/
TableDataInfo<QcQualityReviewVo> queryPageList(QcQualityReviewBo bo, PageQuery pageQuery);
/**
* 查询评审单列表
*/
List<QcQualityReviewVo> queryList(QcQualityReviewBo bo);
/**
* 创建评审单
*/
Boolean insertByBo(QcQualityReviewBo bo);
/**
* 修改评审单
*/
Boolean updateByBo(QcQualityReviewBo bo);
/**
* 批量删除评审单
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 提交送审flowStatus: 1→2
*/
Boolean submit(Long reviewId);
/**
* 审批通过flowStatus: 2→3
*/
Boolean approve(QcQualityReviewApproveBo bo);
/**
* 驳回flowStatus: 2→4
*/
Boolean reject(Long reviewId, String reason);
/**
* 执行改判(写改判记录 + 更新钢卷状态)
*/
Boolean execute(Long reviewId);
/**
* 查询钢卷明细列表
*/
List<QcQualityReviewCoilVo> queryCoilListByReviewId(Long reviewId);
/**
* 查询审批日志列表
*/
List<QcQualityReviewLogVo> queryLogListByReviewId(Long reviewId);
}

View File

@@ -0,0 +1,389 @@
package com.klp.mes.qc.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.utils.StringUtils;
import com.klp.domain.WmsCoilQualityRejudge;
import com.klp.domain.WmsMaterialCoil;
import com.klp.mapper.WmsCoilQualityRejudgeMapper;
import com.klp.mapper.WmsMaterialCoilMapper;
import com.klp.mes.qc.domain.QcQualityReview;
import com.klp.mes.qc.domain.QcQualityReviewCoil;
import com.klp.mes.qc.domain.QcQualityReviewLog;
import com.klp.mes.qc.domain.bo.QcQualityReviewApproveBo;
import com.klp.mes.qc.domain.bo.QcQualityReviewBo;
import com.klp.mes.qc.domain.bo.QcQualityReviewCoilBo;
import com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewLogVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewVo;
import com.klp.mes.qc.mapper.QcQualityReviewCoilMapper;
import com.klp.mes.qc.mapper.QcQualityReviewLogMapper;
import com.klp.mes.qc.mapper.QcQualityReviewMapper;
import com.klp.mes.qc.service.IQcQualityReviewService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 质量评审单Service业务层处理
*
* @author klp
* @date 2026-06-30
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class QcQualityReviewServiceImpl implements IQcQualityReviewService {
private final QcQualityReviewMapper baseMapper;
private final QcQualityReviewCoilMapper coilMapper;
private final QcQualityReviewLogMapper logMapper;
private final WmsMaterialCoilMapper wmsMaterialCoilMapper;
private final WmsCoilQualityRejudgeMapper wmsCoilQualityRejudgeMapper;
/**
* 查询评审单详情(含钢卷明细和审批日志)
*/
@Override
public QcQualityReviewVo queryById(Long reviewId) {
QcQualityReviewVo vo = baseMapper.selectVoById(reviewId);
if (vo != null) {
vo.setCoilList(coilMapper.selectVoListByReviewId(reviewId));
vo.setLogList(logMapper.selectVoListByReviewId(reviewId));
}
return vo;
}
/**
* 分页查询评审单列表
*/
@Override
public TableDataInfo<QcQualityReviewVo> queryPageList(QcQualityReviewBo bo, PageQuery pageQuery) {
QcQualityReviewVo queryVo = new QcQualityReviewVo();
BeanUtil.copyProperties(bo, queryVo);
Page<QcQualityReviewVo> result = baseMapper.selectVoPage(pageQuery.build(), queryVo);
return TableDataInfo.build(result);
}
/**
* 查询评审单列表
*/
@Override
public List<QcQualityReviewVo> queryList(QcQualityReviewBo bo) {
QcQualityReviewVo queryVo = new QcQualityReviewVo();
BeanUtil.copyProperties(bo, queryVo);
return baseMapper.selectVoList(queryVo);
}
/**
* 创建评审单(草稿状态)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(QcQualityReviewBo bo) {
// 1. 保存主表
QcQualityReview add = BeanUtil.toBean(bo, QcQualityReview.class);
// 设置默认值
add.setFlowStatus(1L); // 1=待提交
add.setReviewNo(generateReviewNo());
if (StringUtils.isBlank(add.getTransmitDept())) {
add.setTransmitDept("品质部");
}
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setReviewId(add.getReviewId());
// 2. 保存钢卷明细
if (CollUtil.isNotEmpty(bo.getCoilList())) {
saveCoilList(add.getReviewId(), bo.getCoilList());
}
}
return flag;
}
/**
* 修改评审单(仅草稿状态可修改)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(QcQualityReviewBo bo) {
QcQualityReview update = BeanUtil.toBean(bo, QcQualityReview.class);
validEntityBeforeSave(update);
boolean flag = baseMapper.updateById(update) > 0;
if (flag) {
// 先删除原有明细,再重新插入
coilMapper.delete(Wrappers.<QcQualityReviewCoil>lambdaQuery()
.eq(QcQualityReviewCoil::getReviewId, bo.getReviewId()));
if (CollUtil.isNotEmpty(bo.getCoilList())) {
saveCoilList(bo.getReviewId(), bo.getCoilList());
}
}
return flag;
}
/**
* 批量删除评审单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 校验:只能删除待提交状态的
for (Long id : ids) {
QcQualityReview review = baseMapper.selectById(id);
if (review != null && !Long.valueOf(1L).equals(review.getFlowStatus())) {
throw new RuntimeException("评审单【" + review.getReviewNo() + "】不是待提交状态,无法删除");
}
}
}
// 级联删除钢卷明细
coilMapper.delete(Wrappers.<QcQualityReviewCoil>lambdaQuery()
.in(QcQualityReviewCoil::getReviewId, ids));
// 删除审批日志
logMapper.delete(Wrappers.<QcQualityReviewLog>lambdaQuery()
.in(QcQualityReviewLog::getReviewId, ids));
// 删除主表
return baseMapper.deleteBatchIds(ids) > 0;
}
// ==================== 业务操作 ====================
/**
* 提交送审(允许待提交→待审批,已驳回→待审批)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean submit(Long reviewId) {
QcQualityReview review = baseMapper.selectById(reviewId);
if (review == null) {
throw new RuntimeException("评审单不存在");
}
Long status = review.getFlowStatus();
if (!Long.valueOf(1L).equals(status) && !Long.valueOf(4L).equals(status)) {
throw new RuntimeException("当前状态不允许送审");
}
// 校验:必须有品质部意见
if (StringUtils.isBlank(review.getDeptOpinion())) {
throw new RuntimeException("请先填写品质部评审意见");
}
// 校验:至少有一个钢卷
Long coilCount = coilMapper.selectCount(
Wrappers.<QcQualityReviewCoil>lambdaQuery()
.eq(QcQualityReviewCoil::getReviewId, reviewId)
.eq(QcQualityReviewCoil::getDelFlag, 0));
if (coilCount == null || coilCount == 0) {
throw new RuntimeException("请至少添加一个钢卷");
}
// 更新流程状态为待审批,清除驳回原因
baseMapper.update(null, Wrappers.<QcQualityReview>lambdaUpdate()
.eq(QcQualityReview::getReviewId, reviewId)
.set(QcQualityReview::getFlowStatus, 2L)
.set(QcQualityReview::getRejectReason, null));
// 记录审批日志
addLog(reviewId, "submit", "提交送审", null);
return true;
}
/**
* 审批通过
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean approve(QcQualityReviewApproveBo bo) {
QcQualityReview review = baseMapper.selectById(bo.getReviewId());
if (review == null) {
throw new RuntimeException("评审单不存在");
}
if (!Long.valueOf(2L).equals(review.getFlowStatus())) {
throw new RuntimeException("只有待审批状态的评审单才能审批");
}
if (CollUtil.isEmpty(bo.getCoilRegradeList())) {
throw new RuntimeException("请为每个钢卷指定改判后质量状态");
}
// 1. 更新主表流程状态
baseMapper.update(null, Wrappers.<QcQualityReview>lambdaUpdate()
.eq(QcQualityReview::getReviewId, bo.getReviewId())
.set(QcQualityReview::getFlowStatus, 3L)
.set(QcQualityReview::getLeaderOpinion, bo.getLeaderOpinion())
.set(QcQualityReview::getLeaderSign, bo.getLeaderSign())
.set(QcQualityReview::getLeaderSignDate, new java.sql.Date(System.currentTimeMillis())));
// 2. 更新每个钢卷的改判状态
for (QcQualityReviewApproveBo.CoilRegradeBo regrade : bo.getCoilRegradeList()) {
coilMapper.update(null, Wrappers.<QcQualityReviewCoil>lambdaUpdate()
.eq(QcQualityReviewCoil::getDetailId, regrade.getDetailId())
.set(QcQualityReviewCoil::getRegradeQuality, regrade.getRegradeQuality()));
}
// 3. 记录审批日志
addLog(bo.getReviewId(), "approve", bo.getLeaderOpinion(), bo.getLeaderSign());
return true;
}
/**
* 驳回
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean reject(Long reviewId, String reason) {
QcQualityReview review = baseMapper.selectById(reviewId);
if (review == null) {
throw new RuntimeException("评审单不存在");
}
if (!Long.valueOf(2L).equals(review.getFlowStatus())) {
throw new RuntimeException("只有待审批状态的评审单才能驳回");
}
if (StringUtils.isBlank(reason)) {
throw new RuntimeException("请填写驳回原因");
}
// 更新流程状态为已驳回
baseMapper.update(null, Wrappers.<QcQualityReview>lambdaUpdate()
.eq(QcQualityReview::getReviewId, reviewId)
.set(QcQualityReview::getFlowStatus, 4L)
.set(QcQualityReview::getRejectReason, reason));
// 记录审批日志
addLog(reviewId, "reject", reason, null);
return true;
}
/**
* 执行改判
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean execute(Long reviewId) {
QcQualityReview review = baseMapper.selectById(reviewId);
if (review == null) {
throw new RuntimeException("评审单不存在");
}
if (!Long.valueOf(3L).equals(review.getFlowStatus())) {
throw new RuntimeException("只有已通过的评审单才能执行改判");
}
// 查询待执行的钢卷明细
List<QcQualityReviewCoil> coilList = coilMapper.selectList(
Wrappers.<QcQualityReviewCoil>lambdaQuery()
.eq(QcQualityReviewCoil::getReviewId, reviewId)
.eq(QcQualityReviewCoil::getDelFlag, 0)
.eq(QcQualityReviewCoil::getExecuteStatus, 0));
if (CollUtil.isEmpty(coilList)) {
throw new RuntimeException("没有待执行的钢卷");
}
Date now = new Date();
for (QcQualityReviewCoil coil : coilList) {
// 1. 写入 wms_coil_quality_rejudge 改判记录
WmsCoilQualityRejudge rejudge = new WmsCoilQualityRejudge();
rejudge.setCoilId(coil.getCoilId());
rejudge.setBeforeQuality(coil.getBeforeQuality());
rejudge.setAfterQuality(coil.getRegradeQuality());
rejudge.setRejudgeReason("异常产品评审改判,评审单号:" + review.getReviewNo());
wmsCoilQualityRejudgeMapper.insert(rejudge);
// 2. 更新钢卷的 qualityStatus 为改判后的值
if (coil.getCoilId() != null && StringUtils.isNotBlank(coil.getRegradeQuality())) {
wmsMaterialCoilMapper.update(null, Wrappers.<WmsMaterialCoil>lambdaUpdate()
.eq(WmsMaterialCoil::getCoilId, coil.getCoilId())
.set(WmsMaterialCoil::getQualityStatus, coil.getRegradeQuality()));
}
// 3. 更新明细执行状态
coilMapper.update(null, Wrappers.<QcQualityReviewCoil>lambdaUpdate()
.eq(QcQualityReviewCoil::getDetailId, coil.getDetailId())
.set(QcQualityReviewCoil::getExecuteStatus, 1L)
.set(QcQualityReviewCoil::getExecuteTime, now));
}
return true;
}
// ==================== 查询方法 ====================
@Override
public List<QcQualityReviewCoilVo> queryCoilListByReviewId(Long reviewId) {
return coilMapper.selectVoListByReviewId(reviewId);
}
@Override
public List<QcQualityReviewLogVo> queryLogListByReviewId(Long reviewId) {
return logMapper.selectVoListByReviewId(reviewId);
}
// ==================== 私有方法 ====================
/**
* 保存钢卷明细列表
*/
private void saveCoilList(Long reviewId, List<QcQualityReviewCoilBo> coilBoList) {
int seq = 1;
for (QcQualityReviewCoilBo coilBo : coilBoList) {
QcQualityReviewCoil coil = BeanUtil.toBean(coilBo, QcQualityReviewCoil.class);
coil.setReviewId(reviewId);
coil.setExecuteStatus(0L);
if (coil.getGroupSeq() == null) {
coil.setGroupSeq(seq);
}
// 若未传改判前质量等级,从钢卷表获取
if (StringUtils.isBlank(coil.getBeforeQuality()) && coil.getCoilId() != null) {
WmsMaterialCoil wmsCoil = wmsMaterialCoilMapper.selectById(coil.getCoilId());
if (wmsCoil != null) {
coil.setBeforeQuality(wmsCoil.getQualityStatus());
if (StringUtils.isBlank(coil.getCurrentCoilNo())) {
coil.setCurrentCoilNo(wmsCoil.getCurrentCoilNo());
}
if (StringUtils.isBlank(coil.getSupplierCoilNo())) {
coil.setSupplierCoilNo(wmsCoil.getSupplierCoilNo());
}
if (coil.getNetWeight() == null) {
coil.setNetWeight(wmsCoil.getNetWeight());
}
}
}
coilMapper.insert(coil);
seq++;
}
}
/**
* 添加审批日志
*/
private void addLog(Long reviewId, String action, String opinion, String operator) {
QcQualityReviewLog log = new QcQualityReviewLog();
log.setReviewId(reviewId);
log.setAction(action);
log.setOpinion(opinion);
log.setOperator(operator);
log.setOperateTime(new Date());
logMapper.insert(log);
}
/**
* 生成评审单编号: QR-20260630-0001
*/
private String generateReviewNo() {
String dateStr = DateUtil.format(new Date(), "yyyyMMdd");
Long count = baseMapper.selectCount(Wrappers.<QcQualityReview>lambdaQuery()
.likeRight(QcQualityReview::getReviewNo, "QR-" + dateStr));
return "QR-" + dateStr + "-" + String.format("%04d", count + 1);
}
private void validEntityBeforeSave(QcQualityReview entity) {
// TODO 数据校验
}
}

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.mes.qc.mapper.QcQualityReviewCoilMapper">
<resultMap type="com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo" id="QcQualityReviewCoilVo">
<id property="detailId" column="detail_id"/>
<result property="reviewId" column="review_id"/>
<result property="groupSeq" column="group_seq"/>
<result property="groupRemark" column="group_remark"/>
<result property="coilId" column="coil_id"/>
<result property="currentCoilNo" column="current_coil_no"/>
<result property="supplierCoilNo" column="supplier_coil_no"/>
<result property="spec" column="spec"/>
<result property="netWeight" column="net_weight"/>
<result property="defectDesc" column="defect_desc"/>
<result property="beforeQuality" column="before_quality"/>
<result property="regradeQuality" column="regrade_quality"/>
<result property="executeStatus" column="execute_status"/>
<result property="executeTime" column="execute_time"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<select id="selectVoListByReviewId" resultMap="QcQualityReviewCoilVo">
SELECT * FROM qc_quality_review_coil
WHERE review_id = #{reviewId} AND del_flag = 0
ORDER BY group_seq ASC, detail_id ASC
</select>
</mapper>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.mes.qc.mapper.QcQualityReviewLogMapper">
<resultMap type="com.klp.mes.qc.domain.vo.QcQualityReviewLogVo" id="QcQualityReviewLogVo">
<id property="logId" column="log_id"/>
<result property="reviewId" column="review_id"/>
<result property="action" column="action"/>
<result property="operator" column="operator"/>
<result property="opinion" column="opinion"/>
<result property="operateTime" column="operate_time"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="selectVoListByReviewId" resultMap="QcQualityReviewLogVo">
SELECT * FROM qc_quality_review_log
WHERE review_id = #{reviewId}
ORDER BY operate_time ASC
</select>
</mapper>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.mes.qc.mapper.QcQualityReviewMapper">
<resultMap type="com.klp.mes.qc.domain.vo.QcQualityReviewVo" id="QcQualityReviewVo">
<id property="reviewId" column="review_id"/>
<result property="reviewNo" column="review_no"/>
<result property="productName" column="product_name"/>
<result property="transmitDept" column="transmit_dept"/>
<result property="transmitUser" column="transmit_user"/>
<result property="transmitDate" column="transmit_date"/>
<result property="prodDateRange" column="prod_date_range"/>
<result property="flowStatus" column="flow_status"/>
<result property="deptOpinion" column="dept_opinion"/>
<result property="deptSign" column="dept_sign"/>
<result property="deptSignDate" column="dept_sign_date"/>
<result property="leaderOpinion" column="leader_opinion"/>
<result property="leaderSign" column="leader_sign"/>
<result property="leaderSignDate" column="leader_sign_date"/>
<result property="rejectReason" column="reject_reason"/>
<result property="remark" column="remark"/>
<result property="delFlag" column="del_flag"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<select id="selectVoPage" resultMap="QcQualityReviewVo">
SELECT * FROM qc_quality_review
<where>
del_flag = 0
<if test="vo.reviewNo != null and vo.reviewNo != ''">
AND review_no LIKE CONCAT('%', #{vo.reviewNo}, '%')
</if>
<if test="vo.productName != null and vo.productName != ''">
AND product_name LIKE CONCAT('%', #{vo.productName}, '%')
</if>
<if test="vo.flowStatus != null">
AND flow_status = #{vo.flowStatus}
</if>
<if test="vo.transmitUser != null and vo.transmitUser != ''">
AND transmit_user LIKE CONCAT('%', #{vo.transmitUser}, '%')
</if>
<if test="vo.params != null and vo.params.beginTime != null and vo.params.beginTime != ''">
AND create_time &gt;= #{vo.params.beginTime}
</if>
<if test="vo.params != null and vo.params.endTime != null and vo.params.endTime != ''">
AND create_time &lt;= #{vo.params.endTime}
</if>
</where>
ORDER BY create_time DESC
</select>
<select id="selectVoById" resultMap="QcQualityReviewVo">
SELECT * FROM qc_quality_review WHERE review_id = #{reviewId} AND del_flag = 0
</select>
<select id="selectVoList" resultMap="QcQualityReviewVo">
SELECT * FROM qc_quality_review
<where>
del_flag = 0
<if test="vo.reviewNo != null and vo.reviewNo != ''">
AND review_no LIKE CONCAT('%', #{vo.reviewNo}, '%')
</if>
<if test="vo.flowStatus != null">
AND flow_status = #{vo.flowStatus}
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>