diff --git a/mes-module-heli/mes-module-heli-biz/pom.xml b/mes-module-heli/mes-module-heli-biz/pom.xml index 556fb794..f6debc24 100644 --- a/mes-module-heli/mes-module-heli-biz/pom.xml +++ b/mes-module-heli/mes-module-heli-biz/pom.xml @@ -32,6 +32,11 @@ mes-module-system-biz ${revision} + + com.chanko.yunxi + mes-module-infra-biz + ${revision} + @@ -117,6 +122,12 @@ mes-spring-boot-starter-file + + com.github.dadiyang + equator + 1.0.4 + + diff --git a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/controller/admin/projectorder/vo/ProjectOrderRespVO.java b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/controller/admin/projectorder/vo/ProjectOrderRespVO.java index a072dda7..d808d2d6 100644 --- a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/controller/admin/projectorder/vo/ProjectOrderRespVO.java +++ b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/controller/admin/projectorder/vo/ProjectOrderRespVO.java @@ -1,15 +1,15 @@ package com.chanko.yunxi.mes.module.heli.controller.admin.projectorder.vo; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.chanko.yunxi.mes.framework.excel.core.annotations.DictFormat; +import com.chanko.yunxi.mes.framework.excel.core.convert.DictConvert; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; +import lombok.Data; + import java.math.BigDecimal; -import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import com.chanko.yunxi.mes.framework.excel.core.annotations.DictFormat; -import com.chanko.yunxi.mes.framework.excel.core.convert.DictConvert; +import java.util.Set; @Schema(description = "管理后台 - 项目订单 Response VO") @Data @@ -173,4 +173,7 @@ public class ProjectOrderRespVO { @ExcelProperty("快照原始单据日期") private LocalDateTime snapshotOrderTime; + @Schema(description = "变更的字段列表") + private Set alterFieldNames; + } diff --git a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/controller/admin/projectorder/vo/ProjectOrderSaveReqVO.java b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/controller/admin/projectorder/vo/ProjectOrderSaveReqVO.java index ea069491..3431f4bf 100644 --- a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/controller/admin/projectorder/vo/ProjectOrderSaveReqVO.java +++ b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/controller/admin/projectorder/vo/ProjectOrderSaveReqVO.java @@ -102,7 +102,6 @@ public class ProjectOrderSaveReqVO { private Integer hasBlueprint; @Schema(description = "图纸/数模 说明", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") - @NotEmpty(message = "图纸/数模 说明不能为空") private String blueprintRemark; @Schema(description = "状态,1表示正常,2表示禁用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") diff --git a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/dal/dataobject/projectorder/ProjectOrderDO.java b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/dal/dataobject/projectorder/ProjectOrderDO.java index 40893aa1..675bef61 100644 --- a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/dal/dataobject/projectorder/ProjectOrderDO.java +++ b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/dal/dataobject/projectorder/ProjectOrderDO.java @@ -1,18 +1,18 @@ package com.chanko.yunxi.mes.module.heli.dal.dataobject.projectorder; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.chanko.yunxi.mes.framework.mybatis.core.dataobject.BaseDO; import com.chanko.yunxi.mes.framework.operatelog.core.enums.OperateTypeEnum; import com.chanko.yunxi.mes.module.heli.enums.ProjectOrderStatusEnum; import com.chanko.yunxi.mes.module.heli.enums.YesOrNoEnum; import lombok.*; -import java.util.*; + import java.math.BigDecimal; import java.time.LocalDateTime; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import com.chanko.yunxi.mes.framework.mybatis.core.dataobject.BaseDO; +import java.util.Set; /** * 项目订单 DO @@ -196,6 +196,12 @@ public class ProjectOrderDO extends BaseDO { @TableField(exist = false) private String businessManName; + /** + * 变更的字段列表 + * */ + @TableField(exist = false) + private Set alterFieldNames; + public boolean canSave(){ return this.orderStatus.intValue() <= ProjectOrderStatusEnum.SAVE.getCode(); } diff --git a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/dal/dataobject/projectorder/ProjectOrderSubDO.java b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/dal/dataobject/projectorder/ProjectOrderSubDO.java index faba23f5..6e5325b2 100644 --- a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/dal/dataobject/projectorder/ProjectOrderSubDO.java +++ b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/dal/dataobject/projectorder/ProjectOrderSubDO.java @@ -1,11 +1,13 @@ package com.chanko.yunxi.mes.module.heli.dal.dataobject.projectordersub; -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; import com.chanko.yunxi.mes.framework.mybatis.core.dataobject.BaseDO; +import lombok.*; + +import java.util.Set; /** * 项目订单子项目 DO @@ -64,5 +66,10 @@ public class ProjectOrderSubDO extends BaseDO { * 删除 物理删除 */ private Boolean deleted; + /** + * 变更的字段列表 + * */ + @TableField(exist = false) + private Set alterFieldNames; } diff --git a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/service/projectorder/ProjectOrderServiceImpl.java b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/service/projectorder/ProjectOrderServiceImpl.java index 3161c82e..f5a9f0db 100644 --- a/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/service/projectorder/ProjectOrderServiceImpl.java +++ b/mes-module-heli/mes-module-heli-biz/src/main/java/com/chanko/yunxi/mes/module/heli/service/projectorder/ProjectOrderServiceImpl.java @@ -17,6 +17,12 @@ import com.chanko.yunxi.mes.module.heli.enums.ProjectOrderStatusEnum; import com.chanko.yunxi.mes.module.heli.enums.YesOrNoEnum; import com.chanko.yunxi.mes.module.heli.service.customer.CustomerService; import com.chanko.yunxi.mes.module.heli.service.serialnumber.SerialNumberService; +import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FilePageReqVO; +import com.chanko.yunxi.mes.module.infra.dal.dataobject.file.FileDO; +import com.chanko.yunxi.mes.module.infra.service.file.FileService; +import com.github.dadiyang.equator.FieldInfo; +import com.github.dadiyang.equator.GetterBaseEquator; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -24,8 +30,8 @@ import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import java.text.SimpleDateFormat; import java.time.LocalDateTime; -import java.util.Date; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; import static com.chanko.yunxi.mes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.chanko.yunxi.mes.module.heli.enums.CodeEnum.PROJECT_ORDER; @@ -37,10 +43,13 @@ import static com.chanko.yunxi.mes.module.heli.enums.ErrorCodeConstants.PROJECT_ * * @author 管理员 */ +@Slf4j @Service @Validated public class ProjectOrderServiceImpl implements ProjectOrderService { + private static final GetterBaseEquator FIELD_EQUATOR = new GetterBaseEquator(); + @Resource private ProjectOrderMapper projectOrderMapper; @Resource @@ -52,6 +61,9 @@ public class ProjectOrderServiceImpl implements ProjectOrderService { @Resource private CustomerService customerService; + @Resource + private FileService fileService; + @Override @Transactional(rollbackFor = Exception.class) public Long createProjectOrder(ProjectOrderSaveReqVO createReqVO) { @@ -92,8 +104,24 @@ public class ProjectOrderServiceImpl implements ProjectOrderService { .setOrderTime(LocalDateTime.now()) ; projectOrderMapper.insert(projectOrder); - + // 子项目 createProjectOrderSubList(projectOrder.getId(), createReqVO.getProjectOrderSubs()); + // 附件 + PageResult filePage = fileService.getFilePage(new FilePageReqVO() {{ + setPageNo(1); + setPageSize(99); + setBusinessId(createReqVO.getId()); + setBusinessType(BusinesTypeEnum.PROJECT_ORDER.name()); + }}); + if(filePage.getTotal() > 0){ + List list = filePage.getList(); + list.forEach(fileDO -> { + fileDO.setId(null) + .setBusinessId(projectOrder.getId()) + .setBusinessType(BusinesTypeEnum.PROJECT_ORDER_SNAPSHOT.name()); + }); + fileService.insertBatch(list); + } // 回写序列记录 serialNumberService.updateSerialNumber(serialNumberDO); } @@ -144,7 +172,55 @@ public class ProjectOrderServiceImpl implements ProjectOrderService { @Override public ProjectOrderDO getProjectOrder(Long id) { - return projectOrderMapper.selectById(id); + ProjectOrderDO projectOrderDO = projectOrderMapper.selectById(id); + // 查询最近归档历史并对比变化字段 + try { + ProjectOrderDO lastSnapshot = projectOrderMapper.selectOne(new LambdaQueryWrapper() {{ + eq(ProjectOrderDO::getSnapshotId, id); + orderByDesc(ProjectOrderDO::getId); + last("LIMIT 1"); + }}); + if(lastSnapshot != null){ + List diffFields = FIELD_EQUATOR.getDiffFields(projectOrderDO, lastSnapshot); + projectOrderDO.setAlterFieldNames(diffFields.stream().map(FieldInfo::getFieldName).collect(Collectors.toSet())); + + // 附件 + List fileList = new ArrayList<>(16); + PageResult filePage = fileService.getFilePage(new FilePageReqVO() {{ + setPageNo(1); + setPageSize(99); + setBusinessId(id); + setBusinessType(BusinesTypeEnum.PROJECT_ORDER.name()); + }}); + if(filePage.getTotal() > 0) { + fileList.addAll(filePage.getList()); + } + + List lastSnapshotFilelist = new ArrayList<>(16);; + PageResult lastSnapshotFilePage = fileService.getFilePage(new FilePageReqVO() {{ + setPageNo(1); + setPageSize(99); + setBusinessId(lastSnapshot.getId()); + setBusinessType(BusinesTypeEnum.PROJECT_ORDER_SNAPSHOT.name()); + }}); + if(lastSnapshotFilePage.getTotal() > 0) { + lastSnapshotFilelist.addAll(lastSnapshotFilePage.getList()); + } + if(fileList.size() != lastSnapshotFilelist.size()){ + projectOrderDO.getAlterFieldNames().add("attachments"); + }else{ + boolean allMatch = fileList.stream().allMatch(fileDO -> lastSnapshotFilelist.stream().anyMatch(sFile -> fileDO.getPath().equals(sFile.getPath()))); + if(!allMatch){ + projectOrderDO.getAlterFieldNames().add("attachments"); + } + } + } + }catch (Exception e){ + // do nothing + log.error("generate alterFieldNames error, id: {}, exception: {}", id, e.getMessage(), e); + } + + return projectOrderDO; } @Override @@ -156,7 +232,31 @@ public class ProjectOrderServiceImpl implements ProjectOrderService { @Override public List getProjectOrderSubListByProjectOrderId(Long projectOrderId) { - return projectOrderSubMapper.selectListByProjectOrderId(projectOrderId); + List projectOrderSubDOList = projectOrderSubMapper.selectListByProjectOrderId(projectOrderId); + if(!projectOrderSubDOList.isEmpty()){ + // 查询最近归档历史并对比变化字段 + try { + ProjectOrderDO lastSnapshot = projectOrderMapper.selectOne(new LambdaQueryWrapper() {{ + eq(ProjectOrderDO::getSnapshotId, projectOrderId); + orderByDesc(ProjectOrderDO::getId); + last("LIMIT 1"); + }}); + if(lastSnapshot != null){ + List lastSnapshotSubDOList = projectOrderSubMapper.selectList(ProjectOrderSubDO::getProjectOrderId, lastSnapshot.getId()); + Map> nameGroups = lastSnapshotSubDOList.stream().collect(Collectors.groupingBy(ProjectOrderSubDO::getName)); + projectOrderSubDOList.forEach(projectOrderSubDO -> { + List lastSnapshotSubs = nameGroups.get(projectOrderSubDO.getName()); + if(lastSnapshotSubs.isEmpty()) return; + List diffFields = FIELD_EQUATOR.getDiffFields(projectOrderSubDO, lastSnapshotSubs.get(0)); + projectOrderSubDO.setAlterFieldNames(diffFields.stream().map(FieldInfo::getFieldName).collect(Collectors.toSet())); + }); + } + }catch (Exception e){ + log.error("generate sub alterFieldNames error, id: {}, exception: {}", projectOrderId, e.getMessage(), e); + } + + } + return projectOrderSubDOList; } @Override diff --git a/mes-module-infra/mes-module-infra-biz/src/main/java/com/chanko/yunxi/mes/module/infra/service/file/FileService.java b/mes-module-infra/mes-module-infra-biz/src/main/java/com/chanko/yunxi/mes/module/infra/service/file/FileService.java index f265611d..dd56ec2e 100644 --- a/mes-module-infra/mes-module-infra-biz/src/main/java/com/chanko/yunxi/mes/module/infra/service/file/FileService.java +++ b/mes-module-infra/mes-module-infra-biz/src/main/java/com/chanko/yunxi/mes/module/infra/service/file/FileService.java @@ -4,6 +4,8 @@ import com.chanko.yunxi.mes.framework.common.pojo.PageResult; import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FilePageReqVO; import com.chanko.yunxi.mes.module.infra.dal.dataobject.file.FileDO; +import java.util.List; + /** * 文件 Service 接口 * @@ -53,4 +55,10 @@ public interface FileService { * @param id */ void deleteFileLogic(Long id); + + /** + * 批量插入 + * @param fileDOList + */ + void insertBatch(List fileDOList); } diff --git a/mes-module-infra/mes-module-infra-biz/src/main/java/com/chanko/yunxi/mes/module/infra/service/file/FileServiceImpl.java b/mes-module-infra/mes-module-infra-biz/src/main/java/com/chanko/yunxi/mes/module/infra/service/file/FileServiceImpl.java index 5fd3a1cf..398429e1 100644 --- a/mes-module-infra/mes-module-infra-biz/src/main/java/com/chanko/yunxi/mes/module/infra/service/file/FileServiceImpl.java +++ b/mes-module-infra/mes-module-infra-biz/src/main/java/com/chanko/yunxi/mes/module/infra/service/file/FileServiceImpl.java @@ -14,6 +14,8 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.util.List; + import static com.chanko.yunxi.mes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.chanko.yunxi.mes.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS; @@ -101,9 +103,14 @@ public class FileServiceImpl implements FileService { @Override public void deleteFileLogic(Long id) { // 校验存在 - FileDO file = validateFileExists(id); + validateFileExists(id); // 删除记录 fileMapper.deleteById(id); } + @Override + public void insertBatch(List fileDOList){ + fileMapper.insertBatch(fileDOList); + } + } diff --git a/mes-server/pom.xml b/mes-server/pom.xml index 9caac39b..4b66340c 100644 --- a/mes-server/pom.xml +++ b/mes-server/pom.xml @@ -20,6 +20,18 @@ + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.jupiter + junit-jupiter + 5.4.0 + test + + com.chanko.yunxi mes-module-system-biz @@ -39,11 +51,6 @@ mes-module-heli-biz ${revision} - - com.github.dadiyang - equator - 1.0.4 - diff --git a/mes-ui/mes-ui-admin-vue3/src/views/heli/projectorder/detail.vue b/mes-ui/mes-ui-admin-vue3/src/views/heli/projectorder/detail.vue index b07bca65..2d8bc82c 100644 --- a/mes-ui/mes-ui-admin-vue3/src/views/heli/projectorder/detail.vue +++ b/mes-ui/mes-ui-admin-vue3/src/views/heli/projectorder/detail.vue @@ -126,6 +126,7 @@ :disabled="detailDisabled" v-model="formData.referenceTechnology" placeholder="请输入可引用的原有技术" + :class="{'alter-class': fieldHasAlter('referenceTechnology')}" /> @@ -141,6 +142,7 @@ placeholder="请输入检验要求" show-word-limit maxlength="200" + :class="{'alter-class': fieldHasAlter('referenceTechnology')}" /> @@ -197,6 +199,7 @@ - + - + @@ -357,12 +365,14 @@ :min="0" :precision="6" style="width: 150px" + :class="{'alter-class': fieldHasAlter('price')}" /> - + - + - + @@ -484,7 +502,7 @@ class="mb-0px!" > @@ -499,7 +517,7 @@ class="mb-0px!" > @@ -519,6 +537,7 @@ placeholder="请输入数量" :min="0" :precision="0" + :class="{'alter-class': fieldHasAlterInRow('amount', row)}" /> @@ -534,6 +553,7 @@ :disabled="detailDisabled" v-model="row.unit" placeholder="请选择单位" + :class="{'alter-class': fieldHasAlterInRow('unit', row)}" > @@ -610,11 +632,15 @@ - + @@ -813,7 +839,7 @@ import * as CustomerApi from '@/api/heli/customer/index' import * as CompositionApi from '@/api/heli/composition/index' import { getAccessToken, getTenantId } from '@/utils/auth' import { UploadUserFile } from 'element-plus' -import { deleteFile, downloadFile, getFilePage } from '@/api/infra/file' +import { deleteFileLogic, downloadFile, getFilePage } from '@/api/infra/file' import { propTypes } from '@/utils/propTypes' import download from '@/utils/download' import { useUserStore } from '@/store/modules/user' @@ -869,7 +895,8 @@ const formData = ref({ snapshotId: undefined, snapshotCode: undefined, orderTime: new Date(), - snapshotOrderTime: undefined + snapshotOrderTime: undefined, + alterFieldNames: [] }) const formRules = reactive({ businessDeptId: [{ required: true, message: '提出部门不能为空', trigger: 'blur' }], @@ -898,6 +925,14 @@ const subFormRules = reactive({ status: [{ required: true, message: '状态,1表示正常,2表示禁用不能为空', trigger: 'blur' }] }) +const fieldHasAlter = (fieldName) => { + return formData.value.alterFieldNames && formData.value.alterFieldNames.indexOf(fieldName) > -1 +} + +const fieldHasAlterInRow = (fieldName, row) => { + return row.alterFieldNames && row.alterFieldNames.indexOf(fieldName) > -1 +} + const deptList = ref([]) // 树形结构 const userList = ref([]) // 用户列表 const userSelectList = ref([]) @@ -970,28 +1005,29 @@ const queryData = async (type: string, id?: number) => { if (id) { formData.value = await ProjectOrderApi.getProjectOrder(id) // 子项列表 - formData.value.projectOrderSubs = - await ProjectOrderApi.getProjectOrderSubListByProjectOrderId(id) + formData.value.projectOrderSubs = await ProjectOrderApi.getProjectOrderSubListByProjectOrderId(id) - // 如为归档日志 + // 如为归档 + let businessType = 'PROJECT_ORDER'; if(type == 'detailArchive'){ - id = formData.value.snapshotId - } - // 操作日志 - let logParams = { - pageNo: 1, - pageSize: 99, - businessId: id, - businessType: 'PROJECT_ORDER' + businessType = 'PROJECT_ORDER_SNAPSHOT' + }else{ + // 操作日志 + let logParams = { + pageNo: 1, + pageSize: 99, + businessId: id, + businessType: businessType + } + formData.value.operateLogs = (await getOperateLogPage(logParams)).list } - formData.value.operateLogs = (await getOperateLogPage(logParams)).list // 附件信息 let attParams = { pageNo: 1, pageSize: 99, businessId: id, - businessType: 'PROJECT_ORDER' + businessType: businessType } formData.value.attachments = (await getFilePage(attParams)).list } @@ -1152,7 +1188,8 @@ const onAddItem = () => { compositionId: undefined, unit: undefined, remark: undefined, - status: 1 + status: 1, + alterFieldNames: [] } row.projectOrderId = formData.value.id formData.value.projectOrderSubs.push(row) @@ -1189,7 +1226,7 @@ const protocolUploadChange = (file, files) => { } const refreshAttachments = (files, type) => { formData.value.attachments = formData.value.attachments.filter((value, index, array) => { - return value.businessFileType != type + return value.businessFileType != type || value.id }) for (let i = 0; i < files.length; i++) { let file = files[i] @@ -1199,7 +1236,7 @@ const refreshAttachments = (files, type) => { } // 排序 formData.value.attachments.sort((v1, v2) => { - return v1.createTime - v2.createTime < 0 + return (v1.createTime - v2.createTime) > 0 }) } @@ -1210,7 +1247,7 @@ const handleDeleteAttachment = async (index, type) => { const attachment = deletedAttachments[i] if (attachment.id) { // 清理已上传文件 - await deleteFile(attachment.id) + await deleteFileLogic(attachment.id) } // 清理待上传文件 contractUploadFiles.value = contractUploadFiles.value.filter((file1) => { @@ -1271,6 +1308,7 @@ const resetForm = () => { snapshotCode: undefined, orderTime: new Date(), snapshotOrderTime: undefined, + alterFieldNames: [] } formRef.value?.resetFields() } @@ -1302,4 +1340,10 @@ a { display: inline-block; margin-right: 20px; } + +.hl-card .alter-class { + position: relative; + border: solid 1px orange; + outline: solid 1px orange; +}