diff --git a/pmapi/pom.xml b/pmapi/pom.xml index 639f416..e6e7f2d 100644 --- a/pmapi/pom.xml +++ b/pmapi/pom.xml @@ -250,6 +250,26 @@ com.ningdatech nd-flowable-starter + + + com.alibaba.xxpt + zwdd + 1.2.0 + system + ${basedir}/src/lib/zwdd-sdk-java-1.2.0.jar + + + + org.apache.httpcomponents + httpclient + + + + joda-time + joda-time + 2.10.6 + + diff --git a/pmapi/src/lib/zwdd-sdk-java-1.2.0.jar b/pmapi/src/lib/zwdd-sdk-java-1.2.0.jar new file mode 100644 index 0000000..44405ec Binary files /dev/null and b/pmapi/src/lib/zwdd-sdk-java-1.2.0.jar differ diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/App.java b/pmapi/src/main/java/com/ningdatech/pmapi/App.java index 39024fd..55f6281 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/App.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/App.java @@ -3,6 +3,7 @@ package com.ningdatech.pmapi; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/config/BeanConfig.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/config/BeanConfig.java new file mode 100644 index 0000000..93bfd09 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/config/BeanConfig.java @@ -0,0 +1,46 @@ +package com.ningdatech.pmapi.common.config; + +import com.alibaba.xxpt.gateway.shared.client.http.ExecutableClient; +import com.ningdatech.pmapi.common.util.SpringContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; + +/** + * @author CMM + * @since 2023/02/03 09:24 + */ +@Configuration +@Slf4j +public class BeanConfig { + @Bean + public SpringContextHolder springContextHolder() { + return new SpringContextHolder(); + } + + @Bean("authExecutableClient") + @DependsOn(value = "govDingProperties") + public ExecutableClient executableAuthClientInit() { + ExecutableClient executableClient = ExecutableClient.getInstance(); + executableClient.setAccessKey(GovDingProperties.appAuthkey); + executableClient.setSecretKey(GovDingProperties.appAuthsecret); + executableClient.setDomainName(GovDingProperties.domain); + executableClient.setProtocal("https"); + executableClient.init(); + return executableClient; + } + + @Bean("executableClient") + @DependsOn(value = "govDingProperties") + public ExecutableClient executableClientInit() { + ExecutableClient executableClient = new ExecutableClient(); + executableClient.setAccessKey(GovDingProperties.appkey); + executableClient.setSecretKey(GovDingProperties.appsecret); + executableClient.setDomainName(GovDingProperties.domain); + executableClient.setProtocal("https"); + executableClient.init(); + return executableClient; + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/config/GovDingProperties.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/config/GovDingProperties.java new file mode 100644 index 0000000..34ccd97 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/config/GovDingProperties.java @@ -0,0 +1,65 @@ +package com.ningdatech.pmapi.common.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import lombok.Data; + +/** + * @description: 浙政钉配置 + * @author: liushuai + * @date: 2022/5/5 15:10 + */ +@Data +@Component +public class GovDingProperties { + public static String appkey; + public static String appsecret; + public static String appAuthkey; + public static String appAuthsecret; + + public static String appSsoAuthkey; + public static String appSsoAuthsecret; + public static Long tenantId; + + public static String domain; + + @Value("${ding.app-key}") + public void setPrivateKey(String appkey) { + GovDingProperties.appkey = appkey; + } + + @Value("${ding.app-secret}") + public void setAppsecret(String appsecret) { + GovDingProperties.appsecret = appsecret; + } + + @Value("${ding.app-auth-key}") + public void setAppAuthKey(String appAuthkey) { + GovDingProperties.appAuthkey = appAuthkey; + } + + @Value("${ding.app-auth-secret}") + public void setAppAuthsecret(String appAuthsecret) { + GovDingProperties.appAuthsecret = appAuthsecret; + } + + @Value("${ding.app-sso-auth-key}") + public void setAppSsoAuthKey(String appSsoAuthkey) { + GovDingProperties.appSsoAuthkey = appSsoAuthkey; + } + + @Value("${ding.app-sso-auth-secret}") + public void setAppSsoAuthsecret(String appSsoAuthsecret) { + GovDingProperties.appSsoAuthsecret = appSsoAuthsecret; + } + + @Value("${ding.tenantId}") + public void setTenantId(Long tenantId) { + GovDingProperties.tenantId = tenantId; + } + @Value("${ding.domain}") + public void setDomain(String domain) { + GovDingProperties.domain = domain; + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/constant/DingConstant.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/constant/DingConstant.java new file mode 100644 index 0000000..ebe14a2 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/constant/DingConstant.java @@ -0,0 +1,12 @@ +package com.ningdatech.pmapi.common.constant; +/** + * 浙政钉常量 + * @author CMM + * @since 2023/02/01 14:49 + */ +public interface DingConstant { + /** + * 工作通知 + */ + String WORKING_NOTICE = "/message/workNotification"; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/util/CallBack.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/util/CallBack.java new file mode 100644 index 0000000..c82d6c0 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/util/CallBack.java @@ -0,0 +1,25 @@ +package com.ningdatech.pmapi.common.util; + +/** + * @author qinxianyun + * @see {@link SpringContextHolder} + * 针对某些初始化方法,在SpringContextHolder 初始化前时,
+ * 可提交一个 提交回调任务。
+ * 在SpringContextHolder 初始化后,进行回调使用 + */ + +public interface CallBack { + /** + * 回调执行方法 + */ + void executor(); + + /** + * 本回调任务名称 + * @return / + */ + default String getCallBackName() { + return Thread.currentThread().getId() + ":" + this.getClass().getName(); + } +} + diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/util/SpringContextHolder.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/util/SpringContextHolder.java new file mode 100644 index 0000000..ebd0e3b --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/util/SpringContextHolder.java @@ -0,0 +1,150 @@ +package com.ningdatech.pmapi.common.util; + +import java.util.ArrayList; +import java.util.List; + + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.env.Environment; +import org.springframework.lang.NonNull; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author qinxianyun + */ +@Slf4j +public class SpringContextHolder implements ApplicationContextAware, DisposableBean { + + private static ApplicationContext applicationContext = null; + private static final List CALL_BACKS = new ArrayList<>(); + private static boolean addCallback = true; + + /** + * 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。 + * 在SpringContextHolder 初始化后,进行回调使用 + * + * @param callBack 回调函数 + */ + public synchronized static void addCallBacks(CallBack callBack) { + if (addCallback) { + SpringContextHolder.CALL_BACKS.add(callBack); + } else { + log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName()); + callBack.executor(); + } + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) { + assertContextInjected(); + return (T) applicationContext.getBean(name); + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + public static T getBean(Class requiredType) { + assertContextInjected(); + return applicationContext.getBean(requiredType); + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @param defaultValue 默认值 + * @param requiredType 返回类型 + * @return / + */ + public static T getProperties(String property, T defaultValue, Class requiredType) { + T result = defaultValue; + try { + result = getBean(Environment.class).getProperty(property, requiredType); + } catch (Exception ignored) { + } + return result; + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @return / + */ + public static String getProperties(String property) { + return getProperties(property, null, String.class); + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @param requiredType 返回类型 + * @return / + */ + public static T getProperties(String property, Class requiredType) { + return getProperties(property, null, requiredType); + } + + /** + * 检查ApplicationContext不为空. + */ + private static void assertContextInjected() { + if (applicationContext == null) { + throw new IllegalStateException("applicationContext属性未注入, 请在applicationContext" + + ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder."); + } + } + + /** + * 清除SpringContextHolder中的ApplicationContext为Null. + */ + private static void clearHolder() { + log.debug("清除SpringContextHolder中的ApplicationContext:" + + applicationContext); + applicationContext = null; + } + + @Override + public void destroy() { + SpringContextHolder.clearHolder(); + } + + @Override + public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException { + if (SpringContextHolder.applicationContext != null) { + log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext); + } + SpringContextHolder.applicationContext = applicationContext; + if (addCallback) { + for (CallBack callBack : SpringContextHolder.CALL_BACKS) { + callBack.executor(); + } + CALL_BACKS.clear(); + } + SpringContextHolder.addCallback = false; + } + + /** + * 获取当前启用的配置文件 + *
    + *
  • 生产环境:prod
  • + *
  • 开发环境:dev
  • + *
+ * + * @return java.lang.String + * @author WendyYang + **/ + public static String getActiveProfile() { + String[] strings = applicationContext.getEnvironment().getActiveProfiles(); + return strings[0]; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/projectlib/model/entity/Project.java b/pmapi/src/main/java/com/ningdatech/pmapi/projectlib/model/entity/Project.java index b7f7cfe..58ff074 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/projectlib/model/entity/Project.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/projectlib/model/entity/Project.java @@ -287,6 +287,16 @@ public class Project implements Serializable { @ApiModelProperty("项目二级状态") private Integer projectStatusSecond; + + @ApiModelProperty("单位名称") + private String orgName; + @ApiModelProperty("单位code") + private String orgCode; + + @ApiModelProperty("流程状态") + private Integer processStatus; + + private Long createBy; private Long updateBy; diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/Attachment.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/Attachment.java new file mode 100644 index 0000000..274335e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/Attachment.java @@ -0,0 +1,26 @@ +package com.ningdatech.pmapi.todocenter.bean.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 审核附件实体 + * + * @author CMM + * @since 2023/02/01 16:27 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Attachment { + private String id; + //文件名 + private String name; + //文件类型 + private Boolean isImage; + //访问地址 + private String url; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProcessComment.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProcessComment.java new file mode 100644 index 0000000..23a3e03 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProcessComment.java @@ -0,0 +1,35 @@ +package com.ningdatech.pmapi.todocenter.bean.entity; + +import com.wflow.workflow.bean.vo.ProcessHandlerParamsVo; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Collections; +import java.util.List; + +/** + * 审核意见实体 + * + * @author CMM + * @since 2023/02/01 16:25 + */ +@Data +@Builder +@NoArgsConstructor +public class ProcessComment { + /** + * 文字评论 + */ + protected String text; + /** + * 评论附件 + */ + protected List attachments; + + public ProcessComment(String text, List attachments) { + this.text = text; + this.attachments = null == attachments ? Collections.emptyList() : attachments; + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProgressNode.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProgressNode.java index baca90d..fa2e2f7 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProgressNode.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProgressNode.java @@ -1,11 +1,11 @@ package com.ningdatech.pmapi.todocenter.bean.entity; +import com.ningdatech.pmapi.todocenter.bean.vo.ProgressNodeAuditInfoVo; +import com.ningdatech.pmapi.todocenter.enums.ProcessHandlerEnum; import com.ningdatech.pmapi.todocenter.model.dto.req.ReqProcessHandlerDTO; import com.wflow.workflow.bean.process.OrgUser; import com.wflow.workflow.bean.process.enums.ApprovalModeEnum; import com.wflow.workflow.bean.process.enums.NodeTypeEnum; -import com.wflow.workflow.bean.vo.ProcessHandlerParamsVo; -import com.wflow.workflow.bean.vo.TaskCommentVo; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -52,11 +52,15 @@ public class ProgressNode { /** * 该节点动作操作类型 */ - private ReqProcessHandlerDTO.Action action; + private ProcessHandlerEnum action; + /** + * 审核信息 + */ + private List auditInfos; /** * 处理结果 */ - private ReqProcessHandlerDTO.Action result; + private ProcessHandlerEnum result; /** * 开始时间 */ diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java index 502f292..061d00e 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java @@ -1,6 +1,9 @@ package com.ningdatech.pmapi.todocenter.bean.vo; +import com.ningdatech.pmapi.common.model.FileBasicInfo; +import com.ningdatech.pmapi.projectlib.model.entity.Project; import com.ningdatech.pmapi.todocenter.bean.entity.ProgressNode; +import com.ningdatech.pmapi.user.entity.UserInfo; import com.wflow.workflow.bean.process.OrgUser; import com.wflow.workflow.bean.process.form.Form; import lombok.AllArgsConstructor; @@ -8,6 +11,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDateTime; import java.util.Date; import java.util.List; import java.util.Map; @@ -58,13 +62,21 @@ public class ProcessProgressDetailVo { /** * 发起人 */ - private OrgUser staterUser; + private OrgUser startTempUser; + /** + * 发起人 + */ + private UserInfo startUser; /** * 发起人部门 */ - private String starterDept; + private String startDept; /** * 发起时间 */ private Date startTime; + /** + * 项目申报信息 + */ + private Project declaredProjectInfo; } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProgressNodeAuditInfoVo.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProgressNodeAuditInfoVo.java new file mode 100644 index 0000000..2e91abd --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProgressNodeAuditInfoVo.java @@ -0,0 +1,32 @@ +package com.ningdatech.pmapi.todocenter.bean.vo; + +import com.ningdatech.pmapi.todocenter.bean.entity.ProcessComment; +import com.wflow.workflow.bean.process.OrgUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * 流程节点审核信息 + * + * @author CMM + * @since 2023/02/01 16:52 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ProgressNodeAuditInfoVo extends ProcessComment { + private String id; + + private String type; + + private String taskId; + + private String commentType; + + private OrgUser user; + + private Date createTime; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/constant/WorkNotice.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/constant/WorkNotice.java new file mode 100644 index 0000000..aa2296f --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/constant/WorkNotice.java @@ -0,0 +1,12 @@ +package com.ningdatech.pmapi.todocenter.constant; +/** + * 工作通知格式常量 + * @author CMM + * @since 2023/02/01 14:56 + */ +public interface WorkNotice { + public final String PASS_MSG_TEMPLATE = "标题:审核任务 内容:【%s】的【%s】需要您审核。"; + public final String PASS_MSG_TEMPLATE2 = "【%s】已通过【%s】,请及时开始下一步操作。"; + + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java index 1b5863d..daf8535 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java @@ -41,9 +41,9 @@ public class TodoCenterController { * @param param * @return */ - @GetMapping("/query-project-list") - public ApiResponse> queryProjectList(@Valid @ModelAttribute ReqToBeProcessedDTO param){ - PageVo result = todoCenterManage.queryProjectList(param); + @GetMapping("/query-todo-list") + public ApiResponse> queryPendingProjectList(@Valid @ModelAttribute ReqToBeProcessedDTO param){ + PageVo result = todoCenterManage.queryPendingProjectList(param); return ApiResponse.ofSuccess(result); } @@ -54,9 +54,9 @@ public class TodoCenterController { * @param response * @return void */ - @GetMapping("/export") - public void exportProjectList(ReqToBeProcessedDTO param, HttpServletResponse response){ - ExcelDownUtil.downXlsx(response,param,todoCenterManage::exportProjectList); + @GetMapping("/exportPending") + public void exportPendingProjectList(ReqToBeProcessedDTO param, HttpServletResponse response){ + ExcelDownUtil.downXlsx(response,param,todoCenterManage::exportPendingProjectList); } /** @@ -65,7 +65,7 @@ public class TodoCenterController { * @param nodeId 当前获取流程人员关联的流程节点ID * @return 流程进度及表单详情 */ - @GetMapping("progress/{instanceId}/{nodeId}") + @GetMapping("/progress/{instanceId}/{nodeId}") public ApiResponse getProcessDetail(@PathVariable String instanceId, @PathVariable(required = false) String nodeId) { return ApiResponse.ofSuccess(todoCenterManage.getProcessDetail(nodeId, instanceId)); @@ -81,4 +81,51 @@ public class TodoCenterController { todoCenterManage.handler(param); return ApiResponse.ofSuccess(); } + + /** + * 待办中心-待我处理项目列表查询 + * @param param + * @return + */ + @GetMapping("/query-do-list") + public ApiResponse> queryHandledProjectList(@Valid @ModelAttribute ReqToBeProcessedDTO param){ + PageVo result = todoCenterManage.queryHandledProjectList(param); + return ApiResponse.ofSuccess(result); + } + /** + * 待办中心-我已处理项目列表导出 + * + * @param param + * @param response + * @return void + */ + @GetMapping("/exportHandled") + public void exportHandledProjectList(ReqToBeProcessedDTO param, HttpServletResponse response){ + ExcelDownUtil.downXlsx(response,param,todoCenterManage::exportHandledProjectList); + } + + /** + * 待办中心-我发起的项目列表查询 + * @param param + * @return + */ + @GetMapping("/query-mySubmitted-list") + public ApiResponse> queryMySubmittedProjectList(@Valid @ModelAttribute ReqToBeProcessedDTO param){ + PageVo result = todoCenterManage.queryMySubmittedProjectList(param); + return ApiResponse.ofSuccess(result); + } + + /** + * 待办中心-我发起的项目列表导出 + * + * @param param + * @param response + * @return void + */ + @GetMapping("/exportMySubmitted") + public void exportMySubmittedProjectList(ReqToBeProcessedDTO param, HttpServletResponse response){ + ExcelDownUtil.downXlsx(response,param,todoCenterManage::exportMySubmittedProjectList); + } + + } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessHandlerEnum.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessHandlerEnum.java new file mode 100644 index 0000000..bd67d5c --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessHandlerEnum.java @@ -0,0 +1,97 @@ +package com.ningdatech.pmapi.todocenter.enums; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +import java.util.Objects; + +/** + * 流程处理类型 + * @author CMM + * @since 2023/02/01 16:58 + */ +@Getter +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(value = "ProcessHandlerEnum", description = "流程处理类型-枚举") +public enum ProcessHandlerEnum { + /** + * 通过 + */ + + PASS(1, "通过"), + + /** + * 盖章并通过 + */ + SEAL_PASS(2, "盖章并通过"), + + /** + * 退回 + */ + BACK(3,"退回"), + + /** + * 撤回 + */ + WITHDRAW(4,"撤回"), + /** + * 驳回 + */ + REJECT(5,"驳回"); + + + private Integer code; + private String desc; + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public static String getDescByCode(Integer code) { + if(Objects.isNull(code)){ + return StringUtils.EMPTY; + } + for (ProcessHandlerEnum t : ProcessHandlerEnum.values()) { + if (code.equals(t.getCode())) { + return t.desc; + } + } + return StringUtils.EMPTY; + } + + public static Integer getCodeByDesc(String desc) { + if(Objects.isNull(desc)){ + return null; + } + for (ProcessHandlerEnum t : ProcessHandlerEnum.values()) { + if (desc.equals(t.getCode())) { + return t.code; + } + } + return null; + } + + public static ProcessHandlerEnum getEnumByValue(Integer code) { + if(Objects.isNull(code)){ + return null; + } + for (ProcessHandlerEnum t : ProcessHandlerEnum.values()) { + if (code.equals(t.getCode())) { + return t; + } + } + return null; + } + + public boolean eq(String val) { + return this.name().equals(val); + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessStatusEnum.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessStatusEnum.java index 822f5c8..91d311d 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessStatusEnum.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessStatusEnum.java @@ -37,7 +37,11 @@ public enum ProcessStatusEnum { /** * 审核通过 */ - APPROVED(4,"审核通过"); + APPROVED(4,"审核通过"), + /** + * 被撤回 + */ + WITHDRAW(5,"被撤回"); private Integer code; diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/BackToHisApprovalNodeCmd.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/BackToHisApprovalNodeCmd.java index 34a7286..4414063 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/BackToHisApprovalNodeCmd.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/BackToHisApprovalNodeCmd.java @@ -1,12 +1,11 @@ package com.ningdatech.pmapi.todocenter.extension.cmd; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; +import com.ningdatech.pmapi.todocenter.enums.ProcessHandlerEnum; +import lombok.RequiredArgsConstructor; import org.assertj.core.util.Sets; import org.flowable.bpmn.model.FlowNode; import org.flowable.bpmn.model.Process; @@ -16,6 +15,7 @@ import org.flowable.common.engine.api.FlowableObjectNotFoundException; import org.flowable.common.engine.impl.interceptor.Command; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager; @@ -31,9 +31,11 @@ import com.wflow.workflow.utils.FlowableUtils; * @author : willian fu * @date : 2022/10/14 */ +@RequiredArgsConstructor public class BackToHisApprovalNodeCmd implements Command, Serializable { private static final long serialVersionUID = -80075781855060928L; + protected TaskService taskService; protected RuntimeService runtimeService; protected String taskId; @@ -74,8 +76,12 @@ public class BackToHisApprovalNodeCmd implements Command, Serializable { String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement, targetFlowElement); // 实际应操作的当前节点ID String sourceRealActivityId = sourceAndTargetRealActivityId[0]; - // 实际应操作的目标节点ID - String targetRealActivityId = sourceAndTargetRealActivityId[1]; + + //// 实际应操作的目标节点ID + //String targetRealActivityId = sourceAndTargetRealActivityId[1]; + + // 实际应操作的目标节点的发起人ID + String targetRealActivityId = sourceAndTargetRealActivityId[0]; Map> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process); // 当前节点处在的并行网关list @@ -140,6 +146,10 @@ public class BackToHisApprovalNodeCmd implements Command, Serializable { if (targetRealSpecialGateway != null) { createTargetInSpecialGatewayEndExecutions(commandContext, realExecutions, process, targetInSpecialGatewayList, targetRealSpecialGateway); } + // TODO 调用原生的excute方法 在流程实例表中END_ACT_ID添加对应的字段标识 + Map var = new HashMap<>(16); + var.put("approve_" + task.getId(), ProcessHandlerEnum.BACK); + taskService.complete(task.getId(), var); return targetRealActivityId; } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java index 0fe44ae..d6ae78f 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java @@ -3,28 +3,51 @@ package com.ningdatech.pmapi.todocenter.manage; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.xxpt.gateway.shared.api.request.OapiMessageWorkNotificationRequest; +import com.alibaba.xxpt.gateway.shared.api.response.OapiMessageWorkNotificationResponse; +import com.alibaba.xxpt.gateway.shared.client.http.ExecutableClient; +import com.alibaba.xxpt.gateway.shared.client.http.IntelligentGetClient; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ningdatech.basic.enumeration.Status; import com.ningdatech.basic.exception.BizException; import com.ningdatech.basic.model.PageVo; import com.ningdatech.basic.util.NdDateUtils; +import com.ningdatech.pmapi.common.constant.DingConstant; import com.ningdatech.pmapi.common.constant.ProjectDeclareConstants; import com.ningdatech.pmapi.common.util.ExcelDownUtil; import com.ningdatech.pmapi.common.util.ExcelExportStyle; +import com.ningdatech.pmapi.projectlib.model.entity.Project; +import com.ningdatech.pmapi.projectlib.service.IProjectService; +import com.ningdatech.pmapi.todocenter.bean.entity.ProcessComment; import com.ningdatech.pmapi.todocenter.bean.entity.ProgressNode; import com.ningdatech.pmapi.todocenter.bean.vo.ProcessProgressDetailVo; import com.ningdatech.pmapi.todocenter.constant.HisProInsEndActId; +import com.ningdatech.pmapi.todocenter.enums.IsAppendProjectEnum; +import com.ningdatech.pmapi.todocenter.enums.ProcessHandlerEnum; import com.ningdatech.pmapi.todocenter.enums.ProcessStatusEnum; import com.ningdatech.pmapi.todocenter.extension.cmd.BackToHisApprovalNodeCmd; import com.ningdatech.pmapi.todocenter.model.dto.req.ReqProcessHandlerDTO; import com.ningdatech.pmapi.todocenter.model.dto.req.ReqToBeProcessedDTO; +import com.ningdatech.pmapi.todocenter.model.dto.res.ResHandledExportDTO; +import com.ningdatech.pmapi.todocenter.model.dto.res.ResMySubmittedExportDTO; import com.ningdatech.pmapi.todocenter.model.dto.res.ResToBeProcessedDTO; -import com.ningdatech.pmapi.todocenter.model.dto.res.ResToBeProjectListExportDTO; +import com.ningdatech.pmapi.todocenter.model.dto.res.ResToBeExportDTO; +import com.ningdatech.pmapi.todocenter.zwdd.model.MessageContent; +import com.ningdatech.pmapi.todocenter.zwdd.model.MessageText; +import com.ningdatech.pmapi.user.entity.UserInfo; +import com.ningdatech.pmapi.user.service.IUserInfoService; import com.ningdatech.pmapi.user.util.LoginUserUtil; import com.wflow.bean.do_.UserDo; import com.wflow.bean.entity.WflowCcTasks; import com.wflow.bean.entity.WflowModelHistorys; +import com.wflow.bean.entity.WflowModels; +import com.wflow.exception.BusinessException; import com.wflow.mapper.WflowCcTasksMapper; import com.wflow.mapper.WflowModelHistorysMapper; import com.wflow.service.OrgRepositoryService; @@ -35,31 +58,45 @@ import com.wflow.workflow.bean.process.enums.ApprovalModeEnum; import com.wflow.workflow.bean.process.enums.NodeTypeEnum; import com.wflow.workflow.bean.process.form.Form; import com.wflow.workflow.bean.process.props.ApprovalProps; +import com.wflow.workflow.bean.vo.ProcessHandlerParamsVo; +import com.wflow.workflow.bean.vo.ProcessInstanceVo; import com.wflow.workflow.bean.vo.ProcessProgressVo; import com.wflow.workflow.bean.vo.ProcessTaskVo; import com.wflow.workflow.config.WflowGlobalVarDef; import com.wflow.workflow.service.FormService; import com.wflow.workflow.service.*; +import com.wflow.workflow.utils.Executor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.assertj.core.util.Maps; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; import org.flowable.engine.*; import org.flowable.engine.history.HistoricActivityInstance; import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.util.ProcessDefinitionUtil; import org.flowable.engine.runtime.Execution; -import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; +import org.flowable.task.api.TaskInfo; import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.service.history.NativeHistoricTaskInstanceQuery; import org.flowable.variable.api.history.HistoricVariableInstance; +import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; +import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; +import static com.ningdatech.pmapi.todocenter.constant.WorkNotice.PASS_MSG_TEMPLATE; +import static com.ningdatech.pmapi.todocenter.constant.WorkNotice.PASS_MSG_TEMPLATE2; + /** * @author CMM * @since 2023/01/12 16:09 @@ -80,96 +117,114 @@ public class TodoCenterManage { private final WflowModelHistorysMapper modelHistorysMapper; private final ProcessNodeCatchService nodeCatchService; private final OrgRepositoryService orgRepositoryService; - private final ProcessTaskService processTaskService; private final WflowCcTasksMapper ccTasksMapper; - public PageVo queryProjectList(ReqToBeProcessedDTO param) { + private final ProcessModelService processModelService; + private final IUserInfoService userInfoService; + + private final IProjectService projectService; + @Resource(name = "executableClient") + private ExecutableClient client; + /** + * 待办中心待我处理项目列表查询 + * @param param + * @return com.ningdatech.basic.model.PageVo + * @author CMM + * @since 2023/02/01 17:44 + */ + public PageVo queryPendingProjectList(ReqToBeProcessedDTO param) { // 获取登录用户ID - Long userId = LoginUserUtil.getUserId(); - // 获取入参分页信息 - Integer pageSize = param.getPageSize(); - Integer pageNumber = param.getPageNumber(); + // long userId = LoginUserUtil.getUserId(); + + Long userId = 381496L; TaskQuery taskQuery = taskService.createTaskQuery(); taskQuery.active().taskCandidateOrAssigned(String.valueOf(userId)).orderByTaskCreateTime().desc(); - Page page = param.page(); - List taskList = taskQuery.listPage(pageSize * (pageNumber - 1), pageSize); + List taskList = taskQuery.list(); Set staterUsers = new HashSet<>(); - List resVos = taskList.stream().map(task -> { - String nodeId = task.getTaskDefinitionKey(); - String instanceId = task.getProcessInstanceId(); - - ProcessProgressVo instanceProgress = processService.getInstanceProgress(nodeId, instanceId); - Map formData = instanceProgress.getFormData(); - String status = instanceProgress.getStatus(); - Boolean temporarySupplement = (Boolean) formData.get(ProjectDeclareConstants.BasicInformation.TEMPORARY_SUPPLEMENT); + Page page = param.page(); + + // 将待办任务流程实例一次性取出来,减少查询次数 + Map instanceMap = CollectionUtil.isNotEmpty(taskList) ? + historyService.createHistoricProcessInstanceQuery().processInstanceIds(taskList.stream() + .map(Task::getProcessInstanceId).collect(Collectors.toSet())) + .list().stream().collect(Collectors.toMap(HistoricProcessInstance::getId, v -> v)) : new HashMap<>(); + List processInsIds = taskList.stream().map(Task::getProcessInstanceId).collect(Collectors.toList()); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(Project.class); + wrapper.like(StrUtil.isNotBlank(param.getProjectName()),Project::getProjectName,param.getProjectName()) + .like(StrUtil.isNotBlank(param.getBuildUnitName()),Project::getBuildUnitName,param.getBuildUnitName()) + .eq(Objects.nonNull(param.getProjectYear()),Project::getProjectYear,param.getProjectYear()) + .eq(Objects.nonNull(param.getIsSupplement()),Project::getIsTemporaryAugment,param.getIsSupplement()) + .ge(Objects.nonNull(param.getProcessLaunchStartTime()),Project::getBeginTime,param.getProcessLaunchStartTime()) + .le(Objects.nonNull(param.getProcessLaunchEndTime()),Project::getEndTime,param.getProcessLaunchEndTime()); + projectService.page(page,wrapper); + + List resVos = page.getRecords().stream().filter(d -> processInsIds.contains(d.getInstCode())).map(d -> { + ResToBeProcessedDTO res = new ResToBeProcessedDTO(); - if (temporarySupplement.equals(param.getIsSupplement())) { - ProcessDefinition processDef = repositoryService.createProcessDefinitionQuery() - .processDefinitionId(task.getProcessDefinitionId()) - .singleResult(); - //从缓存取 - ProcessInstanceUserDto owner = runtimeService.getVariable(task.getExecutionId(), "owner", ProcessInstanceUserDto.class); - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); - staterUsers.add(owner.getUserId()); - ProcessTaskVo processTaskVo = ProcessTaskVo.builder() - .taskId(task.getId()) - .taskName(task.getName()) - .taskDefKey(task.getTaskDefinitionKey()) - .processDefId(task.getProcessDefinitionId()) - .executionId(task.getExecutionId()) - .nodeId(nodeId) - .deployId(processDef.getDeploymentId()) - .processDefName(processDef.getName()) - .version(processDef.getVersion()) - .instanceId(instanceId) - .ownerId(processInstance.getStartUserId()) - .ownerDeptId(owner.getOrgCode()) - .ownerDeptName(owner.getOrgName()) - .createTime(processInstance.getStartTime()) - .taskCreateTime(task.getCreateTime()) - .build(); - res.setProcessTaskInfo(processTaskVo); - String projectName = (String) formData.get(ProjectDeclareConstants.BasicInformation.PROJECT_NAME); - res.setProjectName(projectName); - res.setReportUnitId(owner.getOrgCode()); - res.setReportUnitName(owner.getOrgName()); - res.setReportAmount((Integer) formData.get(ProjectDeclareConstants.FundDeclareInfo.DECLARE_AMOUNT)); - res.setBudgetYear((Integer) formData.get(ProjectDeclareConstants.BasicInformation.BUDGET_YEAR)); - res.setProcessStatus(ProcessStatusEnum.getCodeByDesc(status)); - res.setProcessStatusName(status); - res.setProcessLaunchTime(NdDateUtils.date2LocalDateTime(processInstance.getStartTime())); - } + BeanUtils.copyProperties(d, res); + res.setProcessStatusName(ProcessStatusEnum.getDescByCode(d.getProcessStatus())); + LocalDateTime processLaunchTime = d.getCreateOn(); + String format = NdDateUtils.format(processLaunchTime, "yyyy-MM-dd HH:mm"); + LocalDateTime launchTime = LocalDateTime.parse(format, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + res.setProcessLaunchTime(launchTime); + + HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().processInstanceId(d.getInstCode()).singleResult(); + HistoricProcessInstance instance = instanceMap.get(task.getProcessInstanceId()); + //从缓存取 + staterUsers.add(String.valueOf(userId)); + ProcessTaskVo processTaskVo = ProcessTaskVo.builder() + .taskId(task.getId()) + .taskName(task.getName()) + .taskDefKey(task.getTaskDefinitionKey()) + .processDefId(task.getProcessDefinitionId()) + .executionId(task.getExecutionId()) + .nodeId(task.getTaskDefinitionKey()) + .deployId(instance.getDeploymentId()) + .processDefName(instance.getProcessDefinitionName()) + .version(instance.getProcessDefinitionVersion()) + .instanceId(task.getProcessInstanceId()) + .ownerId(instance.getStartUserId()) + .createTime(instance.getStartTime()) + .taskCreateTime(task.getCreateTime()) + .build(); + res.setProcessTaskInfo(processTaskVo); return res; }).collect(Collectors.toList()); - List result = resVos.stream() - .filter(r -> r.getProjectName().equals(param.getProjectName())) - .filter(r -> r.getReportUnitName().equals(param.getReportUnitName())) - .filter(r -> r.getBudgetYear().equals(param.getBudgetYear())) - .filter(r -> r.getProcessLaunchTime().isBefore(param.getProcessLaunchStartTime())) - .filter(r -> r.getProcessLaunchTime().isAfter(param.getProcessLaunchEndTime())).collect(Collectors.toList()); //取用户信息,减少数据库查询,一次构建 + List result = null; if (CollectionUtil.isNotEmpty(staterUsers)) { Map userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers); - page.setRecords(result.stream().peek(v -> v.getProcessTaskInfo().setOwner(userMap.get(v.getProcessTaskInfo().getOwnerId()))).collect(Collectors.toList())); + result = resVos.stream().peek(v -> v.getProcessTaskInfo().setOwner(userMap.get(v.getProcessTaskInfo().getOwnerId()))) + .collect(Collectors.toList()); + // Map userMap = userInfoService.getUserMapByIds(staterUsers); + // result = resVos.stream().peek(v -> v.setOwner(userMap.get(userId))) + // .collect(Collectors.toList()); } - return PageVo.of(resVos,page.getTotal()); + return PageVo.of(result,page.getTotal()); } - - public void exportProjectList(HttpServletResponse response, ReqToBeProcessedDTO param) { - PageVo page = - queryProjectList(param); + /** + * 待办中心列表导出 + * @param response + * @param param + * @return void + * @author CMM + * @since 2023/02/01 17:44 + */ + public void exportPendingProjectList(HttpServletResponse response, ReqToBeProcessedDTO param) { + PageVo page = queryPendingProjectList(param); List collect = (List) page.getRecords(); String fileName = null; - if (param.getIsSupplement()){ - fileName = "增补项目列表"; - }else { - fileName = "非增补项目列表"; + if (IsAppendProjectEnum.APPEND_PROJECT.getCode().equals(param.getIsSupplement())){ + fileName = "待办中心_待我处理_增补项目列表"; + }else if (IsAppendProjectEnum.NOT_APPEND_PROJECT.getCode().equals(param.getIsSupplement())){ + fileName = "待办中心_待我处理_非增补项目列表"; } ExcelDownUtil.setFileName(fileName,response); //数据导出处理函数 try { - EasyExcel.write(response.getOutputStream(), ResToBeProjectListExportDTO.class) + EasyExcel.write(response.getOutputStream(), ResToBeExportDTO.class) .autoCloseStream(false) .registerWriteHandler(ExcelExportStyle.formalStyle()) .sheet(fileName) @@ -178,50 +233,44 @@ public class TodoCenterManage { throw new RuntimeException(e); } } - + /** + * 流程处理相关操作 + * @param param + * @return void + * @author CMM + * @since 2023/02/01 17:43 + */ public void handler(ReqProcessHandlerDTO param) { Long userId = LoginUserUtil.getUserId(); Task task = taskService.createTaskQuery().taskId(param.getTaskId()).active().singleResult(); HashMap formData = new HashMap<>(32); - String format = NdDateUtils.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm"); - LocalDateTime auditTime = LocalDateTime.parse(format); if (Objects.isNull(task)) { throw new BizException("任务不存在"); } + if (hasComment(param.getAuditInfo())) { + taskService.addComment(param.getTaskId(), param.getInstanceId(), JSONObject.toJSONString(param.getAuditInfo())); + } switch (param.getAction()) { // 通过 - case pass: - formData.put("audit_pass_opinion",param.getAuditPassOpinion()); - formData.put("audit_pass_appendix",param.getAuditPassAppendix()); - formData.put("audit_pass_time", auditTime); + case PASS: formService.updateInstanceFormData(param.getInstanceId(), formData); doPass(task, param); // 盖章并通过 - case seal_pass: - formData.put("seal_pass_opinion",param.getSealPassOpinion()); - formData.put("seal_pass_appendix",param.getSealPassAppendix()); - Date sealPassTime = NdDateUtils.localDateTime2Date(LocalDateTime.now()); - formData.put("seal_pass_time",sealPassTime); + case SEAL_PASS: formService.updateInstanceFormData(param.getInstanceId(), formData); doSealPass(task, param); // 驳回 - case reject: - formData.put("audit_reject_opinion",param.getAuditRejectOpinion()); - formData.put("audit_reject_appendix",param.getAuditRejectAppendix()); - formData.put("audit_reject_time", auditTime); + case REJECT: formService.updateInstanceFormData(param.getInstanceId(), formData); doReject(task, param); break; // 退回 - case back: - formData.put("audit_back_opinion",param.getAuditBackOpinion()); - formData.put("audit_back_appendix",param.getAuditBackAppendix()); - formData.put("audit_back_time", auditTime); + case BACK: formService.updateInstanceFormData(param.getInstanceId(), formData); - doBackTask(task.getTaskDefinitionKey(), userId, param); + doBackTask(task, userId, param); break; // 撤回 - case withdraw: + case WITHDRAW: doWithDrawProcess(task); break; default: @@ -231,21 +280,41 @@ public class TodoCenterManage { /** * 审批任务:驳回 - * * @param task 当前任务 * @param param 参数 */ private void doReject(Task task, ReqProcessHandlerDTO param) { Map var = new HashMap<>(16); var.put("approve_" + task.getId(), param.getAction()); - // TODO 中止流程并使项目进入对应状态 - // TODO 给项目创建人、流程发起人发送浙政钉工作通知:【项目名称】的【流程名称】被驳回,请及时处理。 + // TODO 中止流程并使项目进入对应状态,给项目创建人、流程发起人发送浙政钉工作通知:【项目名称】的【流程名称】被驳回,请及时处理。 + //获取流程定义 + Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId()); + String projectName = getProjectName(task); + // 获取根节点即流程发起节点 + FlowNode rootNode = (FlowNode) process.getFlowElement("root", true); + // TODO 中止流程并使项目进入对应状态,给项目创建人、流程发起人发送浙政钉工作通知:【项目名称】的【流程名称】被驳回,请及时处理。 + sendWorkNoticeToStartUser(task,projectName,rootNode); taskService.complete(param.getTaskId(), var); } + /** + * 获取最新版本的流程配置 + * @param task 当前任务 + * @return com.wflow.bean.entity.WflowModels + * @author CMM + * @since 2023/02/01 17:30 + */ + private WflowModels getLastWflowModels(Task task) { + WflowModels wflowModels = processModelService.getOne(Wrappers.lambdaQuery(WflowModels.class) + .eq(WflowModels::getProcessDefId, task.getProcessDefinitionId())); + if (ObjectUtil.isNull(wflowModels)) { + log.warn("流程{}不存在", wflowModels.getFormId()); + throw new BusinessException("不存在该表单"); + } + return wflowModels; + } /** * 审批任务:盖章并通过 - * * @param task 当前任务 * @param param 参数 */ @@ -253,61 +322,255 @@ public class TodoCenterManage { Map var = new HashMap<>(16); var.put("approve_" + task.getId(), param.getAction()); // TODO 判断项目申报单位级别,区县单位申报有上级主管单位意见栏,市级单位没有 + // TODO 市级单位:为大数据局;区县单位:为大数据中心(根据附件区分?) + taskService.complete(param.getTaskId(), var); } /** * 审批任务:通过 - * * @param task 当前任务 * @param param 参数 */ private void doPass(Task task, ReqProcessHandlerDTO param) { + + String projectName = getProjectName(task); Map var = new HashMap<>(16); var.put("approve_" + task.getId(), param.getAction()); - // TODO 获取流程下一个节点的审核人 + //获取流程定义 + Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId()); + // 获取当前节点 + FlowNode currentNode = (FlowNode) process.getFlowElement(task.getTaskDefinitionKey(), true); + // 获取流程下一个节点浙政钉用户ID + String nextUserId = getNextUserId(currentNode); + // 若有下一个审核人,向其发送浙政钉工作通知:标题:审核任务 内容:【单位名称】的【项目名称】需要您审核。 + if (Objects.nonNull(nextUserId)){ + UserInfo auditUserInfo = userInfoService.getById(Long.valueOf(nextUserId)); + // TODO 向其发送浙政钉工作通知 + String msg = String.format(PASS_MSG_TEMPLATE,null,projectName); + sendWorkNotice(auditUserInfo,msg); + }else { + // 若没有,向发起人发送浙政钉工作通知:【项目名称】已通过【流程名称】,请及时开始下一步操作。 + // TODO 向其发送浙政钉工作通知 获取根节点的孩子节点(即发起人节点),向其发送浙政钉工作通知 + // 获取根节点即流程发起节点 + FlowNode rootNode = (FlowNode) process.getFlowElement("root", true); + sendWorkNoticeToStartUser(task, projectName, rootNode); + } + taskService.complete(param.getTaskId(), var); + } + /** + * 给流程发起人发送工作通知 + * @param task 当前任务 + * @param projectName 项目名称 + * @param rootNode 流程发起节点 + * @return void + * @author CMM + * @since 2023/02/03 10:05 + */ + private void sendWorkNoticeToStartUser(Task task, String projectName, FlowNode rootNode) { + String startUserId = getRootUserId(rootNode); + UserInfo startUserInfo = userInfoService.getById(Long.valueOf(startUserId)); + // 从历史表获取最新版本的流程 + WflowModels wflowModels = getLastWflowModels(task); + String formName = wflowModels.getFormName(); + String msg = String.format(PASS_MSG_TEMPLATE2, projectName, formName); + sendWorkNotice(startUserInfo, msg); + } - // TODO 若有下一个审核人,向其发送浙政钉工作通知:标题:审核任务 内容:【单位名称】的【项目名称】需要您审核。 + /** + * 获取流程发起节点的浙政钉用户ID + * @param rootNode 根节点 + * @return java.lang.String + * @author CMM + * @since 2023/02/02 21:47 + */ + private String getRootUserId(FlowNode rootNode) { + String rootUserId = null; + // 输出连线 + List outgoingFlows = rootNode.getOutgoingFlows(); + // 遍历返回下一个节点信息 + for (SequenceFlow currentOutgoingFlow : outgoingFlows) { + // 类型自己判断 + FlowElement targetFlowElement = currentOutgoingFlow.getTargetFlowElement(); + // TODO 若要会签需判断候选人 + // 发起事件 + if (targetFlowElement instanceof StartEvent) { + UserTask userTask = (UserTask) targetFlowElement; + rootUserId = userTask.getAssignee(); + break; + } + } + return rootUserId; - // TODO 若没有,向发起人发送浙政钉工作通知:【项目名称】已通过【流程名称】,请及时开始下一步操作。 - taskService.complete(param.getTaskId(), var); } /** + * 获取当前节点的下一个节点的浙政钉用户ID + * @param currentNode 当前节点 + * @return java.lang.String 下一个节点的浙政钉用户ID + * @author CMM + * @since 2023/02/02 16:49 + */ + private String getNextUserId(FlowNode currentNode) { + String nextUserId = null; + // 输出连线 + List outgoingFlows = currentNode.getOutgoingFlows(); + // 遍历返回下一个节点信息 + for (SequenceFlow currentOutgoingFlow : outgoingFlows) { + // 类型自己判断 + FlowElement targetFlowElement = currentOutgoingFlow.getTargetFlowElement(); + // TODO 若要会签需判断候选人 + // 用户任务 + if (targetFlowElement instanceof UserTask) { + UserTask userTask = (UserTask) targetFlowElement; + nextUserId = userTask.getAssignee(); + break; + } + } + return nextUserId; + } + /** + * 获取任务节点的申报项目名称 + * @param task 当前任务 + * @return java.lang.String 项目名称 + * @author CMM + * @since 2023/02/01 17:27 + */ + private String getProjectName(Task task) { + String nodeId = task.getTaskDefinitionKey(); + String instanceId = task.getProcessInstanceId(); + ProcessProgressVo instanceProgress = processService.getInstanceProgress(nodeId, instanceId); + Map formData = instanceProgress.getFormData(); + String projectName = (String) formData.get(ProjectDeclareConstants.BasicInformation.PROJECT_NAME); + return projectName; + } + /** + * 判断处理操作是否含有审核意见 + * @param comment 审核意见 + * @return boolean + * @author CMM + * @since 2023/02/01 17:33 + */ + private boolean hasComment(ProcessComment comment) { + return Objects.nonNull(comment) && (StrUtil.isNotBlank(comment.getText()) + || CollectionUtil.isNotEmpty(comment.getAttachments())); + } + /** + * 发送浙政钉工作通知 + * @param auditUserInfo 审核人信息 + * @param msg 要发送的工作通知 + * @return void + * @author CMM + * @since 2023/02/01 17:32 + */ + private void sendWorkNotice(UserInfo auditUserInfo, String msg) { + // TODO 获取浙政钉唯一标识 + String dingKey = null; + IntelligentGetClient intelligentGetClient = client.newIntelligentGetClient(DingConstant.WORKING_NOTICE); + OapiMessageWorkNotificationRequest request = new OapiMessageWorkNotificationRequest(); + //消息体(参考下文示例消息格式) + MessageText messageText = new MessageText(); + messageText.setMsgType("text"); + MessageContent messageContent = new MessageContent(); + + messageContent.setContent(msg); + messageText.setText(messageContent); + request.setMsg(JSONUtil.toJsonStr(messageText)); + // 构建唯一的消息ID + // String bizMsgId = "ZB_URGE_NOTICE_" + "_" + auditUserInfo.getDeptId() + "_" + auditUserInfo.getUserId(); + // request.setBizMsgId(bizMsgId); + request.setBizMsgId(null); + //租户id + // request.setTenantId(GovDingProperties.tenantId.toString()); + request.setReceiverIds(dingKey); + // 获取结果 + OapiMessageWorkNotificationResponse apiResult = intelligentGetClient.get(request); + if (!apiResult.getSuccess() || !JSONUtil.parseObj(apiResult.getContent()).getBool("success")) { + log.warn("发送工作通知失败: {}", apiResult.getContent()); + throw new BizException(Status.BAD_REQUEST.toString()); + } + + } + /** * 撤销流程处理 * * @param task 当前任务 */ private void doWithDrawProcess(Task task) { - // TODO 若是流程发起人点击撤回,项目回到上一个状态,并删除当前审核人对应的待办记录 - // TODO 若是前一个审核人点击撤回,在审核记录中移除自己提交过的审核意见、待我处理中移除当前审核人的待办记录、待我处理中增加自己的待办记录、我已处理中去掉自己之前处理的记录 - List executions = runtimeService.createExecutionQuery() - .processInstanceId(task.getProcessInstanceId()) - .onlyChildExecutions().list(); - // 强制流程指向撤回 - runtimeService.createChangeActivityStateBuilder() - .processInstanceId(task.getProcessInstanceId()) - .moveActivityIdTo(task.getTaskDefinitionKey(), HisProInsEndActId.WITHDRAW) - .moveExecutionsToSingleActivityId(executions.stream().map(Execution::getId) - .collect(Collectors.toList()), HisProInsEndActId.WITHDRAW) - .changeState(); + //获取流程定义 + Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId()); + // 获取当前运行流程的发起人节点信息 + FlowNode rootNode = (FlowNode) process.getFlowElement("root", true); + // 获取当前节点 + FlowNode currentNode = (FlowNode) process.getFlowElement(task.getTaskDefinitionKey(), true); + String rootUserId = getRootUserId(rootNode); + // 判断当前登录用户是否是流程发起人 + Long userId = LoginUserUtil.getUserId(); + if (rootUserId.equals(String.valueOf(userId))){ + // TODO 若是流程发起人点击撤回,项目回到上一个状态,并删除当前审核人对应的待办记录 + // 获取当前流程项目状态 + Project declaredProject = projectService.getOne(Wrappers.lambdaQuery(Project.class) + .eq(Project::getInstCode, task.getProcessInstanceId())); + Integer projectStatusSecond = declaredProject.getProjectStatusSecond(); + + List executions = runtimeService.createExecutionQuery() + .processInstanceId(task.getProcessInstanceId()) + .onlyChildExecutions().list(); + // 强制流程指向撤回 + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdTo(task.getTaskDefinitionKey(), HisProInsEndActId.WITHDRAW) + .moveExecutionsToSingleActivityId(executions.stream().map(Execution::getId) + .collect(Collectors.toList()), HisProInsEndActId.WITHDRAW) + .changeState(); + }else { + FlowElementsContainer parentContainer = currentNode.getParentContainer(); + for (FlowElement flowElement : parentContainer.getFlowElements()) { + UserTask beforeUserTask = (UserTask) flowElement; + if (beforeUserTask.getAssignee().equals(String.valueOf(userId))){ + // TODO 若是前一个审核人点击撤回,在审核记录中移除自己提交过的审核意见、 + // 待我处理中移除当前审核人的待办记录、 + // 待我处理中增加自己的待办记录、 + // 我已处理中去掉自己之前处理的记录 + HistoricTaskInstance beforeTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(beforeUserTask.getId()).singleResult(); + List executions = runtimeService.createExecutionQuery() + .processInstanceId(beforeTaskInstance.getProcessInstanceId()) + .onlyChildExecutions().list(); + // 强制流程指向撤回 + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdTo(flowElement.getId(), HisProInsEndActId.WITHDRAW) + .moveExecutionsToSingleActivityId(executions.stream().map(Execution::getId) + .collect(Collectors.toList()), HisProInsEndActId.WITHDRAW) + .changeState(); + break; + } + } + } } /** * 退回流程处理 - * @param current 当前流程定义key - * @param userId 当前登录用户ID - * @param param 参数 + * + * @param task + * @param task 当前任务 + * @param userId 当前登录用户ID + * @param param 参数 */ - private void doBackTask(String current, Long userId, ReqProcessHandlerDTO param) { - // TODO 流程变成【被退回】状态 - // TODO 待我处理中,为流程发起人增加一条待办记录 - // TODO 给项目创建人、流程发起人发送浙政钉工作通知:【项目名称】的【流程名称】被退回,请及时处理。 - //执行自定义回退逻辑 - managementService.executeCommand(new BackToHisApprovalNodeCmd(runtimeService, param.getTaskId(), param.getTargetNode())); + private void doBackTask(Task task, Long userId, ReqProcessHandlerDTO param) { + // 获取项目名称 + String projectName = getProjectName(task); + //获取流程定义 + Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId()); + // 获取根节点即流程发起节点 + FlowNode rootNode = (FlowNode) process.getFlowElement("root", true); + // TODO 流程变成【被退回】状态,待我处理中,为流程发起人增加一条待办记录,给项目创建人、流程发起人发送浙政钉工作通知:【项目名称】的【流程名称】被退回,请及时处理。 + sendWorkNoticeToStartUser(task,projectName,rootNode); + //执行自定义回退逻辑,回退到流程发起人 + managementService.executeCommand(new BackToHisApprovalNodeCmd(runtimeService, param.getTaskId(), rootNode.getId())); runtimeService.setVariables(param.getInstanceId(), Maps.newHashMap("approve_" + param.getTaskId(), param.getAction())); - log.info("用户[{}] 退回流程[{}] [{} -> {}]", userId, param.getInstanceId(), current, param.getTargetNode()); + log.info("用户[{}] 退回流程[{}] [{} -> {}]", userId, param.getInstanceId(), task.getTaskDefinitionKey(), param.getTargetNode()); } /** @@ -336,6 +599,7 @@ public class TodoCenterManage { } UserDo users = orgRepositoryService.getUserById(instance.getStartUserId()); OrgUser startUser = OrgUser.builder().id(users.getUserId()).name(users.getUserName()).avatar(users.getAvatar()).build(); + List taskRecords = getHisTaskRecords(instanceId, nodePropsValue); // 获取添加抄送任务 taskRecords.addAll(getCcTaskRecords(instanceId)); @@ -354,7 +618,7 @@ public class TodoCenterManage { .startTime(instance.getStartTime()) .finishTime(instance.getStartTime()) .taskId("root") - .result(ReqProcessHandlerDTO.Action.pass) + .result(ProcessHandlerEnum.PASS) .build()); // 提取全量表单数据 Map formData = formDatas.stream().collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue)); @@ -367,21 +631,28 @@ public class TodoCenterManage { .formItems(formService.filterFormAndDataByPermConfig((List
) forms.getValue(), formData, currentNode)) .formData(formData) .processDefName(instance.getProcessDefinitionName()) - .staterUser(startUser) - .starterDept(null == owner ? null : owner.getOrgCode()) + .startTempUser(startUser) + .startDept(null == owner ? null : owner.getOrgName()) .result(instance.getEndActivityId()) .startTime(instance.getStartTime()) .progress(taskRecords) .build(); + // UserInfo userInfo = userInfoService.getById(Long.valueOf(instance.getStartUserId())); + // res.setStartUser(userInfo); if (Objects.isNull(instance.getEndActivityId())){ res.setStatus(ProcessStatusEnum.UNDER_REVIEW.name()); } else if (HisProInsEndActId.BACK.equals(instance.getEndActivityId())) { + // TODO 被退回的审核节点状态这里只是暂时这么判断,具体怎么保存这个退回状态,后面讨论 res.setStatus(ProcessStatusEnum.BE_BACKED.name()); } else if (HisProInsEndActId.REJECT.equals(instance.getEndActivityId())) { res.setStatus(ProcessStatusEnum.BE_REJECTED.name()); } else if (HisProInsEndActId.END.equals(instance.getEndActivityId())) { res.setStatus(ProcessStatusEnum.APPROVED.name()); } + // 查询项目申报信息 + Project projectInfo = projectService.getOne(Wrappers.lambdaQuery(Project.class) + .eq(Project::getInstCode, instanceId)); + res.setDeclaredProjectInfo(projectInfo); return res; } @@ -424,9 +695,9 @@ public class TodoCenterManage { .processInstanceId(instanceId).orderByHistoricActivityInstanceStartTime().asc().list(); Set userSet = new HashSet<>(); //获取节点处理结果 - Map varMap = historyService.createHistoricVariableInstanceQuery() + Map varMap = historyService.createHistoricVariableInstanceQuery() .processInstanceId(instanceId).variableNameLike("approve_%").list().stream() - .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, v -> (ReqProcessHandlerDTO.Action) v.getValue())); + .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, v -> (ProcessHandlerEnum) v.getValue())); List progressNodes = list.stream().filter(his -> ObjectUtil.isNotNull(his.getTaskId())).map(his -> { Object props = nodeProps.get(his.getActivityId()); ApprovalModeEnum approvalMode = null; @@ -457,4 +728,272 @@ public class TodoCenterManage { //根据流程遍历后续节点,期间要穿越后续包含并行网关和条件网关的节点 return Collections.emptyList(); } + /** + * 待办中心我已处理项目列表查询 + * @param param + * @return com.ningdatech.basic.model.PageVo + * @author CMM + * @since 2023/02/03 10:07 + */ + public PageVo queryHandledProjectList(ReqToBeProcessedDTO param) { + // 获取登录用户ID + // long userId = LoginUserUtil.getUserId(); + Long userId = 381496L; + // 获取入参分页信息 + Page page = param.page(); + + //自定义sql查询所有已办的任务 + String nativeSql = "SELECT aht.* FROM ACT_HI_TASKINST AS aht \n" + + "LEFT JOIN ACT_HI_VARINST AS ahv ON SUBSTRING(ahv.NAME_, 9) = aht.ID_ AND ahv.NAME_ LIKE 'approve_%'\n" + + "WHERE aht.ASSIGNEE_ = " + userId + " AND ahv.NAME_ IS NOT NULL \n" + + "ORDER BY aht.END_TIME_ DESC"; + + NativeHistoricTaskInstanceQuery taskInstanceQuery = historyService.createNativeHistoricTaskInstanceQuery().sql(nativeSql); + List taskInstances = taskInstanceQuery.list(); + + //把已办任务流程实例一次性取出来,减少查询次数 + Map instanceMap = CollectionUtil.isNotEmpty(taskInstances) ? + historyService.createHistoricProcessInstanceQuery().processInstanceIds(taskInstances.stream() + .map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet())) + .list().stream().collect(Collectors.toMap(HistoricProcessInstance::getId, v -> v)) : new HashMap<>(); + + List processInsIds = taskInstances.stream().map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toList()); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(Project.class); + wrapper.like(StrUtil.isNotBlank(param.getProjectName()),Project::getProjectName,param.getProjectName()) + .like(StrUtil.isNotBlank(param.getBuildUnitName()),Project::getBuildUnitName,param.getBuildUnitName()) + .eq(Objects.nonNull(param.getProjectYear()),Project::getProjectYear,param.getProjectYear()) + .eq(Objects.nonNull(param.getIsSupplement()),Project::getIsTemporaryAugment,param.getIsSupplement()) + .ge(Objects.nonNull(param.getProcessLaunchStartTime()),Project::getBeginTime,param.getProcessLaunchStartTime()) + .le(Objects.nonNull(param.getProcessLaunchEndTime()),Project::getEndTime,param.getProcessLaunchEndTime()); + projectService.page(page,wrapper); + Set staterUsers = new HashSet<>(); + List resVos = page.getRecords().stream().filter(d -> processInsIds.contains(d.getInstCode())).map(d -> { + + ResToBeProcessedDTO res = new ResToBeProcessedDTO(); + BeanUtils.copyProperties(d, res); + res.setProcessStatusName(ProcessStatusEnum.getDescByCode(d.getProcessStatus())); + LocalDateTime processLaunchTime = d.getCreateOn(); + String launchTimeFormat = NdDateUtils.format(processLaunchTime, "yyyy-MM-dd HH:mm"); + LocalDateTime launchTime = LocalDateTime.parse(launchTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + res.setProcessLaunchTime(launchTime); + LocalDateTime processHandleTime = d.getUpdateOn(); + String handleTimeFormat = NdDateUtils.format(processHandleTime, "yyyy-MM-dd HH:mm"); + LocalDateTime handleTime = LocalDateTime.parse(handleTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + res.setProcessHandleTime(handleTime); + + HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().processInstanceId(d.getInstCode()).singleResult(); + HistoricProcessInstance instance = instanceMap.get(task.getProcessInstanceId()); + HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(instance.getId()).variableName("approve_" + task.getId()).singleResult(); + //从缓存取 + staterUsers.add(instance.getStartUserId()); + ProcessTaskVo processTaskVo = ProcessTaskVo.builder() + .taskId(task.getId()) + .taskName(task.getName()) + .taskDefKey(task.getTaskDefinitionKey()) + .processDefId(task.getProcessDefinitionId()) + .executionId(task.getExecutionId()) + .nodeId(task.getTaskDefinitionKey()) + .deployId(instance.getDeploymentId()) + .processDefName(instance.getProcessDefinitionName()) + .version(instance.getProcessDefinitionVersion()) + .instanceId(task.getProcessInstanceId()) + .ownerId(instance.getStartUserId()) + .createTime(instance.getStartTime()) + .taskCreateTime(task.getCreateTime()) + .taskEndTime(task.getEndTime()) + .taskResult(String.valueOf(Objects.nonNull(variableInstance) ? + variableInstance.getValue() : ProcessHandlerParamsVo.Action.recall)) + .build(); + res.setProcessTaskInfo(processTaskVo); + return res; + }).collect(Collectors.toList()); + //取用户信息,减少数据库查询,一次构建 + List result = null; + if (CollectionUtil.isNotEmpty(staterUsers)) { + Map userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers); + result = resVos.stream().peek(v -> v.getProcessTaskInfo() + .setOwner(userMap.get(v.getProcessTaskInfo().getOwnerId()))) + .collect(Collectors.toList()); + // Map userMap = userInfoService.getUserMapByIds(staterUsers); + // result = resVos.stream().peek(v -> v.setOwner(userMap.get(userId))) + // .collect(Collectors.toList()); + + } + return PageVo.of(result,resVos.size()); + } + + /** + * 导出我已处理项目列表 + * @param response 返回数据流 + * @param param 请求参数 + * @return void + * @author CMM + * @since 2023/02/06 16:25 + */ + public void exportHandledProjectList(HttpServletResponse response, ReqToBeProcessedDTO param) { + PageVo page = queryHandledProjectList(param); + List collect = (List) page.getRecords(); + String fileName = null; + if (IsAppendProjectEnum.APPEND_PROJECT.getCode().equals(param.getIsSupplement())){ + fileName = "待办中心_我已处理_增补项目列表"; + }else if (IsAppendProjectEnum.NOT_APPEND_PROJECT.getCode().equals(param.getIsSupplement())){ + fileName = "待办中心_我已处理_非增补项目列表"; + } + ExcelDownUtil.setFileName(fileName,response); + //数据导出处理函数 + try { + EasyExcel.write(response.getOutputStream(), ResHandledExportDTO.class) + .autoCloseStream(false) + .registerWriteHandler(ExcelExportStyle.formalStyle()) + .sheet(fileName) + .doWrite(collect); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 查询我提交的项目列表 + * @param param + * @return com.ningdatech.basic.model.PageVo + * @author CMM + * @since 2023/02/06 16:18 + */ + public PageVo queryMySubmittedProjectList(ReqToBeProcessedDTO param) { + HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery(); + // Long startUserId = LoginUserUtil.getUserId(); + Long startUserId = 381496L; + Page page = param.page(); + Executor.builder().ifNotBlankNext(String.valueOf(startUserId), instanceQuery::startedBy); + List historicProcessInstances = instanceQuery + .orderByProcessInstanceStartTime().desc() + .orderByProcessInstanceEndTime().desc() + .list(); + //把已办任务流程实例一次性取出来,减少查询次数 + Map instanceMap = CollectionUtil.isNotEmpty(historicProcessInstances) ? + historyService.createHistoricProcessInstanceQuery().processInstanceIds(historicProcessInstances.stream() + .map(h -> h.getId()).collect(Collectors.toSet())).list().stream() + .collect(Collectors.toMap(HistoricProcessInstance::getId, v -> v)) : new HashMap<>(); + Set historicProcessInstanceIds = historicProcessInstances.stream().map(h -> h.getId()).collect(Collectors.toSet()); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(Project.class); + wrapper.like(StrUtil.isNotBlank(param.getProjectName()),Project::getProjectName,param.getProjectName()) + .like(StrUtil.isNotBlank(param.getBuildUnitName()),Project::getBuildUnitName,param.getBuildUnitName()) + .eq(Objects.nonNull(param.getProjectYear()),Project::getProjectYear,param.getProjectYear()) + .eq(Objects.nonNull(param.getIsSupplement()),Project::getIsTemporaryAugment,param.getIsSupplement()) + .ge(Objects.nonNull(param.getProcessLaunchStartTime()),Project::getBeginTime,param.getProcessLaunchStartTime()) + .le(Objects.nonNull(param.getProcessLaunchEndTime()),Project::getEndTime,param.getProcessLaunchEndTime()); + projectService.page(page,wrapper); + Set staterUsers = new HashSet<>(); + List resVos = page.getRecords().stream().filter(d -> historicProcessInstanceIds.contains(d.getInstCode())).map(d -> { + ResToBeProcessedDTO res = new ResToBeProcessedDTO(); + BeanUtils.copyProperties(d, res); + res.setProcessStatusName(ProcessStatusEnum.getDescByCode(d.getProcessStatus())); + LocalDateTime processLaunchTime = d.getCreateOn(); + String launchTimeFormat = NdDateUtils.format(processLaunchTime, "yyyy-MM-dd HH:mm"); + LocalDateTime launchTime = LocalDateTime.parse(launchTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + res.setProcessLaunchTime(launchTime); + HistoricProcessInstance ist = instanceMap.get(d.getInstCode()); + staterUsers.add(ist.getStartUserId()); + ProcessInstanceVo processInstanceVo = getProcessInstanceVos(ist); + res.setProcessInstanceInfo(processInstanceVo); + return res; + }).collect(Collectors.toList()); + + if (CollectionUtil.isNotEmpty(staterUsers)) { + Map userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers); + resVos.stream().map(v -> { + v.getProcessInstanceInfo().setStaterUser(userMap.get(v.getProcessInstanceInfo().getStaterUserId())); + return v; + }).collect(Collectors.toList()); + } + + //if (CollectionUtil.isNotEmpty(staterUsers)) { + // Map userMap = userInfoService.getUserMapByIds(staterUsers); + // resVos.stream().peek(v -> v.setOwner(userMap.get(startUserId))) + // .collect(Collectors.toList()); + //} + return PageVo.of(resVos,resVos.size()); + } + + /** + * 获取流程实例信息 + * + * @param ist 流程实例 + * @return java.util.List + * @author CMM + * @since 2023/02/06 17:34 + */ + private ProcessInstanceVo getProcessInstanceVos( HistoricProcessInstance ist) { + Map instanceNodeMap = new HashMap<>(); + ProcessInstanceVo instanceVo = ProcessInstanceVo.builder() + .processDefId(ist.getProcessDefinitionId()) + .instanceId(ist.getId()) + .nodeId(instanceNodeMap.get(ist.getId())) + .formId(ist.getProcessDefinitionKey()) + .staterUserId(ist.getStartUserId()) + .startTime(ist.getStartTime()) + .finishTime(ist.getEndTime()) + .processDefName(ist.getProcessDefinitionName()) + .result(ist.getEndActivityId()) + .version(ist.getProcessDefinitionVersion()) + .build(); + + if (Objects.isNull(ist.getEndActivityId())) { + instanceVo.setStatus(ProcessStatusEnum.UNDER_REVIEW.name()); + } else if (HisProInsEndActId.BACK.equals(ist.getEndActivityId())) { + // TODO 被退回的审核节点状态这里只是暂时这么判断,具体怎么保存这个退回状态,后面讨论 + instanceVo.setStatus(ProcessStatusEnum.BE_BACKED.name()); + } else if (HisProInsEndActId.REJECT.equals(ist.getEndActivityId())) { + instanceVo.setStatus(ProcessStatusEnum.BE_REJECTED.name()); + } else if (HisProInsEndActId.END.equals(ist.getEndActivityId())) { + instanceVo.setStatus(ProcessStatusEnum.APPROVED.name()); + } + + if (ObjectUtil.isNull(ist.getEndActivityId())) { + // 没有结束,还在走流程,获取任务 + List list = + taskService.createTaskQuery().processInstanceId(ist.getId()).includeIdentityLinks().active().list(); + instanceVo.setNodeId(Optional.ofNullable(instanceVo.getNodeId()).orElseGet(() -> { + if (CollectionUtil.isNotEmpty(list)) { + return list.get(0).getTaskDefinitionKey(); + } + return null; + })); + instanceVo.setTaskName(StrUtil.join("、", list.stream().map(TaskInfo::getName).collect(Collectors.toSet()))); + } else { + if (HisProInsEndActId.WITHDRAW.equals(ist.getEndActivityId())) { + instanceVo.setTaskName(ProcessStatusEnum.WITHDRAW.getDesc()); + } else if (HisProInsEndActId.REJECT.equals(ist.getEndActivityId())) { + instanceVo.setTaskName(ProcessStatusEnum.BE_REJECTED.getDesc()); + } else { + instanceVo.setTaskName(ProcessStatusEnum.APPROVED.getDesc()); + } + } + + return instanceVo; + } + + public void exportMySubmittedProjectList(HttpServletResponse response, ReqToBeProcessedDTO param) { + PageVo page = queryMySubmittedProjectList(param); + List collect = (List) page.getRecords(); + String fileName = null; + if (IsAppendProjectEnum.APPEND_PROJECT.getCode().equals(param.getIsSupplement())){ + fileName = "待办中心_我发起的_增补项目列表"; + }else if (IsAppendProjectEnum.NOT_APPEND_PROJECT.getCode().equals(param.getIsSupplement())){ + fileName = "待办中心_我发起的_非增补项目列表"; + } + ExcelDownUtil.setFileName(fileName,response); + //数据导出处理函数 + try { + EasyExcel.write(response.getOutputStream(), ResMySubmittedExportDTO.class) + .autoCloseStream(false) + .registerWriteHandler(ExcelExportStyle.formalStyle()) + .sheet(fileName) + .doWrite(collect); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqProcessHandlerDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqProcessHandlerDTO.java index 1e9e424..3a8bbb1 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqProcessHandlerDTO.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqProcessHandlerDTO.java @@ -1,5 +1,7 @@ package com.ningdatech.pmapi.todocenter.model.dto.req; import com.ningdatech.pmapi.common.model.FileBasicInfo; +import com.ningdatech.pmapi.todocenter.bean.entity.ProcessComment; +import com.ningdatech.pmapi.todocenter.enums.ProcessHandlerEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -31,7 +33,7 @@ public class ReqProcessHandlerDTO { /** * 操作类型 */ - private Action action; + private ProcessHandlerEnum action; /** * 目标用户 */ @@ -40,43 +42,48 @@ public class ReqProcessHandlerDTO { * 目标节点 */ private String targetNode; - /** - * 审核通过意见 - */ - private String auditPassOpinion; - - /** - * 审核通过附件 - */ - private FileBasicInfo auditPassAppendix; - /** - * 盖章通过意见 - */ - private String sealPassOpinion; - - /** - * 盖章通过附件 - */ - private FileBasicInfo sealPassAppendix; - /** - * 审核退回意见 - */ - private String auditBackOpinion; - /** - * 审核退回附件 - */ - private FileBasicInfo auditBackAppendix; - /** - * 审核驳回意见 - */ - private String auditRejectOpinion; /** - * 审核驳回附件 + * 审核信息 */ - private FileBasicInfo auditRejectAppendix; - public enum Action{ - //通过、盖章并通过、退回、撤回、驳回,审核意见类型 - pass, seal_pass ,back, withdraw, reject; - } + private ProcessComment auditInfo; + ///** + // * 审核通过意见 + // */ + //private String auditPassOpinion; + // + ///** + // * 审核通过附件 + // */ + //private FileBasicInfo auditPassAppendix; + ///** + // * 盖章通过意见 + // */ + //private String sealPassOpinion; + // + ///** + // * 盖章通过附件 + // */ + //private FileBasicInfo sealPassAppendix; + ///** + // * 审核退回意见 + // */ + //private String auditBackOpinion; + ///** + // * 审核退回附件 + // */ + //private FileBasicInfo auditBackAppendix; + ///** + // * 审核驳回意见 + // */ + //private String auditRejectOpinion; + // + ///** + // * 审核驳回附件 + // */ + //private FileBasicInfo auditRejectAppendix; + //public enum Action{ + // //通过、盖章并通过、退回、撤回、驳回 + // pass, seal_pass ,back, withdraw, reject; + //} } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqToBeProcessedDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqToBeProcessedDTO.java index 8e8f96e..37ed70d 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqToBeProcessedDTO.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqToBeProcessedDTO.java @@ -10,6 +10,8 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import javax.validation.constraints.NotNull; + /** * 待办中心-待我处理查询实体信息 * @@ -26,10 +28,10 @@ public class ReqToBeProcessedDTO extends PagePo implements Serializable { private String projectName; @ApiModelProperty("申报单位名称") - private String reportUnitName; + private String buildUnitName; @ApiModelProperty("预算年度") - private Integer budgetYear; + private Integer projectYear; @ApiModelProperty("流程发起开始时间") private LocalDateTime processLaunchStartTime; @@ -37,6 +39,7 @@ public class ReqToBeProcessedDTO extends PagePo implements Serializable { @ApiModelProperty("流程发起结束时间") private LocalDateTime processLaunchEndTime; - @ApiModelProperty(value = "是否增补项目",allowableValues = "非增补项目 false,增补项目 true") - private Boolean isSupplement; + @ApiModelProperty(value = "是否增补项目",allowableValues = "非增补项目 0,增补项目 1") + @NotNull(message = "是否增补字段不能为空!") + private Integer isSupplement; } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResHandledExportDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResHandledExportDTO.java new file mode 100644 index 0000000..0492bdb --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResHandledExportDTO.java @@ -0,0 +1,44 @@ +package com.ningdatech.pmapi.todocenter.model.dto.res; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 待办中心我已处理项目列表导出实体 + * + * @author CMM + * @since 2023/01/19 16:42 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ResHandledExportDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @ExcelProperty("项目名称") + private String projectName; + + @ExcelProperty("申报单位") + private String reportUnitName; + + @ExcelProperty("申报金额") + private Integer reportAmount; + + @ExcelProperty("预算年度") + private Integer budgetYear; + + @ExcelProperty("流程状态") + private String processStatusName; + + @ExcelProperty("发起时间") + private LocalDateTime processLaunchTime; + + @ExcelProperty("处理时间") + private LocalDateTime processHandleTime; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProjectListExportDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResMySubmittedExportDTO.java similarity index 87% rename from pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProjectListExportDTO.java rename to pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResMySubmittedExportDTO.java index 600eea6..ce8bd02 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProjectListExportDTO.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResMySubmittedExportDTO.java @@ -1,15 +1,16 @@ package com.ningdatech.pmapi.todocenter.model.dto.res; +import java.io.Serializable; +import java.time.LocalDateTime; + import com.alibaba.excel.annotation.ExcelProperty; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import java.io.Serializable; -import java.time.LocalDateTime; - /** - * 待办中心待我处理项目列表导出实体 + * 待办中心我发起的项目列表导出实体 * * @author CMM * @since 2023/01/19 16:42 @@ -17,7 +18,7 @@ import java.time.LocalDateTime; @Data @NoArgsConstructor @AllArgsConstructor -public class ResToBeProjectListExportDTO implements Serializable { +public class ResMySubmittedExportDTO implements Serializable { private static final long serialVersionUID = 1L; @ExcelProperty("项目名称") diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeExportDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeExportDTO.java new file mode 100644 index 0000000..e3b83d1 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeExportDTO.java @@ -0,0 +1,42 @@ +package com.ningdatech.pmapi.todocenter.model.dto.res; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 待办中心待我处理项目列表导出实体 + * + * @author CMM + * @since 2023/01/19 16:42 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ResToBeExportDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @ExcelProperty("项目名称") + private String projectName; + + @ExcelProperty("申报单位") + private String buildUnitName; + + @ExcelProperty("申报金额") + private Integer declareAmount; + + @ExcelProperty("预算年度") + private Integer projectYear; + + @ExcelProperty("流程状态") + private String processStatusName; + + @ExcelProperty("发起时间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm") + private LocalDateTime processLaunchTime; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProcessedDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProcessedDTO.java index 0df7691..88725c1 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProcessedDTO.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProcessedDTO.java @@ -1,13 +1,20 @@ package com.ningdatech.pmapi.todocenter.model.dto.res; import java.io.Serializable; +import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.List; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ningdatech.pmapi.user.entity.UserInfo; +import com.wflow.workflow.bean.vo.ProcessInstanceVo; import com.wflow.workflow.bean.vo.ProcessTaskVo; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; /** * 待办中心-待我处理返回实体信息 @@ -18,26 +25,27 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor +@ExcelIgnoreUnannotated public class ResToBeProcessedDTO implements Serializable { private static final long serialVersionUID = 1L; - @ApiModelProperty("项目ID") - private String projectId; + @ApiModelProperty("流程实例编号") + private String instanceCode; @ApiModelProperty("项目名称") private String projectName; @ApiModelProperty("申报单位ID") - private String reportUnitId; + private String buildUnitId; @ApiModelProperty("申报单位名称") - private String reportUnitName; + private String buildUnitName; @ApiModelProperty("申报金额") - private Integer reportAmount; + private BigDecimal declareAmount; @ApiModelProperty("预算年度") - private Integer budgetYear; + private Integer projectYear; @ApiModelProperty("流程状态") private Integer processStatus; @@ -46,8 +54,19 @@ public class ResToBeProcessedDTO implements Serializable { private String processStatusName; @ApiModelProperty("流程发起时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private LocalDateTime processLaunchTime; + @ApiModelProperty("流程处理时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm") + private LocalDateTime processHandleTime; + @ApiModelProperty("流程任务信息") private ProcessTaskVo processTaskInfo; + + @ApiModelProperty("流程实例信息") + private ProcessInstanceVo processInstanceInfo; + + @ApiModelProperty("流程发起人信息") + private UserInfo owner; } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/zwdd/model/MessageContent.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/zwdd/model/MessageContent.java new file mode 100644 index 0000000..167ae02 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/zwdd/model/MessageContent.java @@ -0,0 +1,13 @@ +package com.ningdatech.pmapi.todocenter.zwdd.model; + +import lombok.Data; + +/** + * 消息内容 + * @author CMM + * @since 2023/02/01 14:54 + */ +@Data +public class MessageContent { + private String content; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/zwdd/model/MessageText.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/zwdd/model/MessageText.java new file mode 100644 index 0000000..4c84252 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/zwdd/model/MessageText.java @@ -0,0 +1,17 @@ +package com.ningdatech.pmapi.todocenter.zwdd.model; + +import lombok.Data; + +/** + * 文本消息 + * @author CMM + * @since 2023/02/01 14:53 + */ +@Data +public class MessageText { + + private String msgType; + + private MessageContent text; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/service/IUserInfoService.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/service/IUserInfoService.java index caf95c4..f93880c 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/service/IUserInfoService.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/service/IUserInfoService.java @@ -3,6 +3,9 @@ package com.ningdatech.pmapi.user.service; import com.ningdatech.pmapi.user.entity.UserInfo; import com.baomidou.mybatisplus.extension.service.IService; +import java.util.Map; +import java.util.Set; + /** *

* 用户信息表 服务类 @@ -13,4 +16,5 @@ import com.baomidou.mybatisplus.extension.service.IService; */ public interface IUserInfoService extends IService { + Map getUserMapByIds(Set staterUsers); } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/service/impl/UserInfoServiceImpl.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/service/impl/UserInfoServiceImpl.java index 602dac4..19ae598 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/service/impl/UserInfoServiceImpl.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/service/impl/UserInfoServiceImpl.java @@ -4,8 +4,17 @@ import com.ningdatech.pmapi.user.entity.UserInfo; import com.ningdatech.pmapi.user.mapper.UserInfoMapper; import com.ningdatech.pmapi.user.service.IUserInfoService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.catalina.User; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + /** *

* 用户信息表 服务实现类 @@ -15,6 +24,12 @@ import org.springframework.stereotype.Service; * @since 2023-01-04 */ @Service +@RequiredArgsConstructor public class UserInfoServiceImpl extends ServiceImpl implements IUserInfoService { - + private final UserInfoMapper userInfoMapper; + @Override + public Map getUserMapByIds(Set staterUsers) { + List userInfos = userInfoMapper.selectBatchIds(staterUsers); + return userInfos.stream().collect(Collectors.toMap(UserInfo::getId,u -> u )); + } } diff --git a/pmapi/src/main/resources/application-dev.yml b/pmapi/src/main/resources/application-dev.yml index 8892203..96fe00f 100644 --- a/pmapi/src/main/resources/application-dev.yml +++ b/pmapi/src/main/resources/application-dev.yml @@ -153,4 +153,19 @@ sa-token: # token风格 token-style: uuid # 是否输出操作日志 - is-log: false \ No newline at end of file + is-log: false + +#专有钉钉 +ding: + #扫码 + app-auth-key: file-manage_dingoa-zte2LbiAfIj + app-auth-secret: H794aFZf271QbfUr50pbBpBTlXSrWIP71q9RTR34 + #扫码 + app-sso-auth-key: fgdn_wjlzjkxt_hz + app-sso-auth-secret: dafe1e6f7d424032acb81f5c2a797a1f + #免登/获取信息 + app-key: file-manage-4Mjx9358wuxjyYFjY3 + app-secret: hE41938wqyQ5LOpc1QDRA9e7gb5YugoClWD3nY4O + #专有钉钉在开发管理工作台,右键查看网页源码realmId: '31141',浙政钉固定196729 + tenantId: 31141 + domain: openplatform.dg-work.cn \ No newline at end of file