@@ -0,0 +1,22 @@ | |||||
package com.ningdatech.pmapi.common.compare; | |||||
import java.lang.annotation.ElementType; | |||||
import java.lang.annotation.Retention; | |||||
import java.lang.annotation.RetentionPolicy; | |||||
import java.lang.annotation.Target; | |||||
/** | |||||
* 字段标记注解 | |||||
* | |||||
* @author zpf | |||||
* @since 2023/07/21 | |||||
*/ | |||||
@Target(ElementType.FIELD) | |||||
@Retention(RetentionPolicy.RUNTIME) | |||||
public @interface Compare { | |||||
/** | |||||
* 字段名称 | |||||
*/ | |||||
String value(); | |||||
} |
@@ -0,0 +1,47 @@ | |||||
package com.ningdatech.pmapi.common.compare; | |||||
/** | |||||
* @author zpf | |||||
* @since 2023/07/21 | |||||
*/ | |||||
public class CompareNode { | |||||
/** | |||||
* 字段 | |||||
*/ | |||||
private String fieldKey; | |||||
/** | |||||
* 字段值 | |||||
*/ | |||||
private Object fieldValue; | |||||
/** | |||||
* 字段名称 | |||||
*/ | |||||
private String fieldName; | |||||
public String getFieldKey() { | |||||
return fieldKey; | |||||
} | |||||
public void setFieldKey(String fieldKey) { | |||||
this.fieldKey = fieldKey; | |||||
} | |||||
public Object getFieldValue() { | |||||
return fieldValue; | |||||
} | |||||
public void setFieldValue(Object fieldValue) { | |||||
this.fieldValue = fieldValue; | |||||
} | |||||
public String getFieldName() { | |||||
return fieldName; | |||||
} | |||||
public void setFieldName(String fieldName) { | |||||
this.fieldName = fieldName; | |||||
} | |||||
} |
@@ -0,0 +1,181 @@ | |||||
package com.ningdatech.pmapi.common.compare; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import java.lang.reflect.Field; | |||||
import java.util.*; | |||||
/** | |||||
* 使用须知: <br> | |||||
* (1)该工具类主要用于两个同类对象的属性值比较; <br> | |||||
* (2)使用本工具类前,请将对应的类属性上打上 @Compare("xxx") 注解,其中xxx为字段的表达名称;<br> | |||||
* (3)为了比较灵活,只有打了该注解才会进行比较,不打的字段则不会进行比较 <br> | |||||
* (4)比较后,只会返回有变化的字段,无变化的字符则不返回 <br> | |||||
* | |||||
* @author zpf | |||||
* @since 2023/07/21 | |||||
*/ | |||||
public class CompareUtils<T> { | |||||
private static final String COMMA = ","; | |||||
/** | |||||
* 属性比较 | |||||
* | |||||
* @param source 源数据对象 | |||||
* @param target 目标数据对象 | |||||
* @return 对应属性值的比较变化 | |||||
*/ | |||||
public String compare(T source, T target) { | |||||
return compare(source, target, null); | |||||
} | |||||
/** | |||||
* 属性比较 | |||||
* | |||||
* @param source 源数据对象 | |||||
* @param target 目标数据对象 | |||||
* @param ignoreCompareFields 忽略比较的字段 | |||||
* @return 对应属性值的比较变化 | |||||
*/ | |||||
public String compare(T source, T target, List<String> ignoreCompareFields) { | |||||
if (Objects.isNull(source) && Objects.isNull(target)) { | |||||
return ""; | |||||
} | |||||
Map<String, CompareNode> sourceMap = this.getFiledValueMap(source); | |||||
Map<String, CompareNode> targetMap = this.getFiledValueMap(target); | |||||
if (sourceMap.isEmpty() && targetMap.isEmpty()) { | |||||
return ""; | |||||
} | |||||
// 如果源数据为空,则只显示目标数据,不显示属性变化情况 | |||||
if (sourceMap.isEmpty()) { | |||||
return doEmpty(targetMap, ignoreCompareFields); | |||||
} | |||||
// 如果源数据为空,则显示属性变化情况 | |||||
String s = doCompare(sourceMap, targetMap, ignoreCompareFields); | |||||
if (!s.endsWith(COMMA)) { | |||||
return s; | |||||
} | |||||
return s.substring(0, s.length() - 1); | |||||
} | |||||
public JSONObject compareToJson(T source, T target, List<String> ignoreCompareFields) { | |||||
JSONObject res = new JSONObject(); | |||||
if (Objects.isNull(source) && Objects.isNull(target)) { | |||||
return res; | |||||
} | |||||
Map<String, CompareNode> sourceMap = this.getFiledValueMap(source); | |||||
Map<String, CompareNode> targetMap = this.getFiledValueMap(target); | |||||
if (sourceMap.isEmpty() && targetMap.isEmpty()) { | |||||
return res; | |||||
} | |||||
// 如果源数据为空,则只显示目标数据,不显示属性变化情况 | |||||
if (sourceMap.isEmpty()) { | |||||
return res; | |||||
} | |||||
return doCompareJson(sourceMap, targetMap, ignoreCompareFields); | |||||
} | |||||
private String doEmpty(Map<String, CompareNode> targetMap, List<String> ignoreCompareFields) { | |||||
StringBuilder sb = new StringBuilder(); | |||||
Collection<CompareNode> values = targetMap.values(); | |||||
int size = values.size(); | |||||
int current = 0; | |||||
for (CompareNode node : values) { | |||||
current++; | |||||
Object o = Optional.ofNullable(node.getFieldValue()).orElse(""); | |||||
if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(node.getFieldKey())) { | |||||
continue; | |||||
} | |||||
if (o.toString().length() > 0) { | |||||
sb.append("[" + node.getFieldName() + ":" + o + "]"); | |||||
if (current < size) { | |||||
sb.append(COMMA); | |||||
} | |||||
} | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
private String doCompare(Map<String, CompareNode> sourceMap, Map<String, CompareNode> targetMap, List<String> ignoreCompareFields) { | |||||
StringBuilder sb = new StringBuilder(); | |||||
Set<String> keys = sourceMap.keySet(); | |||||
int size = keys.size(); | |||||
int current = 0; | |||||
for (String key : keys) { | |||||
current++; | |||||
CompareNode sn = sourceMap.get(key); | |||||
CompareNode tn = targetMap.get(key); | |||||
if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(sn.getFieldKey())) { | |||||
continue; | |||||
} | |||||
String sv = Optional.ofNullable(sn.getFieldValue()).orElse("").toString(); | |||||
String tv = Optional.ofNullable(tn.getFieldValue()).orElse("").toString(); | |||||
// 只有两者属性值不一致时, 才显示变化情况 | |||||
if (!sv.equals(tv)) { | |||||
sb.append(String.format("[%s:%s -> %s]", sn.getFieldName(), sv, tv)); | |||||
if (current < size) { | |||||
sb.append(COMMA); | |||||
} | |||||
} | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
private JSONObject doCompareJson(Map<String, CompareNode> sourceMap, Map<String, CompareNode> targetMap, List<String> ignoreCompareFields) { | |||||
JSONObject res = new JSONObject(); | |||||
Set<String> keys = sourceMap.keySet(); | |||||
int size = keys.size(); | |||||
int current = 0; | |||||
for (String key : keys) { | |||||
current++; | |||||
CompareNode sn = sourceMap.get(key); | |||||
CompareNode tn = targetMap.get(key); | |||||
if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(sn.getFieldKey())) { | |||||
continue; | |||||
} | |||||
String sv = Optional.ofNullable(sn.getFieldValue()).orElse("").toString(); | |||||
String tv = Optional.ofNullable(tn.getFieldValue()).orElse("").toString(); | |||||
// 只有两者属性值不一致时, 才显示变化情况 | |||||
if (!sv.equals(tv)) { | |||||
JSONObject valueChange = new JSONObject(); | |||||
valueChange.put("old",sv); | |||||
valueChange.put("new",tv); | |||||
res.put(sn.getFieldName(),valueChange); | |||||
} | |||||
} | |||||
return res; | |||||
} | |||||
private Map<String, CompareNode> getFiledValueMap(T t) { | |||||
if (Objects.isNull(t)) { | |||||
return Collections.emptyMap(); | |||||
} | |||||
Field[] fields = t.getClass().getDeclaredFields(); | |||||
if (Objects.isNull(fields) || fields.length == 0) { | |||||
return Collections.emptyMap(); | |||||
} | |||||
Map<String, CompareNode> map = new LinkedHashMap(); | |||||
for (Field field : fields) { | |||||
Compare compareAnnotation = field.getAnnotation(Compare.class); | |||||
if (Objects.isNull(compareAnnotation)) { | |||||
continue; | |||||
} | |||||
field.setAccessible(true); | |||||
try { | |||||
String fieldKey = field.getName(); | |||||
CompareNode node = new CompareNode(); | |||||
node.setFieldKey(fieldKey); | |||||
node.setFieldValue(field.get(t)); | |||||
node.setFieldName(compareAnnotation.value()); | |||||
map.put(field.getName(), node); | |||||
} catch (IllegalArgumentException | IllegalAccessException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
return map; | |||||
} | |||||
} |
@@ -14,6 +14,7 @@ import com.ningdatech.basic.model.PageVo; | |||||
import com.ningdatech.basic.util.CollUtils; | import com.ningdatech.basic.util.CollUtils; | ||||
import com.ningdatech.file.entity.File; | import com.ningdatech.file.entity.File; | ||||
import com.ningdatech.file.service.FileService; | import com.ningdatech.file.service.FileService; | ||||
import com.ningdatech.pmapi.common.compare.CompareUtils; | |||||
import com.ningdatech.pmapi.common.constant.BizConst; | import com.ningdatech.pmapi.common.constant.BizConst; | ||||
import com.ningdatech.pmapi.common.constant.CommonConst; | import com.ningdatech.pmapi.common.constant.CommonConst; | ||||
import com.ningdatech.pmapi.common.constant.RegionConst; | import com.ningdatech.pmapi.common.constant.RegionConst; | ||||
@@ -1121,6 +1122,35 @@ public class ProjectLibManage { | |||||
} | } | ||||
/** | /** | ||||
* 判断 当前项目是不是被驳回 或者 退回过 | |||||
* @param projectId | |||||
* @return | |||||
*/ | |||||
public Boolean isChangeRecord(Long projectId) { | |||||
//1.先判断下 项目存不存在 | |||||
Project project = projectService.getById(projectId); | |||||
if(Objects.isNull(project)){ | |||||
return Boolean.FALSE; | |||||
} | |||||
//2.要判断 项目在当前状态 有没有被驳回和退回过 | |||||
//当前项目状态流程的 实例 看看是不是有2个以上 有2个说明 有退回 驳回 | |||||
List<ProjectInst> pis = projectInstService.list(Wrappers.lambdaQuery(ProjectInst.class) | |||||
.eq(ProjectInst::getProjectId, projectId) | |||||
.orderByDesc(ProjectInst::getInstType) | |||||
.last("limit 2")); | |||||
if(CollUtil.isEmpty(pis)){ | |||||
return Boolean.FALSE; | |||||
} | |||||
if( pis.size() < 2 || | |||||
!pis.get(0).getInstType().equals(pis.get(1).getInstType())) { | |||||
return Boolean.FALSE; | |||||
} | |||||
return Boolean.TRUE; | |||||
} | |||||
/** | |||||
* 查看 项目的 变更记录(驳回 重新发起的 和上个版本的变更字段) | * 查看 项目的 变更记录(驳回 重新发起的 和上个版本的变更字段) | ||||
* @param projectId | * @param projectId | ||||
* @return | * @return | ||||
@@ -1132,20 +1162,35 @@ public class ProjectLibManage { | |||||
Project project = projectService.getById(projectId); | Project project = projectService.getById(projectId); | ||||
VUtils.isTrue(Objects.isNull(project)).throwMessage("项目不存在"); | VUtils.isTrue(Objects.isNull(project)).throwMessage("项目不存在"); | ||||
//2.要判断 项目在当前状态 有没有被驳回和退回过 | //2.要判断 项目在当前状态 有没有被驳回和退回过 | ||||
//当前项目状态流程的 实例 看看是不是有2个以上 有2个说明 有退回 驳回 | //当前项目状态流程的 实例 看看是不是有2个以上 有2个说明 有退回 驳回 | ||||
List<ProjectInst> pis = projectInstService.list(Wrappers.lambdaQuery(ProjectInst.class) | |||||
.eq(ProjectInst::getProjectId, projectId) | |||||
.orderByDesc(ProjectInst::getInstType) | |||||
.last("limit 2")); | |||||
VUtils.isTrue(CollUtil.isEmpty(pis)).throwMessage("该项目没有审批流"); | |||||
VUtils.isTrue( pis.size() < 2 || | |||||
!pis.get(0).getInstType().equals(pis.get(1).getInstType())) | |||||
.throwMessage("该项目在当前流程没有被驳回 撤回过"); | |||||
Boolean isChangeRecord = isChangeRecord(projectId); | |||||
if(!isChangeRecord){ | |||||
return res; | |||||
} | |||||
//3. 去对比 当前版本和上个版本 的字段对比 | //3. 去对比 当前版本和上个版本 的字段对比 | ||||
String projectCode = project.getProjectCode(); | String projectCode = project.getProjectCode(); | ||||
List<Project> twoVersions = projectService.list(Wrappers.lambdaQuery(Project.class) | |||||
.eq(Project::getProjectCode, projectCode) | |||||
.orderByDesc(Project::getVersion) | |||||
.last("limit 2")); | |||||
if(CollUtil.isEmpty(twoVersions) || twoVersions.size() < 2){ | |||||
return res; | |||||
} | |||||
return checkTwoProjectChanges(res,twoVersions); | |||||
} | |||||
/** | |||||
* 列出 2个版本项目的 不同字段 | |||||
* @param res | |||||
* @param twoVersions | |||||
* @return | |||||
*/ | |||||
private JSONObject checkTwoProjectChanges(JSONObject res, List<Project> twoVersions) { | |||||
res = new CompareUtils<Project>().compareToJson(twoVersions.get(0), twoVersions.get(1),null); | |||||
return res; | return res; | ||||
} | } | ||||
} | } |
@@ -563,6 +563,8 @@ public class TodoCenterManage { | |||||
res.setCanWithdraw(withDrawHandle.checkCanWithdraw(instanceId,progressInstanceDetail,request.getTaskId())); | res.setCanWithdraw(withDrawHandle.checkCanWithdraw(instanceId,progressInstanceDetail,request.getTaskId())); | ||||
res.setIsHighLine(isHighLine); | res.setIsHighLine(isHighLine); | ||||
passHandle.checkCanPassOrSeal(request.getInstanceId(),request.getTaskId(),employeeCode,res); | passHandle.checkCanPassOrSeal(request.getInstanceId(),request.getTaskId(),employeeCode,res); | ||||
//是不是被 驳回|退回 | |||||
res.setIsChange(projectLibManage.isChangeRecord(projectId)); | |||||
return res; | return res; | ||||
} | } | ||||
@@ -58,4 +58,9 @@ public class ProcessProgressDetailVo { | |||||
* 当前审批人 是不是流程配置里的 上级条线主管 | * 当前审批人 是不是流程配置里的 上级条线主管 | ||||
*/ | */ | ||||
private Boolean isHighLine = Boolean.FALSE; | private Boolean isHighLine = Boolean.FALSE; | ||||
/** | |||||
* 当前项目 是不是被 驳回|退回过 | |||||
*/ | |||||
private Boolean isChange = Boolean.FALSE; | |||||
} | } |