package cn.iocoder.yudao.module.mes.service.pro.wage; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.pro.wage.vo.MesProProcessWageFieldSaveListReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.wage.vo.MesProProcessWageFieldSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.wage.MesProProcessWageFieldDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.wage.MesProProcessWageFieldMapper; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 工序工资字段 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class MesProProcessWageFieldServiceImpl implements MesProProcessWageFieldService { @Resource private MesProProcessWageFieldMapper fieldMapper; @Resource private MesProProcessService processService; @Override public List getFieldList(Long processId) { if (processId == null) { return Collections.emptyList(); } return fieldMapper.selectListByProcessId(processId); } @Override @Transactional(rollbackFor = Exception.class) public void saveFieldList(MesProProcessWageFieldSaveListReqVO reqVO) { MesProProcessDO process = processService.getProcess(reqVO.getProcessId()); if (process == null) { throw exception(PRO_FEEDBACK_PROCESS_NOT_EXISTS, reqVO.getProcessId()); } List list = reqVO.getList() == null ? Collections.emptyList() : reqVO.getList(); validateFieldList(process, list); // 1. 已存在的字段编号 List existingList = fieldMapper.selectListByProcessId(process.getId()); Set existingIds = new HashSet<>(); existingList.forEach(field -> existingIds.add(field.getId())); Set retainedIds = new HashSet<>(); // 2. 新增 / 修改 for (MesProProcessWageFieldSaveReqVO item : list) { MesProProcessWageFieldDO field = BeanUtils.toBean(item, MesProProcessWageFieldDO.class); field.setProcessId(process.getId()); field.setFieldCode(StrUtil.trim(field.getFieldCode())); field.setFieldName(StrUtil.trim(field.getFieldName())); field.setStatus(item.getStatus() != null ? item.getStatus() : CommonStatusEnum.ENABLE.getStatus()); field.setSort(item.getSort() != null ? item.getSort() : 0); if (field.getId() == null) { fieldMapper.insert(field); retainedIds.add(field.getId()); } else { if (!existingIds.contains(field.getId())) { throw exception(PRO_PROCESS_WAGE_FIELD_NOT_EXISTS); } fieldMapper.updateById(field); retainedIds.add(field.getId()); } } // 3. 删除本次未保留的字段 for (Long existingId : existingIds) { if (!retainedIds.contains(existingId)) { fieldMapper.deleteById(existingId); } } } private void validateFieldList(MesProProcessDO process, List list) { if (CollUtil.isEmpty(list)) { return; } Set codes = new HashSet<>(); for (MesProProcessWageFieldSaveReqVO item : list) { String code = StrUtil.trim(item.getFieldCode()); String name = StrUtil.trim(item.getFieldName()); if (!codes.add(code)) { throw new ServiceException(PRO_PROCESS_WAGE_FIELD_CODE_DUPLICATE.getCode(), StrUtil.format("工序[{}]下字段编码[{}]重复,请修改字段编码后重试", process.getName(), code)); } Integer fieldType = item.getFieldType(); if (fieldType == null || fieldType < MesProProcessWageFieldDO.FIELD_TYPE_NUMBER || fieldType > MesProProcessWageFieldDO.FIELD_TYPE_BOOLEAN) { throw new ServiceException(PRO_PROCESS_WAGE_FIELD_TYPE_INVALID.getCode(), StrUtil.format("工序[{}]字段[{}]的字段类型[{}]不合法;期望 1-数值 / 2-文本 / 3-下拉 / 4-布尔,请重新选择", process.getName(), name, fieldType)); } Integer calcRole = item.getCalcRole(); if (calcRole == null || calcRole < MesProProcessWageFieldDO.CALC_ROLE_COEFFICIENT || calcRole > MesProProcessWageFieldDO.CALC_ROLE_RECORD_ONLY) { throw new ServiceException(PRO_PROCESS_WAGE_FIELD_ROLE_INVALID.getCode(), StrUtil.format("工序[{}]字段[{}]的计算角色[{}]不合法;期望 1-系数 / 2-加减金额 / 3-计价数量 / 4-仅记录,请重新选择", process.getName(), name, calcRole)); } boolean numericRole = calcRole == MesProProcessWageFieldDO.CALC_ROLE_COEFFICIENT || calcRole == MesProProcessWageFieldDO.CALC_ROLE_AMOUNT || calcRole == MesProProcessWageFieldDO.CALC_ROLE_QUANTITY_PRICE; if (numericRole && !Objects.equals(fieldType, MesProProcessWageFieldDO.FIELD_TYPE_NUMBER)) { throw new ServiceException(PRO_PROCESS_WAGE_FIELD_TYPE_INVALID.getCode(), StrUtil.format("工序[{}]字段[{}]参与工资计算,字段类型必须为「数值」,实际为[{}];请改为数值类型后重试", process.getName(), name, fieldTypeName(fieldType))); } if (numericRole) { // 固定值允许留空:由质检在过程检验单上填写,计算时系数按 1、金额/数量按 0 处理 BigDecimal value = parseValue(process.getName(), name, item.getFieldValue()); if (calcRole == MesProProcessWageFieldDO.CALC_ROLE_COEFFICIENT && value != null && value.signum() <= 0) { throw new ServiceException(PRO_PROCESS_WAGE_FIELD_VALUE_INVALID.getCode(), StrUtil.format("工序[{}]字段[{}]为工资系数,期望大于 0 的数值,实际为[{}];请填写大于 0 的系数,或留空由质检填写", process.getName(), name, item.getFieldValue())); } if (calcRole == MesProProcessWageFieldDO.CALC_ROLE_QUANTITY_PRICE && (item.getUnitPrice() == null || item.getUnitPrice().signum() < 0)) { throw new ServiceException(PRO_PROCESS_WAGE_FIELD_VALUE_INVALID.getCode(), StrUtil.format("工序[{}]字段[{}]为计价数量,期望配置单价且不小于 0,实际为[{}];请补充单价后重试", process.getName(), name, item.getUnitPrice())); } } } } private BigDecimal parseValue(String processName, String fieldName, String fieldValue) { // 留空返回 null,由质检在过程检验单填写;非空则必须是数值 if (StrUtil.isBlank(fieldValue)) { return null; } try { return new BigDecimal(fieldValue.trim()); } catch (NumberFormatException ex) { throw new ServiceException(PRO_PROCESS_WAGE_FIELD_VALUE_INVALID.getCode(), StrUtil.format("工序[{}]字段[{}]参与工资计算,期望数值,实际为[{}];请改为数值后重试", processName, fieldName, fieldValue)); } } private static String fieldTypeName(Integer fieldType) { if (fieldType == null) { return "未知"; } return switch (fieldType) { case MesProProcessWageFieldDO.FIELD_TYPE_NUMBER -> "数值"; case MesProProcessWageFieldDO.FIELD_TYPE_TEXT -> "文本"; case MesProProcessWageFieldDO.FIELD_TYPE_SELECT -> "下拉"; case MesProProcessWageFieldDO.FIELD_TYPE_BOOLEAN -> "布尔"; default -> "未知"; }; } }