diff --git a/black-list/black-list-service/src/main/java/cn/axzo/nanopart/server/job/BlackUserSyncJob.java b/black-list/black-list-service/src/main/java/cn/axzo/nanopart/server/job/BlackUserSyncJob.java index 6b569bac..ef865a35 100644 --- a/black-list/black-list-service/src/main/java/cn/axzo/nanopart/server/job/BlackUserSyncJob.java +++ b/black-list/black-list-service/src/main/java/cn/axzo/nanopart/server/job/BlackUserSyncJob.java @@ -40,6 +40,9 @@ public class BlackUserSyncJob extends IJobHandler{ @Resource private BlackAndWhiteListService blackAndWhiteListService; + + @Resource + private BlackProducer blackProducer; /** * 定时任务执行逻辑 * 这具数据比较少,一直性发个几百条我问题不在 @@ -79,7 +82,7 @@ public class BlackUserSyncJob extends IJobHandler{ blackList.forEach(item -> sends.add(item.getParam())); String jsonStr = JSONUtil.toJsonStr(sends); if(Objects.nonNull(blackModuleEnum) && Objects.nonNull(event)){ - BlackProducer.send(event, jsonStr); + blackProducer.send(event, jsonStr); } }); return ReturnT.SUCCESS; diff --git a/black-list/black-list-service/src/main/java/cn/axzo/nanopart/server/mq/producer/BlackProducer.java b/black-list/black-list-service/src/main/java/cn/axzo/nanopart/server/mq/producer/BlackProducer.java index 3a439d73..7333115e 100644 --- a/black-list/black-list-service/src/main/java/cn/axzo/nanopart/server/mq/producer/BlackProducer.java +++ b/black-list/black-list-service/src/main/java/cn/axzo/nanopart/server/mq/producer/BlackProducer.java @@ -11,13 +11,16 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; +import javax.annotation.Resource; import java.util.List; @Slf4j @Service -public class BlackProducer implements InitializingBean { - private static EventProducer eventProducer; - public static void send(MQEventEnum mqEventEnum, String data) { +public class BlackProducer { + + @Resource + private EventProducer eventProducer; + public void send(MQEventEnum mqEventEnum, String data) { if(mqEventEnum == null){ throw new ServiceException("无法正确发送mq: mqEventEnum不能为空"); } @@ -33,10 +36,4 @@ public class BlackProducer implements InitializingBean { .data(data) .build()); } - - @Override - public void afterPropertiesSet() { - eventProducer = SpringUtil.getBean(EventProducer.class); - } - } diff --git a/nanopart-server/pom.xml b/nanopart-server/pom.xml index 92d8fba2..178abe7f 100644 --- a/nanopart-server/pom.xml +++ b/nanopart-server/pom.xml @@ -56,6 +56,11 @@ axzo-test-spring-boot-starter + + io.github.openfeign + feign-httpclient + + cn.axzo.pokonyan pokonyan @@ -133,6 +138,18 @@ 2.0.0-SNAPSHOT + + cn.axzo.nanopart + visa-api + 2.0.0-SNAPSHOT + + + + cn.axzo.nanopart + visa-server + 2.0.0-SNAPSHOT + + cn.axzo.im.center im-center-api diff --git a/nanopart-server/src/main/java/cn/axzo/nanopart/NanopartApplication.java b/nanopart-server/src/main/java/cn/axzo/nanopart/NanopartApplication.java index 521bb1b8..17be39b1 100644 --- a/nanopart-server/src/main/java/cn/axzo/nanopart/NanopartApplication.java +++ b/nanopart-server/src/main/java/cn/axzo/nanopart/NanopartApplication.java @@ -1,20 +1,23 @@ package cn.axzo.nanopart; import cn.axzo.nanopart.config.RocketMQEventConfiguration; +import cn.azxo.framework.common.logger.MethodAroundLogAspect; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.Import; +@ComponentScan(value = {"cn.axzo.nanopart","cn.axzo.basics"}) @MapperScan(value = {"cn.axzo.**.mapper"}) @SpringBootApplication @EnableFeignClients(basePackages = { "cn.axzo" }) @EnableAspectJAutoProxy() -@Import(RocketMQEventConfiguration.class) +@Import({RocketMQEventConfiguration.class, MethodAroundLogAspect.class}) public class NanopartApplication { public static void main(String[] args) { SpringApplication.run(NanopartApplication.class, args); diff --git a/nanopart-server/src/main/java/cn/axzo/nanopart/config/FeignConfig.java b/nanopart-server/src/main/java/cn/axzo/nanopart/config/FeignConfig.java index dbb23e00..819cfbcb 100644 --- a/nanopart-server/src/main/java/cn/axzo/nanopart/config/FeignConfig.java +++ b/nanopart-server/src/main/java/cn/axzo/nanopart/config/FeignConfig.java @@ -69,6 +69,11 @@ public class FeignConfig implements RequestInterceptor, EnvironmentAware { private String dataCollectionUrl; @Value("${attendanceUrl:http://dev-app.axzo.cn/attendance}") private String attendanceApi; + @Value("${thorUrl:http://dev-app.axzo.cn/thor}") + private String thorUrl; + @Value("${orgGateway:http://dev-app.axzo.cn/org-gateway}") + private String orgGatewayUrl; + private static String POD_NAMESPACE; static { @@ -107,6 +112,8 @@ public class FeignConfig implements RequestInterceptor, EnvironmentAware { url = url.replace("http://karma:8080", karmaEnvUrl); url = url.replace("http://data-collection:21200", dataCollectionUrl); url = url.replace("http://attendance:8080", attendanceApi); + url = url.replace("http://thor:8080", thorUrl); + url = url.replace("http://org-gateway:8080", orgGatewayUrl); String profile = environment.getProperty("spring.profiles.active"); if(Objects.equals(profile, "test") && url.contains("dev-app.axzo.cn")) { url = url.replace("dev-app", "test-api"); diff --git a/nanopart-server/src/main/java/cn/axzo/nanopart/config/RocketMQEventConfiguration.java b/nanopart-server/src/main/java/cn/axzo/nanopart/config/RocketMQEventConfiguration.java index 5d5d3788..d1520e8a 100644 --- a/nanopart-server/src/main/java/cn/axzo/nanopart/config/RocketMQEventConfiguration.java +++ b/nanopart-server/src/main/java/cn/axzo/nanopart/config/RocketMQEventConfiguration.java @@ -242,4 +242,48 @@ public class RocketMQEventConfiguration { super.onEvent(message, eventConsumer); } } + + /** + * 变洽签业务消费单据日志 + */ + @Slf4j + @Component + @RocketMQMessageListener(topic = "topic_nanopart_${spring.profiles.active}", + consumerGroup = "GID_visa_change_log_${spring.application.name}_${spring.profiles.active}", + consumeMode = ConsumeMode.ORDERLY, + nameServer = "${rocketmq.name-server}" + ) + public static class VisaChangeLogListener extends BaseListener implements RocketMQListener { + + @Autowired + private EventConsumer eventConsumer; + + @Override + public void onMessage(MessageExt message) { + log.info("VisaChangeLogListener onMessage nanopart, message:{}", JSON.toJSONString(message)); + super.onEvent(message, eventConsumer); + } + } + +// /** +// * IM 事件处理 +// */ +// @Slf4j +// @Component +// @RocketMQMessageListener(topic = "topic_im_center_${spring.profiles.active}", +// consumerGroup = "GID_im_${spring.application.name}_${spring.profiles.active}", +// consumeMode = ConsumeMode.ORDERLY, +// nameServer = "${rocketmq.name-server}" +// ) +// public static class ImCenterListener extends BaseListener implements RocketMQListener { +// +// @Autowired +// private EventConsumer eventConsumer; +// +// @Override +// public void onMessage(MessageExt message) { +// log.info("ImCenterListener onMessage nanopart, message:{}", JSON.toJSONString(message)); +// super.onEvent(message, eventConsumer); +// } +// } } diff --git a/nanopart-server/src/main/java/cn/axzo/nanopart/config/exception/ExceptionAdviceHandler.java b/nanopart-server/src/main/java/cn/axzo/nanopart/config/exception/ExceptionAdviceHandler.java index 13db216b..1eb2de51 100644 --- a/nanopart-server/src/main/java/cn/axzo/nanopart/config/exception/ExceptionAdviceHandler.java +++ b/nanopart-server/src/main/java/cn/axzo/nanopart/config/exception/ExceptionAdviceHandler.java @@ -2,6 +2,7 @@ package cn.axzo.nanopart.config.exception; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.pokonyan.exception.BusinessException; +import cn.axzo.workflow.common.exception.WorkflowEngineException; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.util.CollectionUtils; @@ -37,13 +38,13 @@ public class ExceptionAdviceHandler { } @ExceptionHandler(cn.axzo.framework.domain.ServiceException.class) - public ApiResult domainServiceExceptionHandler(cn.axzo.framework.domain.ServiceException e){ + public ApiResult domainServiceExceptionHandler(cn.axzo.framework.domain.ServiceException e) { log.warn("业务异常", e); return ApiResult.err(e.getMessage()); } @ExceptionHandler(BusinessException.class) - public ApiResult businessExceptionHandler(BusinessException e){ + public ApiResult businessExceptionHandler(BusinessException e) { log.warn("业务异常", e); return ApiResult.err(e.getErrorMsg()); } @@ -60,4 +61,10 @@ public class ExceptionAdviceHandler { return ApiResult.err(objectErrorDefaultMessage); } + @ExceptionHandler(WorkflowEngineException.class) + public ApiResult workflowExceptionHandler(WorkflowEngineException e) { + log.warn("业务异常", e); + return ApiResult.err(e.getMessage()); + } + } diff --git a/nanopart-server/src/main/resources/bootstrap.yml b/nanopart-server/src/main/resources/bootstrap.yml index 3cc71161..857b00ee 100644 --- a/nanopart-server/src/main/resources/bootstrap.yml +++ b/nanopart-server/src/main/resources/bootstrap.yml @@ -14,6 +14,9 @@ spring: main: allow-bean-definition-overriding: true +server: + shutdown: graceful + arthas: app-name: ${spring.application.name} agent-id: ${ARTHAS_AGENT_ID:${spring.profiles.active}-${spring.application.name}} diff --git a/pom.xml b/pom.xml index d9478beb..0fc12277 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ 1.4.2.Final 5.2.2 3.3.3 + 11.8 @@ -34,6 +35,7 @@ op banner dictionary + visa @@ -53,6 +55,12 @@ pom import + + + io.github.openfeign + feign-httpclient + ${feign-httpclient.version} + diff --git a/visa/pom.xml b/visa/pom.xml new file mode 100644 index 00000000..645978ab --- /dev/null +++ b/visa/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + + cn.axzo.nanopart + nanopart + ${revision} + ../pom.xml + + + cn.axzo.nanopart + visa + pom + visa + + + 2.0.0-SNAPSHOT + 2.0.0-SNAPSHOT + 1.18.22 + 1.4.2.Final + 1.5.2-SNAPSHOT + + + + + + + cn.axzo.infra + axzo-bom + ${axzo-bom.version} + pom + import + + + cn.axzo.infra + axzo-dependencies + ${axzo-dependencies.version} + pom + import + + + cn.axzo.workflow + workflow-engine-spring-boot-starter + ${workflow-engine.version} + + + + + + + + + org.projectlombok + lombok + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + junit + junit + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + + + + + axzo + axzo repository + https://nexus.axzo.cn/repository/axzo/ + + + + visa-api + visa-server + + diff --git a/visa/visa-api/pom.xml b/visa/visa-api/pom.xml new file mode 100644 index 00000000..f61c301c --- /dev/null +++ b/visa/visa-api/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + + visa + cn.axzo.nanopart + ${revision} + ../pom.xml + + + visa-api + jar + visa-api + + + + org.springframework.cloud + spring-cloud-openfeign-core + + + cn.axzo.framework + axzo-common-domain + + + cn.axzo.framework.rocketmq + axzo-common-rocketmq + + + com.alibaba + fastjson + + + io.github.openfeign + feign-httpclient + + + cn.axzo.maokai + maokai-common + ${project.version} + + + cn.axzo.workflow + workflow-engine-common + ${workflow-engine.version} + + + + diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordApi.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordApi.java new file mode 100644 index 00000000..4d02ef5e --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordApi.java @@ -0,0 +1,177 @@ +package cn.axzo.nanopart.visa.api.changerecord; + +import cn.axzo.framework.domain.web.result.ApiPageResult; +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.nanopart.visa.api.request.ChangeRecordButtonOperationReq; +import cn.axzo.nanopart.visa.api.request.ChangeStatusRequest; +import cn.axzo.nanopart.visa.api.request.CheckVisaWithVisaIdReq; +import cn.axzo.nanopart.visa.api.request.FetchVisaAllConfirmReq; +import cn.axzo.nanopart.visa.api.request.GetBillIfRelatedReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveOnlyReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeDiscussCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeExportReq; +import cn.axzo.nanopart.visa.api.request.VisaChangePageSearchReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaDetailByIdRequest; +import cn.axzo.nanopart.visa.api.request.VisaInitiatorListReq; +import cn.axzo.nanopart.visa.api.request.VisaSearchReq; +import cn.axzo.nanopart.visa.api.response.VisaChangeDiscussCreateResp; +import cn.axzo.nanopart.visa.api.response.VisaChangeInitiatorResp; +import cn.axzo.nanopart.visa.api.response.VisaChangePageSearchResp; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import cn.axzo.nanopart.visa.api.response.VisaSearchResp; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录 + */ +@FeignClient(name = "nanopart", url = "${axzo.service.nanopart:http://nanopart:8080}") +public interface ChangeRecordApi { + + /** + * 暂存变更签证记录 + * + * @param req {@link VisaChangeTempCreateReq} 签证变更表单数据 + * @return 签证变更记录id + */ + @PostMapping("/api/visa/change/tempCreate") + ApiResult tempCreateVisaChangeRecord(@RequestBody VisaChangeTempCreateReq req); + + /** + * 创建群聊洽商变更签证 + * + * @param req {@link VisaChangeDiscussCreateReq} + * @return 签证变更记录id + */ + @PostMapping("/api/visa/change/discussCreate") + ApiResult discussCreateVisaChangeRecord(@RequestBody VisaChangeDiscussCreateReq req); + + /** + * 发起变更签证审批 + * + * @param req {@link VisaChangeApproveCreateReq} 签证变更表单数据域审批人信息 + * @return 签证变更记录id + */ + @PostMapping("/api/visa/change/approveCreate") + ApiResult approveCreateVisaChangeRecord(@RequestBody @Validated VisaChangeApproveCreateReq req); + + /** + * 变签状态变更 + */ + @PostMapping("/api/visa/changeStatus") + ApiResult changeStatus(@RequestBody @Valid ChangeStatusRequest request); + + /** + * 变签详情 + */ + @PostMapping("/api/visa/visaDetailById") + ApiResult visaDetailById(@RequestBody @Valid VisaDetailByIdRequest request); + + /** + * 分页搜索 + * + * @param req {@link VisaChangePageSearchReq} 筛查条件 + * @return 分页记录 + */ + @PostMapping("/api/visa/change/page") + ApiPageResult pageSearchVisaChangeRecord(@RequestBody @Validated VisaChangePageSearchReq req); + + /** + * 导出 + * + * @param req {@link VisaChangeExportReq} 筛选条件和导出字段 + * @return void + */ + @PostMapping("/api/visa/change/export") + ApiResult exportVisaChangeRecord(@RequestBody @Validated VisaChangeExportReq req); + + /** + * 获取发起人列表 + * + * @param req {@link VisaInitiatorListReq} + * @return 发起人列表 + */ + @PostMapping("/api/visa/change/initiator/list") + ApiResult> listInitiator(@RequestBody @Validated VisaInitiatorListReq req); + + /** + * IM 变洽签群中,点击“提交审批”前拉取对应的确认人列表 + * + * @return + */ + @PostMapping("/api/visa/change/confirm/list") + ApiResult> getVisaAllConfirm(@Validated @RequestBody FetchVisaAllConfirmReq req); + + /** + * IM 变洽签群,“提交审批”按钮 + * + * @return + */ + @PostMapping("/api/visa/change/approve/only/create") + ApiResult approveCreateOnlyVisaChangeRecord(@Validated @RequestBody VisaChangeApproveOnlyReq req); + + /** + * IM 变洽签群,“废止”按钮 + * + * @return + */ + @PostMapping("/api/visa/change/record/forbid") + ApiResult forbidChangeRecord(@Validated @RequestBody ChangeRecordButtonOperationReq req); + + /** + * IM 变洽签群,“重新发起”按钮 + * + * @return + */ + @PostMapping("/api/visa/change/reDecision") + ApiResult reDecisionChangeRecord(@Validated @RequestBody ChangeRecordButtonOperationReq req); + + /** + * IM 变洽签群,“发起执行”按钮 + * + * @param req + * @return + */ + @PostMapping("/api/visa/change/execute") + ApiResult executeChangeRecord(@Validated @RequestBody ChangeRecordButtonOperationReq req); + + /** + * 变洽签,“删除”按钮 + * + * @return void + */ + @PostMapping("/api/visa/change/delete") + ApiResult deleteChangeRecord(@Validated @RequestBody ChangeRecordButtonOperationReq req); + + /** + * 校验 + */ + @PostMapping("/api/visa/change/checkVisaWithVisaId") + ApiResult checkVisaWithVisaId(@Validated @RequestBody CheckVisaWithVisaIdReq req); + + /** + * 获取子单据的关联状态 + * + * @param req {@link GetBillIfRelatedReq} + * @return Map true:关联 false:不关联 + */ + @PostMapping("/api/visa/change/getBillIfRelated") + ApiResult> getBillIfRelated(@RequestBody @Validated GetBillIfRelatedReq req); + + /** + * 查询单据 + */ + @PostMapping("/api/visa/change/list") + ApiPageResult list(@RequestBody @Validated VisaSearchReq req); + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordLogApi.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordLogApi.java new file mode 100644 index 00000000..0d794319 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordLogApi.java @@ -0,0 +1,26 @@ +package cn.axzo.nanopart.visa.api.changerecord; + +import cn.axzo.framework.domain.web.result.ApiListResult; +import cn.axzo.nanopart.visa.api.response.FetchVisaLogByVisaIdResponse; +import cn.axzo.nanopart.visa.api.request.FetchVisaLogByVisaIdRequest; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录操作日志 + */ +@FeignClient(name = "nanopart", url = "${axzo.service.nanopart:http://nanopart:8080}") +public interface ChangeRecordLogApi { + + /** + * 根据变签Id,获取变签日志 + */ + @PostMapping("/api/visa/log/fetchVisaLogByVisaId") + ApiListResult fetchVisaLogByVisaId(@RequestBody @Valid FetchVisaLogByVisaIdRequest req); + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordRelationApi.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordRelationApi.java new file mode 100644 index 00000000..e6f09f10 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/changerecord/ChangeRecordRelationApi.java @@ -0,0 +1,42 @@ +package cn.axzo.nanopart.visa.api.changerecord; + +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.nanopart.visa.api.request.ChangeRecordAppendAttachReq; +import cn.axzo.nanopart.visa.api.request.ImGroupOperationReq; +import cn.axzo.nanopart.visa.api.request.ImGroupTipsQueryReq; +import cn.axzo.nanopart.visa.api.request.VisaRelationReq; +import cn.axzo.nanopart.visa.api.response.ImGroupTipsResp; +import cn.axzo.nanopart.visa.api.response.VisaRelationResp; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录内容关系表 + */ +@FeignClient(name = "nanopart", url = "${axzo.service.nanopart:http://nanopart:8080}") +public interface ChangeRecordRelationApi { + + @PostMapping("/api/visa/relation/list") + ApiResult> listByVisaId(@Validated @RequestBody VisaRelationReq req) ; + + @PostMapping("/api/visa/relation/im/operation") + ApiResult saveImOperationParticipate(@Validated @RequestBody ImGroupOperationReq req); + + @PostMapping("/api/visa/relation/im/tips") + ApiResult queryImGroupTips(@Validated @RequestBody ImGroupTipsQueryReq req); + + /** + * 变洽签追加附件 + * + * @param req + * @return + */ + @PostMapping("/api/visa/relation/attach/append") + ApiResult appendAttachment(@Validated @RequestBody ChangeRecordAppendAttachReq req); +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/config/VisaApiAutoConfiguration.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/config/VisaApiAutoConfiguration.java new file mode 100644 index 00000000..b867bf9b --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/config/VisaApiAutoConfiguration.java @@ -0,0 +1,9 @@ +package cn.axzo.nanopart.visa.api.config; + +import cn.axzo.nanopart.visa.api.constant.VisaConstant; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@EnableFeignClients(VisaConstant.BASIC_FEIGN_PACKAGE) +public class VisaApiAutoConfiguration { + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/constant/VisaConstant.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/constant/VisaConstant.java new file mode 100644 index 00000000..f957f29e --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/constant/VisaConstant.java @@ -0,0 +1,75 @@ +package cn.axzo.nanopart.visa.api.constant; + +/** + * 变洽签 API 基础包路径 + * + * @author wangli + * @since 2025-01-14 17:20 + */ +public interface VisaConstant { + String BASIC_FEIGN_PACKAGE = "cn.axzo.visa.api"; + + /** + * 用于在流程作用域外识别变洽签的单据类型 + */ + String WORKFLOW_VAR_VISA_TYPE_KEY = "visa-type"; + /** + * 用于发送待办的变量名 + */ + String WORKFLOW_PENDING_TOPIC = "topic"; + String WORKFLOW_PENDING_INITIATOR_NAME = "initiatorName"; + String MSG_CENTER_APP_CODE = "nanopart-visa"; + /** + * IM 群的扩展信息:变洽签 ID + */ + String IM_GROUP_BIZ_INFO_VISA_ID = "visaId"; + /** + * IM 群扩展信息:变洽签单据类型 + */ + String IM_GROUP_BIZ_INFO_VISA_TYPE = "visaType"; + /** + * IM 群扩展信息:群主 workspaceId + */ + String IM_GROUP_BIZ_INFO_INITIATOR_WORKSPACE_ID = "initiatorWorkspaceId"; + // **************** 业务传入审批的表单项组件的唯一的 KEY ***************** + // 单号 + String FORM_FIELD_NO = "no"; + // 提出时间 + String FORM_FIELD_HAPPEN_TIME = "happenTime"; + // 主题 + String FORM_FIELD_TOPIC = "topic"; + // 工作台名称 + String FORM_FIELD_WORKSPACE_NAME = "workspaceName"; + // 关联工程 + String FORM_FIELD_RELATION_PROJECT = "relationProject"; + // 关联专业 + String FORM_FIELD_RELATION_PROFESSIONAL = "relationProfessional"; + // 发生原因 + String FORM_FIELD_REASON = "reason"; + // 发生区域 + String FORM_FIELD_RELATION_AREA = "relationArea"; + // 发生内容及说明 + String FORM_FIELD_CONTEXT_DESCRIPTION = "contextDescription"; + String FORM_FIELD_CONTEXT = "context"; + String FORM_FIELD_DESCRIPTION = "description"; + // 相关单位和人员 + String FORM_FIELD_UNIT_PERSON = "unitPerson"; + String FORM_FIELD_UNIT = "unit"; + String FORM_FIELD_PERSON = "person"; + // 变更金额 + String FORM_FIELD_AMOUNT_CHANGE = "amountChange"; + // 关联的任务单单据 + String FORM_FIELD_RELATION_TASK_ORDER = "relationTaskOrders"; + // 关联的整改单单据 + String FORM_FIELD_RELATION_RECTIFICATION_ORDER = "relationRectificationOrders"; + // 关联的变更单单据 + String FORM_FIELD_RELATION_VISA_ORDER = "relationVisaOrders"; + // 附件 + String FORM_FIELD_ATTACH = "attach"; + + // ***************** IM-Group-Tips ****************** + String IM_GROUP_OWNER_TIPS = "群成员共计%d人,收到回复%d人,其中%d人同意,%d人拒绝。"; + String IM_GROUP_PARTICIPATE_TIPS = "由 %s 提交的【%s】,需要你处理,是否同意?"; + String IM_GROUP_PARTICIPATE_TIPS_OPERATED = "你已%s由 %s 提交的【%s】"; +} + diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/MQEventEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/MQEventEnum.java new file mode 100644 index 00000000..81f7f07b --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/MQEventEnum.java @@ -0,0 +1,31 @@ +package cn.axzo.nanopart.visa.api.enums; + +import cn.axzo.framework.rocketmq.Event; +import lombok.Getter; + +/** + * @Author zr + * @Date 2024/3/19 18:01 + * @Description + **/ +@Getter +public enum MQEventEnum { + + VISA_CHANGE_LOG("nanopart", "visa-change-log", "变洽签单据日志") + ; + private final String model; + private final String tag; + private final String desc; + private final Event.EventCode eventCode; + + MQEventEnum(String model, String tag, String desc) { + this.eventCode = Event.EventCode.builder() + .module(model) + .name(tag) + .build(); + this.model = model; + this.tag = tag; + this.desc = desc; + } + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaBillTypeEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaBillTypeEnum.java new file mode 100644 index 00000000..0bf38b9e --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaBillTypeEnum.java @@ -0,0 +1,26 @@ +package cn.axzo.nanopart.visa.api.enums; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 变更签证单据类型 + * @author xudawei + * @since 2025/01/20 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum VisaBillTypeEnum { + + TASK("task", "任务单"), + RECTIFY("rectify", "整改单"), + DESIGN_VISA("design_visa", "设计变更单"), + ; + + + private String code; + + private String desc; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaButtonTypeEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaButtonTypeEnum.java new file mode 100644 index 00000000..5a333ae8 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaButtonTypeEnum.java @@ -0,0 +1,213 @@ +package cn.axzo.nanopart.visa.api.enums; + +import cn.axzo.framework.domain.ServiceException; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 按钮枚举 + * + * @author xudawei + * @since 2025-01-15 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum VisaButtonTypeEnum { + + UPDATE("update", "编辑",55), + DELETE("delete", "删除",50), + FORBID("forbid", "作废",5), + EXECUTE("execute", "发起执行",40), + REDECISION("redecision", "重新发起",0), + TO_APPROVE("to_approve", "提交审批",30), + AGAIN_TO_APPROVE("again_to_approve", "重新提交审批",20), + AGREE("agree", "同意",0), + REJECT("reject", "拒绝",0), + UPLOAD_FILE("upload_file", "上传附件",25), + CHAT_GROUP_RECORD("chat_group_record", "查看洽商记录",15), + APPROVAL_RECORD("approval_record", "查看审批记录",10), + TO_HANDLE("to_handle", "去处理", 35), + PRINT("print", "打印",45), + DETAIL("detail", "详情",0), + ; + + + private String code; + + private String desc; + + /** + * 倒序-数值越大,排序越靠前 + * 中间新增按钮-步数为5 + */ + private int orderBy; + + //分页列表页Map,key:角色+单据状态,value:功能权限的按钮列表 + private static Map> bizTypeBtnMap = Maps.newHashMap(); + + static { + // 发起人不同状态按钮权限-详情页 + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.TO_REPORT.name(), Sets.newHashSet(UPDATE, DELETE)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.REPORT_FROM_APPROVE.name(), Sets.newHashSet(UPDATE, FORBID)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.DECIDING.name(), Sets.newHashSet(UPDATE, EXECUTE, TO_APPROVE, FORBID, CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.DECIDING_FROM_APPROVE.name(), Sets.newHashSet(UPDATE, EXECUTE, TO_APPROVE, FORBID, CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.EXECUTING.name(), Sets.newHashSet(UPDATE, TO_APPROVE, FORBID, CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.APPROVING.name(), Sets.newHashSet(UPLOAD_FILE, FORBID, CHAT_GROUP_RECORD, APPROVAL_RECORD,PRINT)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.COMPLETED.name(), Sets.newHashSet(UPLOAD_FILE, CHAT_GROUP_RECORD, APPROVAL_RECORD,PRINT)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.FORBIDED.name(), Sets.newHashSet(CHAT_GROUP_RECORD, APPROVAL_RECORD)); + + // 决策人不同状态按钮权限-详情页 + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.DECIDING.name(), Sets.newHashSet(UPDATE, CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.DECIDING_FROM_APPROVE.name(), Sets.newHashSet(UPDATE, CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.EXECUTING.name(), Sets.newHashSet(UPDATE, CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.APPROVING.name(), Sets.newHashSet(UPLOAD_FILE, CHAT_GROUP_RECORD, APPROVAL_RECORD,PRINT)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.COMPLETED.name(), Sets.newHashSet(UPLOAD_FILE, CHAT_GROUP_RECORD, APPROVAL_RECORD,PRINT)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.FORBIDED.name(), Sets.newHashSet(CHAT_GROUP_RECORD, APPROVAL_RECORD)); + + //审核人不同状态按钮权限-详情页 + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.APPROVE.name() + VisaStatusEnum.APPROVING.name(), Sets.newHashSet(TO_HANDLE, CHAT_GROUP_RECORD,PRINT)); + + //其他不同状态按钮权限-详情页 + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.DECIDING.name(), Sets.newHashSet(CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.DECIDING_FROM_APPROVE.name(), Sets.newHashSet(CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.EXECUTING.name(), Sets.newHashSet(CHAT_GROUP_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.APPROVING.name(), Sets.newHashSet(PRINT, CHAT_GROUP_RECORD, APPROVAL_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.COMPLETED.name(), Sets.newHashSet(PRINT, CHAT_GROUP_RECORD, APPROVAL_RECORD)); + bizTypeBtnMap.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.FORBIDED.name(), Sets.newHashSet(CHAT_GROUP_RECORD, APPROVAL_RECORD)); + + //[打印]按钮是通过OMS统一配置,故此处不处理 + + + } + + //分页列表页Map,key:角色+单据状态,value:功能权限的按钮列表 + private static Map> bizTypeBtnMapWhenPage = Maps.newHashMap(); + + static { + // 发起人不同状态按钮权限-分页列表页 + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.TO_REPORT.name(), Sets.newHashSet(DETAIL, UPDATE)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.REPORT_FROM_APPROVE.name(), Sets.newHashSet(DETAIL, UPDATE)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.DECIDING.name(), Sets.newHashSet(DETAIL, UPDATE)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.DECIDING_FROM_APPROVE.name(), Sets.newHashSet(DETAIL, UPDATE)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.EXECUTING.name(), Sets.newHashSet(DETAIL, UPDATE)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.APPROVING.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.COMPLETED.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CREATE.name() + VisaStatusEnum.FORBIDED.name(), Sets.newHashSet(DETAIL)); + + + // 确认人不同状态按钮权限-分页列表页 + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.TO_REPORT.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.REPORT_FROM_APPROVE.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.DECIDING.name(), Sets.newHashSet(DETAIL, UPDATE)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.DECIDING_FROM_APPROVE.name(), Sets.newHashSet(DETAIL, UPDATE)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.EXECUTING.name(), Sets.newHashSet(DETAIL, UPDATE)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.APPROVING.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.COMPLETED.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.CONFIRM.name() + VisaStatusEnum.FORBIDED.name(), Sets.newHashSet(DETAIL)); + + // 其他人不同状态按钮权限-分页列表页 + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.TO_REPORT.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.REPORT_FROM_APPROVE.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.DECIDING.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.DECIDING_FROM_APPROVE.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.EXECUTING.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.APPROVING.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.COMPLETED.name(), Sets.newHashSet(DETAIL)); + bizTypeBtnMapWhenPage.put(VisaConfirmBizTypeEnum.OTHER.name() + VisaStatusEnum.FORBIDED.name(), Sets.newHashSet(DETAIL)); + } + + /** + * 根据角色集合与单据状态获取按钮权限 + */ + public static Set fetchBtnSetByBizTypeAndStatus(Set bizTypeSet, VisaStatusEnum status) { + Set returnBtnSet = Sets.newHashSet(); + if (Objects.isNull(status)) { + return Sets.newHashSet(); + } + + if (CollectionUtils.isEmpty(bizTypeSet)) { + bizTypeSet.add(VisaConfirmBizTypeEnum.OTHER); + } + + bizTypeSet.stream() + .filter(item -> Objects.nonNull(item)) + .forEach(item -> { + returnBtnSet.addAll(fetchBtnSetByBizTypeAndStatus(item, status)); + }); + return returnBtnSet; + } + + /** + * 根据角色与单据状态获取按钮权限 + */ + public static Set fetchBtnSetByBizTypeAndStatus(VisaConfirmBizTypeEnum bizTypeSet, VisaStatusEnum status) { + if (Objects.isNull(bizTypeSet) || Objects.isNull(status)) { + return Sets.newHashSet(); + } + switch (bizTypeSet) { + case CREATE: + case CONFIRM: + case OTHER: + case APPROVE: + Set buttonTypeEnumSet = bizTypeBtnMap.get(bizTypeSet.name() + status.name()); + return CollectionUtils.isNotEmpty(buttonTypeEnumSet) ? buttonTypeEnumSet : Sets.newHashSet(); + default: + throw new ServiceException("单据确认业务类型不匹配"); + } + } + + /** + * 根据角色与单据状态获取按钮权限 + */ + public static Set fetchBtnsByBizTypeAndStatusWhenPage(Set bizTypeSet, VisaStatusEnum status) { + if (Objects.isNull(bizTypeSet) || Objects.isNull(status)) { + return Sets.newHashSet(); + } + + Set returnBtnSet = Sets.newHashSet(); + + bizTypeSet.stream().filter(Objects::nonNull).forEach(item -> { + switch (item) { + case CREATE: + case CONFIRM: + case OTHER: + case APPROVE: + Set buttonTypeEnumSet = bizTypeBtnMapWhenPage.get(item.name() + status.name()); + returnBtnSet.addAll(CollectionUtils.isNotEmpty(buttonTypeEnumSet) ? buttonTypeEnumSet : Sets.newHashSet()); + break; + default: + throw new ServiceException("单据确认业务类型不匹配"); + } + }); + return returnBtnSet; + } + + public static Set all() { + return Sets.newHashSet(VisaButtonTypeEnum.values()); + } + + /** + * 按照orderBy排序-前端要求 + */ + public static List sort(Set btnSet) { + if (CollectionUtils.isEmpty(btnSet)) { + return Lists.newArrayList(); + } + return Lists.newArrayList(btnSet).stream() + .sorted(Comparator.comparing(VisaButtonTypeEnum::getOrderBy).reversed()) + .collect(Collectors.toList()); + } + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaChangeFieldEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaChangeFieldEnum.java new file mode 100644 index 00000000..683877ee --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaChangeFieldEnum.java @@ -0,0 +1,44 @@ +package cn.axzo.nanopart.visa.api.enums; + +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/16 17:10 + */ +@Getter +public enum VisaChangeFieldEnum { + NO("no", "单号"), + TYPE("type", "单据类型"), + STATUS("status", "单据状态"), + TOPIC("topic", "主题"), + AMOUNT_CHANGE("amountChange", "金额变化"), + INITIATOR("initiator", "发起人"), + CREATE_AT("happenTime", "提出时间"), + APPROVAL_COMPLETE_TIME("approvalCompleteTime", "审批通过时间"), + RELATION_PROJECT("relationProject", "所属工程"), + ; + + private final String value; + + private final String desc; + + VisaChangeFieldEnum(String value, String desc) { + this.value = value; + this.desc = desc; + } + + public static VisaChangeFieldEnum getByName(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + for (VisaChangeFieldEnum value : values()) { + if (value.value.equals(name)) { + return value; + } + } + return null; + } +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaConfirmBizTypeEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaConfirmBizTypeEnum.java new file mode 100644 index 00000000..90882861 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaConfirmBizTypeEnum.java @@ -0,0 +1,44 @@ +package cn.axzo.nanopart.visa.api.enums; + +import com.google.common.collect.Sets; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Set; + +/** + * 业务类型 + * @author xudawei + * @since 2025-01-17 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum VisaConfirmBizTypeEnum { + + CONFIRM("CONFIRM", "确认"), + CREATE("CREATE", "创建"), + APPROVE("APPROVE", "审核"), + OTHER("OTHER", "其他"), + ; + + + private String code; + + private String desc; + + /** + * 确认与创建 + */ + private static Set confirmCreate = Sets.newHashSet(CONFIRM, CREATE); + /** + * 确认 + */ + private static Set confirm = Sets.newHashSet(CONFIRM); + /** + * 创建 + */ + private static Set create = Sets.newHashSet(CREATE); + + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaLogTypeEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaLogTypeEnum.java new file mode 100644 index 00000000..2bc931f2 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaLogTypeEnum.java @@ -0,0 +1,65 @@ +package cn.axzo.nanopart.visa.api.enums; + +import com.google.common.collect.Sets; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; +import java.util.Set; + +/** + * 变签日志类型 + * @author xudawei + * @since 2025-01-15 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum VisaLogTypeEnum { + + CHAT_GROUP("chat_group", "发起群聊洽商","发起群聊洽商","%s发起群聊洽商"), + EDIT_FORM("edit_form", "编辑表单","%s编辑表单","%s编辑表单"), + EDIT_AMOUNT("edit_amount", "编辑金额变化","%s编辑金额变化","原值:金额变化:%s;
新值:金额变化:%s;"), + UPLOAD_FILE("upload_file", "上传附件","%s上传附件","%s上传%d个附件:%s"), + DELETE_UPLOAD_FILE("delete_upload_file", "删除附件","%s删除附件","%s删除%d个附件:%s"), + TO_EXECUTE("to_execute", "发起执行","发起执行", "%s发起执行"), + TO_APPRROVE("to_apprrove", "发起审批","发起%s审批", "%s发起%s审批"), + APPROVED_AGRESS("approved_agress", "审批流:某审批人审批通过","%s审批通过", "%s审批通过"), + FORBIDED("forbided", "单据作废","单据废止", "%s操作废止单据"), + REVERT_APPROVE("revert_approve", "审批流:撤回审批","审批流程撤回", "%s撤回审批流程"), + REJECT_APPROVE("reject_approve", "审批流:已驳回","%s驳回审批", "%s已驳回,原因为%s"), + TRANSMIT_APPROVE("transmit_approve", "审批流操作:转交","审批人变更", "%s将审批转交至%s,原因为%s"), + FALLBACK_APPROVE("fallback_approve", "审批流操作:回退","%s回退审批", "%s回退审批至审批节点:%s"), + APPROVE_COMPLETED("approved_completed", "审批流:审批通过","%s通过", "%s流程已完成"), + ; + + /** + * 审批不通过的日志集合 + */ + private static final Set approveNotAgree = Sets.newHashSet(REVERT_APPROVE, REJECT_APPROVE, FALLBACK_APPROVE); + + /** + * 审批不通过的日志集合 + */ +// private static final Set approveNotAgree = Sets.newHashSet(EDIT_FORM, EDIT_AMOUNT, UPLOAD_FILE, DELETE_UPLOAD_FILE, APPROVED_AGRESS); + + + + private String code; + + private String action; + + private String title; + + private String content; + +// private boolean + + /** + * 是否审批不通过 + */ + public static boolean isApproveNotAgree(VisaLogTypeEnum logType) { + return Objects.nonNull(logType) && approveNotAgree.contains(logType); + } + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaRelationFieldEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaRelationFieldEnum.java new file mode 100644 index 00000000..c05d9322 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaRelationFieldEnum.java @@ -0,0 +1,42 @@ +package cn.axzo.nanopart.visa.api.enums; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +/** + * 变洽签业务的 relation 表数据行的类型枚举 + * + * @author wangli + * @since 2025-01-16 09:42 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum VisaRelationFieldEnum { + //发生内容及说明、相关单位及人员,单据,附件,审批记录 + CONTENT_DESCRIPTION("content_description", "发生内容及说明"), + TASK_ORDER("task_order", "任务单"), + RECTIFICATION_ORDER("rectification_order", "整改单"), + VISA_ORDER("visa_order", "变洽签变更单"), + ATTACHMENT("attachment", "附件"), + PROCESS_INSTANCE_OF_VISA("process_instance_of_visa", "变洽签审批实例"), + PROCESS_INSTANCE_OF_ESS("process_instance_of_ess", "用印审批实例"), + ORDER("order", "单据"), + IM_GROUP_PARTICIPATE("im_group_participate", "im群聊参与人"), + ; + private final String code; + private final String desc; + + /** + * 是否附件类型 + * @param name 枚举的名称 + * @return true:是附件类型;false:不是附件类型 + */ + public static boolean isAttach(String name) { + if (StringUtils.isBlank(name)) { + return false; + } + return VisaRelationFieldEnum.ATTACHMENT.name().equals(name); + } +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaRelationVarTypeEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaRelationVarTypeEnum.java new file mode 100644 index 00000000..e830577d --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaRelationVarTypeEnum.java @@ -0,0 +1,22 @@ +package cn.axzo.nanopart.visa.api.enums; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/20 16:21 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum VisaRelationVarTypeEnum { + + STRING("string"), + LONG("long"), + JSON("json"); + + private final String type; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaStatusEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaStatusEnum.java new file mode 100644 index 00000000..4cee0a85 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaStatusEnum.java @@ -0,0 +1,72 @@ +package cn.axzo.nanopart.visa.api.enums; + +import com.google.common.collect.Sets; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * 签证业务状态 + * + * @author xudawei + * @since 2025-01-15 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum VisaStatusEnum { + + TO_REPORT("to_report", "待提报", null), + DECIDING("deciding", "决策中", VisaLogTypeEnum.CHAT_GROUP), + EXECUTING("executing","执行中", VisaLogTypeEnum.TO_EXECUTE), + APPROVING("approving","审批中", VisaLogTypeEnum.TO_APPRROVE), + COMPLETED("completed","已完成",VisaLogTypeEnum.APPROVE_COMPLETED), + FORBIDED("forbided","已废除",VisaLogTypeEnum.FORBIDED), + REPORT_FROM_APPROVE("report_from_approve","审批不通过到待提报", null), + DECIDING_FROM_APPROVE("deciding_from_approve","审批不通过到决策中", null), + ; + + /** + * 可以编辑变更签证单的状态 + */ + private static Set editFormStatus = Sets.newHashSet(DECIDING, EXECUTING, DECIDING_FROM_APPROVE); + + /** + * 无效状态集合,审批不通过到待提报/已废除 + */ + private static Set detailPageInvalidStatus = Sets.newHashSet(REPORT_FROM_APPROVE, FORBIDED); + + private final String code; + private final String desc; + private final VisaLogTypeEnum logType; + + /** + * 是否编辑变更签证单的状态 + */ + public static boolean isEditForm(VisaStatusEnum status) { + if (Objects.isNull(status)) { + return false; + } + return editFormStatus.contains(status); + } + + /** + * 获取所有的状态枚举 + */ + public static List allStatus() { + return Arrays.asList(VisaStatusEnum.values()); + } + + /** + * 无效状态集合,审批不通过到待提报/已废除 + */ + public static Set detailPageInvalidStatus() { + return detailPageInvalidStatus; + } + + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaTypeEnum.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaTypeEnum.java new file mode 100644 index 00000000..e68d11da --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/enums/VisaTypeEnum.java @@ -0,0 +1,45 @@ +package cn.axzo.nanopart.visa.api.enums; + +import com.google.common.collect.Sets; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Set; + +/** + * 签证业务类型 + * + * @author wangli + * @since 2025-01-15 16:00 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum VisaTypeEnum { + + DESIGN_CHANGE("bgqz01","design_change", "设计变更"), + TECHNOLOGY_APPROVED("bgqz02","technology_approved", "技术核定"), + PROJECT_VISA("bgqz03", "project_visa","工程签证"); + + private final String processDefinitionKey; + private final String code; + private final String desc; + + + private static final Set changeApprovedSet = Sets.newHashSet(DESIGN_CHANGE, TECHNOLOGY_APPROVED); + private static final Set projectVisaSet = Sets.newHashSet(PROJECT_VISA); + + /** + * 设计变更/技术核定 + */ + public static Set fetchChangeApprovedSet() { + return changeApprovedSet; + } + + /** + * 工程签证 + */ + public static Set fetchProjectVisaSet() { + return projectVisaSet; + } +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/AddVisaLogRequest.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/AddVisaLogRequest.java new file mode 100644 index 00000000..febeb506 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/AddVisaLogRequest.java @@ -0,0 +1,52 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author xudawei + * @date 2025/01/16 + * @desc 添加变签日志 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class AddVisaLogRequest { + + /** + * 变签Id + */ + @NotNull(message = "变签Id不能为空") + private Long visaId; + + /** + * 日志类型 + */ + @NotNull(message = "日志类型不能为空") + private VisaLogTypeEnum logType; + + /** + * 操作人Id + */ + @NotNull(message = "操作人Id不能为空") + private Long personId; + /** + * 操作人单位Id + */ + @NotNull(message = "操作人单位Id不能为空") + private Long ouId; + /** + * 操作人项目Id + */ + @NotNull(message = "操作人项目Id不能为空") + private Long workspaceId; + + + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeRecordAppendAttachReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeRecordAppendAttachReq.java new file mode 100644 index 00000000..3c73c04c --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeRecordAppendAttachReq.java @@ -0,0 +1,41 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 变洽签增加附件 + * + * @author wangli + * @since 2025-02-08 14:19 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ChangeRecordAppendAttachReq { + + /** + * 变洽签主键 ID + */ + @NotNull(message = "变洽签主键 ID") + private Long visaId; + + /** + * 新增的附件 + */ + private List attach; + + private Long operatorPersonId; + + private Long operatorOuId; + + private Long workspaceId; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeRecordButtonOperationReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeRecordButtonOperationReq.java new file mode 100644 index 00000000..6e0e219b --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeRecordButtonOperationReq.java @@ -0,0 +1,50 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * 变洽签废止 + * + * @author wangli + * @since 2025-02-05 20:26 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ChangeRecordButtonOperationReq { + + /** + * 变洽签主键 ID + */ + @NotNull(message = "变洽签 ID 不能为空") + private Long visaId; + + /** + * IM 群 ID + */ + private Long imGroupId; + + /** + * 操作人 personId + */ + @NotNull(message = "操作人 personId 不能为空") + private Long operatorPersonId; + + /** + * 操作人 ouId + */ + @NotNull(message = "操作人 ouId 不能为空") + private Long operatorOuId; + + /** + * 操作人 workspaceId + */ + @NotNull(message = "操作人 workspaceId 不能为空") + private Long operatorWorkspaceId; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeStatusRequest.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeStatusRequest.java new file mode 100644 index 00000000..674ee59d --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ChangeStatusRequest.java @@ -0,0 +1,97 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.List; +import java.util.Objects; + +/** + * @author xudawei + * @date 2025/01/16 + * @desc 变签Id获取变签日志 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ChangeStatusRequest { + + /** + * 变签Id + */ + @NotNull(message = "变签Id不能为空") + private Long visaId; + /** + * 更新成状态 + */ + @NotNull(message = "更新成状态不能为空") + private VisaStatusEnum updateStatus; + /** + * 操作人Id + */ + @NotNull(message = "操作人Id不能为空") + private Long personId; + /** + * 操作人单位Id + */ + @NotNull(message = "操作人单位Id不能为空") + private Long ouId; + /** + * 操作人项目Id + */ + @NotNull(message = "操作人项目Id不能为空") + private Long workspaceId; + + /** + * 日志类型, + * 审批不通过(已驳回、已撤回、已中止) 才需要传这个信息 + * 1 REVERT_APPROVE("revert_approve", "审批流:撤回审批","审批流程撤回", "%s撤回审批流程"), + * 2 REJECT_APPROVE("reject_approve", "审批流:已驳回","%s驳回审批", "%s已驳回,原因为%s"), + * 3 FALLBACK_APPROVE("fallback_approve", "审批流操作:回退","%s回退审批", "%s回退审批至审批节点:%s"), + */ + private VisaLogTypeEnum logType; + + /** + * 新的金额,只有在[决策中|执行中] -> [审批中],编辑表单是才用,操作日志记录金额变更 + */ + private BigDecimal newAmountChange; + + /** + * 原金额 + */ + private BigDecimal oldAmountChange; + + /** + * 新的附件 + */ + private List newAttach; + /** + * 原的附件 + */ + private List oldAttach; + + /** + * 是否编辑表单 + * true:编辑表单 + * false:非编辑表单 + */ + private boolean editFormFlag = true; + + + public void check() { + if (Objects.nonNull(logType) && !VisaLogTypeEnum.isApproveNotAgree(logType)) { + throw new ServiceException("不是审核不通过日志"); + } + } + + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/CheckVisaWithVisaIdReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/CheckVisaWithVisaIdReq.java new file mode 100644 index 00000000..536d2d59 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/CheckVisaWithVisaIdReq.java @@ -0,0 +1,61 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.apache.commons.collections4.CollectionUtils; + +/** + * @author xudawei@axzo.cn + * @version 1.0 + * @date 2025/1/16 11:53 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class CheckVisaWithVisaIdReq { + + /** + * 变更单号Id,编辑和详情时传入,与{@code creatFormData}互斥使用 + */ +// @NotNull(message = "变更签证单据Id不能为空") + private Long visaId; + + /** + * 创建单据状态下的表单数据,创建时传入,与{@code visaId}互斥使用 + */ + private VisaChangeTempCreateReq creatFormData; + + /** + * 校验 + */ + public void check(VisaDetailByIdResponse response) { + AssertUtil.notNull(response.getRelationWorkspaceId(), "关联项目不能为空"); + AssertUtil.notNull(response.getType(), "单据类型不能为空"); + AssertUtil.notEmpty(response.getNo(), "单号不能为空"); + AssertUtil.notEmpty(response.getTopic(), "主题不能为空"); + AssertUtil.notNull(response.getRelationProject(), "关联工程不能为空"); + AssertUtil.notEmpty(response.getHappenTime(), "提出时间不能为空"); + AssertUtil.notEmpty(response.getReason(), "发生原因不能为空"); +// AssertUtil.notEmpty(response.getRelationProfessionalList(), "专业不能为空"); + if (CollectionUtils.isNotEmpty(response.getRelationOuAndPersonList())) { + response.getRelationOuAndPersonList().forEach(item -> { + AssertUtil.notEmpty(item.getRelationPersonList(), "确认人不能为空"); + }); + } + } + + public void check() { + AssertUtil.notNull(this.getCreatFormData().getRelationWorkspaceId(), "关联项目不能为空"); + AssertUtil.notNull(this.getCreatFormData().getType(), "单据类型不能为空"); + AssertUtil.notEmpty(this.getCreatFormData().getNo(), "单号不能为空"); + AssertUtil.notEmpty(this.getCreatFormData().getTopic(), "主题不能为空"); + AssertUtil.notNull(this.getCreatFormData().getRelationProject(), "关联工程不能为空"); + AssertUtil.notNull(this.getCreatFormData().getHappenTime(), "提出时间不能为空"); + AssertUtil.notEmpty(this.getCreatFormData().getReason(), "发生原因不能为空"); + } +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/FetchVisaAllConfirmReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/FetchVisaAllConfirmReq.java new file mode 100644 index 00000000..bc7962e3 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/FetchVisaAllConfirmReq.java @@ -0,0 +1,27 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * 拉取指定已存在变洽签的确认人全部列表 + * + * @author wangli + * @since 2025-02-06 13:56 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class FetchVisaAllConfirmReq { + /** + * 变洽签 ID + */ + @NotNull(message = "变洽签 ID 不能为空") + private Long visaId; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/FetchVisaLogByVisaIdRequest.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/FetchVisaLogByVisaIdRequest.java new file mode 100644 index 00000000..f4176ea8 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/FetchVisaLogByVisaIdRequest.java @@ -0,0 +1,26 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author xudawei + * @date 2025/01/16 + * @desc 变签Id获取变签日志 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class FetchVisaLogByVisaIdRequest { + + /** + * 变签Id + */ + @NotNull(message = "变签Id不能为空") + private Long visaId; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/GetBillIfRelatedReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/GetBillIfRelatedReq.java new file mode 100644 index 00000000..ad5ba61d --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/GetBillIfRelatedReq.java @@ -0,0 +1,49 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/2/18 11:09 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetBillIfRelatedReq { + + /** + * 主单据类型 + * + * @see VisaTypeEnum + */ + @NotNull(message = "type不能为空") + private VisaTypeEnum visaType; + + /** + * 子单据类型 + * + * @see VisaBillTypeEnum + */ + @NotNull(message = "billType不能为空") + private VisaBillTypeEnum billType; + + /** + * 子单据id + *

+ * {@code billType}为{@link VisaBillTypeEnum#TASK}时,为任务单号
+ * {@code billType}为{@link VisaBillTypeEnum#RECTIFY}时,为整改单id
+ * {@code billType}为{@link VisaBillTypeEnum#DESIGN_VISA}时,为设计变更单id} + *

+ */ + private List billIds; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupOperationClearReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupOperationClearReq.java new file mode 100644 index 00000000..16c53bc0 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupOperationClearReq.java @@ -0,0 +1,24 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * IM 群成员投票操作的清除 + * + * @author wangli + * @since 2025-02-07 15:39 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class ImGroupOperationClearReq extends ImGroupOperationReq{ + + private Boolean clearAll; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupOperationReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupOperationReq.java new file mode 100644 index 00000000..53075e8a --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupOperationReq.java @@ -0,0 +1,56 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.NotNull; + +/** + * Im 群聊中的操作 + * + * @author wangli + * @since 2025-02-05 17:30 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class ImGroupOperationReq { + /** + * 变洽签 ID + */ + @NotNull(message = "变洽签 ID 不能为空") + private Long visaId; + + /** + * IM 群 ID + */ + @NotNull(message = "IM 群 ID 不能为空") + private Long imGroupId; + + /** + * 操作类型 + */ + @NotNull(message = "按钮类型不能为空") + private VisaButtonTypeEnum buttonType; + /** + * 操作人 OuId + */ + @NotNull(message = "操作人 OuId 不能为空") + private Long operatorOuId; + + /** + * 操作人workspaceId + */ + @NotNull(message = "操作人 WorkspaceId 不能为空") + private Long operatorWorkspaceId; + + /** + * 操作人peronId + */ + @NotNull(message = "操作人 PersonId 不能为空") + private Long operatorPersonId; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupTipsQueryReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupTipsQueryReq.java new file mode 100644 index 00000000..468da89d --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/ImGroupTipsQueryReq.java @@ -0,0 +1,60 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * 用于查询 IM 群聊的中每个人的上方提示横条 + * + * @author wangli + * @since 2025-02-05 16:56 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ImGroupTipsQueryReq { + /** + * IM 群的额外扩展信息,变洽签的 ID + */ + @NotNull(message = "变洽签 ID 不能为空") + private Long visaId; + + /** + * IM 群 ID + */ + @NotNull(message = "IM 群 ID 不能为空") + private Long groupId; + + /** + * 当前登录人是否是群主 + */ + @NotNull(message = "群主标识不能为空") + private Boolean isGroupOwner; + + /** + * 当前群成员总数, 包含群主 + */ + private Integer groupTotalPersonCount; + + /** + * 群成员的 PersonId + */ + @NotNull(message = "群成员的 PersonId 不能为空") + private Long personId; + + /** + * 群成员的 OuId + */ + @NotNull(message = "群成员的 OuId 不能为空") + private Long ouId; + + /** + * 群成员的 workspaceId + */ + private Long workspaceId; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeApproveCreateReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeApproveCreateReq.java new file mode 100644 index 00000000..e98a5b44 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeApproveCreateReq.java @@ -0,0 +1,80 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/16 13:33 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class VisaChangeApproveCreateReq extends VisaChangeTempCreateReq { + + /** + * 需要设置审批人的节点标识 + */ + @NotBlank(message = "待设置审批人的节点为空") + private String activityId; + /** + * 审批人信息 + */ + private List approvePersonInfoList; + + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ApprovePersonInfo { + /** + * 单位类型 1:施工单位 2:建设单位 3:监理单位 4:劳务分包 5:专业分包 9:项目内班组 13:设计单位 + * + * @see cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum + */ + private SaasCooperateShipCooperateTypeEnum type; + + /** + * 项目id + */ + private Long workspaceId; + + /** + * 单位id + */ + private Long ouId; + + /** + * 节点 Id + */ + private Long nodeId; + /** + * 审批人personId列表 + */ + private Long personId; + + /** + * (主要用于回显)审批人的姓名 + */ + private String realName; + + /** + * (主要用于回显)审批人头像 + */ + private String avatarUrl; + + } + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeApproveOnlyReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeApproveOnlyReq.java new file mode 100644 index 00000000..f3118b88 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeApproveOnlyReq.java @@ -0,0 +1,47 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 通过 IM 群聊中发起的审批入参模型 + * + * @author wangli + * @since 2025-02-06 13:59 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaChangeApproveOnlyReq { + + /** + * 变洽签 ID + */ + @NotNull(message = "变洽签 ID 不能为空") + private Long visaId; + + /** + * 需要设置审批人的节点标识 + */ + @NotBlank(message = "待设置审批人的节点为空") + private String activityId; + + /** + * 审批人信息 + */ + private List approvePersonInfoList; + + private Long operatorPersonId; + + private Long operatorOuId; + + private Long operatorWorkspaceId; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeDiscussCreateReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeDiscussCreateReq.java new file mode 100644 index 00000000..6b9399b9 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeDiscussCreateReq.java @@ -0,0 +1,61 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/16 11:55 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@SuperBuilder +@NoArgsConstructor +public class VisaChangeDiscussCreateReq extends VisaChangeTempCreateReq { + + /** + * 群聊人员信息 + */ + private List discussPersonInfoList; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class DiscussPersonInfo { + + /** + * 单位类型 1:施工单位 2:建设单位 3:监理单位 4:劳务分包 5:专业分包 9:项目内班组 13:设计单位 + * + * @see cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum + */ + private Integer type; + + /** + * 项目id + */ + private Long workspaceId; + + /** + * 单位id + */ + private Long unitId; + + /** + * 群聊成员personId列表 + */ + private Long personId; + + /** + * 是否群主(即发起人) + */ + private Boolean isAdmin; + } +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeExportReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeExportReq.java new file mode 100644 index 00000000..2460db94 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeExportReq.java @@ -0,0 +1,39 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.nanopart.visa.api.enums.VisaChangeFieldEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/16 17:38 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaChangeExportReq { + + /** + * 筛选条件 + */ + private VisaChangePageSearchReq filter; + + /** + * 导出字段 + */ + private List fields; + + /*操作人信息*/ + private Long personId; + private Long identityId; + private Integer identityType; + private Long workspaceId; + private Long ouId; + private String terminal; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangePageSearchReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangePageSearchReq.java new file mode 100644 index 00000000..299c4e05 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangePageSearchReq.java @@ -0,0 +1,159 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/16 15:46 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaChangePageSearchReq { + + /** + * 项目id + */ + @NotNull(message = "项目id不能为空") + @Min(value = 1, message = "项目id不合法") + private Long workspaceId; + + /** + * 单据类型 + *

+ * DESIGN_CHANGE:设计变更 + * TECHNOLOGY_APPROVED:技术核定 + * PROJECT_VISA:工程签证 + *

+ * + * @see VisaTypeEnum + */ + private VisaTypeEnum type; + + /** + * 单号或主题 + *

+ * 单号精确搜索,主题模糊搜索 + *

+ */ + private String keyword; + + /** + * 所属工程 + */ + private Long projectId; + + /** + * 发起人 + */ + private List initiators; + + /** + * 状态 + */ + private List statuses; + + /** + * 变更金额区间 + *

+ * 0:最小值 1:最大值 + * 当最小最大值均为正值表示增加,均为负值表示减少 + * 元素均为0表示金额不变 + *

+ */ + private List amountChange; + + /** + * 提出时间区间 + * 0:起始时间 1:结束时间 + */ + private List happenTime; + + /** + * 审批完成时间区间 + * 0:起始时间 1:结束时间 + */ + private List approveCompletedDate; + + private Long page; + + private Long pageSize; + /** + * 当前登录人的personId + */ + private Long currentPersonId; + /** + * 当前登录人的项目Id + */ + private Long currentWorkspaceId; + /** + * 当前登录人的单位Id + */ + private Long currentOuId; + + /** + * 变更签证Id集合 + */ + private Collection visaIds; + + /** + * 变更签证Id集合-权限 + */ + private Collection permissionVisaIds; + + /** + * 是否需要主单据(即是否被其他单据关联)信息 + */ + private Boolean needRelatedBill = false; + /** + * 变更签证Id + */ + private Long visaId; + + /** + * 调用场景 + *

+ * PAGE: 分页列表页面调用 + * SELECT: 选择器页面调用 + *

+ */ + private FromEnum from; + + /** + * 主单据类型 + *

+ * 仅当{@code from}为{@link FromEnum#SELECT}且{@code needRelatedBill}为{@code true}时,该字段才有效且不能为空 + *

+ */ + private VisaTypeEnum visaType; + + public static enum FromEnum { + PAGE("分页列表页面调用"), + SELECT("选择器页面调用"); + + private final String desc; + + FromEnum(String desc) { + this.desc = desc; + } + + public String getDesc() { + return desc; + } + } + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeTempCreateReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeTempCreateReq.java new file mode 100644 index 00000000..090c2871 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeTempCreateReq.java @@ -0,0 +1,258 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import com.alibaba.fastjson.JSONArray; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/16 11:53 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class VisaChangeTempCreateReq { + + + /** + * 变更单号Id + */ + private Long id; + + /** + * 单号 + */ + @NotBlank(message = "请输入单号") + @Size(max = 100, message = "单号长度不能超过100个字符") + private String no; + + /** + * 主题 + */ + @NotBlank(message = "请输入主题") + @Size(max = 100, message = "主题长度不能超过100个字符") + private String topic; + + /** + * 项目 + */ + @NotNull(message = "关联项目不能为空") + @Min(value = 1, message = "关联项目不能为空") + private Long relationWorkspaceId; + + /** + * 工程 + */ + @NotNull(message = "请选择工程") + @Min(value = 1, message = "请选择工程") + private Long relationProject; + /** + * 工程名称 + */ + @NotBlank(message = "工程名称不能为空") + private String relationProjectName; + + /** + * 区域集合 + * 示例: + *
+     * [
+     *     {"areaId": 249556},
+     *     {"areaId": 249555}
+     * ]
+     *  
+ */ + private JSONArray relationArea; + + /** + * 提出时间 + */ + @NotNull(message = "请选择提出时间") + private Date happenTime; + + /** + * 专业集合 + * 示例: + *
+     * [
+     *     {"code":"code1"},
+     *     {"code":"code2"},
+     * ]
+     * 
+ */ +// @NotNull(message = "请选择专业") + private JSONArray relationProfessional; + + /** + * 发生原因 + */ + @NotBlank(message = "请填写发生原因") + @Size(max = 500, message = "发生原因长度不能超过500个字符") + private String reason; + + /** + * 单据类型 + */ + @NotNull(message = "请选择变更签证类型") + private VisaTypeEnum type; + + /** + * 相关单位及人员 + */ + private List relationUnitAndPersonList; + + /** + * 发生内容说明 + */ + private List changeContextAndDescriptionList; + + /** + * 有符号的变更金额 + */ + private BigDecimal amountChange; + + /** + * 关联的单据,key: 单据类型,value: 单据id集合 + *

+ * TASK :任务单 RECTIFY :整改单 DESIGN_VISA:变更单 + * 只有变更签证类型即{@code type}为“技术核定”或“工程签证”时才可能会有关联变更单 + *

+ * + * @see VisaBillTypeEnum + */ + private Map> relationOrderMap; + + /** + * 附件,json列表 + *

+ * { + * "fileUrl": "http://www.baidu.com/img1.jpeg", + * "fileKey": "jskjksjgnaqkjkgag", + * "fileName": "附件1.jpg" + * } + *

+ */ + @Size(max = 30, message = "最多支持上传30个附件,已超出请重新选择") + private List attach; + + private Long operatorOuId; + + private Integer operatorOuType; + + private Long operatorNodeId; + + /** + * 操作人peronId + */ + private Long operatorPersonId; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class OrderSimpleModel { + /** + * 单据主键 ID + */ + private Long id; + + /** + * 单据编号 + */ + private String no; + + /** + * 单据名称 + */ + private String name; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationUnitAndPerson { + /** + * 单位类型 1:施工单位 2:建设单位 3:监理单位 4:劳务分包 5:专业分包 9:项目内班组 13:设计单位 + * + * @see cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum + */ + private SaasCooperateShipCooperateTypeEnum type; + + /** + * 单位 ID + */ + private Long ouId; + + /** + * 确认人所在部门节点id或项目内班组节点id + */ + private Long nodeId; + + /** + * 确认人personId + */ + private Long personId; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ChangeContextAndDescription { + /** + * 内容类型,1:图片 2:图纸批注 + */ + private Integer type; + + /** + * 图片或图纸url + */ + private String fileUrl; + + /** + * 图片或图纸key + */ + private String fileKey; + + /** + * 图片或图纸名称 + */ + private String fileName; + + /** + * 图纸批注id,{@code type}为2时使用 + */ + private Long annotationId; + + /** + * 图纸图框id,{@code type}为2时使用 + */ + private Long borderId; + + /** + * 内容说明 + */ + private String text; + + } +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaDetailByIdRequest.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaDetailByIdRequest.java new file mode 100644 index 00000000..169f0e0a --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaDetailByIdRequest.java @@ -0,0 +1,44 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author xudawei + * @date 2025/01/16 + * @desc 变签Id获取变签日志 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class VisaDetailByIdRequest { + + /** + * 变签Id + */ + @NotNull(message = "变签Id不能为空") + private Long visaId; + + /** + * 人员Id + */ + private Long personId; + + private Long workspaceId; + + private Long ouId; + + private Long projectId; + + /** + * 是否需要btnList + * true:需要, 则personId/ouId/workspaceId 必填,需要查询确认人信息 + * false:不需要 + */ + private boolean needBtnList = true; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaInitiatorListReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaInitiatorListReq.java new file mode 100644 index 00000000..4a8d1686 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaInitiatorListReq.java @@ -0,0 +1,36 @@ +package cn.axzo.nanopart.visa.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/22 9:41 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaInitiatorListReq { + + @NotNull(message = "workspaceId不能为空") + private Long workspaceId; + + /** + * 当前登录人的personId + */ + private Long currentPersonId; + /** + * 当前登录人的项目Id + */ + private Long currentWorkspaceId; + /** + * 当前登录人的单位Id + */ + private Long currentOuId; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaRelationReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaRelationReq.java new file mode 100644 index 00000000..ea9a5c45 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaRelationReq.java @@ -0,0 +1,36 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotNull; + +/** + * 变洽签关联数据查询入参模型 + * + * @author wangli + * @since 2025-01-16 10:46 + */ +@Data +@Accessors(chain = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaRelationReq { + + /** + * 变洽签单据 ID + */ + @NotNull(message = "变洽签单据 ID不能为空") + private Long visaId; + + /** + * 关联表中指定类型 + * 可为空,则查询所有关联的数据 + */ + private VisaRelationFieldEnum fieldEnum; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaSearchReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaSearchReq.java new file mode 100644 index 00000000..715aad60 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaSearchReq.java @@ -0,0 +1,81 @@ +package cn.axzo.nanopart.visa.api.request; + +import cn.axzo.core.domain.PageRequest; +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.util.StringUtils; + +import java.util.Objects; + +/** + * @author xudawei + * @date 2025/01/16 + * @desc 变签Id获取变签日志 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class VisaSearchReq extends PageRequest { + + + /** + * 变签Id + */ + private Long visaId; + /** + * 项目Id + */ + private Long workspaceId; + + /** + * 变签编号 + */ + private String visaNo; + + /** + * 单据类型,任务单:TASK;整改单:RECTIFY;变更单:VISA + */ + private VisaBillTypeEnum billType; + + /** + * 单据Id + */ + private Long billId; + + /** + * 单据编号 + */ + private String billNo; + + /** + * 校验 + */ + public void check() { + if (Objects.isNull(this.getBillId()) + && Objects.isNull(this.getBillNo()) + && Objects.isNull(this.getBillType()) + && Objects.isNull(this.getVisaId()) + && !StringUtils.hasText(this.getVisaNo())) { + throw new ServiceException("参数不合法"); + } + repairPage(); + } + + /** + * 修复page + */ + public void repairPage() { + if (Objects.isNull(this.page)) { + this.setPage(1); + } + if (Objects.isNull(this.pageSize)) { + this.setPageSize(20); + } + } + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/FetchVisaLogByVisaIdResponse.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/FetchVisaLogByVisaIdResponse.java new file mode 100644 index 00000000..5bbcf828 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/FetchVisaLogByVisaIdResponse.java @@ -0,0 +1,36 @@ +package cn.axzo.nanopart.visa.api.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * @author xudawei + * @date 2025/01/16 + * @desc 变签Id获取变签日志 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class FetchVisaLogByVisaIdResponse { + + /** + * 操作标题 + */ + private String title; + + /** + * 操作内容 + */ + private String content; + + /** + * 创建时间 + */ + private Date createAt; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupButton.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupButton.java new file mode 100644 index 00000000..dc13b2f8 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupButton.java @@ -0,0 +1,29 @@ +package cn.axzo.nanopart.visa.api.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * IM 群聊中的按钮 + * + * @author wangli + * @since 2025-02-05 16:47 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ImGroupButton { + /** + * 按钮文案 + */ + private String name; + + /** + * 按钮类型 + */ + private String type; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupTipsResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupTipsResp.java new file mode 100644 index 00000000..3dfb0181 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupTipsResp.java @@ -0,0 +1,36 @@ +package cn.axzo.nanopart.visa.api.response; + +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * IM 群聊上方横条 + * + * @author wangli + * @since 2025-02-05 16:37 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ImGroupTipsResp { + + private VisaStatusEnum status; + + private String statusText; + + private String tipsText; + + private List buttonList; + + private VisaTypeEnum visaType; + + private String visaTypeText; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeDiscussCreateResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeDiscussCreateResp.java new file mode 100644 index 00000000..bb0f3633 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeDiscussCreateResp.java @@ -0,0 +1,33 @@ +package cn.axzo.nanopart.visa.api.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/2/10 14:17 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaChangeDiscussCreateResp { + + /** + * 变洽签单据id + */ + private Long visaId; + + /** + * 群聊id + */ + private Long imGroupId; + + /** + * 群聊名称 + */ + private String imGroupName; +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeInitiatorResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeInitiatorResp.java new file mode 100644 index 00000000..eafeefc5 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeInitiatorResp.java @@ -0,0 +1,64 @@ +package cn.axzo.nanopart.visa.api.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/16 18:43 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaChangeInitiatorResp { + + /** + * 发起人personId + */ + private Long personId; + + /** + * 发起人姓名 + */ + private String name; + + /** + * 发起人头像 + */ + private String avatar; + + /** + * 岗位id + */ + private Long jobId; + + /** + * 岗位名称 + */ + private String jobName; + + /** + * 岗位编码 + */ + private String jobCode; + + /** + * 单位id + */ + private Long ouId; + + /** + * 所属单位节点id或班组节点id + */ + private Long nodeId; + + /** + * 所属单位名称或班组名称 + */ + private String unitOrTeamName; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangePageSearchResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangePageSearchResp.java new file mode 100644 index 00000000..1ea06939 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangePageSearchResp.java @@ -0,0 +1,155 @@ +package cn.axzo.nanopart.visa.api.response; + +import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.Objects; +import java.util.Set; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/16 16:29 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaChangePageSearchResp { + + private Long id; + + /** + * 单号 + */ + private String no; + + /** + * 主题 + */ + private String topic; + + /** + * 项目 + */ + private Long relationWorkspaceId; + + /** + * 工程 + */ + private Long relationProject; + + /** + * 工程名称 + */ + private String relationProjectName; + + /** + * 提出时间 + */ + private Date happenTime; + + + /** + * 最终审批时间 + */ + private Date approvalCompleteTime; + + /** + * 单据类型 + *

+ * DESIGN_CHANGE: 设计变更 + * TECHNOLOGY_APPROVED: 技术核定 + * PROJECT_VISA: 工程签证 + *

+ * + * @see VisaTypeEnum + */ + private VisaTypeEnum type; + + /** + * 单据类型描述 + */ + private String typeDesc; + + /** + * 状态 + *

+ * TO_REPORT: 待提报 + * DECIDING: 决策中 + * EXECUTING: 执行中 + * APPROVING: 审批中 + * COMPLETED: 已完成 + * FORBIDED: 已废除 + *

+ * + * @see VisaStatusEnum + */ + private VisaStatusEnum status; + + /** + * 状态描述 + */ + private String statusDesc; + + /** + * 有符号的变更金额 + */ + private BigDecimal amountChange; + + + /** + * 发起人 + */ + private VisaChangeInitiatorResp initiator; + + /** + * 发生原因 + */ + private String reason; + /** + * 是否被其他变更签证单单据关联 + */ + private Boolean isRelated; + + /** + * 操作按钮 + *

+ * UPDATE: 编辑 + * DETAIL: 详情 + * UPLOAD_FILE: 上传附件 + * APPROVE_RECORD: 查看审批记录 + *

+ * + * @see VisaButtonTypeEnum + */ + private Set operateBtnSet; + + public String getTypeDesc() { + if (Objects.isNull(type)) { + return null; + } + return type.getDesc(); + } + + public String getStatusDesc() { + if (Objects.isNull(status)) { + return null; + } + if (VisaStatusEnum.REPORT_FROM_APPROVE.equals(status)) { + return VisaStatusEnum.TO_REPORT.getDesc(); + } + if (VisaStatusEnum.DECIDING_FROM_APPROVE.equals(status)) { + return VisaStatusEnum.DECIDING.getDesc(); + } + return status.getDesc(); + } + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaDetailByIdResponse.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaDetailByIdResponse.java new file mode 100644 index 00000000..d05a1f43 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaDetailByIdResponse.java @@ -0,0 +1,613 @@ +package cn.axzo.nanopart.visa.api.response; + +import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author xudawei + * @date 2025/01/16 + * @desc 变签Id获取变签日志 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class VisaDetailByIdResponse { + + /** + * 变签Id + */ + private Long visaId; + + + /** + * 单号 + */ + private String no; + + /** + * 单据类型 + */ + private VisaTypeEnum type; + + /** + * 发生原因 + */ + private String reason; + + /** + * 发起人姓名 + */ + private String createName; + + /** + * 发起人头像 + */ + private String createAvatarUrl; + + /** + * 发起人岗位名称 + */ + private String createJobName; + + /** + * 发起人岗位code + */ + private String createJobCode; + + /** + * 发起人单位名称 + */ + private String createOuName; + + /** + * 发起人单位Id + */ + private Long createOuId; + + /** + * 发起人Id + */ + private Long createBy; + + /** + * 所属工程 + */ + private Long relationProject; + + /** + * 所属工程名称 + */ + private String relationProjectName; + + /** + * 主题 + */ + private String topic; + + /** + * 项目 + */ + private Long relationWorkspaceId; + + /** + * 项目名称 + */ + private String relationWorkspaceName; + + /** + * 区域集合 + */ + private List relationAreaList; + + /** + * 区域集合名称 + */ + private String relationAreaName; + + /** + * 提出时间 + */ + private String happenTime; + + + /** + * 最终审批时间 + */ + private String approvalCompleteTime; + + /** + * 专业集合 + */ + private List relationProfessionalList; + + /** + * 专业集合名称 + */ + private String relationProfessionalName; + + /** + * 相关单位及人员 + */ + private List relationOuAndPersonList; + + /** + * 发生内容说明 + */ + private List changeContextAndDescriptionList; + + /** + * 有符号的变更金额 + */ + private String amountChange; + + /** + * 附件 + */ + private List attach; + + /** + * 按钮 + */ + private List btnList; + + + /** + * 任务单 + */ + private List relationTaskList; + + /** + * 整改单 + */ + private List relationRectifyList; + + /** + * 变更单 + */ + private List relationVisaList; + + /** + * 关联无效的变更单 + */ + private List relationInvalidVisaList; + + /** + * 状态 + */ + private VisaStatusEnum status; + + /** + * 群组ID + */ + private Long imGroupId; + + /** + * 最终审批ID + */ + private String approvalId; + + /** + * 最终审批状态 + */ + private String approvalStatus; + + /** + * 当前登录人的personId + */ + private Long currentPersonId; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationOuAndPerson { + /** + * 相关单位 + */ + private RelationUnit relationOu; + + /** + * 相关确认人 + */ + private List relationPersonList; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationUnit { + /** + * 类型 + */ + private String type; + + /** + * 单位id + */ + private Long ouId; + + /** + * 单位名称 + */ + private String ouName; + /** + * 节点Id + */ + private Long nodeId; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationPerson { + /** + * 类型 + */ + private SaasCooperateShipCooperateTypeEnum type; + + /** + * 单位id + */ + private Long ouId; + + /** + * 确认人名称 + */ + private String realName; + + /** + * 头像 + */ + private String avatarUrl; + + /** + * 节点id + */ + private Long nodeId; + + /** + * 确认人personId + */ + private Long personId; + + /** + * 项目Id + */ + private Long workspaceId; + + /** + * 工程Id + */ + private Long projectId; + + /** + * 岗位Id + */ + private Long jobId; + /** + * 岗位名称 + */ + private String jobName; + } + + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ChangeContextAndDescription { + /** + * 内容类型,1:图片 2:图纸批注 + */ + private Integer Type; + + /** + * 图片或图纸 + */ + private String imgUrl; + + /** + * 内容说明 + */ + private String text; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationProfessional { + + /** + * code + */ + private String code; + } + + + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationProfessionalDetail { + /** + * code + */ + private String code; + + /** + * 构建专业集合-对象集合 + */ + public static List buildProfessionWithObject(JSONArray relationProfessional) { + + if (CollectionUtils.isEmpty(relationProfessional)) { + return Lists.newArrayList(); + } + List professionalList = relationProfessional.toJavaList(VisaDetailByIdResponse.RelationProfessionalDetail.class); + + if (CollectionUtils.isEmpty(professionalList)) { + return Lists.newArrayList(); + } + + List filter = professionalList.stream().filter(item -> Objects.nonNull(item) && StringUtils.isNotBlank(item.getCode())).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(filter)) { + return Lists.newArrayList(); + } + return filter; + } + + /** + * 构建专业集合-code集合 + */ + public static Set buildProfessionWithCode(JSONArray relationProfessional) { + List relationProfessionalDetails = RelationProfessionalDetail.buildProfessionWithObject(relationProfessional); + if (CollectionUtils.isEmpty(relationProfessionalDetails)) { + return Sets.newHashSet(); + } + + return relationProfessionalDetails.stream() + .filter(item -> Objects.nonNull(item) && StringUtils.isNotBlank(item.getCode())) + .map(RelationProfessionalDetail::getCode).collect(Collectors.toSet()); + } + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationAreaDetail { + /** + * id + */ + private Long areaId; + + + public static List buildAreaWithObject(JSONArray relationArea) { + if (CollectionUtils.isEmpty(relationArea)) { + return Lists.newArrayList(); + } + List relationAreaList = relationArea.toJavaList(VisaDetailByIdResponse.RelationAreaDetail.class); + if (CollectionUtils.isEmpty(relationAreaList)) { + return Lists.newArrayList(); + } + List filter = relationAreaList.stream().filter(item -> Objects.nonNull(item) && Objects.nonNull(item.getAreaId())).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(filter)) { + return Lists.newArrayList(); + } + return filter; + } + + public static List buildAreaWithId(JSONArray relationArea) { + List areaDetailList = RelationAreaDetail.buildAreaWithObject(relationArea); + + if (CollectionUtils.isEmpty(areaDetailList)) { + return Lists.newArrayList(); + } + return areaDetailList.stream().map(VisaDetailByIdResponse.RelationAreaDetail::getAreaId).collect(Collectors.toList()); + } + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationRectifyOrder { + + /** + * 整改单id + */ + private Long id; + + /** + * 整改单号 + */ + private String rectifyNo; + + /** + * 名称 + */ + private String name; + + /** + * 开始时间 + */ + private String startTime; + + /** + * 结束时间 + */ + private String endTime; + + /** + * 共计天数 + */ + private String totalDays; + + /** + * 发起人 + */ + private String createName; + /** + * 发起人Id + */ + private Long createBy; + /** + * 发起人岗位名称 + */ + private String createJobName; + /** + * 发起人岗位Id + */ + private Long createJobId; + /** + * 发起人单位名称 + */ + private String createOuName; + /** + * 发起人单位Id + */ + private String createOuId; + + /** + * 责任人 + */ + private String rectifierName; + /** + * 责任人Id + */ + private Long rectifier; + /** + * 责任人岗位名称 + */ + private String rectifierJobName; + /** + * 责任人岗位Id + */ + private Long rectifierJobId; + /** + * 责任人单位名称 + */ + private String rectifierOuName; + /** + * 责任人单位Id + */ + private String rectifierOuId; + + /** + * 销项人 + */ + private String verifierName; + /** + * 销项人Id + */ + private Long verifier; + /** + * 销项人岗位名称 + */ + private String verifierJobName; + /** + * 销项人岗位Id + */ + private Long verifierJobId; + /** + * 销项人单位名称 + */ + private String verifierOuName; + /** + * 销项人单位Id + */ + private String verifierOuId; + + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelationVisaDetail { + /** + * 主题 + */ + private String topic; + + /** + * 单号 + */ + private String no; + /** + * 有符号的变更金额 + */ + private String amountChange; + + /** + * 发起人 + */ + private String createName; + + /** + * 提出时间 + */ + private Date happenTime; + + } + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class VisaUploadFile { + /** + * 文件Url + */ + private String fileUrl; + /** + * 文件key + */ + private String fileKey; + /** + * 文件名称 + */ + private String fileName; + + /** + * 创建集合 + */ + public static List createListByJson(List jsonObjectList) { + if (CollectionUtils.isEmpty(jsonObjectList)) { + return Lists.newArrayList(); + } + List fileDtoList = Lists.newArrayList(); + for(JSONObject json : jsonObjectList) { + fileDtoList.add(json.toJavaObject(VisaUploadFile.class)); + } + return fileDtoList; + } + } +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaRelationResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaRelationResp.java new file mode 100644 index 00000000..4a03db07 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaRelationResp.java @@ -0,0 +1,58 @@ +package cn.axzo.nanopart.visa.api.response; + +import cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 变洽签关联的行数据相应模型 + * + * @author wangli + * @since 2025-01-16 09:50 + */ +@Data +@Accessors(chain = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaRelationResp { + + private Long id; + /** + * 变洽签主单据编号 + */ + private String visaId; + /** + * 变洽签单据关联的行数据类型 + * {@link VisaRelationFieldEnum} + */ + private String varName; + + /** + * 数据类型的扩展内容 + */ + private String varExt; + /** + * 数据值的 java 类型 + */ + private String varType; + /** + * 数据值 + */ + private String content; + /** + * 数据值的一些额外描述 + */ + private String contentExt; + + /** + * 创建时间 + */ + private Date createAt; + +} diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaSearchResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaSearchResp.java new file mode 100644 index 00000000..f4bada46 --- /dev/null +++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaSearchResp.java @@ -0,0 +1,144 @@ +package cn.axzo.nanopart.visa.api.response; + +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.List; + +/** + * @author xudawei@axzo.cn + * @date 2025/2/18 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaSearchResp { + + /** + * 变更单据Id + */ + private Long visaId; + + /** + * 单据号 + */ + private String no; + + /** + * 主题 + */ + private String topic; + + /** + * 项目 + */ + private Long relationWorkspaceId; + + /** + * 工程 + */ + private Long relationProject; + + /** + * 区域集合 + * 示例: + *
+     * [
+     *     {"areaId": 249556},
+     *     {"areaId": 249555}
+     * ]
+     *  
+ */ + private List relationArea; + + /** + * 专业集合 + * 示例: + *
+     * [
+     *     {"code":"code1"},
+     *     {"code":"code2"},
+     * ]
+     * 
+ */ + private List relationProfessional; + + /** + * 提出时间 + */ + private Date happenTime; + + /** + * 有符号的变更金额 + */ + private String amountChange; + + /** + * 发生原因 + */ + private String reason; + + /** + * 单据类型 + */ + private VisaTypeEnum type; + + /** + * 状态 + */ + private VisaStatusEnum status; + + /** + * 最终审批ID + */ + private String approvalId; + + /** + * 最终审批状态 + */ + private String approvalStatus; + + /** + * 最终审批时间 + */ + private Date approvalCompleteTime; + + /** + * IM群组ID + */ + private Long imGroupId; + + /** + * 额外信息 + */ + private JSONObject extra; + + /** + * 创建人id + */ + private Long createBy; + + /** + * 修改人id + */ + private Long updateBy; + + /** + * 创建时间 + */ + protected Date createAt; + + /** + * 修改时间 + */ + protected Date updateAt; + + +} diff --git a/visa/visa-api/src/main/resources/META-INF/spring.factories b/visa/visa-api/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..c3000f13 --- /dev/null +++ b/visa/visa-api/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +cn.axzo.nanopart.visa.api.config.VisaApiAutoConfiguration \ No newline at end of file diff --git a/visa/visa-server/pom.xml b/visa/visa-server/pom.xml new file mode 100644 index 00000000..b27ba208 --- /dev/null +++ b/visa/visa-server/pom.xml @@ -0,0 +1,132 @@ + + + + cn.axzo.nanopart + visa + ${revision} + ../pom.xml + + 4.0.0 + + visa-server + jar + + visa-server + + + + cn.axzo.framework + axzo-web-spring-boot-starter + + + cn.axzo.framework + axzo-spring-cloud-starter + + + cn.axzo.framework + axzo-consumer-spring-cloud-starter + + + cn.axzo.framework + axzo-processor-spring-boot-starter + + + + cn.axzo.framework + axzo-mybatisplus-spring-boot-starter + + + + cn.axzo.framework + axzo-swagger-yapi-spring-boot-starter + + + mysql + mysql-connector-java + + + cn.hutool + hutool-all + + + cn.axzo.framework + axzo-logger-spring-boot-starter + + + cn.axzo.framework.rocketmq + axzo-common-rocketmq + + + cn.axzo.pokonyan + pokonyan + + + cn.axzo.workflow + workflow-engine-spring-boot-starter + + + cn.axzo.maokai + maokai-api + + + cn.axzo.nanopart + visa-api + ${project.version} + + + cn.axzo.basics + basics-profiles-api + + + cn.axzo.maokai + maokai-common + ${project.version} + + + cn.axzo + thor-api + 1.0.0-SNAPSHOT + + + cn.axzo.apollo + apollo-api + 2.0.0-SNAPSHOT + + + cn.axzo.basics + basics-domainless-api + 2.0.0-SNAPSHOT + + + cn.axzo.org + org-api + 1.0.0-SNAPSHOT + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + cn.axzo.karma + karma-api + 2.0.0-SNAPSHOT + + + + cn.axzo.platform + axzo-log-api + 1.0.0-SNAPSHOT + + + + cn.axzo.digital + digital-api + 1.0.0-SNAPSHOT + + + + + diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/config/RefreshableConfiguration.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/config/RefreshableConfiguration.java new file mode 100644 index 00000000..e4bf9023 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/config/RefreshableConfiguration.java @@ -0,0 +1,53 @@ +package cn.axzo.nanopart.visa.server.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +/** + * 可 nacos 动态更新的对象 + * + * @author wangli + * @since 2025-02-07 16:03 + */ +@Data +@Component +@RefreshScope +public class RefreshableConfiguration { + + /** + * IM 群创建成功后发送卡片模板 + */ + @Value("${visa.im.card:261c7fa3db344fb69f3553f695a5a19c}") + private String imGroupCardTemplateCode; + /** + * 变更签证洽商通知 + * IM 群添加成员,给新成员发送的通知 + */ + @Value("${visa.im.notice.addMember:2a4fe042020f4dd783b73fdbb6e6ac98}") + private String imGroupAddMemberNoticeTemplateCode; + /** + * 配合上面的通知发送使用 + */ + @Value("${visa.im.event.addMember:visaNegotiation}") + private String imGroupAddMemberNoticeEventCode; + + /** + * 相关单位确认人人数限制 + */ + @Value("${visa.verifyPersonLimit: 8}") + private Integer verifyPersonLimit; + + /** + * 关联变更单个数限制 + */ + @Value("${visa.verifyVisaLimit: 5}") + private Integer verifyVisaLimit; + + /** + * 发生内容及说明数量限制 + */ + @Value("${visa.verifyChangeContextLimit: 50}") + private Integer verifyChangeContextLimit; +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordController.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordController.java new file mode 100644 index 00000000..a071f049 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordController.java @@ -0,0 +1,184 @@ +package cn.axzo.nanopart.visa.server.controller; + +import cn.axzo.framework.domain.web.result.ApiPageResult; +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.framework.domain.web.result.PageData; +import cn.axzo.nanopart.visa.api.changerecord.ChangeRecordApi; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.request.ChangeRecordButtonOperationReq; +import cn.axzo.nanopart.visa.api.request.ChangeStatusRequest; +import cn.axzo.nanopart.visa.api.request.CheckVisaWithVisaIdReq; +import cn.axzo.nanopart.visa.api.request.FetchVisaAllConfirmReq; +import cn.axzo.nanopart.visa.api.request.GetBillIfRelatedReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveOnlyReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeDiscussCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeExportReq; +import cn.axzo.nanopart.visa.api.request.VisaChangePageSearchReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaDetailByIdRequest; +import cn.axzo.nanopart.visa.api.request.VisaInitiatorListReq; +import cn.axzo.nanopart.visa.api.request.VisaSearchReq; +import cn.axzo.nanopart.visa.api.response.VisaChangeDiscussCreateResp; +import cn.axzo.nanopart.visa.api.response.VisaChangeInitiatorResp; +import cn.axzo.nanopart.visa.api.response.VisaChangePageSearchResp; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import cn.axzo.nanopart.visa.api.response.VisaSearchResp; +import cn.axzo.nanopart.visa.server.service.ChangeRecordBillService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordConfirmService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class ChangeRecordController implements ChangeRecordApi { + + @Autowired + private ChangeRecordService changeRecordService; + + @Autowired + private ChangeRecordConfirmService changeRecordConfirmService; + + @Autowired + private ChangeRecordBillService changeRecordBillService; + + @Override + public ApiResult tempCreateVisaChangeRecord(VisaChangeTempCreateReq req) { + return ApiResult.ok(changeRecordService.tempCreateVisaChangeRecord(req)); + } + + @Override + public ApiResult checkVisaWithVisaId(CheckVisaWithVisaIdReq req) { + changeRecordService.checkVisaWithVisaId(req); + return ApiResult.ok(); + } + + @Override + public ApiResult discussCreateVisaChangeRecord(VisaChangeDiscussCreateReq req) { + return ApiResult.ok(changeRecordService.discussCreateVisaChangeRecord(req)); + } + + @Override + public ApiResult approveCreateVisaChangeRecord(VisaChangeApproveCreateReq req) { + return ApiResult.ok(changeRecordService.approveCreateVisaChangeRecord(req)); + } + + /** + * 变签状态变更 + */ + @Override + public ApiResult changeStatus(@RequestBody @Valid ChangeStatusRequest request) { + return ApiResult.ok(changeRecordService.changeStatus(request)); + } + + + /** + * 变签详情 + */ + @Override + public ApiResult visaDetailById(@RequestBody @Valid VisaDetailByIdRequest req) { + VisaDetailByIdResponse resp = changeRecordService.detailById(req.getVisaId()); + + if (req.isNeedBtnList()) { +// resp.setBtnList(VisaButtonTypeEnum.sort(VisaButtonTypeEnum.all())); + resp.setBtnList(changeRecordConfirmService.fetchBtnsByCondition(req.getVisaId(), req.getPersonId(), req.getOuId(), req.getWorkspaceId(), resp.getStatus(), resp.getApprovalId())); + } + return ApiResult.ok(resp); + } + + @Override + public ApiPageResult pageSearchVisaChangeRecord(VisaChangePageSearchReq req) { + //重新赋值属性,待提报:TO_REPORT/REPORT_FROM_APPROVE,决策中:DECIDING/DECIDING_FROM_APPROVE + this.rechangeAttr(req); + PageData pageData = changeRecordService.page(req); + return ApiPageResult.ok(pageData.getList(), pageData.getTotalCount(), pageData.getPage(), pageData.getPageSize()); + } + + @Override + public ApiPageResult list(VisaSearchReq req) { + PageData page = changeRecordService.list(req); + return ApiPageResult.ok(page.getList(), page.getTotalCount(), page.getPage(), page.getPageSize()); + } + + /** + * 重新赋值属性 + * 待提报:TO_REPORT/REPORT_FROM_APPROVE + * 决策中:DECIDING/DECIDING_FROM_APPROVE + */ + private void rechangeAttr(VisaChangePageSearchReq req) { + if (Objects.nonNull(req) && CollectionUtils.isNotEmpty(req.getStatuses()) && req.getStatuses().contains(VisaStatusEnum.TO_REPORT)) { + req.getStatuses().add(VisaStatusEnum.REPORT_FROM_APPROVE); + } + + if (Objects.nonNull(req) && CollectionUtils.isNotEmpty(req.getStatuses()) && req.getStatuses().contains(VisaStatusEnum.DECIDING)) { + req.getStatuses().add(VisaStatusEnum.DECIDING_FROM_APPROVE); + } + } + + @Override + public ApiResult exportVisaChangeRecord(VisaChangeExportReq req) { + changeRecordService.export(req); + return ApiResult.ok(); + } + + @Override + public ApiResult> listInitiator(VisaInitiatorListReq req) { + return ApiResult.ok(changeRecordService.listInitiator(req)); + } + + @Override + public ApiResult> getVisaAllConfirm(FetchVisaAllConfirmReq req) { + return ApiResult.ok(changeRecordConfirmService.listAllConfirmByVisaId(req.getVisaId())); + } + + @Override + public ApiResult approveCreateOnlyVisaChangeRecord(VisaChangeApproveOnlyReq req) { + return ApiResult.ok(changeRecordService.approveCreateOnlyVisaChangeRecord(req)); + } + + @Override + public ApiResult forbidChangeRecord(ChangeRecordButtonOperationReq req) { + changeRecordService.forbid(req); + return ApiResult.ok(); + } + + @Override + public ApiResult reDecisionChangeRecord(ChangeRecordButtonOperationReq req) { + changeRecordService.reDecision(req); + return ApiResult.ok(); + } + + @Override + public ApiResult executeChangeRecord(ChangeRecordButtonOperationReq req) { + changeRecordService.doExecute(req); + return ApiResult.ok(); + } + + @Override + public ApiResult deleteChangeRecord(ChangeRecordButtonOperationReq req) { + changeRecordService.delete(req); + return ApiResult.ok(); + } + + @Override + public ApiResult> getBillIfRelated(GetBillIfRelatedReq req) { + return ApiResult.ok(changeRecordBillService.getBillIfRelated(req)); + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordLogController.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordLogController.java new file mode 100644 index 00000000..ce209c63 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordLogController.java @@ -0,0 +1,39 @@ +package cn.axzo.nanopart.visa.server.controller; + +import cn.axzo.framework.domain.web.result.ApiListResult; +import cn.axzo.nanopart.visa.api.changerecord.ChangeRecordLogApi; +import cn.axzo.nanopart.visa.api.request.FetchVisaLogByVisaIdRequest; +import cn.axzo.nanopart.visa.api.response.FetchVisaLogByVisaIdResponse; +import cn.axzo.nanopart.visa.server.service.ChangeRecordLogService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录操作日志 + */ +@Slf4j +@RestController +public class ChangeRecordLogController implements ChangeRecordLogApi { + + @Autowired + private ChangeRecordLogService changeRecordLogService; + + /** + * 根据变签Id,获取变签日志 + */ + @Override + public ApiListResult fetchVisaLogByVisaId(@RequestBody @Valid FetchVisaLogByVisaIdRequest req) { + List responseList = changeRecordLogService.fetchVisaLogByVisaId(req.getVisaId()); + return ApiListResult.ok(responseList); + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordRelationController.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordRelationController.java new file mode 100644 index 00000000..36ae9695 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordRelationController.java @@ -0,0 +1,69 @@ +package cn.axzo.nanopart.visa.server.controller; + +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.nanopart.visa.api.changerecord.ChangeRecordRelationApi; +import cn.axzo.nanopart.visa.api.request.ChangeRecordAppendAttachReq; +import cn.axzo.nanopart.visa.api.request.ImGroupOperationReq; +import cn.axzo.nanopart.visa.api.request.ImGroupTipsQueryReq; +import cn.axzo.nanopart.visa.api.request.VisaRelationReq; +import cn.axzo.nanopart.visa.api.response.ImGroupTipsResp; +import cn.axzo.nanopart.visa.api.response.VisaRelationResp; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordRelation; +import cn.axzo.nanopart.visa.server.dto.VisaRelationDto; +import cn.axzo.nanopart.visa.server.service.ChangeRecordRelationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录内容关系表ø + */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class ChangeRecordRelationController implements ChangeRecordRelationApi { + + private final ChangeRecordRelationService changeRecordRelationService; + + @Override + public ApiResult> listByVisaId(VisaRelationReq req) { + return ApiResult.ok(changeRecordRelationService.findByCondition(VisaRelationDto.builder() + .visaId(req.getVisaId()) + .varName(Objects.nonNull(req.getFieldEnum()) ? req.getFieldEnum().name() : null) + .build()) + .stream().sorted(Comparator.comparing(ChangeRecordRelation::getCreateAt)) + .map(i -> VisaRelationResp.builder() + .id(i.getId()) + .visaId(String.valueOf(i.getVisaId())) + .varName(i.getVarName()) + .varExt(i.getVarExt()) + .varType(i.getVarType()) + .content(i.getContent()) + .contentExt(i.getContentExt()) + .createAt(i.getCreateAt()) + .build()).collect(Collectors.toList())); + } + + @Override + public ApiResult saveImOperationParticipate(ImGroupOperationReq req) { + return ApiResult.ok(changeRecordRelationService.saveImOperationParticipate(req)); + } + + @Override + public ApiResult queryImGroupTips(ImGroupTipsQueryReq req) { + return ApiResult.ok(changeRecordRelationService.queryImGroupTips(req)); + } + + @Override + public ApiResult appendAttachment(ChangeRecordAppendAttachReq req) { + changeRecordRelationService.appendAttachment(req); + return ApiResult.ok(); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecord.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecord.java new file mode 100644 index 00000000..61ae9886 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecord.java @@ -0,0 +1,158 @@ +package cn.axzo.nanopart.visa.server.domain; + +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +@TableName(value = "visa_change_record", autoResultMap = true) +public class ChangeRecord extends BaseEntity { + + /** + * 单据号 + */ + @TableField(value = "no") + private String no; + + /** + * 主题 + */ + @TableField(value = "topic") + private String topic; + + /** + * 项目 + */ + @TableField(value = "relation_workspaceId") + private Long relationWorkspaceId; + + /** + * 工程 + */ + @TableField(value = "relation_project") + private Long relationProject; + + /** + * 区域集合 + * 示例: + *
+     * [
+     *     {"areaId": 249556},
+     *     {"areaId": 249555}
+     * ]
+     *  
+ */ + @TableField(value = "relation_area", typeHandler = FastjsonTypeHandler.class) + private JSONArray relationArea; + + /** + * 专业集合 + * 示例: + *
+     * [
+     *     {"code":"code1"},
+     *     {"code":"code2"},
+     * ]
+     * 
+ */ + @TableField(value = "relation_professional", typeHandler = FastjsonTypeHandler.class) + private JSONArray relationProfessional; + + /** + * 提出时间 + */ + @TableField(value = "happen_time") + private Date happenTime; + + /** + * 有符号的变更金额 + */ + @TableField(value = "amount_change") + private BigDecimal amountChange; + + /** + * 发生原因 + */ + @TableField(value = "reason") + private String reason; + + /** + * 单据类型 + */ + @TableField(value = "type") + private VisaTypeEnum type; + + /** + * 状态 + */ + @TableField(value = "status") + private VisaStatusEnum status; + + /** + * 最终审批ID + */ + @TableField(value = "approval_id") + private String approvalId; + + /** + * 最终审批状态 + */ + @TableField(value = "approval_status") + private String approvalStatus; + + /** + * 最终审批时间 + */ + @TableField(value = "approval_complete_time", updateStrategy = FieldStrategy.IGNORED) + private Date approvalCompleteTime; + + /** + * IM群组ID + */ + @TableField(value = "im_group_id") + private Long imGroupId; + + /** + * 额外信息 + */ + @TableField(value = "extra", typeHandler = FastjsonTypeHandler.class) + private JSONObject extra; + + /** + * 创建人id + */ + @TableField(value = "create_by") + private Long createBy; + + /** + * 修改人id + */ + @TableField(value = "update_by") + private Long updateBy; + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordBill.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordBill.java new file mode 100644 index 00000000..9d6f4b67 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordBill.java @@ -0,0 +1,87 @@ +package cn.axzo.nanopart.visa.server.domain; + +import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * @author xudawei + * @date 2025/01/20 + * @desc 变更签证记录单据关系表 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +@TableName(value = "visa_change_record_bill", autoResultMap = true) +public class ChangeRecordBill extends BaseEntity { + + /** + * 变更签证记录id + */ + @TableField(value = "visa_id") + private Long visaId; + + /** + * 变更签证单据类型 + */ + @TableField(value = "visa_type") + private VisaTypeEnum visaType; + + /** + * 单据id + */ + @TableField(value = "bill_id") + private Long billId; + + /** + * 单据编号 + */ + @TableField(value = "bill_no") + private String billNo; + + /** + * 单据类型 + */ + @TableField(value = "bill_type") + private VisaBillTypeEnum billType; + + /** + * 单据被关联状态 + * true 1 被关联 + * false 0 未关联 + */ + @TableField(value = "relation_status") + private Boolean relationStatus; + + /** + * 额外信息 + */ + @TableField(value = "extra", typeHandler = FastjsonTypeHandler.class) + private JSONObject extra; + + /** + * 创建人id + */ + @TableField(value = "create_by") + private Long createBy; + + /** + * 修改人id + */ + @TableField(value = "update_by") + private Long updateBy; + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordConfirm.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordConfirm.java new file mode 100644 index 00000000..7de9642b --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordConfirm.java @@ -0,0 +1,105 @@ +package cn.axzo.nanopart.visa.server.domain; + +import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.nanopart.visa.api.enums.VisaConfirmBizTypeEnum; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录确认 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +@TableName(value = "visa_change_record_confirm", autoResultMap = true) +public class ChangeRecordConfirm extends BaseEntity { + + /** + * 变更签id + */ + @TableField(value = "visa_id") + private Long visaId; + + /** + * 类型 + */ + @TableField(value = "type") + private SaasCooperateShipCooperateTypeEnum type; + + /** + * 业务类型,CONFIRM:确认;CREATE:创建 + */ + @TableField(value = "biz_type") + private VisaConfirmBizTypeEnum bizType; + + + /** + * 业务类型,CONFIRM:确认;CREATE:创建 + */ + @TableField(value = "visa_type") + private VisaTypeEnum visaType; + + /** + * 确认人 + */ + @TableField(value = "person_id") + private Long personId; + + /** + * 确认单位 + */ + @TableField(value = "ou_id") + private Long ouId; + + /** + * 确认项目 + */ + @TableField(value = "workspace_id") + private Long workspaceId; + + /** + * 确认工程 + */ + @TableField(value = "project_id") + private Long projectId; + + /** + * 确认节点 + */ + @TableField(value = "node_id") + private Long nodeId; + + /** + * 额外信息 + */ + @TableField(value = "extra",typeHandler = FastjsonTypeHandler.class) + private JSONObject extra; + + /** + * 创建人id + */ + @TableField(value = "create_by") + private Long createBy; + + /** + * 修改人id + */ + @TableField(value = "update_by") + private Long updateBy; + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordLog.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordLog.java new file mode 100644 index 00000000..aece4fc2 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordLog.java @@ -0,0 +1,66 @@ +package cn.axzo.nanopart.visa.server.domain; + +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录操作日志 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +@TableName(value = "visa_change_record_log", autoResultMap = true) +public class ChangeRecordLog extends BaseEntity { + + /** + * 变更签证记录id + */ + @TableField(value = "visa_id") + private Long visaId; + + /** + * 操作动作 + */ + @TableField(value = "action") + private String action; + + /** + * 操作标题 + */ + @TableField(value = "title") + private String title; + + /** + * 操作内容 + */ + @TableField(value = "content") + private String content; + + /** + * 排序 + */ + @TableField(value = "order_by") + private Integer orderBy; + + /** + * 创建人id + */ + @TableField(value = "create_by") + private Long createBy; + + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordRelation.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordRelation.java new file mode 100644 index 00000000..079a9eaf --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/domain/ChangeRecordRelation.java @@ -0,0 +1,74 @@ +package cn.axzo.nanopart.visa.server.domain; + +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录内容关系表 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@TableName(value = "visa_change_record_relation", autoResultMap = true) +public class ChangeRecordRelation extends BaseEntity { + + /** + * 变更签证记录id + */ + @TableField(value = "visa_id") + private Long visaId; + + /** + * 关联的内容名称(发生内容及说明、相关单位及人员,单据,附件,审批记录) + */ + @TableField(value = "var_name") + private String varName; + + /** + * 扩展内容名称 + */ + @TableField(value = "var_ext") + private String varExt; + + /** + * 值类型 + */ + + @TableField(value = "var_type") + private String varType; + + /** + * 内容值 + */ + @TableField(value = "content") + private String content; + + /** + * 内容描述 + */ + @TableField(value = "content_ext") + private String contentExt; + + /** + * 创建人id + */ + @TableField(value = "create_by") + private Long createBy; + + /** + * 修改人id + */ + @TableField(value = "update_by") + private Long updateBy; + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaAddLogContext.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaAddLogContext.java new file mode 100644 index 00000000..9c1ac9a9 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaAddLogContext.java @@ -0,0 +1,55 @@ +package cn.axzo.nanopart.visa.server.dto; + +import cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordRelation; +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.compress.utils.Lists; + +import java.util.List; +import java.util.stream.Collectors; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class VisaAddLogContext { + + /** + * 人员Id + */ + private Long personId; + /** + * 项目Id + */ + private Long workspaceId; + /** + * 单位id + */ + private Long ouId; + + private VisaStatusEnum status; + + private ChangeRecord changeRecord; + + private List relationList; + + /** + * 获取附件集合 + */ + public List getAttachList() { + if (CollectionUtils.isEmpty(relationList)) { + return Lists.newArrayList(); + } + return relationList.stream() + .filter(item -> VisaRelationFieldEnum.isAttach(item.getVarName())) + .map(item -> JSONObject.parseObject(item.getContent())).collect(Collectors.toList()); + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaBillDto.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaBillDto.java new file mode 100644 index 00000000..7b6d619b --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaBillDto.java @@ -0,0 +1,70 @@ +package cn.axzo.nanopart.visa.server.dto; + +import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * @author xudawei + * @date 2025/01/17 + * @desc 变更签证记录确认 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class VisaBillDto { + + /** + * 变更签证记录id + */ + private Long visaId; + + /** + * 变更签证单据类型 + */ + private VisaTypeEnum visaType; + + /** + * 单据id + */ + private Long billId; + + /** + * 单据编号 + */ + private String billNo; + + /** + * 单据类型 + */ + private VisaBillTypeEnum billType; + /** + * 额外信息 + */ + private JSONObject extra; + + /** + * 创建人id + */ + private Long createBy; + + /** + * 修改人id + */ + private Long updateBy; + + /** + * 单据被关联状态 + * true 1 被关联 + * false 0 未关联 + */ + private Boolean relationStatus; + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaConfirmDto.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaConfirmDto.java new file mode 100644 index 00000000..23edf2f1 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaConfirmDto.java @@ -0,0 +1,156 @@ +package cn.axzo.nanopart.visa.server.dto; + +import cn.axzo.karma.client.feign.tyr.response.MergeMatchDataResp; +import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaConfirmBizTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.collections.CollectionUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author xudawei + * @date 2025/01/17 + * @desc 变更签证记录确认 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class VisaConfirmDto { + + /** + * 变更签id + */ + private Long visaId; + + private Collection visaIds; + + /** + * 类型 + */ + private SaasCooperateShipCooperateTypeEnum type; + + /** + * 业务类型,CONFIRM:确认;CREATE:创建 + */ + private VisaConfirmBizTypeEnum bizType; + + /** + * 确认人 + */ + private Long personId; + /** + * 确认人集合 + */ + private Collection personIds; + + /** + * 确认单位 + */ + private Long ouId; + + /** + * 确认单位集合 + */ + private Collection ouIds; + + /** + * 确认项目 + */ + private Long workspaceId; + + /** + * 确认项目集合 + */ + private Collection workspaceIds; + + /** + * 确认工程 + */ + private Long projectId; + + /** + * 确认工程集合 + */ + private Collection projectIds; + + /** + * 确认节点 + */ + private Long nodeId; + /** + * 确认节点集合 + */ + private Collection nodeIds; + + private Long operator; + + /** + * 变更签证类型集合 + */ + private Set visaTypes; + + + /** + * 变更签证类型 + */ + private VisaTypeEnum visaType; + /** + * 根据DataItem创建Dto + */ + public static VisaConfirmDto create(List dataItems, Set visaTypes) { + + VisaConfirmDto dto = new VisaConfirmDto(); + + if (CollectionUtils.isNotEmpty(dataItems)) { + Collection personIds = dataItems.stream() + .filter(item -> CollectionUtils.isNotEmpty(item.getPersonIds())) + .flatMap(item -> item.getPersonIds().stream()).collect(Collectors.toSet()); + if (CollectionUtils.isNotEmpty(personIds)) { + dto.setPersonIds(personIds); + } + + Collection ouIds = dataItems.stream() + .filter(item -> Objects.nonNull(item.getOuId())) + .map(item -> item.getOuId()).collect(Collectors.toSet()); + if (CollectionUtils.isNotEmpty(ouIds)) { + dto.setOuIds(ouIds); + } + + Collection workspaceIds = dataItems.stream() + .filter(item -> Objects.nonNull(item.getWorkspaceId())) + .map(item -> item.getWorkspaceId()).collect(Collectors.toSet()); + if (CollectionUtils.isNotEmpty(workspaceIds)) { + dto.setWorkspaceIds(workspaceIds); + } + + Set nodeIds = dataItems.stream() + .filter(item -> Objects.nonNull(item.getNodeId())) + .map(item -> item.getNodeId()).collect(Collectors.toSet()); + if (CollectionUtils.isNotEmpty(nodeIds)) { + dto.setNodeIds(nodeIds); + } + + Collection projectIds = dataItems.stream() + .filter(item -> CollectionUtils.isNotEmpty(item.getProjectIds())) + .flatMap(item -> item.getProjectIds().stream()).collect(Collectors.toSet()); + if (CollectionUtils.isNotEmpty(projectIds)) { + dto.setProjectIds(projectIds); + } + } + dto.setVisaTypes(visaTypes); + return dto; + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaExportDto.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaExportDto.java new file mode 100644 index 00000000..69b1aeff --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaExportDto.java @@ -0,0 +1,77 @@ +package cn.axzo.nanopart.visa.server.dto; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/23 10:19 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisaExportDto { + + + /** + * 单据号 + */ + @ExcelProperty("单号") + private String no; + + /** + * 单据类型 + */ + @ExcelProperty("单据类型") + private String type; + + /** + * 状态 + */ + @ExcelProperty("单据状态") + private String status; + + /** + * 主题 + */ + @ExcelProperty("主题") + private String topic; + + /** + * 有符号的变更金额 + */ + @ExcelProperty("金额变化") + private BigDecimal amountChange; + + /** + * 发起人 + */ + @ExcelProperty("发起人") + private String initiator; + + /** + * 提出时间 + */ + @ExcelProperty("提出时间") + private Date happenTime; + + /** + * 最终审批时间 + */ + @ExcelProperty("审批通过时间") + private Date approvalCompleteTime; + + /** + * 工程 + */ + @ExcelProperty("所属工程") + private String relationProject; +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaLogDto.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaLogDto.java new file mode 100644 index 00000000..731ff412 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaLogDto.java @@ -0,0 +1,31 @@ +package cn.axzo.nanopart.visa.server.dto; + +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * @author xudawei + * @date 2025/01/21 + * @desc 变更签证记录日志确认 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class VisaLogDto { + + /** + * 变更签证Id + */ + private Long visaId; + /** + * 类型 + */ + private VisaLogTypeEnum logType; + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaLogParam.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaLogParam.java new file mode 100644 index 00000000..8dd99778 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaLogParam.java @@ -0,0 +1,163 @@ +package cn.axzo.nanopart.visa.server.dto; + +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import cn.hutool.core.collection.CollUtil; +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.compress.utils.Lists; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author xudawei + * @date 2025/01/21 + * @desc 变更签证记录日志确认 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class VisaLogParam { + + /** + * 人员Id + */ + private Long personId; + /** + * 单位Id + */ + private Long ouId; + /** + * 项目Id + */ + private Long workspaceId; + + /** + * 原变更金额 + */ + private BigDecimal oldAmountChange; + + /** + * 新变更金额 + */ + private BigDecimal newAmountChange; + + /** + * 上传文件 + */ + private List uploadAttach; + + /** + * 删除文件 + */ + private List deleteAttach; + + /** + * 日志类型 + */ + private VisaLogTypeEnum logType; + + /** + * 原因:审批流的驳回原因/转交原因 + */ + private String reason; + + /** + * 审批流转交给的用户 + */ + private String transmitTo; + /** + * 新状态 + */ + private VisaStatusEnum newStatus; + /** + * 原状态 + */ + private VisaStatusEnum oldStatus; + + + /** + * 获取删除附件信息 + */ + public static List deleteAttach(List oldAttach, List newAttach) { + return fetchChangeAttach(oldAttach, newAttach); + } + + /** + * 获取删除附件信息 + */ + public static List deleteAttachWithObject(List oldAttach, List newAttach) { + return fetchChangeAttachWithObject(oldAttach, newAttach); + } + + /** + * 获取上传的附件信息 + */ + public static List uploadAttach(List newAttach, List oldAttach) { + return fetchChangeAttach(newAttach, oldAttach); + } + + /** + * 获取上传的附件信息 + */ + public static List uploadAttachWithObject(List newAttach, List oldAttach) { + return fetchChangeAttachWithObject(newAttach, oldAttach); + } + + + + /** + * 获取变动的附件信息 + * 计算集合的单差集,即只返回【集合1】中有,但是【集合2】中没有的元素,例如: + * subtractToList([1,2,3,4],[2,3,4,5]) -》 [1] + */ + private static List fetchChangeAttach(List compareFir, List compareSec) { + if (CollectionUtils.isEmpty(compareFir)) { + return Lists.newArrayList(); + } + List oldFileDtoList = VisaDetailByIdResponse.VisaUploadFile.createListByJson(compareFir); + + if (CollectionUtils.isEmpty(compareSec)) { + return oldFileDtoList.stream().map(VisaDetailByIdResponse.VisaUploadFile::getFileName).collect(Collectors.toList()); + } + List newFileDtoList = VisaDetailByIdResponse.VisaUploadFile.createListByJson(compareSec); + + return fetchChangeAttachWithObject(oldFileDtoList, newFileDtoList); + } + + + + /** + * 获取变动的附件信息 + * 计算集合的单差集,即只返回【集合1】中有,但是【集合2】中没有的元素,例如: + * subtractToList([1,2,3,4],[2,3,4,5]) -》 [1] + */ + private static List fetchChangeAttachWithObject(List compareFir, List compareSec) { + if (CollectionUtils.isEmpty(compareFir)) { + return Lists.newArrayList(); + } + + if (CollectionUtils.isEmpty(compareSec)) { + return compareFir.stream().map(VisaDetailByIdResponse.VisaUploadFile::getFileName).collect(Collectors.toList()); + } + + Collection substractList = CollUtil.subtractToList(compareFir, compareSec); + if (CollectionUtils.isEmpty(substractList)) { + return Lists.newArrayList(); + } + return substractList.stream().map(VisaDetailByIdResponse.VisaUploadFile::getFileName).collect(Collectors.toList()); + } + + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaRelationDto.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaRelationDto.java new file mode 100644 index 00000000..69d40f88 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/dto/VisaRelationDto.java @@ -0,0 +1,46 @@ +package cn.axzo.nanopart.visa.server.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * @author xudawei + * @date 2025/01/21 + * @desc 变更签证记录内容关系表 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class VisaRelationDto { + + /** + * 变更签证记录id + */ + private Long visaId; + + /** + * 类型 + */ + private String varName; + + /** + * 内容 + */ + private String content; + + /** + * 内容扩展 + */ + private String contentExt; + + /** + * 创建人 + */ + private Long createBy; + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordBillDao.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordBillDao.java new file mode 100644 index 00000000..6bf5f9ad --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordBillDao.java @@ -0,0 +1,15 @@ +package cn.axzo.nanopart.visa.server.mapper; + +import cn.axzo.nanopart.visa.server.domain.ChangeRecordBill; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录单据关系表 + */ +@Mapper +public interface ChangeRecordBillDao extends BaseMapper { + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordConfirmDao.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordConfirmDao.java new file mode 100644 index 00000000..770a3dc3 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordConfirmDao.java @@ -0,0 +1,15 @@ +package cn.axzo.nanopart.visa.server.mapper; + +import cn.axzo.nanopart.visa.server.domain.ChangeRecordConfirm; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录确认 + */ +@Mapper +public interface ChangeRecordConfirmDao extends BaseMapper { + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordDao.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordDao.java new file mode 100644 index 00000000..54050363 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordDao.java @@ -0,0 +1,15 @@ +package cn.axzo.nanopart.visa.server.mapper; + +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录 + */ +@Mapper +public interface ChangeRecordDao extends BaseMapper { + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordLogDao.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordLogDao.java new file mode 100644 index 00000000..1ce735c9 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordLogDao.java @@ -0,0 +1,15 @@ +package cn.axzo.nanopart.visa.server.mapper; + +import cn.axzo.nanopart.visa.server.domain.ChangeRecordLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录操作日志 + */ +@Mapper +public interface ChangeRecordLogDao extends BaseMapper { + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordRelationDao.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordRelationDao.java new file mode 100644 index 00000000..925dc2af --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mapper/ChangeRecordRelationDao.java @@ -0,0 +1,15 @@ +package cn.axzo.nanopart.visa.server.mapper; + +import cn.axzo.nanopart.visa.server.domain.ChangeRecordRelation; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录内容关系表 + */ +@Mapper +public interface ChangeRecordRelationDao extends BaseMapper { + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupAddMembersEventHandler.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupAddMembersEventHandler.java new file mode 100644 index 00000000..5aaf3f4e --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupAddMembersEventHandler.java @@ -0,0 +1,108 @@ +package cn.axzo.nanopart.visa.server.mq.listener.im; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.im.center.api.vo.group.GroupInfo; +import cn.axzo.im.center.api.vo.group.GroupMemberInfo; +import cn.axzo.im.center.api.vo.mq.GroupMembersChangeMessage; +import cn.axzo.msg.center.api.MessageAPIV3; +import cn.axzo.msg.center.api.request.v3.MessageSendReqV3; +import cn.axzo.msg.center.service.dto.PersonV3DTO; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.nanopart.visa.server.config.RefreshableConfiguration; +import cn.axzo.nanopart.visa.server.rpc.VisaProfileGateway; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.groovy.util.Maps; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Map; +import java.util.Objects; + +import static cn.axzo.im.center.api.enums.MqEventType.GROUP_ADD_MEMBERS; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_INITIATOR_WORKSPACE_ID; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_VISA_ID; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_VISA_TYPE; + +/** + * 监听 IM 添加群成员的广播事件 + * + * @author wangli + * @since 2025-02-07 13:53 + */ +@Slf4j +@Component +public class ImGroupAddMembersEventHandler implements EventHandler, InitializingBean { + @Autowired + private EventConsumer eventConsumer; + @Resource + private RefreshableConfiguration refreshableConfiguration; + @Resource + private MessageAPIV3 noticeApi; + @Resource + private VisaProfileGateway visaProfileGateway; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + if (Objects.isNull(event) || Objects.isNull(event.getEventCode())) { + log.warn("illegal event code: {}", JSON.toJSONString(event)); + return; + } + log.info("receive add member event, push notice : {}", JSON.toJSONString(event)); + GroupMembersChangeMessage groupMembersChangeMessage = event.normalizedData(GroupMembersChangeMessage.class); + GroupInfo group = groupMembersChangeMessage.getGroup(); + AssertUtil.notNull(group, "im group is null"); + + GroupMemberInfo member = groupMembersChangeMessage.getMember(); + AssertUtil.notNull(member, "im member is null"); + + Map profileMap = visaProfileGateway.getProfileMap(Lists.newArrayList(group.getOwnerPersonId(), member.getPersonId())); + + + Long visaId = Long.valueOf(group.getBizGroupInfo().getOrDefault(IM_GROUP_BIZ_INFO_VISA_ID, 0).toString()); + String visaType = (String) group.getBizGroupInfo().getOrDefault(IM_GROUP_BIZ_INFO_VISA_TYPE, ""); + Long workspaceId = Long.valueOf(group.getBizGroupInfo().getOrDefault(IM_GROUP_BIZ_INFO_INITIATOR_WORKSPACE_ID, 0).toString()); + + PersonProfileDto memberProfile = profileMap.getOrDefault(member.getPersonId(), null); + PersonProfileDto ownerProfile = profileMap.getOrDefault(group.getOwnerPersonId(), null); + + if (Objects.nonNull(memberProfile) && Objects.nonNull(ownerProfile)) { + MessageSendReqV3 notice = new MessageSendReqV3(); + notice.setSender(PersonV3DTO.builder().build()); + + notice.setReceivers(Lists.newArrayList(PersonV3DTO.builder() + .id(member.getPersonId()) + .name(memberProfile.getRealName()) + .imReceiveModel(new PersonV3DTO.ReceiveModel(member.getPersonOuId(), workspaceId)) + .build())); + notice.setBizEventMappingCode(refreshableConfiguration.getImGroupAddMemberNoticeEventCode()); + notice.setBizCode(String.valueOf(visaId)); + notice.setBizExtParams(new JSONObject(Maps.of( + "visaId", visaId, + "initiatorName", ownerProfile.getRealName(), + "visaType", StringUtils.isNotBlank(visaType) ? VisaTypeEnum.valueOf(visaType).getDesc() : "", + "topic", group.getName()))); + notice.setRouterParams(new JSONObject(Maps.of( + "visaId", visaId, + "ouId", member.getPersonOuId(), + "workspaceId", workspaceId + ))); + noticeApi.send(notice); + } + log.info("receive add member event, push notice success!"); + } + + @Override + public void afterPropertiesSet() { + eventConsumer.registerHandler(GROUP_ADD_MEMBERS.getEventCode(), this); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupDismissedEventHandler.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupDismissedEventHandler.java new file mode 100644 index 00000000..9595df8c --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupDismissedEventHandler.java @@ -0,0 +1,41 @@ +package cn.axzo.nanopart.visa.server.mq.listener.im; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +import static cn.axzo.im.center.api.enums.MqEventType.GROUP_DISMISSED; + +/** + * 监听 IM 群解散的广播事件 + * + * @author wangli + * @since 2025-02-07 13:53 + */ +@Slf4j +@Component +public class ImGroupDismissedEventHandler implements EventHandler, InitializingBean { + @Autowired + private EventConsumer eventConsumer; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + if (Objects.isNull(event) || Objects.isNull(event.getEventCode())) { + log.warn("illegal event code: {}", JSON.toJSONString(event)); + return; + } + + } + + @Override + public void afterPropertiesSet() { + eventConsumer.registerHandler(GROUP_DISMISSED.getEventCode(), this); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupRemoveMembersEventHandler.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupRemoveMembersEventHandler.java new file mode 100644 index 00000000..047969d9 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupRemoveMembersEventHandler.java @@ -0,0 +1,73 @@ +package cn.axzo.nanopart.visa.server.mq.listener.im; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.im.center.api.vo.group.GroupInfo; +import cn.axzo.im.center.api.vo.group.GroupMemberInfo; +import cn.axzo.im.center.api.vo.mq.GroupMembersChangeMessage; +import cn.axzo.nanopart.visa.api.request.ImGroupOperationClearReq; +import cn.axzo.nanopart.visa.server.service.ChangeRecordRelationService; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Objects; + +import static cn.axzo.im.center.api.enums.MqEventType.GROUP_REMOVE_MEMBERS; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_VISA_ID; + +/** + * 监听 IM 群成员被移除的广播事件 + * + * @author wangli + * @since 2025-02-07 13:53 + */ +@Slf4j +@Component +public class ImGroupRemoveMembersEventHandler implements EventHandler, InitializingBean { + @Autowired + private EventConsumer eventConsumer; + @Resource + private ChangeRecordRelationService changeRecordRelationService; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + if (Objects.isNull(event) || Objects.isNull(event.getEventCode())) { + log.warn("illegal event code: {}", JSON.toJSONString(event)); + return; + } + log.info("receive remove member event: {}", JSON.toJSONString(event)); + GroupMembersChangeMessage groupMembersChangeMessage = event.normalizedData(GroupMembersChangeMessage.class); + GroupInfo group = groupMembersChangeMessage.getGroup(); + AssertUtil.notNull(group, "im group is null"); + + GroupMemberInfo member = groupMembersChangeMessage.getMember(); + AssertUtil.notNull(member, "im member is null"); + + // 移除该群成员的投票信息 + removeMemberVote(group, member); + log.info("receive remove member event handle success!"); + } + + private void removeMemberVote(GroupInfo group, GroupMemberInfo member) { + Long visaId = Long.valueOf(group.getBizGroupInfo().getOrDefault(IM_GROUP_BIZ_INFO_VISA_ID, 0).toString()); + + changeRecordRelationService.clearImOperationParticipate(ImGroupOperationClearReq.builder() + .clearAll(false) + .imGroupId(group.getTid()) + .visaId(visaId) + .operatorPersonId(member.getPersonId()) + .operatorOuId(member.getPersonOuId()) + .build()); + } + + @Override + public void afterPropertiesSet() { + eventConsumer.registerHandler(GROUP_REMOVE_MEMBERS.getEventCode(), this); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupsCreatedEventHandler.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupsCreatedEventHandler.java new file mode 100644 index 00000000..f49d53a5 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/im/ImGroupsCreatedEventHandler.java @@ -0,0 +1,161 @@ +package cn.axzo.nanopart.visa.server.mq.listener.im; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.im.center.api.vo.PersonAccountAttribute; +import cn.axzo.im.center.api.vo.group.GroupInfo; +import cn.axzo.im.center.api.vo.group.GroupMemberInfo; +import cn.axzo.im.center.api.vo.mq.GroupChangedMessage; +import cn.axzo.im.center.api.vo.req.GroupGetOwnerRequest; +import cn.axzo.im.center.api.vo.req.SendChatMessageRequest; +import cn.axzo.im.center.api.vo.resp.GroupGetOwnerResponse; +import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO; +import cn.axzo.msg.center.service.dto.PeerPerson; +import cn.axzo.msg.center.service.pending.request.CardSendRequest; +import cn.axzo.nanopart.visa.api.enums.VisaConfirmBizTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.nanopart.visa.server.config.RefreshableConfiguration; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordConfirm; +import cn.axzo.nanopart.visa.server.dto.VisaConfirmDto; +import cn.axzo.nanopart.visa.server.rpc.MsgCenterGateway; +import cn.axzo.nanopart.visa.server.rpc.OrganizationalUnitGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaProfileGateway; +import cn.axzo.nanopart.visa.server.service.ChangeRecordConfirmService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordService; +import cn.axzo.nanopart.visa.server.service.VisaHelper; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.groovy.util.Maps; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static cn.axzo.im.center.api.enums.MqEventType.GROUP_CREATED; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_INITIATOR_WORKSPACE_ID; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_VISA_ID; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_VISA_TYPE; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.MSG_CENTER_APP_CODE; + +/** + * 监听 IM 群创建的广播事件 + * + * @author wangli + * @since 2025-02-07 13:53 + */ +@Slf4j +@Component +public class ImGroupsCreatedEventHandler implements EventHandler, InitializingBean { + @Autowired + private EventConsumer eventConsumer; + @Resource + private MsgCenterGateway msgCenterGateway; + @Resource + private RefreshableConfiguration refreshableConfiguration; + @Resource + private OrganizationalUnitGateway organizationalUnitGateway; + @Resource + private ChangeRecordService changeRecordService; + @Resource + private ChangeRecordConfirmService changeRecordConfirmService; + @Resource + private VisaProfileGateway visaProfileGateway; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + if (Objects.isNull(event) || Objects.isNull(event.getEventCode())) { + log.warn("illegal event code: {}", JSON.toJSONString(event)); + return; + } + log.info("receive create im group event: {}", JSON.toJSONString(event)); + GroupChangedMessage groupChangedMessage = event.normalizedData(GroupChangedMessage.class); + GroupInfo group = groupChangedMessage.getGroup(); + AssertUtil.notNull(group, "group info is null"); + try { + // 转移到创建群聊立即发送卡片 + // sendCardAndMsgToImGroup(group); + } catch (Exception e) { + log.error("send msg to im group error: {}", e.getMessage(), e); + throw e; + } + log.info("receive create im group event, handle success: {}", JSON.toJSONString(event)); + } + + private void sendCardAndMsgToImGroup(GroupInfo group) { + Map bizGroupInfo = group.getBizGroupInfo(); + HashSet imReceiveAccounts = Sets.newHashSet(String.valueOf(group.getTid())); + GroupGetOwnerRequest ownerRequest = new GroupGetOwnerRequest(); + ownerRequest.setTid(group.getTid()); + GroupGetOwnerResponse groupOwner = msgCenterGateway.getGroupOwner(ownerRequest); + AssertUtil.isTrue(Objects.nonNull(groupOwner) && Objects.nonNull(groupOwner.getOwner()), "im group owner is null"); + GroupMemberInfo owner = groupOwner.getOwner(); + + Long visaId = Long.valueOf(bizGroupInfo.getOrDefault(IM_GROUP_BIZ_INFO_VISA_ID, 0).toString()); + String visaType = (String) bizGroupInfo.getOrDefault(IM_GROUP_BIZ_INFO_VISA_TYPE, ""); + Long ownerWorkspaceId = Long.valueOf(bizGroupInfo.getOrDefault(IM_GROUP_BIZ_INFO_INITIATOR_WORKSPACE_ID, 0).toString()); + + ChangeRecord visa = changeRecordService.getById(visaId); + AssertUtil.notNull(visa, "visa record is null"); + + List creators = changeRecordConfirmService.findByCondition(VisaConfirmDto.builder() + .visaId(visaId) + .bizType(VisaConfirmBizTypeEnum.CREATE) + .build()); + AssertUtil.notEmpty(creators, "visa creator is null"); + + + // 发送卡片 + CardSendRequest cardRequest = new CardSendRequest(); + cardRequest.setAppCode(MSG_CENTER_APP_CODE); + cardRequest.setTemplateCode(refreshableConfiguration.getImGroupCardTemplateCode()); + cardRequest.setBizCode(IM_GROUP_BIZ_INFO_VISA_ID + ":" + visaId); + cardRequest.setSender(PeerPerson.create(owner.getPersonId(), owner.getPersonOuId(), ownerWorkspaceId)); + cardRequest.setImSenderAccountAppType(AppTypeEnum.CMP); + cardRequest.setImReceiveAccounts(imReceiveAccounts); + + ChangeRecordConfirm creator = creators.get(0); + OrganizationalUnitVO unitInfo = organizationalUnitGateway.getUnitInfo(creator.getOuId()); + PersonProfileDto profile = visaProfileGateway.getProfile(creator.getPersonId()); + + cardRequest.setBizParam(new JSONObject(Maps.of( + "topic", group.getName(), + "reason", visa.getReason(), + "initiatorName", profile.getRealName(), + "initiatorUnitName", unitInfo.getName(), + "visaType", StringUtils.isNotBlank(visaType) ? VisaTypeEnum.valueOf(visaType).getDesc() : "", + "amountChange", VisaHelper.buildChangeAmount(visa.getAmountChange()) + ))); + msgCenterGateway.sendCardToCroup(cardRequest); + + // 普通群消息 + SendChatMessageRequest msgRequest = new SendChatMessageRequest(); + msgRequest.setAsTextMessage(group.getName() + ",请各位审阅"); + msgRequest.setImReceiveAccounts(imReceiveAccounts); + msgRequest.setSender(PersonAccountAttribute.builder() + .personId(String.valueOf(owner.getPersonId())) + .ouId(owner.getPersonOuId()) + .workspaceId(ownerWorkspaceId) + .appType(AppTypeEnum.CMP) + .build()); + msgRequest.setBizId(IM_GROUP_BIZ_INFO_VISA_ID + ":" + visaId); + msgCenterGateway.sendMsgToGroup(msgRequest); + } + + @Override + public void afterPropertiesSet() { + eventConsumer.registerHandler(GROUP_CREATED.getEventCode(), this); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/visa/ChangeRecordLogEventHandle.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/visa/ChangeRecordLogEventHandle.java new file mode 100644 index 00000000..c2a90e2c --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/visa/ChangeRecordLogEventHandle.java @@ -0,0 +1,35 @@ +package cn.axzo.nanopart.visa.server.mq.listener.visa; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.nanopart.visa.api.enums.MQEventEnum; +import cn.axzo.nanopart.visa.server.mq.producer.VisaChangeLogPayload; +import cn.axzo.nanopart.visa.server.service.ChangeRecordLogService; +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Component; + +/** + * 添加处理变洽签日志的逻辑 + * + * @author wangli + * @since 2025-02-08 11:18 + */ +@Component +@AllArgsConstructor +public class ChangeRecordLogEventHandle implements EventHandler, InitializingBean { + private final EventConsumer eventConsumer; + private final ChangeRecordLogService changeRecordLogService; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + VisaChangeLogPayload log = event.normalizedData(VisaChangeLogPayload.class); + changeRecordLogService.addLog(log.createRecordLogByPayload()); + } + + @Override + public void afterPropertiesSet() { + eventConsumer.registerHandler(MQEventEnum.VISA_CHANGE_LOG.getEventCode(), this); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/BasicLogSupport.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/BasicLogSupport.java new file mode 100644 index 00000000..16af4be1 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/BasicLogSupport.java @@ -0,0 +1,53 @@ +package cn.axzo.nanopart.visa.server.mq.listener.workflow; + +import cn.axzo.framework.rocketmq.EventProducer; +import cn.axzo.maokai.api.vo.request.OrgNodeUserBriefInfoListReq; +import cn.axzo.maokai.api.vo.response.OrgNodeUserBriefInfoResp; +import cn.axzo.nanopart.visa.server.rpc.VisaOrganizationalNodeUserGateway; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import com.google.common.collect.Lists; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Objects; + +/** + * @author wangli + * @since 2025-01-20 10:18 + */ +public abstract class BasicLogSupport { + protected final EventProducer eventProducer; + protected final VisaOrganizationalNodeUserGateway visaOrganizationalNodeUserGateway; + + protected BasicLogSupport(EventProducer eventProducer, VisaOrganizationalNodeUserGateway visaOrganizationalNodeUserGateway) { + this.eventProducer = eventProducer; + this.visaOrganizationalNodeUserGateway = visaOrganizationalNodeUserGateway; + } + + protected String buildLogUserInfo(BpmnTaskDelegateAssigner assigner) { + OrgNodeUserBriefInfoResp oneUserInfo = getOneUserInfo(assigner); + return buildLogUserInfo(oneUserInfo); + } + protected String buildLogUserInfo(OrgNodeUserBriefInfoResp user) { + String userInfo = ""; + if (Objects.nonNull(user)) { + userInfo = user.getRealName() + "(" + user.getJob().getName() + "-" + user.getOrganizationalUnitName() + ")"; + } + return userInfo; + } + + protected OrgNodeUserBriefInfoResp getOneUserInfo(BpmnTaskDelegateAssigner assigner) { + if (Objects.isNull(assigner)) { + return null; + } + List users = visaOrganizationalNodeUserGateway.listOrgNodeUsers(OrgNodeUserBriefInfoListReq.builder() + .workspaceId(Long.valueOf(assigner.getTenantId())) + .ouId(Long.valueOf(assigner.getOuId())) + .personIds(Lists.newArrayList(Long.valueOf(assigner.getPersonId()))) + .needUnit(true) + .needJob(true) + .build()); + return CollectionUtils.isEmpty(users) ? null : users.get(0); + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/process/ProcessInstanceAllEventHandler.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/process/ProcessInstanceAllEventHandler.java new file mode 100644 index 00000000..e0671c21 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/process/ProcessInstanceAllEventHandler.java @@ -0,0 +1,200 @@ +package cn.axzo.nanopart.visa.server.mq.listener.workflow.process; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventProducer; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.nanopart.visa.api.request.ChangeStatusRequest; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordLog; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordRelation; +import cn.axzo.nanopart.visa.server.mq.listener.workflow.BasicLogSupport; +import cn.axzo.nanopart.visa.server.mq.producer.VisaChangeLogPayload; +import cn.axzo.nanopart.visa.server.rpc.VisaOrganizationalNodeUserGateway; +import cn.axzo.nanopart.visa.server.service.ChangeRecordBillService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordRelationService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordService; +import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.axzo.workflow.common.model.response.mq.ProcessInstanceDTO; +import cn.axzo.workflow.starter.handler.ProcessInstanceEventHandler; +import com.google.common.collect.Lists; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.util.Date; +import java.util.List; +import java.util.Objects; + +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.WORKFLOW_VAR_VISA_TYPE_KEY; +import static cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum.REJECT_APPROVE; +import static cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum.REVERT_APPROVE; +import static cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum.PROCESS_INSTANCE_OF_VISA; +import static cn.axzo.nanopart.visa.api.enums.VisaTypeEnum.DESIGN_CHANGE; +import static cn.axzo.nanopart.visa.api.enums.VisaTypeEnum.PROJECT_VISA; +import static cn.axzo.nanopart.visa.api.enums.VisaTypeEnum.TECHNOLOGY_APPROVED; + +/** + * 工作流广播的实例维度的所有事件类型 + * + * @author wangli + * @since 2025-01-17 11:35 + */ +@Component +public class ProcessInstanceAllEventHandler extends BasicLogSupport implements ProcessInstanceEventHandler { + protected final ChangeRecordService changeRecordService; + protected final ChangeRecordRelationService changeRecordRelationService; + protected final ChangeRecordBillService changeRecordBillService; + public static List SUPPORTED_DEFINITION_KEYS = Lists.newArrayList( + DESIGN_CHANGE.getProcessDefinitionKey(), + TECHNOLOGY_APPROVED.getProcessDefinitionKey(), + PROJECT_VISA.getProcessDefinitionKey() + ); + + public ProcessInstanceAllEventHandler(EventProducer eventProducer, + VisaOrganizationalNodeUserGateway visaOrganizationalNodeUserGateway, + ChangeRecordService changeRecordService, + ChangeRecordRelationService changeRecordRelationService, + ChangeRecordBillService changeRecordBillService) { + super(eventProducer, visaOrganizationalNodeUserGateway); + this.changeRecordService = changeRecordService; + this.changeRecordRelationService = changeRecordRelationService; + this.changeRecordBillService = changeRecordBillService; + } + + @Override + public int getOrder() { + return Integer.MIN_VALUE; + } + + @Override + public boolean accept(ProcessInstanceDTO dto, Event event, EventConsumer.Context context) { + return SUPPORTED_DEFINITION_KEYS.contains(dto.getProcessDefinitionKey()); + } + + @Override + public void onCreated(ProcessInstanceDTO dto) { + ProcessInstanceEventHandler.super.onCreated(dto); + } + + private String parseVisaType(ProcessInstanceDTO dto) { + String visaType = (String) dto.getVariables().getOrDefault(WORKFLOW_VAR_VISA_TYPE_KEY, ""); + return StringUtils.hasText(visaType) ? VisaTypeEnum.valueOf(visaType).getDesc() : ""; + } + + @Override + public void onCompleted(ProcessInstanceDTO dto) { + String visaTypeDesc = parseVisaType(dto); + + updateChangeRecordApprovalStatus(dto, BpmnProcessInstanceResultEnum.APPROVED); + } + + /** + * 流程撤回 + * + * @param dto + */ + @Override + public void onCancelled(ProcessInstanceDTO dto) { + BpmnTaskDelegateAssigner initiator = dto.getInitiator(); + String visaTypeDesc = parseVisaType(dto); + + ChangeRecordLog log = ChangeRecordLog.builder() + .visaId(Long.valueOf(dto.getBusinessKey())) + .action(dto.getType().getTag()) + .title(String.format(REVERT_APPROVE.getTitle(), visaTypeDesc)) + .content(String.format(REVERT_APPROVE.getContent(), buildLogUserInfo(initiator))) + .build(); + eventProducer.send(VisaChangeLogPayload.form(log)); + + updateChangeRecordApprovalStatus(dto, BpmnProcessInstanceResultEnum.CANCELLED); + } + + /** + * 流程驳回 + * + * @param dto + */ + @Override + public void onRejected(ProcessInstanceDTO dto) { + BpmnTaskDelegateAssigner lastOperationAssigner = dto.getLastOperationAssigner(); + ChangeRecordLog log = ChangeRecordLog.builder() + .visaId(Long.valueOf(dto.getBusinessKey())) + .action(dto.getType().getTag()) + .title(String.format(REJECT_APPROVE.getTitle(), lastOperationAssigner.getAssignerName())) + .content(String.format(REJECT_APPROVE.getContent(), buildLogUserInfo(lastOperationAssigner), dto.getReason())) + .build(); + eventProducer.send(VisaChangeLogPayload.form(log)); + + updateChangeRecordApprovalStatus(dto, BpmnProcessInstanceResultEnum.REJECTED); + } + + @Override + public void onAborted(ProcessInstanceDTO dto) { + ProcessInstanceEventHandler.super.onAborted(dto); + + updateChangeRecordApprovalStatus(dto, BpmnProcessInstanceResultEnum.ABORTED); + } + + private void updateChangeRecordApprovalStatus(ProcessInstanceDTO dto, BpmnProcessInstanceResultEnum resultEnum) { + // 更新主表审批状态信息 + Long visaId = Long.valueOf(dto.getBusinessKey()); + changeRecordService.lambdaQuery() + .eq(ChangeRecord::getId, visaId) + .eq(ChangeRecord::getApprovalId, dto.getProcessInstanceId()) + .eq(ChangeRecord::getIsDelete, 0) + .oneOpt() + .ifPresent(changeRecord -> { + if (Objects.equals(resultEnum, BpmnProcessInstanceResultEnum.APPROVED)) { + changeRecord.setApprovalStatus(resultEnum.getStatus()); + changeRecord.setApprovalCompleteTime(new Date()); + } else { + changeRecord.setApprovalId(""); + changeRecord.setApprovalStatus(""); + changeRecord.setApprovalCompleteTime(null); + } + changeRecordService.updateById(changeRecord); + }); + + // 更新关联表审批信息 + changeRecordRelationService.lambdaQuery() + .eq(ChangeRecordRelation::getVisaId, visaId) + .eq(ChangeRecordRelation::getVarName, PROCESS_INSTANCE_OF_VISA.name()) + .eq(ChangeRecordRelation::getContent, dto.getProcessInstanceId()) + .oneOpt() + .ifPresent(changeRecordRelation -> { + changeRecordRelation.setContentExt(resultEnum.getStatus()); + changeRecordRelationService.updateById(changeRecordRelation); + }); + + ChangeRecord visa = changeRecordService.getById(visaId); + BpmnTaskDelegateAssigner lastOperationAssigner = dto.getLastOperationAssigner(); + if (Objects.equals(resultEnum, BpmnProcessInstanceResultEnum.APPROVED)) { + changeRecordService.changeStatus(ChangeStatusRequest.builder() + .visaId(visaId) + .personId(Long.valueOf(lastOperationAssigner.getPersonId())) + .ouId(Long.valueOf(lastOperationAssigner.getOuId())) + .workspaceId(Long.valueOf(lastOperationAssigner.getTenantId())) + .updateStatus(VisaStatusEnum.COMPLETED) + .editFormFlag(false) + .build()); + return; + } + // 回退到待提报或决策中 + changeRecordService.changeStatus(ChangeStatusRequest.builder() + .visaId(visaId) + .personId(Long.valueOf(lastOperationAssigner.getPersonId())) + .ouId(Long.valueOf(lastOperationAssigner.getOuId())) + .workspaceId(Long.valueOf(lastOperationAssigner.getTenantId())) + .updateStatus(Objects.isNull(visa.getImGroupId()) ? VisaStatusEnum.REPORT_FROM_APPROVE : VisaStatusEnum.DECIDING_FROM_APPROVE) + .editFormFlag(false) + .build()); + + // 取消当前主单据关联的其他单据的关联状态 + changeRecordBillService.billRelationStatus(visaId, false); + // 取消图纸绑定关系 + changeRecordService.syncDrawAnnotationUnBindRelation(visaId); + + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/task/ProcessTaskAllEventHandler.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/task/ProcessTaskAllEventHandler.java new file mode 100644 index 00000000..821e7416 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/listener/workflow/task/ProcessTaskAllEventHandler.java @@ -0,0 +1,94 @@ +package cn.axzo.nanopart.visa.server.mq.listener.workflow.task; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventProducer; +import cn.axzo.maokai.api.vo.response.OrgNodeUserBriefInfoResp; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordLog; +import cn.axzo.nanopart.visa.server.mq.listener.workflow.BasicLogSupport; +import cn.axzo.nanopart.visa.server.mq.producer.VisaChangeLogPayload; +import cn.axzo.nanopart.visa.server.rpc.VisaOrganizationalNodeUserGateway; +import cn.axzo.workflow.common.model.response.mq.ProcessTaskDTO; +import cn.axzo.workflow.starter.handler.ProcessTaskEventHandler; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +import static cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum.APPROVED_AGRESS; +import static cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum.TRANSMIT_APPROVE; +import static cn.axzo.nanopart.visa.server.mq.listener.workflow.process.ProcessInstanceAllEventHandler.SUPPORTED_DEFINITION_KEYS; + +/** + * 工作流广播的任务维度的所有事件类型 + * + * @author wangli + * @since 2025-01-17 11:34 + */ +@Component +public class ProcessTaskAllEventHandler extends BasicLogSupport implements ProcessTaskEventHandler { + + public ProcessTaskAllEventHandler(EventProducer eventProducer, VisaOrganizationalNodeUserGateway visaOrganizationalNodeUserGateway) { + super(eventProducer, visaOrganizationalNodeUserGateway); + } + + @Override + public int getOrder() { + return Integer.MAX_VALUE; + } + + @Override + public boolean accept(ProcessTaskDTO dto, Event event, EventConsumer.Context context) { + return SUPPORTED_DEFINITION_KEYS.contains(dto.getProcessDefinitionKey()); + } + + @Override + public void onAssigned(ProcessTaskDTO dto) { + ProcessTaskEventHandler.super.onAssigned(dto); + } + + @Override + public void onCreated(ProcessTaskDTO dto) { + ProcessTaskEventHandler.super.onCreated(dto); + } + + /** + * 任务通过 + * + * @param dto + */ + @Override + public void onCompleted(ProcessTaskDTO dto) { + if (Objects.nonNull(dto.getApprover()) + && Objects.equals(dto.getInitiator().getPersonId(), dto.getApprover().getPersonId())) { + return; + } + OrgNodeUserBriefInfoResp user = getOneUserInfo(dto.getApprover()); + + ChangeRecordLog log = ChangeRecordLog.builder() + .visaId(Long.valueOf(dto.getBusinessKey())) + .action(dto.getType().getTag()) + .title(String.format(APPROVED_AGRESS.getTitle(), Objects.isNull(user) ? "" : user.getRealName())) + .content(String.format(APPROVED_AGRESS.getContent(), Objects.isNull(user) ? "系统自动通过" : buildLogUserInfo(user))) + .build(); + eventProducer.send(VisaChangeLogPayload.form(log)); + } + + @Override + public void onDeleted(ProcessTaskDTO dto) { + ProcessTaskEventHandler.super.onDeleted(dto); + } + + @Override + public void onTransfer(ProcessTaskDTO dto) { + ChangeRecordLog log = ChangeRecordLog.builder() + .visaId(Long.valueOf(dto.getBusinessKey())) + .action(dto.getType().getTag()) + .title(String.format(TRANSMIT_APPROVE.getTitle())) + .content(String.format(TRANSMIT_APPROVE.getContent(), + buildLogUserInfo(dto.getApprover()), + buildLogUserInfo(dto.getTransferTargetApprover()), + dto.getAdvice())) + .build(); + eventProducer.send(VisaChangeLogPayload.form(log)); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/producer/VisaChangeLogPayload.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/producer/VisaChangeLogPayload.java new file mode 100644 index 00000000..275f7327 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/producer/VisaChangeLogPayload.java @@ -0,0 +1,63 @@ +package cn.axzo.nanopart.visa.server.mq.producer; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.nanopart.visa.api.enums.MQEventEnum; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordLog; +import cn.axzo.nanopart.visa.server.utils.Constants; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; +import java.util.Date; + +/** + * 变洽签单据日志 MQ Payload + * + * @author wangli + * @since 2025-01-17 16:34 + */ +@Slf4j +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class VisaChangeLogPayload implements Serializable { + private Long visaId; + private String action; + private String title; + private String content; + private Date operateTime; + private Long personId; + private Integer orderBy; + + public static Event form(ChangeRecordLog log) { + VisaChangeLogPayload payload = VisaChangeLogPayload.builder() + .visaId(log.getVisaId()) + .action(log.getAction()) + .title(log.getTitle()) + .content(log.getContent()) + .personId(log.getCreateBy()) + .orderBy(Constants.APPROVE_ORDER_BY) + .build(); + return Event.builder() + .eventCode(MQEventEnum.VISA_CHANGE_LOG.getEventCode()) + .shardingKey(String.valueOf(payload.getVisaId())) + .targetId(String.valueOf(payload.getVisaId())) + .targetType(log.getAction()) + .data(payload) + .build(); + } + + public ChangeRecordLog createRecordLogByPayload() { + return ChangeRecordLog.builder() + .visaId(this.getVisaId()) + .action(this.getAction()) + .title(this.getTitle()) + .content(this.getContent()) + .orderBy(this.getOrderBy()) + .build(); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/producer/VisaChangeLogProducer.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/producer/VisaChangeLogProducer.java new file mode 100644 index 00000000..438f1961 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/mq/producer/VisaChangeLogProducer.java @@ -0,0 +1,15 @@ +package cn.axzo.nanopart.visa.server.mq.producer; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 变洽签单据日志 MQ 生产者 + * + * @author wangli + * @since 2025-01-17 16:33 + */ +@Component +@Slf4j +public class VisaChangeLogProducer { +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/ApolloConstructionAreaGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/ApolloConstructionAreaGateway.java new file mode 100644 index 00000000..912dc186 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/ApolloConstructionAreaGateway.java @@ -0,0 +1,74 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.apollo.api.ApolloConstructionAreaApi; +import cn.axzo.apollo.api.res.ConstructionAreaInfo; +import cn.axzo.apollo.core.web.Result; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.compress.utils.Lists; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author xudawei@axzo.cn + * @date 2025/01/20 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ApolloConstructionAreaGateway { + + private final ApolloConstructionAreaApi apolloConstructionAreaApi; + + public List getAllConstructionAreaByIds(List areaIds) { + try { + if (CollectionUtils.isEmpty(areaIds)) { + return Lists.newArrayList(); + } + log.info("ApolloConstructionAreaGateway getAllConstructionAreaByIds,params:{}", JSON.toJSONString(areaIds)); + Result> result = apolloConstructionAreaApi.getAllConstructionAreaByIds(areaIds); + log.info("ApolloConstructionAreaGateway getAllConstructionAreaByIds,result:{}", JSON.toJSONString(result)); + return result.getData(); + } catch (Exception e) { + log.warn("ApolloConstructionAreaGateway getAllConstructionAreaByIds exception", e); + throw e; + } + + } + + public List getConstructionAreaByIds(List areaIds) { + try { + if (CollectionUtils.isEmpty(areaIds)) { + return Lists.newArrayList(); + } + log.info("ApolloConstructionAreaGateway getConstructionAreaByIds,params:{}", JSON.toJSONString(areaIds)); + Result> result = apolloConstructionAreaApi.getConstructionAreaByIds(areaIds); + log.info("ApolloConstructionAreaGateway getAllConstructionAreaByIds,result:{}", JSON.toJSONString(result)); + return result.getData(); + } catch (Exception e) { + log.warn("ApolloConstructionAreaGateway getConstructionAreaByIds exception", e); + throw e; + } + + } + + public String generateAreaFullNameByAreaIds(List areaIds) { + try { + if (CollectionUtils.isEmpty(areaIds)) { + return ""; + } + log.info("ApolloConstructionAreaGateway generateAreaFullNameByAreaIds,params:{}", JSON.toJSONString(areaIds)); + Result result = apolloConstructionAreaApi.generateAreaFullNameByAreaIds(areaIds); + log.info("ApolloConstructionAreaGateway generateAreaFullNameByAreaIds,result:{}", JSON.toJSONString(result)); + return result.getData(); + } catch (Exception e) { + log.warn("ApolloConstructionAreaGateway generateAreaFullNameByAreaIds exception", e); + throw e; + } + + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/ApolloTaskOrderApiGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/ApolloTaskOrderApiGateway.java new file mode 100644 index 00000000..29916873 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/ApolloTaskOrderApiGateway.java @@ -0,0 +1,28 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.apollo.api.ApolloTaskOrderApi; +import cn.axzo.apollo.api.req.QueryOrderToBasicReq; +import cn.axzo.apollo.api.res.TaskOrderToBasicRes; +import cn.axzo.apollo.core.domain.PageResult; +import cn.axzo.nanopart.visa.server.utils.RpcUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/2/12 11:18 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ApolloTaskOrderApiGateway { + + private final ApolloTaskOrderApi apolloTaskOrderApi; + + public PageResult pageOrderByForBasic(QueryOrderToBasicReq req) { + return RpcUtil.rpcResultProcessor(() -> apolloTaskOrderApi.pageOrderByForBasic(req), + "Page query task orders", req); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DataObjectApiGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DataObjectApiGateway.java new file mode 100644 index 00000000..a691f4ff --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DataObjectApiGateway.java @@ -0,0 +1,42 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.foundation.result.ApiResult; +import cn.axzo.karma.client.feign.tyr.DataObjectApi; +import cn.axzo.karma.client.feign.tyr.request.MatchDataObjectReq; +import cn.axzo.karma.client.feign.tyr.response.MatchDataObjectResp; +import cn.axzo.karma.client.feign.tyr.response.MergeMatchDataResp; +import com.alibaba.fastjson.JSON; +import com.google.common.collect.Lists; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * @author xudawei@axzo.cn + * @date 2025/02/05 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DataObjectApiGateway { + + private final DataObjectApi dataObjectApi; + + public MatchDataObjectResp match(MatchDataObjectReq req) { + if (Objects.isNull(req)) { + return MatchDataObjectResp.builder().build(); + } + try { + log.info("DataObjectApiGateway match, params:{}", JSON.toJSONString(req)); + ApiResult match = dataObjectApi.match(req); + log.info("DataObjectApiGateway match, result:{}", JSON.toJSONString(match)); + return match.getData(); + } catch (Exception e) { + log.warn("DataObjectApiGateway match exception", e); + throw e; + } + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DrawingAnnotationApiGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DrawingAnnotationApiGateway.java new file mode 100644 index 00000000..b7b5e19c --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DrawingAnnotationApiGateway.java @@ -0,0 +1,45 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.nanopart.visa.server.utils.RpcUtil; +import cn.axzo.thor.client.enums.DrawingAnnotationBizEnum; +import cn.axzo.thor.client.feign.DrawingAnnotationApi; +import cn.axzo.thor.client.model.BindDrawingAnnotationDTO; +import cn.axzo.thor.client.model.UnbindDrawingAnnotationDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Set; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/2/19 20:09 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DrawingAnnotationApiGateway { + + private final DrawingAnnotationApi drawingAnnotationApi; + + public void bindBusiness(Long visasId, Set annotationIds) { + BindDrawingAnnotationDTO bindDrawingAnnotationDTO = BindDrawingAnnotationDTO.builder() + .drawingAnnotationIds(annotationIds) + .businessType(DrawingAnnotationBizEnum.VISA_CHANGE) + .businessId(String.valueOf(visasId)) + .build(); + RpcUtil.rpcCommonProcessor(()->drawingAnnotationApi.bindBusiness(bindDrawingAnnotationDTO), + "sync visa and drawing annotation bind relation to thor", bindDrawingAnnotationDTO); + } + + public void unbindBusiness(Long visasId, Set annotationIds) { + UnbindDrawingAnnotationDTO unbindDrawingAnnotationDTO = UnbindDrawingAnnotationDTO.builder() + .drawingAnnotationIds(annotationIds) + .businessType(DrawingAnnotationBizEnum.VISA_CHANGE) + .businessId(String.valueOf(visasId)) + .build(); + RpcUtil.rpcCommonProcessor(()->drawingAnnotationApi.unbindBusiness(unbindDrawingAnnotationDTO), + "sync visa and drawing annotation unbind relation to thor", unbindDrawingAnnotationDTO); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DrawingMajorGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DrawingMajorGateway.java new file mode 100644 index 00000000..2bc46dc7 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/DrawingMajorGateway.java @@ -0,0 +1,37 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.pokonyan.util.RpcUtil; +import cn.axzo.thor.client.feign.DrawingMajorApi; +import cn.axzo.thor.client.model.DrawingMajorResp; +import cn.axzo.thor.client.model.ListDrawingMajorReq; +import cn.azxo.framework.common.logger.MethodAroundLog; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Set; + +/** + * @author xudawei@axzo.cn + * @date 2025/01/20 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DrawingMajorGateway { + + private final DrawingMajorApi drawingMajorApi; + /** + * 获取专业 + */ + @MethodAroundLog(target = "thor", source = "nanopart", value = "获取专业") + public List list(Long projectId, Set majorCodes, Boolean includeDrawingType) { + ListDrawingMajorReq req = new ListDrawingMajorReq(); + req.setProjectId(projectId); + req.setMajorCodes(majorCodes); + req.setIncludeDrawingType(includeDrawingType); + return RpcUtil.rpcCommonProcessor(() -> drawingMajorApi.list(req), "drawingMajorApi#list", req); + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/LogApiGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/LogApiGateway.java new file mode 100644 index 00000000..3352f965 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/LogApiGateway.java @@ -0,0 +1,50 @@ +package cn.axzo.nanopart.visa.server.rpc; + +/** + * @author xudawei@axzo.cn + * @date 2025/2/12 + * @description 日志RPC + */ + +import cn.axzo.log.platform.client.feign.LogApi; +import cn.axzo.log.platform.client.model.req.LogAddReq; +import cn.azxo.framework.common.model.CommonResponse; +import cn.hutool.json.JSONUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 日志RPC + * @author xudawei@axzo.cn + * @since 2025-02-05 15:08 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class LogApiGateway { + + private final LogApi logApi; + + public void addLog(LogAddReq req) { + try { + log.info("LogApiGateway-addLog, req: {}", JSONUtil.toJsonStr(req)); + CommonResponse stringCommonResponse = logApi.addLog(req); + log.info("LogApiGateway-addLog, result: {}", JSONUtil.toJsonStr(stringCommonResponse)); + } catch (Exception e) { + log.warn("LogApiGateway-addLog Exception", e); + } + } + + public void addLog(String scene, String level, List tags, String msg) { + logApi.addLog(LogAddReq.builder() + .scene(scene) + .level(level) + .tags(tags) + .message(msg) + .build()); + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/MsgCenterGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/MsgCenterGateway.java new file mode 100644 index 00000000..081af854 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/MsgCenterGateway.java @@ -0,0 +1,101 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.im.center.api.feign.GroupApi; +import cn.axzo.im.center.api.feign.MessageApi; +import cn.axzo.im.center.api.vo.req.GroupCreateRequest; +import cn.axzo.im.center.api.vo.req.GroupDismissRequest; +import cn.axzo.im.center.api.vo.req.GroupGetMembersRequest; +import cn.axzo.im.center.api.vo.req.GroupGetOwnerRequest; +import cn.axzo.im.center.api.vo.req.SendChatMessageRequest; +import cn.axzo.im.center.api.vo.resp.GroupCreateResponse; +import cn.axzo.im.center.api.vo.resp.GroupGetMembersResponse; +import cn.axzo.im.center.api.vo.resp.GroupGetOwnerResponse; +import cn.axzo.msg.center.service.pending.card.CardClient; +import cn.axzo.msg.center.service.pending.request.CardSendRequest; +import cn.axzo.msg.center.service.pending.request.CardUpdateStateRequest; +import cn.axzo.msg.center.service.pending.response.CardSendResponse; +import cn.azxo.framework.common.logger.MethodAroundLog; +import cn.azxo.framework.common.model.CommonResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * Message / IM 防腐层 + * + * @author wangli + * @since 2025-02-05 15:08 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MsgCenterGateway { + + private final GroupApi groupApi; + private final MessageApi messageApi; + private final CardClient cardClient; + + @MethodAroundLog(target = "imCenter", source = "nanopart", value = "创建 IM 群聊") + public GroupCreateResponse createImGroup(GroupCreateRequest request) { + ApiResult group = groupApi.createGroup(request); + if(group.isSuccess() && Objects.nonNull(group.getData())) { + return group.getData(); + } + AssertUtil.isTrue(group.isSuccess(), group.getMsg()); + return null; + } + + @MethodAroundLog(target = "imCenter", source = "nanopart", value = "解散 IM 群聊") + public void dismissImGroup(GroupDismissRequest request) { + groupApi.dismissGroup(request); + } + + @MethodAroundLog(target = "imCenter", source = "nanopart", value = "获取指定 IM 群成员信息") + public GroupGetMembersResponse getMembers(GroupGetMembersRequest request) { + ApiResult members = groupApi.getMembers(request); + if(members.isSuccess() && Objects.nonNull(members.getData())) { + return members.getData(); + } + AssertUtil.isTrue(members.isSuccess(), members.getMsg()); + return null; + } + + @MethodAroundLog(target = "imCenter", source = "nanopart", value = "获取指定 IM 群群主信息") + public GroupGetOwnerResponse getGroupOwner(GroupGetOwnerRequest request) { + ApiResult owner = groupApi.getOwner(request); + if(owner.isSuccess() && Objects.nonNull(owner.getData())) { + return owner.getData(); + } + AssertUtil.isTrue(owner.isSuccess(), owner.getMsg()); + return null; + } + + @MethodAroundLog(target = "imCenter", source = "nanopart", value = "发送普通消息到群消息") + public Long sendMsgToGroup(SendChatMessageRequest request) { + ApiResult mesageId = messageApi.sendChatMessage(request); + if(mesageId.isSuccess() && Objects.nonNull(mesageId.getData())) { + return mesageId.getData(); + } + AssertUtil.isTrue(mesageId.isSuccess(), mesageId.getMsg()); + return null; + } + + @MethodAroundLog(target = "imCenter", source = "nanopart", value = "发送卡片消息到群消息") + public CardSendResponse sendCardToCroup(CardSendRequest request) { + CommonResponse sendCard = cardClient.send(request); + if (Objects.nonNull(sendCard) && sendCard.getCode() == 200) { + return sendCard.getData(); + } + AssertUtil.isTrue(sendCard.getCode() == 200, sendCard.getMsg()); + return null; + } + + @MethodAroundLog(target = "imCenter", source = "nanopart", value = "更新 IM 群内卡片状态") + public void updateCardState(CardUpdateStateRequest request) { + cardClient.updateState(request); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/OrganizationalUnitGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/OrganizationalUnitGateway.java new file mode 100644 index 00000000..46203d30 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/OrganizationalUnitGateway.java @@ -0,0 +1,58 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.maokai.api.client.OrganizationalUnitApi; +import cn.axzo.maokai.api.vo.request.OrganizationalUnitQuery; +import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO; +import cn.azxo.framework.common.logger.MethodAroundLog; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.utils.Lists; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +/** + * @author xudawei + * @date 2025/01/21 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class OrganizationalUnitGateway { + + private final OrganizationalUnitApi organizationalUnitApi; + + @MethodAroundLog(target = "maokai", source = "nanopart", value = "获取单位信息") + public OrganizationalUnitVO getUnitInfo(Long unitId) { + ApiResult unitResponse = organizationalUnitApi.getById(unitId); + if (unitResponse.isSuccess() && Objects.nonNull(unitResponse.getData())) { + return unitResponse.getData(); + } + AssertUtil.isTrue(unitResponse.isSuccess(), unitResponse.getMsg()); + return null; + } + /** + * 获取单位集合 + */ + @MethodAroundLog(target = "maokai", source = "nanopart", value = "获取单位集合") + public List list(OrganizationalUnitQuery query) { + if (Objects.isNull(query)) { + return Lists.newArrayList(); + } + try { + log.info("OrganizationalUnitGateway list, params:{}", JSON.toJSONString(query)); + ApiResult> list = organizationalUnitApi.list(query); + log.info("OrganizationalUnitGateway list, result:{}", JSON.toJSONString(list)); + List data = list.getData(); + return data; + } catch (Exception e) { + log.warn("OrganizationalUnitGateway list exception", e); + throw e; + } + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/RectifyApiGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/RectifyApiGateway.java new file mode 100644 index 00000000..e2753bb3 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/RectifyApiGateway.java @@ -0,0 +1,29 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.digital.RectifyApi; +import cn.axzo.digital.req.ListRectifyOrderReq; +import cn.axzo.digital.resp.RectifyOrderResp; +import cn.axzo.nanopart.visa.server.utils.RpcUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/2/12 11:37 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class RectifyApiGateway { + + private final RectifyApi rectifyApi; + + public List listRectifyOrders(ListRectifyOrderReq req) { + return RpcUtil.rpcApiResultProcessor(() -> rectifyApi.listRectifyOrders(req), + "Get rectify orders by ids", req); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaCooperateShipGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaCooperateShipGateway.java new file mode 100644 index 00000000..b9dd0ed7 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaCooperateShipGateway.java @@ -0,0 +1,34 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.maokai.api.client.CooperateShipQueryApi; +import cn.axzo.maokai.api.vo.request.CooperateShipQueryReq; +import cn.axzo.maokai.api.vo.response.CooperateShipResp; +import cn.axzo.nanopart.visa.server.utils.RpcUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2024/4/9 11:20 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VisaCooperateShipGateway { + + private final CooperateShipQueryApi cooperateShipQueryApi; + + public List genericQuery(CooperateShipQueryReq req) { + if (Objects.isNull(req)) { + return Collections.emptyList(); + } + return RpcUtil.rpcApiListResultProcessor(() -> cooperateShipQueryApi.genericQuery(req), + "协同关系树查询"); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaOrganizationalNodeGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaOrganizationalNodeGateway.java new file mode 100644 index 00000000..b59d48aa --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaOrganizationalNodeGateway.java @@ -0,0 +1,29 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.maokai.api.client.OrganizationalNodeApi; +import cn.axzo.maokai.api.vo.response.OrganizationalNodeVO; +import cn.axzo.pokonyan.util.RpcUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * TODO + * + * @author wangli + * @since 2025-02-28 11:11 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VisaOrganizationalNodeGateway { + + @Resource + private OrganizationalNodeApi organizationalNodeApi; + + public OrganizationalNodeVO getNode(Long nodeId) { + return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeApi.getNode(nodeId), "", nodeId); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaOrganizationalNodeUserGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaOrganizationalNodeUserGateway.java new file mode 100644 index 00000000..44e30ec3 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaOrganizationalNodeUserGateway.java @@ -0,0 +1,56 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.foundation.page.PageResp; +import cn.axzo.foundation.result.ApiResult; +import cn.axzo.maokai.api.client.OrganizationalNodeUserQueryApi; +import cn.axzo.maokai.api.vo.request.OrgNodeUserBriefInfoListReq; +import cn.axzo.maokai.api.vo.response.OrgNodeUserBriefInfoResp; +import cn.axzo.orggateway.api.nodeuser.OrgNodeUserApi; +import cn.axzo.orggateway.api.nodeuser.dto.OrgNodeUserDTO; +import cn.axzo.orggateway.api.nodeuser.req.ListOrgNodeUserReq; +import cn.axzo.pokonyan.util.RpcUtil; +import cn.azxo.framework.common.logger.MethodAroundLog; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2024/4/8 20:40 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class VisaOrganizationalNodeUserGateway { + + private final OrganizationalNodeUserQueryApi organizationalNodeUserQueryApi; + + private final OrgNodeUserApi orgNodeUserApi; + + @MethodAroundLog(target = "maokai", source = "nanopart", value = "获取用户列表") + public List listOrgNodeUsers(OrgNodeUserBriefInfoListReq req) { + return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeUserQueryApi.listOrgNodeUsers(req), "获取用户列表", req); + } + + @MethodAroundLog(target = "org-gateway", source = "nanopart", value = "获取用户列表") + public PageResp list(ListOrgNodeUserReq req) { + if (Objects.isNull(req)) { + return new PageResp(); + } + try { + log.info("VisaOrganizationalNodeUserGateway list, params:{}", req); + ApiResult> list = orgNodeUserApi.list(req); + log.info("VisaOrganizationalNodeUserGateway list, result:{}", JSON.toJSONString(list)); + return list.getData(); + } catch (Exception e) { + log.warn("VisaOrganizationalNodeUserGateway list exception", e); + throw e; + } + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaProfileGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaProfileGateway.java new file mode 100644 index 00000000..154634d4 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaProfileGateway.java @@ -0,0 +1,79 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.basics.profiles.api.UserProfileServiceApi; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.azxo.framework.common.logger.MethodAroundLog; +import cn.azxo.framework.common.model.CommonResponse; +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Maps; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2024/4/17 19:39 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VisaProfileGateway { + + private final UserProfileServiceApi userProfileServiceApi; + + /** + * 通过personId获取用户档案 + * + * @param personId + * @return + */ + @MethodAroundLog(target = "pudge", source = "nanopart", value = "获取用户档案") + public PersonProfileDto getProfile(Long personId) { + if (Objects.isNull(personId)) { + return null; + } + CommonResponse personProfile = userProfileServiceApi.getPersonProfile(personId); + if (Objects.nonNull(personProfile) && personProfile.getCode() == 200) { + return personProfile.getData(); + } + AssertUtil.isTrue(personProfile.getCode() == 200, personProfile.getMsg()); + return null; + } + + /** + * 通过personId获取用户档案 + * + * @param personIds + * @return + */ + @MethodAroundLog(target = "pudge", source = "nanopart", value = "获取用户档案") + public List getProfiles(List personIds) { + if (CollectionUtil.isEmpty(personIds)) { + return Collections.emptyList(); + } + CommonResponse> personProfiles = userProfileServiceApi.getPersonProfiles(personIds); + if (Objects.nonNull(personProfiles) && personProfiles.getCode() == 200) { + return personProfiles.getData(); + } + return Collections.emptyList(); + } + + @MethodAroundLog(target = "pudge", source = "nanopart", value = "获取用户档案并转换Map") + public Map getProfileMap(List personIds) { + List profiles = getProfiles(personIds); + if (CollectionUtils.isEmpty(profiles)) { + return Maps.newHashMap(); + } + return profiles.stream().collect(Collectors.toMap(PersonProfileDto::getId, Function.identity(), (x, y) -> x)); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaProjectApiGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaProjectApiGateway.java new file mode 100644 index 00000000..656a9248 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/VisaProjectApiGateway.java @@ -0,0 +1,43 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.apollo.workspace.api.workspace.ProjectApi; +import cn.axzo.apollo.workspace.api.workspace.res.ProjectBriefResp; +import cn.axzo.apollo.workspace.api.workspace.res.ProjectDetailRes; +import cn.axzo.basics.common.util.NumberUtil; +import cn.axzo.nanopart.visa.server.utils.RpcUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/20 10:36 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VisaProjectApiGateway { + + private final ProjectApi projectApi; + + public ProjectDetailRes getProjectById(Long id) { + if (NumberUtil.isNotPositiveNumber(id)) { + return null; + } + return RpcUtil.rpcResultProcessor(() -> projectApi.getById(id), "通过id获取工程详情", id); + } + + public List getProjectBriefByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return Collections.emptyList(); + } + return RpcUtil.rpcResultProcessor(() -> projectApi.simpleList(ids), "通过ids获取工程简单信息", ids); + } + + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/WorkflowGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/WorkflowGateway.java new file mode 100644 index 00000000..9f5145b6 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/WorkflowGateway.java @@ -0,0 +1,64 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCheckApproverDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO; +import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO; +import cn.axzo.workflow.starter.api.WorkflowCoreService; +import cn.axzo.workflow.starter.api.WorkflowManageService; +import cn.azxo.framework.common.logger.MethodAroundLog; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +/** + * @author xudawei + * @date 2025/01/22 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class WorkflowGateway { + + private final WorkflowCoreService workflowCoreService; + private final WorkflowManageService workflowManageService; + + @MethodAroundLog(target = "workflow-engine", source = "nanopart", value = "创建流程实例") + public String processInstanceCreate(BpmnProcessInstanceCreateDTO dto) { + return workflowCoreService.createProcessInstance(dto); + } + + @MethodAroundLog(target = "workflow-engine", source = "nanopart", value = "中止流程实例") + public Boolean processInstanceAbort(BpmnProcessInstanceAbortDTO dto) { + return workflowCoreService.abortProcessInstance(dto); + } + + @MethodAroundLog(target = "workflow-engine", source = "nanopart", value = "查询某个人是否是指定流程的审批人") + public Boolean checkInstanceApprover(BpmnProcessInstanceCheckApproverDTO dto) { + try { + log.info("WorkflowManagerGateway checkInstanceApprover,param:{}", JSON.toJSONString(dto)); + Boolean flag = workflowManageService.checkInstanceApprover(dto); + log.info("WorkflowManagerGateway checkInstanceApprover,result:{}", flag); + return flag; + } catch (Exception e) { + log.warn("WorkflowManagerGateway checkInstanceApprover exception", e); + throw e; + } + + } + + @MethodAroundLog(target = "workflow-engine", source = "nanopart", value = "更新指定流程表单最后一次编辑的内容") + public Boolean updateInstanceFormData(FormVariablesUpdateDTO dto) { + return workflowCoreService.updateInstanceFormVariables(dto); + } + + @MethodAroundLog(target = "workflow-engine", source = "nanopart", value = "更新指定流程表单最后一次编辑的内容") + public Boolean hasPrintTemplate(String processInstanceId) { + if(!StringUtils.hasText(processInstanceId)) { + return false; + } + return workflowManageService.hasPrintTemplate(processInstanceId); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/WorkspaceGateway.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/WorkspaceGateway.java new file mode 100644 index 00000000..3996fa13 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/rpc/WorkspaceGateway.java @@ -0,0 +1,44 @@ +package cn.axzo.nanopart.visa.server.rpc; + +import cn.axzo.apollo.core.web.Result; +import cn.axzo.apollo.workspace.api.workspace.WorkspaceApi; +import cn.axzo.apollo.workspace.api.workspace.res.GetDetailRes; +import cn.azxo.framework.common.logger.MethodAroundLog; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * @author xudawei + * @date 2025/01/21 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class WorkspaceGateway { + + private final WorkspaceApi workspaceApi; + + /** + * 通过workspaceId获取对象 + */ + @MethodAroundLog(target = "workspace", source = "nanopart", value = "获取项目信息") + public GetDetailRes getById(Long workspaceId) { + if (Objects.isNull(workspaceId)) { + return GetDetailRes.builder().build(); + } + try { + log.info("WorkspaceGateway getById, params:{}", workspaceId); + Result result = workspaceApi.getById(workspaceId); + log.info("WorkspaceGateway getById, result:{}", JSON.toJSONString(result)); + return result.getData(); + } catch (Exception e) { + log.warn("WorkspaceGateway getById exception", e); + throw e; + } + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordBillService.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordBillService.java new file mode 100644 index 00000000..bd9c3b96 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordBillService.java @@ -0,0 +1,70 @@ +package cn.axzo.nanopart.visa.server.service; + +import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum; +import cn.axzo.nanopart.visa.api.request.GetBillIfRelatedReq; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordBill; +import cn.axzo.nanopart.visa.server.dto.VisaBillDto; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; +import java.util.Map; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录内容关系表 + */ +public interface ChangeRecordBillService extends IService { + + + /** + * 新增 + */ + Boolean addBill(List dtoList); + + /** + * 更新 + */ + Boolean updateBill(Long visaId, List dtoList); + + /** + * 更新关联单据的被关联状态 + * + * @param visaId + * @param related + * @return + */ + Boolean billRelationStatus(Long visaId, Boolean related); + + /** + * 删除 + */ + Boolean deleteByVisaId(Long visaId); + + Boolean deleteByCondition(List billIds, VisaBillTypeEnum billType); + + /** + * 查询记录 + */ + List findByCondition(VisaBillDto dto); + + /** + * 通过Id,查询记录 + */ + List listById(Long visaId); + + Map getBillIfRelated(GetBillIfRelatedReq req); + + /** + * 获取指定类型单据的主单据 + *

+ * 特别提醒:该接口返回的数据包含待提报等非审批态单据的关联单据信息 + *

+ * + * @param billType 单据类型 + * @param billIdNos 单据id或no + * @return 主单据信息 + */ + List getRelatedMainBill(VisaBillTypeEnum billType, List billIdNos); + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordConfirmService.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordConfirmService.java new file mode 100644 index 00000000..a60f4a29 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordConfirmService.java @@ -0,0 +1,84 @@ +package cn.axzo.nanopart.visa.server.service; + +import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaConfirmBizTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveCreateReq; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordConfirm; +import cn.axzo.nanopart.visa.server.dto.VisaConfirmDto; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录确认 + */ +public interface ChangeRecordConfirmService { + + /** + * 新增变更签证确认信息 + * + * @param dtoList 变更签证确认信息 + * @return true:成功;false:失败 + */ + boolean addConfirm(List dtoList); + + /** + * 更新变更签证确认信息 + * + * @param visaId 变更签证Id + * @param dtoList 变更签证确认 + * @return true:成功;false:失败 + */ + boolean updateConfirm(Long visaId, List dtoList); + + /** + * 通过变更签证Id,删除变更签证确认信息 + */ + boolean deleteByVisaId(Long visaId, List bizTypes); + + /** + * 变更签证Id获取map + */ + List listRelationByVisaId(List list, Long workspaceId); + + /** + * 根据条件查询 + * + * @param dto 查询条件 + * @return 人员列表 + */ + List findByCondition(VisaConfirmDto dto); + /** + * 获取按钮集合 + * 确认人与发起人的不同的单据状态,按钮列表不相同 + */ + List fetchBtnsByCondition(Long visaId, Long personId, Long ouId, Long workspaceId, VisaStatusEnum status, String approvalId); + + /** + * 权限过滤 + * @param dataObjectCode OMS提供权限code + * @param currentPersonId 当前登录人personId + * @param currentWorkspaceId 当前登录人项目Id + * @param currentOuId 当前登录人的单位Id + * @return 变更签证visaId集合 + */ + Set permissionVisaData(String dataObjectCode, Long currentPersonId, Long currentWorkspaceId, Long currentOuId, Set visaTypes); + + /** + * 获取指定的变洽签所有的确认人 + * @param visaId + * @return + */ + List listAllConfirmByVisaId(Long visaId); + + /** + * 构建公司的Map,orgIdAndNameMap,key:ouId,value:ouName + */ + Map buildOrgIdAndNameMap(Set ouIdSet); +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordLogService.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordLogService.java new file mode 100644 index 00000000..00d96bfa --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordLogService.java @@ -0,0 +1,63 @@ +package cn.axzo.nanopart.visa.server.service; + +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import cn.axzo.nanopart.visa.api.response.FetchVisaLogByVisaIdResponse; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordLog; +import cn.axzo.nanopart.visa.server.dto.VisaLogParam; + +import java.util.List; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录操作日志 + */ +public interface ChangeRecordLogService { + + /** + * 增加操作日志 + */ + void addLog(ChangeRecordLog log); + + /** + * 增加操作日志 + */ + void batchAddLog(List logList); + + /** + * 增加操作日志 + */ + void batchAddLog(Long visaId, VisaLogTypeEnum type, VisaLogParam param); + + /** + * 构建日志集合 + */ + List buildLogList(Long visaId, VisaLogTypeEnum type, VisaLogParam param); + + /** + * 通过变签Id获取日志 + * @param visaId 变签Id + */ + List fetchVisaLogByVisaId(Long visaId); + + /** + * 构建操作人信息 + */ + String buildUserName(Long personId, Long ouId, Long workspaceId); + + /** + * 通过变签Id获取日志 + * @param visaId 变签Id + * @param logType 类型 + */ + List listByVisaIdType(Long visaId, VisaLogTypeEnum logType); + + /** + * 是否有日志类型 + * @param visaId 变更签证Id + * @param logType 类型 + * @return true:有;false:没有 + */ + boolean hasLogType(Long visaId, VisaLogTypeEnum logType); + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordRelationService.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordRelationService.java new file mode 100644 index 00000000..b0cb25c9 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordRelationService.java @@ -0,0 +1,70 @@ +package cn.axzo.nanopart.visa.server.service; + +import cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum; +import cn.axzo.nanopart.visa.api.request.ChangeRecordAppendAttachReq; +import cn.axzo.nanopart.visa.api.request.ImGroupOperationClearReq; +import cn.axzo.nanopart.visa.api.request.ImGroupOperationReq; +import cn.axzo.nanopart.visa.api.request.ImGroupTipsQueryReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq; +import cn.axzo.nanopart.visa.api.response.ImGroupTipsResp; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordRelation; +import cn.axzo.nanopart.visa.server.dto.VisaRelationDto; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录内容关系表 + */ +public interface ChangeRecordRelationService extends IService { + + boolean save(VisaRelationFieldEnum type, VisaChangeTempCreateReq req, ChangeRecord changeRecord); + + /** + * 获取集合 + */ + List findByCondition(VisaRelationDto dto); + + /** + * 根据名称与变更签证Id,获取集合 + */ + List findByVisaAndVarName(Long visaId, String varName); + + boolean deleteByVisaId(Long visaId, List relationTypes); + + /** + * IM 群聊中,保存 IM 用户操作横条的人 + * + * @param req + * @return + */ + Boolean saveImOperationParticipate(ImGroupOperationReq req); + + /** + * IM 群聊中,群主操作重新发起,清空所有人的操作信息 + * + * @param req + * @return + */ + Boolean clearImOperationParticipate(ImGroupOperationClearReq req); + + /** + * IM 群聊界面,查询当前人的 tips 横条 + * + * @param req + * @return + */ + ImGroupTipsResp queryImGroupTips(ImGroupTipsQueryReq req); + + /** + * 追加附件 + * + * @param req + */ + void appendAttachment(ChangeRecordAppendAttachReq req); + + boolean deleteByIds(List ids); +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordService.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordService.java new file mode 100644 index 00000000..4012af6c --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/ChangeRecordService.java @@ -0,0 +1,160 @@ +package cn.axzo.nanopart.visa.server.service; + +import cn.axzo.framework.domain.web.result.PageData; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.request.ChangeRecordButtonOperationReq; +import cn.axzo.nanopart.visa.api.request.ChangeStatusRequest; +import cn.axzo.nanopart.visa.api.request.CheckVisaWithVisaIdReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveOnlyReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeDiscussCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeExportReq; +import cn.axzo.nanopart.visa.api.request.VisaChangePageSearchReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaInitiatorListReq; +import cn.axzo.nanopart.visa.api.request.VisaSearchReq; +import cn.axzo.nanopart.visa.api.response.VisaChangeDiscussCreateResp; +import cn.axzo.nanopart.visa.api.response.VisaChangeInitiatorResp; +import cn.axzo.nanopart.visa.api.response.VisaChangePageSearchResp; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import cn.axzo.nanopart.visa.api.response.VisaSearchResp; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordRelation; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录 + */ +public interface ChangeRecordService extends IService { + + /** + * 状态变更 + */ + Boolean changeStatus(ChangeStatusRequest request); + + /** + * 更新DB变签状态 + */ + boolean changeDbStatus(Long visaId, List statusList, VisaStatusEnum updateStatus); + + /** + * 通过变更签证Id获取Entity + * + * @param visaId 变更签证Id + * @return 变更签证Entity + */ + ChangeRecord getById(Long visaId); + + /** + * 变更签证详情 + * + * @param visaId 变更签证Id + * @return 变更签证详情 + */ + VisaDetailByIdResponse detailById(Long visaId); + + /** + * 附件对象转换 + */ + List buildAttachUploadFileByRelation(List attachList); + + /** + * 构建上传文件对象集合 + */ + List buildAttachUploadFile(Long visaId); + + /** + * 创建变更签证 + * + * @param req 表单数据 + * @return 变更签证Id + */ + Long tempCreateVisaChangeRecord(VisaChangeTempCreateReq req); + + /** + * 创建群聊洽商变更签证 + * + * @param req 表单数据 + * @return 变更签证Id + */ + VisaChangeDiscussCreateResp discussCreateVisaChangeRecord(VisaChangeDiscussCreateReq req); + + /** + * 创建审批变更签证 + * + * @param req 表单数据 + * @return 变更签证Id + */ + Long approveCreateVisaChangeRecord(VisaChangeApproveCreateReq req); + + /** + * 分页查询 + * + * @param req 筛查条件 + * @return 分页数据 + */ + PageData page(VisaChangePageSearchReq req); + + /** + * 查询发起人列表 + * + * @param req 项目Id + * @return 发起人列表 + */ + List listInitiator(VisaInitiatorListReq req); + + /** + * 导出 + * + * @param req 筛查条件和导出字段 + */ + void export(VisaChangeExportReq req); + + /** + * 变洽签单据废止,并解散 IM 群聊,中止审批 + * + * @param req + */ + void forbid(ChangeRecordButtonOperationReq req); + + /** + * IM 群聊中提交审批 + * + * @param req + * @return + */ + Long approveCreateOnlyVisaChangeRecord(VisaChangeApproveOnlyReq req); + + void reDecision(ChangeRecordButtonOperationReq req); + + void doExecute(ChangeRecordButtonOperationReq req); + + void delete(ChangeRecordButtonOperationReq req); + + /** + * 校验 + */ + void checkVisaWithVisaId(CheckVisaWithVisaIdReq req); + + /** + * 查询变更签证集合 + */ + PageData list(VisaSearchReq req); + + /** + * 同步图纸与变更签证单关联关系 + * @param visaId 变更签证Id + */ + void syncDrawAnnotationBindRelation(Long visaId); + + /** + * 同步图纸与变更签证单解除关联关系 + * @param visaId 变更签证Id + */ + void syncDrawAnnotationUnBindRelation(Long visaId); + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/VisaHelper.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/VisaHelper.java new file mode 100644 index 00000000..8eb1bf88 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/VisaHelper.java @@ -0,0 +1,263 @@ +package cn.axzo.nanopart.visa.server.service; + +import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaConfirmBizTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.request.ChangeStatusRequest; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordConfirm; +import cn.axzo.nanopart.visa.server.dto.VisaAddLogContext; +import cn.axzo.nanopart.visa.server.dto.VisaConfirmDto; +import cn.axzo.nanopart.visa.server.dto.VisaLogParam; +import cn.axzo.nanopart.visa.server.rpc.ApolloConstructionAreaGateway; +import cn.axzo.nanopart.visa.server.rpc.DrawingMajorGateway; +import cn.axzo.nanopart.visa.server.rpc.LogApiGateway; +import cn.axzo.nanopart.visa.server.utils.Constants; +import cn.axzo.thor.client.model.DrawingMajorResp; +import cn.hutool.core.lang.Pair; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONArray; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author xudawei + * @date 2025/01/22 + * @desc 变更签证协助 + */ +@Slf4j +@Service +public class VisaHelper { + + @Resource + private ChangeRecordLogService changeRecordLogService; + + @Resource + private ChangeRecordService changeRecordService; + + @Resource + private DrawingMajorGateway drawingMajorGateway; + + @Resource + private ApolloConstructionAreaGateway apolloConstructionAreaGateway; + + @Resource + private ChangeRecordConfirmService changeRecordConfirmService; + + @Resource + private LogApiGateway logApiGateway; + + /** + * 状态更新与添加日志,此时新增记录时状态没有维护 + */ + public void addLogAndChangeStatus(VisaAddLogContext oldContext, VisaAddLogContext newContext) { + //oldContext为空,newContext不为空,则是新增; + // 操作日志:只有状态变更,没有属性变更日志 + if (Objects.isNull(oldContext) && Objects.nonNull(newContext)) { + changeRecordLogService.batchAddLog(newContext.getChangeRecord().getId(), newContext.getStatus().getLogType(), VisaLogParam.builder() + .personId(newContext.getPersonId()) + .ouId(newContext.getOuId()) + .workspaceId(newContext.getWorkspaceId()).build()); + changeRecordService.changeDbStatus(newContext.getChangeRecord().getId(), VisaStatusEnum.allStatus(), newContext.getStatus()); + return; + } + + //编辑时 + if (Objects.nonNull(oldContext) && Objects.nonNull(newContext)) { + + // 由[决策中|执行中] -> [审批中] 执行变更签证属性变更 + if (Objects.nonNull(oldContext.getStatus()) && Objects.nonNull(newContext.getStatus()) + && VisaStatusEnum.isEditForm(oldContext.getStatus()) + && VisaStatusEnum.isEditForm(newContext.getStatus())) { + + List oldUploadFiles = this.changeRecordService.buildAttachUploadFileByRelation(oldContext.getRelationList()); + List newUploadFiles = this.changeRecordService.buildAttachUploadFile(newContext.getChangeRecord().getId()); + + + changeRecordLogService.batchAddLog(newContext.getChangeRecord().getId(), VisaLogTypeEnum.EDIT_FORM, VisaLogParam.builder() + .personId(newContext.getPersonId()) + .ouId(newContext.getOuId()) + .workspaceId(newContext.getWorkspaceId()) + .newAmountChange(newContext.getChangeRecord().getAmountChange()) + .oldAmountChange(oldContext.getChangeRecord().getAmountChange()) + .uploadAttach(VisaLogParam.uploadAttachWithObject(newUploadFiles, oldUploadFiles)) + .deleteAttach(VisaLogParam.deleteAttachWithObject(oldUploadFiles, newUploadFiles)) + .build()); + + logApiGateway.addLog(Constants.VISA_LOG_SNAPSHOT_SCENE + , Constants.VISA_LOG_SNAPSHOT_LEVEL + , Lists.newArrayList(Constants.VISA_LOG_SNAPSHOT_TAG_SNAPSHOT) + , JSONUtil.toJsonStr(Pair.of(oldContext, newContext))); + } + changeRecordService.changeStatus(ChangeStatusRequest.builder() + .visaId(newContext.getChangeRecord().getId()) + .updateStatus(newContext.getStatus()) + .personId(newContext.getPersonId()) + .ouId(newContext.getOuId()) + .workspaceId(newContext.getWorkspaceId()) + .newAmountChange(newContext.getChangeRecord().getAmountChange()) + .oldAmountChange(oldContext.getChangeRecord().getAmountChange()) +// .newAttach(newContext.getAttachList()) +// .oldAttach(oldContext.getAttachList()) + .editFormFlag(true) + .build()); + } + + } + + /** + * 构建详情的专业回显 + */ + public String buildProfessionalName(Long projectId, JSONArray professionals) { + if (Objects.isNull(projectId) || CollectionUtils.isEmpty(professionals)) { + return ""; + } + Set majorCodes = VisaDetailByIdResponse.RelationProfessionalDetail.buildProfessionWithCode(professionals); + if (CollectionUtils.isEmpty(majorCodes)) { + return ""; + } + List majorRespList = drawingMajorGateway.list(projectId, majorCodes, true); + + // 构建专业集合 key:叶子结点code,value:父节点+子节点的名字 + Map codeNameMap = this.buildProfessionalLeafName(majorRespList); + + if (CollectionUtils.isEmpty(majorCodes)) { + return ""; + } + + return codeNameMap.values().stream().filter(item -> StringUtils.hasText(item)).collect(Collectors.joining(",")); + } + + /** + * 构建区域名称 + */ + public String buildAreaName(JSONArray areas) { + if (CollectionUtils.isEmpty(areas)) { + return ""; + } + return apolloConstructionAreaGateway.generateAreaFullNameByAreaIds(VisaDetailByIdResponse.RelationAreaDetail.buildAreaWithId(areas)); + } + + /** + * @param list 专业集合 + * @return key:叶子结点code,value:父节点+子节点的名字 + */ + private Map buildProfessionalLeafName(List list) { + if (CollectionUtils.isEmpty(list)) { + return Collections.emptyMap(); + } + + Map codeNameMap = new HashMap<>(); + for (DrawingMajorResp resp : list) { + recursionProfessional(resp, resp.getName(), codeNameMap); + } + return codeNameMap; + } + + /** + * 递归专业 + * codeNameMap,key:叶子结点code,value:父节点+子节点的名字 + */ + private void recursionProfessional(DrawingMajorResp drawingMajorResp, String currentName, Map codeNameMap) { + + if (CollectionUtils.isEmpty(drawingMajorResp.getChildren())) { + codeNameMap.put(drawingMajorResp.getCode(), currentName); + return; + } + for (DrawingMajorResp resp : drawingMajorResp.getChildren()) { + recursionProfessional(resp, currentName + Constants.SPLIT_TREE_NODE_CHAT + resp.getName(), codeNameMap); + } + } + + /** + * 查询单据与当前人的角色:[发起人|确认人|其他用户] + */ + public Map> fetchConfirmBizTypes(Collection visaList, Long personId, Long ouId, Long workspaceId) { + + if (CollectionUtils.isEmpty(visaList)) { + return Collections.emptyMap(); + } + List visaIds = visaList.stream().filter(item -> Objects.nonNull(item.getId())) + .map(ChangeRecord::getId).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(visaIds)) { + return Collections.emptyMap(); + } + + List confirmList = changeRecordConfirmService.findByCondition(VisaConfirmDto.builder() + .visaIds(visaIds) + .personId(personId) + .ouId(ouId) + .workspaceId(workspaceId).build()); + if (CollectionUtils.isEmpty(confirmList)) { + return visaIds.stream().collect(Collectors.toMap(visaId -> visaId, visaId -> Sets.newHashSet(VisaConfirmBizTypeEnum.OTHER))); + } + Map> visaBizTypeMap = confirmList.stream().collect(Collectors.groupingBy(ChangeRecordConfirm::getVisaId + , Collectors.mapping(ChangeRecordConfirm::getBizType, Collectors.toSet()))); + visaIds.stream().forEach(item -> { + visaBizTypeMap.put(item, visaBizTypeMap.getOrDefault(item, Sets.newHashSet(VisaConfirmBizTypeEnum.OTHER))); + }); + return visaBizTypeMap; + } + + + /** + * 获取按钮集合 + * 确认人与发起人的不同的单据状态,按钮列表不相同 + */ + public Map> fetchBtnsByBizTypeAndStatus(Collection visaList, Long personId, Long ouId, Long workspaceId) { + if (CollectionUtils.isEmpty(visaList)) { + return Collections.emptyMap(); + } + Map visaIdMap = visaList.stream() + .filter(item -> Objects.nonNull(item.getId())) + .collect(Collectors.toMap(ChangeRecord::getId, Function.identity(), (x, y) -> x)); + Map> visaIdBizTypeMap = fetchConfirmBizTypes(visaList, personId, ouId, workspaceId); + if (Objects.isNull(visaIdBizTypeMap) || visaIdBizTypeMap.isEmpty()) { + return Collections.emptyMap(); + } + Map> returnMap = Maps.newHashMap(); + + visaIdBizTypeMap.entrySet().stream().forEach(entry -> { + Long key = entry.getKey(); + Set value = entry.getValue(); + + ChangeRecord changeRecord = visaIdMap.get(key); + if (Objects.nonNull(changeRecord) && Objects.nonNull(changeRecord.getStatus())) { + returnMap.put(key, VisaButtonTypeEnum.fetchBtnsByBizTypeAndStatusWhenPage(value, changeRecord.getStatus())); + } + }); + return returnMap; + } + + public static String buildChangeAmount(BigDecimal changeAmount) { + if (Objects.isNull(changeAmount)) { + return ""; + } + String amountDesc = "不变"; + if (changeAmount.compareTo(BigDecimal.ZERO) > 0) { + amountDesc = "增加" + changeAmount.abs().toPlainString() + "元"; + } else if (changeAmount.compareTo(BigDecimal.ZERO) < 0) { + amountDesc = "减少" + changeAmount.abs().toPlainString() + "元"; + } + return amountDesc; + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordBillServiceImpl.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordBillServiceImpl.java new file mode 100644 index 00000000..15dde76e --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordBillServiceImpl.java @@ -0,0 +1,167 @@ +package cn.axzo.nanopart.visa.server.service.impl; + +import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum; +import cn.axzo.nanopart.visa.api.request.GetBillIfRelatedReq; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordBill; +import cn.axzo.nanopart.visa.server.dto.VisaBillDto; +import cn.axzo.nanopart.visa.server.mapper.ChangeRecordBillDao; +import cn.axzo.nanopart.visa.server.service.ChangeRecordBillService; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.compress.utils.Lists; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录内容关系表 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ChangeRecordBillServiceImpl extends ServiceImpl implements ChangeRecordBillService { + + /** + * 新增 + */ + public Boolean addBill(List dtoList) { + List billList = BeanUtil.copyToList(dtoList, ChangeRecordBill.class); + return this.saveOrUpdateBatch(billList); + } + + /** + * 更新 + */ + public Boolean updateBill(Long visaId, List dtoList) { + // 删除 + this.deleteByVisaId(visaId); + // 插入 + return this.addBill(dtoList); + } + + public Boolean billRelationStatus(Long visaId, Boolean related) { + return this.lambdaUpdate().eq(ChangeRecordBill::getVisaId, visaId) + .eq(ChangeRecordBill::getIsDelete, 0) + .set(ChangeRecordBill::getRelationStatus, related) + .update(); + } + + /** + * 删除 + */ + public Boolean deleteByVisaId(Long visaId) { + if (Objects.isNull(visaId)) { + return false; + } + return this.lambdaUpdate().eq(ChangeRecordBill::getVisaId, visaId) + .eq(ChangeRecordBill::getIsDelete, 0) + .setSql("is_delete = id").update(); + } + + public Boolean deleteByCondition(List billIds, VisaBillTypeEnum billType) { + if (CollectionUtils.isEmpty(billIds)) { + return false; + } + return this.lambdaUpdate().in(ChangeRecordBill::getBillId, billIds) + .eq(ChangeRecordBill::getBillType, billType) + .eq(ChangeRecordBill::getIsDelete, 0) + .setSql("is_delete = id").update(); + + } + + /** + * 查询记录 + */ + public List findByCondition(VisaBillDto dto) { + return this.list(buildLambdaQueryWrapper(dto)); + } + + /** + * 通过Id,查询记录 + */ + public List listById(Long visaId) { + if (Objects.isNull(visaId)) { + return Lists.newArrayList(); + } + VisaBillDto dto = VisaBillDto.builder().visaId(visaId).build(); + return findByCondition(dto); + } + + @Override + public Map getBillIfRelated(GetBillIfRelatedReq req) { + if (CollUtil.isEmpty(req.getBillIds())) { + return Collections.emptyMap(); + } + List relatedMainBill = getRelatedMainBill(req.getBillType(), req.getBillIds()); + if (CollUtil.isNotEmpty(relatedMainBill)) { + Map> billMainMap; + if (VisaBillTypeEnum.TASK.equals(req.getBillType())) { + billMainMap = relatedMainBill.stream() + .collect(Collectors.groupingBy(ChangeRecordBill::getBillNo)); + } else { + billMainMap = relatedMainBill.stream() + .collect(Collectors.groupingBy(r -> String.valueOf(r.getBillId()))); + } + Map billMainRelatedMap = billMainMap.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, + c -> c.getValue().stream() + .anyMatch(b -> Objects.equals(b.getVisaType(), req.getVisaType())))); + return req.getBillIds().stream() + .collect(Collectors.toMap(Function.identity(), t -> billMainRelatedMap.getOrDefault(t, false))); + } + return req.getBillIds().stream() + .collect(Collectors.toMap(Function.identity(), b -> false)); + } + + @Override + public List getRelatedMainBill(VisaBillTypeEnum billType, List billIdNos) { + LambdaQueryChainWrapper wrapper = lambdaQuery() + .eq(ChangeRecordBill::getBillType, billType) + .eq(ChangeRecordBill::getRelationStatus, true) + .eq(ChangeRecordBill::getIsDelete, 0); + if (VisaBillTypeEnum.TASK.equals(billType)) { + wrapper.in(ChangeRecordBill::getBillNo, billIdNos); + } else { + wrapper.in(ChangeRecordBill::getBillId, billIdNos.stream().map(Long::parseLong).collect(Collectors.toList())); + } + return wrapper.list(); + } + + /** + * 查询数量 + */ + public Integer findCountByCondition(VisaBillDto dto) { + return this.count(buildLambdaQueryWrapper(dto)); + } + + /** + * 构建查询条件 + */ + private LambdaQueryWrapper buildLambdaQueryWrapper(VisaBillDto dto) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + return lambdaQueryWrapper + .eq(Objects.nonNull(dto.getVisaId()), ChangeRecordBill::getVisaId, dto.getVisaId()) + .eq(StringUtils.isNotBlank(dto.getBillNo()), ChangeRecordBill::getBillNo, dto.getBillNo()) + .eq(Objects.nonNull(dto.getBillType()), ChangeRecordBill::getBillType, dto.getBillType()) + .eq(Objects.nonNull(dto.getBillId()), ChangeRecordBill::getBillId, dto.getBillId()) + .eq(Objects.nonNull(dto.getRelationStatus()), ChangeRecordBill::getRelationStatus, dto.getRelationStatus()) + .eq(ChangeRecordBill::getIsDelete, 0) + .orderByDesc(ChangeRecordBill::getUpdateAt); + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordConfirmServiceImpl.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordConfirmServiceImpl.java new file mode 100644 index 00000000..d494b9a8 --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordConfirmServiceImpl.java @@ -0,0 +1,443 @@ +package cn.axzo.nanopart.visa.server.service.impl; + +import cn.axzo.basics.common.BeanMapper; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.karma.client.feign.tyr.request.MatchDataObjectReq; +import cn.axzo.karma.client.feign.tyr.response.MatchDataObjectResp; +import cn.axzo.karma.client.feign.tyr.response.MergeMatchDataResp; +import cn.axzo.maokai.api.vo.request.OrganizationalUnitQuery; +import cn.axzo.maokai.api.vo.response.OrganizationalNodeVO; +import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO; +import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaConfirmBizTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveCreateReq; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordConfirm; +import cn.axzo.nanopart.visa.server.dto.VisaConfirmDto; +import cn.axzo.nanopart.visa.server.mapper.ChangeRecordConfirmDao; +import cn.axzo.nanopart.visa.server.rpc.DataObjectApiGateway; +import cn.axzo.nanopart.visa.server.rpc.OrganizationalUnitGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaOrganizationalNodeGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaOrganizationalNodeUserGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaProfileGateway; +import cn.axzo.nanopart.visa.server.rpc.WorkflowGateway; +import cn.axzo.nanopart.visa.server.service.ChangeRecordConfirmService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordLogService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordService; +import cn.axzo.nanopart.visa.server.utils.Constants; +import cn.axzo.orggateway.api.nodeuser.dto.OrgNodeUserDTO; +import cn.axzo.orggateway.api.nodeuser.req.ListOrgNodeUserReq; +import cn.axzo.orgmanax.dto.nodeuser.req.ListNodeUserReq; +import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCheckApproverDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录确认 + */ +@Slf4j +@Service +public class ChangeRecordConfirmServiceImpl extends ServiceImpl implements ChangeRecordConfirmService { + + @Resource + private VisaProfileGateway visaProfileGateway; + + @Resource + private OrganizationalUnitGateway organizationalUnitGateway; + + @Resource + private WorkflowGateway workflowGateway; + + @Resource + private ChangeRecordService changeRecordService; + + @Resource + private ChangeRecordLogService changeRecordLogService; + + @Resource + private DataObjectApiGateway dataObjectApiGateway; + + + @Resource + private VisaOrganizationalNodeUserGateway visaOrganizationalNodeUserGateway; + + @Resource + private VisaOrganizationalNodeGateway visaOrganizationalNodeGateway; + + /** + * 新增变更签证确认信息 + * + * @param dtoList 变更签证确认信息 + * @return true:成功;false:失败 + */ + public boolean addConfirm(List dtoList) { + List visaConfirmDtos = BeanMapper.copyList(dtoList, ChangeRecordConfirm.class, (s, t) -> { + t.setCreateBy(s.getOperator()); + t.setUpdateBy(s.getOperator()); + t.setVisaType(s.getVisaType()); + }); + return this.saveOrUpdateBatch(visaConfirmDtos); + } + + /** + * 更新变更签证确认信息 + * + * @param visaId 变更签证Id + * @param dtoList 变更签证确认 + * @return true:成功;false:失败 + */ + public boolean updateConfirm(Long visaId, List dtoList) { + // 删除 + this.deleteByVisaId(visaId, null); + // 插入 + return this.addConfirm(dtoList); + } + + /** + * 通过变更签证Id,删除变更签证确认信息 + */ + public boolean deleteByVisaId(Long visaId, List bizTypes) { + if (Objects.isNull(visaId)) { + return false; + } + return this.lambdaUpdate().eq(ChangeRecordConfirm::getVisaId, visaId) + .eq(ChangeRecordConfirm::getIsDelete, 0) + .in(CollectionUtils.isNotEmpty(bizTypes), ChangeRecordConfirm::getBizType, bizTypes) + .setSql("is_delete = id").update(); + } + + /** + * 获取按钮集合 + * 确认人与发起人的不同的单据状态,按钮列表不相同 + */ + @Override + public List fetchBtnsByCondition(Long visaId, Long personId, Long ouId, Long workspaceId, VisaStatusEnum status, String approvalId) { + Set bizTypes = fetchBizTypesByCondition(visaId, personId, ouId, workspaceId); + //添加审批人添加到集合bizTypes中 + this.addBizTypeApprove(bizTypes, visaId, personId, ouId, workspaceId); + + Set visaButtonTypeEnums = VisaButtonTypeEnum.fetchBtnSetByBizTypeAndStatus(bizTypes, status); + //没有点击[审批记录],则不展示[查看审批记录]按钮 + if (!changeRecordLogService.hasLogType(visaId, VisaLogTypeEnum.TO_APPRROVE)) { + visaButtonTypeEnums.remove(VisaButtonTypeEnum.APPROVAL_RECORD); + visaButtonTypeEnums.remove(VisaButtonTypeEnum.PRINT); + } else { + if (visaButtonTypeEnums.contains(VisaButtonTypeEnum.TO_APPROVE)) { + visaButtonTypeEnums.remove(VisaButtonTypeEnum.TO_APPROVE); + visaButtonTypeEnums.add(VisaButtonTypeEnum.AGAIN_TO_APPROVE); + } + } + + if (!workflowGateway.hasPrintTemplate(approvalId)) { + visaButtonTypeEnums.remove(VisaButtonTypeEnum.PRINT); + } + //没有点击[发起洽商群聊],则不展示[查看洽商记录]按钮 + if (!changeRecordLogService.hasLogType(visaId, VisaLogTypeEnum.CHAT_GROUP)) { + visaButtonTypeEnums.remove(VisaButtonTypeEnum.CHAT_GROUP_RECORD); + } + if (CollectionUtils.isEmpty(visaButtonTypeEnums)) { + return Lists.newArrayList(); + } + + return VisaButtonTypeEnum.sort(visaButtonTypeEnums); + } + + /** + * 添加审批人添加到集合bizTypes中 + * + * @param bizTypes 业务类型集合 + * @param visaId 变更签证Id + * @param personId 人员Id + */ + private void addBizTypeApprove(Set bizTypes, Long visaId, Long personId, Long ouId, Long workspaceId) { + ChangeRecord changeRecord = changeRecordService.getById(visaId); + + if (Objects.isNull(changeRecord) || Objects.isNull(changeRecord.getId()) || !StringUtils.hasText(changeRecord.getApprovalId())) { + return; + } + + BpmnProcessInstanceCheckApproverDTO dto = new BpmnProcessInstanceCheckApproverDTO(); + + dto.setProcessInstanceId(changeRecord.getApprovalId()); + + BpmnTaskDelegateAssigner assigner = new BpmnTaskDelegateAssigner(); + assigner.setPersonId(personId.toString()); + assigner.setOuId(ouId.toString()); + assigner.setTenantId(workspaceId.toString()); + + dto.setApprover(assigner); + dto.setOnlyPersonId(true); + + if (workflowGateway.checkInstanceApprover(dto)) { + bizTypes.add(VisaConfirmBizTypeEnum.APPROVE); + } + } + + /** + * 获取单据确认枚举 + */ + private Set fetchBizTypesByCondition(Long visaId, Long personId, Long ouId, Long workspaceId) { + VisaConfirmDto dto = VisaConfirmDto.builder() + .visaId(visaId) + .personId(personId) + .ouId(ouId) + .workspaceId(workspaceId).build(); + List list = this.findByCondition(dto); + if (CollectionUtils.isEmpty(list)) { + return Sets.newHashSet(); + } + return list.stream().map(item -> item.getBizType()).collect(Collectors.toSet()); + } + + /** + * 变更签证Id获取map + */ + @Override + public List listRelationByVisaId(List list, Long workspaceId) { + if (CollectionUtils.isEmpty(list)) { + return Lists.newArrayList(); + } + + // 3 构建关联单位与人员,最终处理成一个类型与单位下,对应多个确认人 + Map> listMap = list.stream() + .collect(Collectors.groupingBy(e -> e.getType() + "#" + e.getOuId())); + return this.buildRelationOuAndPerson(listMap, workspaceId); + } + + /** + * 获取OrgNodeUserDTO集合 + */ + private List fetchOrgNodeUserDTOList(List personIds, Long ouId, Long workspaceId) { + PageResp pageResp = visaOrganizationalNodeUserGateway.list(ListOrgNodeUserReq.builder() + .personIds(personIds) + .organizationalUnitId(ouId) + .workspaceId(workspaceId) + .needs(ListNodeUserReq.Needs.builder().job(true).unit(true).personProfile(true).build()).build()); + if (Objects.isNull(pageResp) || CollectionUtils.isEmpty(pageResp.getData())) { + return Lists.newArrayList(); + } + return pageResp.getData(); + } + + /** + * 构建公司的Map,orgIdAndNameMap,key:ouId,value:ouName + */ + public Map buildOrgIdAndNameMap(Set ouIdSet) { + if (CollectionUtils.isEmpty(ouIdSet)) { + return Maps.newHashMap(); + } + List orgVoList = organizationalUnitGateway.list(OrganizationalUnitQuery.builder().unitIds(Lists.newArrayList(ouIdSet)).build()); + if (CollectionUtils.isEmpty(orgVoList)) { + return Maps.newHashMap(); + } + return orgVoList.stream().collect(Collectors.toMap(OrganizationalUnitVO::getId, OrganizationalUnitVO::getName, (x, y) -> x)); + } + + /** + * 构建关联单位与人员,最终处理成一个类型与单位下,对应多个确认人 + * listMap key:type#ouId,value:ChangeRecordConfirm集合,按照[单位]以及[类型]进行分类 + * personMap key:personId,value:realName,对于确认人进行姓名赋值 + */ + private List buildRelationOuAndPerson(Map> listMap, Long workspaceId) { + List returnList = Lists.newArrayList(); + for (Map.Entry> entry : listMap.entrySet()) { + String key = entry.getKey(); + List confirmList = entry.getValue(); + + if (CollectionUtils.isEmpty(confirmList)) { + continue; + } + + List orgNodeUserDTOS = this.fetchOrgNodeUserDTOList(confirmList.stream().map(ChangeRecordConfirm::getPersonId).collect(Collectors.toList()), Long.valueOf(key.split("#")[1]), workspaceId); + + Map unitNodeUserMap = orgNodeUserDTOS.stream() + .filter(item -> Objects.nonNull(item.getUnit()) && Objects.nonNull(item.getUnit().getId())) + .collect(Collectors.toMap(k -> k.getUnit().getId(), v -> StringUtils.hasText(v.getUnit().getName()) ? v.getUnit().getName() : "", (x, y) -> x)); + + Map> personNodeUserMap = orgNodeUserDTOS.stream() + .filter(item -> Objects.nonNull(item.getPersonId())) + .collect(Collectors.groupingBy(OrgNodeUserDTO::getPersonId)); + + //构建单位 + VisaDetailByIdResponse.RelationUnit relationUnit = this.buildRelationUnit(key, unitNodeUserMap, confirmList.get(0).getNodeId()); + //构建确认人 + List relationPersonList = this.buildRelationPersonList(confirmList, personNodeUserMap); + + returnList.add(VisaDetailByIdResponse.RelationOuAndPerson.builder() + .relationOu(relationUnit) + .relationPersonList(relationPersonList).build()); + } + return returnList; + } + + /** + * 构建单位 + */ + private VisaDetailByIdResponse.RelationUnit buildRelationUnit(String key, Map unitNodeUserMap, Long nodeId) { + String[] keySplit = key.split("#"); + Long ouId = Long.valueOf(keySplit[1]); + OrganizationalNodeVO node = visaOrganizationalNodeGateway.getNode(nodeId); + return VisaDetailByIdResponse.RelationUnit.builder() + .ouId(ouId) + .type(keySplit[0]) + .nodeId(node.getTopNodeId()) + .ouName(StringUtils.hasText(unitNodeUserMap.get(ouId)) ? unitNodeUserMap.get(ouId) : "").build(); + } + + /** + * 构建确认人 + */ + private List buildRelationPersonList(List confirmList, Map> personNodeUserMap) { + return confirmList.stream().map(item -> { + + List orgNodeUserDTOS = personNodeUserMap.get(item.getPersonId()); + //有班组时,优先取班组岗位 + OrgNodeUserDTO nodeUserDTO = Constants.nodeUserDTOByList(orgNodeUserDTOS); + + VisaDetailByIdResponse.RelationPerson relationPerson = VisaDetailByIdResponse.RelationPerson.builder() + .ouId(item.getOuId()) + .workspaceId(item.getWorkspaceId()) + .nodeId(item.getNodeId()) + .personId(item.getPersonId()) + .projectId(item.getProjectId()) + .type(item.getType()) + .build(); + if (Objects.nonNull(nodeUserDTO)) { + relationPerson.setRealName(nodeUserDTO.getRealName()); + + if (Objects.nonNull(nodeUserDTO.getPersonProfile())) { + relationPerson.setAvatarUrl(nodeUserDTO.getPersonProfile().getAvatarUrl()); + } + + if (Objects.nonNull(nodeUserDTO.getJob())) { + relationPerson.setJobId(nodeUserDTO.getJob().getId()); + relationPerson.setJobName(nodeUserDTO.getJob().getName()); + } + } + return relationPerson; + } + ).collect(Collectors.toList()); + } + + + /** + * 构建人员的集合,key:personId,value:realName + */ + private Map buildPersonMap(Set personSet) { + if (CollectionUtils.isEmpty(personSet)) { + return Maps.newHashMap(); + } + List profiles = visaProfileGateway.getProfiles(Lists.newArrayList(personSet)); + if (CollectionUtils.isEmpty(profiles)) { + return Maps.newHashMap(); + } + return profiles.stream().collect(Collectors.toMap(PersonProfileDto::getId, PersonProfileDto::getRealName, (x, y) -> x)); + } + + /** + * 条件查询列表 + */ + @Override + public List findByCondition(VisaConfirmDto dto) { + if (Objects.isNull(dto)) { + return Lists.newArrayList(); + } + return this.buildLambdaQueryWrapper(dto).list(); + } + + /** + * 查询条件Wrapper + */ + private LambdaQueryChainWrapper buildLambdaQueryWrapper(VisaConfirmDto dto) { + return lambdaQuery().eq(Objects.nonNull(dto.getVisaId()), ChangeRecordConfirm::getVisaId, dto.getVisaId()) + .in(CollectionUtils.isNotEmpty(dto.getVisaIds()), ChangeRecordConfirm::getVisaId, dto.getVisaIds()) + .eq(Objects.nonNull(dto.getPersonId()), ChangeRecordConfirm::getPersonId, dto.getPersonId()) + .in(CollectionUtils.isNotEmpty(dto.getPersonIds()), ChangeRecordConfirm::getPersonId, dto.getPersonIds()) + .eq(Objects.nonNull(dto.getOuId()), ChangeRecordConfirm::getOuId, dto.getOuId()) + .in(CollectionUtils.isNotEmpty(dto.getOuIds()), ChangeRecordConfirm::getOuId, dto.getOuIds()) + .eq(Objects.nonNull(dto.getWorkspaceId()), ChangeRecordConfirm::getWorkspaceId, dto.getWorkspaceId()) + .in(CollectionUtils.isNotEmpty(dto.getWorkspaceIds()), ChangeRecordConfirm::getWorkspaceId, dto.getWorkspaceIds()) + .eq(Objects.nonNull(dto.getNodeId()), ChangeRecordConfirm::getNodeId, dto.getNodeId()) + .in(CollectionUtils.isNotEmpty(dto.getNodeIds()), ChangeRecordConfirm::getNodeId, dto.getNodeIds()) + .eq(Objects.nonNull(dto.getProjectId()), ChangeRecordConfirm::getProjectId, dto.getProjectId()) + .in(CollectionUtils.isNotEmpty(dto.getProjectIds()), ChangeRecordConfirm::getProjectId, dto.getProjectIds()) + .eq(Objects.nonNull(dto.getType()), ChangeRecordConfirm::getType, dto.getType()) + .eq(Objects.nonNull(dto.getBizType()), ChangeRecordConfirm::getBizType, dto.getBizType()) + .in(CollectionUtils.isNotEmpty(dto.getVisaTypes()), ChangeRecordConfirm::getVisaType, dto.getVisaTypes()) + .eq(ChangeRecordConfirm::getIsDelete, 0); + } + + /** + * 数据权限过滤 + * + * @param dataObjectCode OMS提供权限code + * @param currentPersonId 当前登录人personId + * @param currentWorkspaceId 当前登录人项目Id + * @param currentOuId 当前登录人的单位Id + * @return 变更签证visaId集合 + */ + public Set permissionVisaData(String dataObjectCode, Long currentPersonId, Long currentWorkspaceId, Long currentOuId, Set visaTypes) { + MatchDataObjectReq req = new MatchDataObjectReq(); + req.setDataObjectCode(dataObjectCode); + req.setPersonId(currentPersonId); + req.setWorkspaceId(currentWorkspaceId); + req.setOuId(currentOuId); + MatchDataObjectResp match = dataObjectApiGateway.match(req); + MergeMatchDataResp mergedDataObject = match.getMergedDataObject(); + List dataItems = mergedDataObject.getDataItems(); + + List confirmList = this.findByCondition(VisaConfirmDto.create(dataItems, visaTypes)); + Set visaIdList = confirmList.stream().map(ChangeRecordConfirm::getVisaId).collect(Collectors.toSet()); + if (CollectionUtils.isEmpty(visaIdList)) { + return Sets.newHashSet(); + } + return visaIdList; + } + + @Override + public List listAllConfirmByVisaId(Long visaId) { + VisaConfirmDto dto = VisaConfirmDto.builder() + .visaId(visaId) + .bizType(VisaConfirmBizTypeEnum.CONFIRM).build(); + List list = this.findByCondition(dto); + if (CollectionUtils.isEmpty(list)) { + return Lists.newArrayList(); + } + Map personMap = visaProfileGateway.getProfileMap(list.stream() + .map(ChangeRecordConfirm::getPersonId) + .collect(Collectors.toList())); + return list.stream().map(i -> VisaChangeApproveCreateReq.ApprovePersonInfo.builder() + .workspaceId(i.getWorkspaceId()) + .ouId(i.getOuId()) + .nodeId(i.getNodeId()) + .personId(i.getPersonId()) + .realName(personMap.getOrDefault(i.getPersonId(), new PersonProfileDto()) + .getRealName()) + .avatarUrl(personMap.getOrDefault(i.getPersonId(), new PersonProfileDto()) + .getAvatarUrl()) + .build()) + .collect(Collectors.toList()); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordLogServiceImpl.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordLogServiceImpl.java new file mode 100644 index 00000000..c82204bf --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordLogServiceImpl.java @@ -0,0 +1,345 @@ +package cn.axzo.nanopart.visa.server.service.impl; + +import cn.axzo.foundation.page.PageResp; +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import cn.axzo.nanopart.visa.api.response.FetchVisaLogByVisaIdResponse; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordLog; +import cn.axzo.nanopart.visa.server.dto.VisaLogDto; +import cn.axzo.nanopart.visa.server.dto.VisaLogParam; +import cn.axzo.nanopart.visa.server.mapper.ChangeRecordLogDao; +import cn.axzo.nanopart.visa.server.rpc.VisaOrganizationalNodeUserGateway; +import cn.axzo.nanopart.visa.server.service.ChangeRecordLogService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordService; +import cn.axzo.nanopart.visa.server.utils.Constants; +import cn.axzo.orggateway.api.nodeuser.dto.OrgNodeUserDTO; +import cn.axzo.orggateway.api.nodeuser.req.ListOrgNodeUserReq; +import cn.axzo.orgmanax.dto.nodeuser.req.ListNodeUserReq; +import cn.hutool.core.util.NumberUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录 + */ +@Slf4j +@Service +public class ChangeRecordLogServiceImpl extends ServiceImpl implements ChangeRecordLogService { + + @Resource + private VisaOrganizationalNodeUserGateway visaOrganizationalNodeUserGateway; + + @Resource + private ChangeRecordService changeRecordService; + + /** + * 增加操作日志 + */ + @Override + public void addLog(ChangeRecordLog log) { + + this.saveOrUpdate(log); + } + + /** + * 增加操作日志 + */ + @Override + public void batchAddLog(List logList) { + if (CollectionUtils.isEmpty(logList)) { + return; + } + this.saveOrUpdateBatch(logList); + } + + /** + * 增加操作日志 + */ + @Override + public void batchAddLog(Long visaId, VisaLogTypeEnum type, VisaLogParam param) { + List logList = this.buildLogList(visaId, type, param); + if (CollectionUtils.isEmpty(logList)) { + return; + } + this.saveOrUpdateBatch(logList); + } + + @Override + public List buildLogList(Long visaId, VisaLogTypeEnum type, VisaLogParam param) { + log.info("buildLogList,visaId={},type={},param={}", visaId, type, JSON.toJSONString(param)); + if (Objects.isNull(visaId) || Objects.isNull(type)) { + return Lists.newArrayList(); + } + + List logList = Lists.newArrayList(); + switch (type) { + case CHAT_GROUP: + // 动作: 创建群聊洽商 + // 状态流转:[待提报|审核不通过的待提报] --> [决策中] + String userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.CHAT_GROUP.name()) + .title(VisaLogTypeEnum.CHAT_GROUP.getTitle()) + .content(String.format(VisaLogTypeEnum.CHAT_GROUP.getContent(), userName)) + .orderBy(Constants.CHANGE_STATUS_FILE_ORDER_BY).build()); + break; + case EDIT_FORM: + // 编辑表单 + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.EDIT_FORM.name()) + .title(String.format(VisaLogTypeEnum.EDIT_FORM.getTitle(), userName)) + .content(String.format(VisaLogTypeEnum.EDIT_FORM.getContent(), userName)) + .orderBy(Constants.EDIT_FORM_ORDER_BY).build()); + case EDIT_AMOUNT: + if (Objects.nonNull(param) + && Objects.nonNull(param.getOldAmountChange()) && Objects.nonNull(param.getNewAmountChange()) + && !Objects.equals(param.getOldAmountChange().compareTo(param.getNewAmountChange()), 0)) { + // 编辑金额变化 + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.EDIT_AMOUNT.name()) + .title(String.format(VisaLogTypeEnum.EDIT_AMOUNT.getTitle(),userName )) + .content(String.format(VisaLogTypeEnum.EDIT_AMOUNT.getContent() + , NumberUtil.roundDown(param.getOldAmountChange(), 2).toPlainString() + , NumberUtil.roundDown(param.getNewAmountChange(), 2).toPlainString())) + .orderBy(Constants.AMOUNT_CHANGE_ORDER_BY).build()); + } + case UPLOAD_FILE: + if (CollectionUtils.isNotEmpty(param.getUploadAttach())) { + // 上传附件 + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.UPLOAD_FILE.name()) + .title(String.format(VisaLogTypeEnum.UPLOAD_FILE.getTitle(), userName)) + .content(String.format(VisaLogTypeEnum.UPLOAD_FILE.getContent(), userName, param.getUploadAttach().size(), this.buildAttach(param.getUploadAttach()))) + .orderBy(Constants.ADD_UPLOAD_FILE_ORDER_BY) + .build()); + } + case DELETE_UPLOAD_FILE: + if (CollectionUtils.isNotEmpty(param.getDeleteAttach())) { + // 删除附件 + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.DELETE_UPLOAD_FILE.name()) + .title(String.format(VisaLogTypeEnum.DELETE_UPLOAD_FILE.getTitle(),userName)) + .content(String.format(VisaLogTypeEnum.DELETE_UPLOAD_FILE.getContent(),userName, param.getDeleteAttach().size(), this.buildAttach(param.getDeleteAttach()))) + .orderBy(Constants.DELETE_UPLOAD_FILE_ORDER_BY) + .build()); + } + break; + case TO_EXECUTE: + // 动作:执行 + // 状态流转:[决策中|审核不通过的待提报]-->[执行中] + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.TO_EXECUTE.name()) + .title(VisaLogTypeEnum.TO_EXECUTE.getTitle()) + .content(String.format(VisaLogTypeEnum.TO_EXECUTE.getContent(), userName)) + .orderBy(Constants.CHANGE_STATUS_FILE_ORDER_BY).build()); + break; + case TO_APPRROVE: + ChangeRecord changeRecord = this.changeRecordService.getById(visaId); + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + // 动作:发起审批 + // 状态流转:[待提报|审批不通过的待提报|决策中|审批不通过的决策中|执行中]-->[审批中] + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.TO_APPRROVE.name()) + .title(String.format(VisaLogTypeEnum.TO_APPRROVE.getTitle(), changeRecord.getType().getDesc())) + .content(String.format(VisaLogTypeEnum.TO_APPRROVE.getContent(), userName, changeRecord.getType().getDesc())) + .orderBy(Constants.CHANGE_STATUS_FILE_ORDER_BY).build()); + break; + case APPROVED_AGRESS: + // 动作:审批流:某审批人审批通过 + // 状态流转:仍然是审批中 [审批中] -> [审批中] + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.APPROVED_AGRESS.name()) + .title(String.format(VisaLogTypeEnum.APPROVED_AGRESS.getTitle(), userName)) + .content(String.format(VisaLogTypeEnum.APPROVED_AGRESS.getContent(), userName)) + .orderBy(Constants.ONE_APPROVED_ORDER_BY).build()); + break; + case REVERT_APPROVE: + // 动作:审批流:撤回审批 + // 状态流转:[审批中] -> [审批到待提报|审批到决策中] + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.REVERT_APPROVE.name()) + .title(VisaLogTypeEnum.REVERT_APPROVE.getTitle()) + .content(String.format(VisaLogTypeEnum.REVERT_APPROVE.getContent(), userName)) + .orderBy(Constants.CHANGE_STATUS_FILE_ORDER_BY).build()); + break; + case REJECT_APPROVE: + // 动作:审批流:撤回审批 + // 状态流转:[审批中] -> [审批到待提报|审批到决策中] + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.REJECT_APPROVE.name()) + .title(String.format(VisaLogTypeEnum.REJECT_APPROVE.getTitle(), userName)) + .content(String.format(VisaLogTypeEnum.REJECT_APPROVE.getContent(), userName, param.getReason())) + .orderBy(Constants.CHANGE_STATUS_FILE_ORDER_BY).build()); + break; + case TRANSMIT_APPROVE: + // 动作:审批流操作:转交 + // 状态流转:[审批中] -> [审批到待提报|审批到决策中] + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.TRANSMIT_APPROVE.name()) + .title(VisaLogTypeEnum.TRANSMIT_APPROVE.getTitle()) + .content(String.format(VisaLogTypeEnum.TRANSMIT_APPROVE.getContent(), userName, param.getTransmitTo(),param.getReason())) + .orderBy(Constants.CHANGE_STATUS_FILE_ORDER_BY).build()); + break; + case FORBIDED: + // 动作:作废 + // 状态流转:[待提报|审核不通过的待提报|决策中|审核不通过的决策中|执行中|审批中]-->[审核不通过的决策中] + userName = this.buildUserName(param.getPersonId(), param.getOuId(), param.getWorkspaceId()); + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.FORBIDED.name()) + .title(VisaLogTypeEnum.FORBIDED.getTitle()) + .content(String.format(VisaLogTypeEnum.FORBIDED.getContent(), userName)) + .orderBy(Constants.CHANGE_STATUS_FILE_ORDER_BY).build()); + break; + case APPROVE_COMPLETED: + changeRecord = this.changeRecordService.getById(visaId); + // 动作:整个审批通过 + // 状态流转:[审批中]-->[已完成] + logList.add(ChangeRecordLog.builder().visaId(visaId) + .action(VisaLogTypeEnum.APPROVE_COMPLETED.name()) + .title(String.format(VisaLogTypeEnum.APPROVE_COMPLETED.getTitle(), changeRecord.getType().getDesc())) + .content(String.format(VisaLogTypeEnum.APPROVE_COMPLETED.getContent(), changeRecord.getType().getDesc())) + .orderBy(Constants.APPROVE_COMPLETED_ORDER_BY).build()); + break; + default: + throw new ServiceException("变签日志类型不匹配"); + } + return logList; + } + + /** + * 构建附件 + */ + private String buildAttach(List attach) { + StringBuilder builder = new StringBuilder(); + attach.stream().forEach(item -> { + builder.append(item).append(","); + }); + String attachName = builder.toString(); + if (StringUtils.hasText(attachName)) { + return attachName.substring(0, attachName.length() - 1); + } + return ""; + } + + /** + * 构建操作人信息 + */ + public String buildUserName(Long personId, Long ouId, Long workspaceId) { + PageResp pageResp = visaOrganizationalNodeUserGateway.list(ListOrgNodeUserReq.builder() + .personId(personId) + .organizationalUnitId(ouId) + .workspaceId(workspaceId) + .needs(ListNodeUserReq.Needs.builder().job(true).unit(true).build()).build()); + if (Objects.isNull(pageResp) || CollectionUtils.isEmpty(pageResp.getData())) { + return ""; + } + OrgNodeUserDTO orgNodeUserDTO = Constants.nodeUserDTOByList(pageResp.getData()); + return orgNodeUserDTO.getRealName() + "(" + orgNodeUserDTO.getJob().getName() + "-" + orgNodeUserDTO.getUnit().getName() + ")"; + } + + /** + * 通过变签Id获取日志 + * @param visaId 变签Id + */ + public List fetchVisaLogByVisaId(Long visaId) { + if (Objects.isNull(visaId)) { + return Lists.newArrayList(); + } + List logList = this.findByCondition(VisaLogDto.builder().visaId(visaId).build()); + if (CollectionUtils.isEmpty(logList)) { + return Lists.newArrayList(); + } + + // 使用 LinkedHashSet 进行去重 + Set seenKeys = new LinkedHashSet<>(); + List returnList = logList.stream() + .filter(item -> seenKeys.add(buildKey(item))) + .map(item -> FetchVisaLogByVisaIdResponse.builder() + .title(item.getTitle()) + .content(item.getContent()) + .createAt(item.getCreateAt()) + .build()) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(returnList)) { + return Lists.newArrayList(); + } + return returnList; + } + + /** + * 构建key + */ + private String buildKey(ChangeRecordLog log) { + return log.getVisaId() + log.getAction() + log.getTitle() + log.getContent() + log.getCreateAt().getTime(); + } + + /** + * 通过变签Id获取日志 + * @param visaId 变签Id + * @param logType 类型 + */ + public List listByVisaIdType(Long visaId, VisaLogTypeEnum logType) { + if (Objects.isNull(visaId) || Objects.isNull(logType)) { + return Lists.newArrayList(); + } + List logList = this.findByCondition(VisaLogDto.builder().visaId(visaId).logType(logType).build()); + if (CollectionUtils.isEmpty(logList)) { + return Lists.newArrayList(); + } + return logList; + } + + /** + * 查询记录 + */ + private List findByCondition(VisaLogDto dto) { + if (Objects.isNull(dto) || Objects.isNull(dto.getVisaId())) { + return Lists.newArrayList(); + } + return this.lambdaQuery().eq(ChangeRecordLog::getVisaId, dto.getVisaId()) + .eq(Objects.nonNull(dto.getLogType()), ChangeRecordLog::getAction, dto.getLogType()) + .eq(ChangeRecordLog::getIsDelete, 0) + .orderByDesc(ChangeRecordLog::getCreateAt) + .orderByDesc(ChangeRecordLog::getOrderBy) + .list(); + } + + /** + * 是否有日志类型 + * @param visaId 变更签证Id + * @param logType 类型 + * @return true:有;false:没有 + */ + public boolean hasLogType(Long visaId, VisaLogTypeEnum logType) { + List logList = this.findByCondition(VisaLogDto.builder().visaId(visaId).logType(logType).build()); + return CollectionUtils.isNotEmpty(logList); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordRelationServiceImpl.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordRelationServiceImpl.java new file mode 100644 index 00000000..5348179d --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordRelationServiceImpl.java @@ -0,0 +1,408 @@ +package cn.axzo.nanopart.visa.server.service.impl; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.basics.common.util.NumberUtil; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.im.center.api.vo.PersonAccountAttribute; +import cn.axzo.im.center.api.vo.req.SendChatMessageRequest; +import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum; +import cn.axzo.nanopart.visa.api.enums.VisaRelationVarTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.request.ChangeRecordAppendAttachReq; +import cn.axzo.nanopart.visa.api.request.ImGroupOperationClearReq; +import cn.axzo.nanopart.visa.api.request.ImGroupOperationReq; +import cn.axzo.nanopart.visa.api.request.ImGroupTipsQueryReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq; +import cn.axzo.nanopart.visa.api.response.ImGroupButton; +import cn.axzo.nanopart.visa.api.response.ImGroupTipsResp; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordRelation; +import cn.axzo.nanopart.visa.server.dto.VisaLogParam; +import cn.axzo.nanopart.visa.server.dto.VisaRelationDto; +import cn.axzo.nanopart.visa.server.mapper.ChangeRecordRelationDao; +import cn.axzo.nanopart.visa.server.rpc.MsgCenterGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaProfileGateway; +import cn.axzo.nanopart.visa.server.rpc.WorkflowGateway; +import cn.axzo.nanopart.visa.server.service.ChangeRecordLogService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordRelationService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordService; +import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.groovy.util.Maps; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_ATTACH; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_VISA_ID; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_OWNER_TIPS; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_PARTICIPATE_TIPS; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_PARTICIPATE_TIPS_OPERATED; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录内容关系表 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ChangeRecordRelationServiceImpl extends ServiceImpl implements ChangeRecordRelationService { + private final ChangeRecordService changeRecordService; + private final VisaProfileGateway visaProfileGateway; + private final MsgCenterGateway msgCenterGateway; + private final WorkflowGateway workflowGateway; + private final ChangeRecordLogService changeRecordLogService; + + /** + * 保存 + * + * @param type 变更签证记录内容关系表类型 + * @param req 保存请求表单数据 + * @param changeRecord 变更签证记录 + * @return 变更签证记录内容关系表id + */ + @Transactional(rollbackFor = Exception.class) + public boolean save(VisaRelationFieldEnum type, VisaChangeTempCreateReq req, ChangeRecord changeRecord) { + if (type == VisaRelationFieldEnum.ATTACHMENT) { + List attach = req.getAttach().stream() + .map(item -> { + ChangeRecordRelation changeRecordRelation = new ChangeRecordRelation(); + changeRecordRelation.setVisaId(changeRecord.getId()); + changeRecordRelation.setVarName(type.name()); + changeRecordRelation.setVarType(VisaRelationVarTypeEnum.JSON.getType()); + changeRecordRelation.setContent(JSON.toJSONString(item)); + changeRecordRelation.setCreateBy(req.getOperatorPersonId()); + changeRecordRelation.setUpdateBy(req.getOperatorPersonId()); + return changeRecordRelation; + }) + .collect(Collectors.toList()); + return saveBatch(attach); + } else if (type == VisaRelationFieldEnum.CONTENT_DESCRIPTION) { + List contextAndDescription = req.getChangeContextAndDescriptionList().stream() + .map(item -> { + ChangeRecordRelation changeRecordRelation = new ChangeRecordRelation(); + changeRecordRelation.setVisaId(changeRecord.getId()); + changeRecordRelation.setVarName(type.name()); + changeRecordRelation.setVarType(VisaRelationVarTypeEnum.JSON.getType()); + changeRecordRelation.setContent(JSONObject.toJSONString(item)); + changeRecordRelation.setCreateBy(req.getOperatorPersonId()); + changeRecordRelation.setUpdateBy(req.getOperatorPersonId()); + return changeRecordRelation; + }) + .collect(Collectors.toList()); + return saveBatch(contextAndDescription); + } else { + throw new ServiceException("未知类型"); + } + } + + + /** + * 根据名称与变更签证Id,获取集合 + */ + @Override + public List findByVisaAndVarName(Long visaId, String varName) { + if (Objects.isNull(visaId) || !StringUtils.hasText(varName)) { + return Lists.newArrayList(); + } + return this.findByCondition(VisaRelationDto.builder().visaId(visaId).varName(varName).build()); + } + + @Override + public boolean deleteByVisaId(Long visaId, List relationTypes) { + if (NumberUtil.isNotPositiveNumber(visaId)) { + return false; + } + return lambdaUpdate().eq(ChangeRecordRelation::getVisaId, visaId) + .eq(ChangeRecordRelation::getIsDelete, 0) + .in(CollectionUtils.isNotEmpty(relationTypes), ChangeRecordRelation::getVarName, + relationTypes.stream().map(VisaRelationFieldEnum::name).collect(Collectors.toList())) + .setSql("is_delete = id") + .update(); + } + + /** + * 获取集合 + */ + @Override + public List findByCondition(VisaRelationDto dto) { + if (Objects.isNull(dto) || Objects.isNull(dto.getVisaId())) { + return Lists.newArrayList(); + } + return this.lambdaQuery().eq(ChangeRecordRelation::getVisaId, dto.getVisaId()) + .eq(Objects.nonNull(dto.getVarName()), ChangeRecordRelation::getVarName, dto.getVarName()) + .eq(StringUtils.hasText(dto.getContent()), ChangeRecordRelation::getContent, dto.getContent()) + .eq(StringUtils.hasText(dto.getContentExt()), ChangeRecordRelation::getContentExt, dto.getContentExt()) + .eq(Objects.nonNull(dto.getCreateBy()), ChangeRecordRelation::getCreateBy, dto.getCreateBy()) + .eq(ChangeRecordRelation::getIsDelete, 0).list(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean saveImOperationParticipate(ImGroupOperationReq req) { + ChangeRecord record = changeRecordService.getById(req.getVisaId()); + AssertUtil.notNull(record, "未找到变洽签记录"); + AssertUtil.isTrue(Objects.equals(record.getImGroupId(), req.getImGroupId()), "IM群信息不匹配"); + + List relations = findByCondition(VisaRelationDto.builder() + .visaId(req.getVisaId()) + .varName(VisaRelationFieldEnum.IM_GROUP_PARTICIPATE.name()) + .content(String.valueOf(req.getOperatorPersonId())) + .build()); + AssertUtil.isEmpty(relations, "不允许重复操作"); + ChangeRecordRelation relation = new ChangeRecordRelation(); + relation.setVisaId(req.getVisaId()); + relation.setVarName(VisaRelationFieldEnum.IM_GROUP_PARTICIPATE.name()); + relation.setVarExt(req.getButtonType().name()); + relation.setVarType(VisaRelationVarTypeEnum.LONG.getType()); + relation.setContentExt(String.valueOf(req.getOperatorWorkspaceId())); + relation.setContent(String.valueOf(req.getOperatorOuId())); + relation.setCreateBy(req.getOperatorPersonId()); + relation.setUpdateBy(req.getOperatorPersonId()); + save(relation); + + // 发送“同意”、“拒绝”的文案至群内 + SendChatMessageRequest msgRequest = new SendChatMessageRequest(); + msgRequest.setAsTextMessage(req.getButtonType().getDesc() + "!"); + msgRequest.setImReceiveAccounts(Sets.newHashSet(String.valueOf(req.getImGroupId()))); + msgRequest.setSender(PersonAccountAttribute.builder() + .personId(String.valueOf(req.getOperatorPersonId())) + .ouId(req.getOperatorOuId()) + .workspaceId(req.getOperatorWorkspaceId()) + .appType(AppTypeEnum.CMP) + .build()); + msgRequest.setBizId(IM_GROUP_BIZ_INFO_VISA_ID + ":" + req.getVisaId()); + msgRequest.setTrySyncSend(true); + msgCenterGateway.sendMsgToGroup(msgRequest); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean clearImOperationParticipate(ImGroupOperationClearReq req) { + ChangeRecord record = changeRecordService.getById(req.getVisaId()); + AssertUtil.notNull(record, "未找到变洽签记录"); + AssertUtil.isTrue(Objects.equals(record.getImGroupId(), req.getImGroupId()), "IM 群信息不匹配"); + AssertUtil.isTrue(!Objects.equals(VisaStatusEnum.APPROVING, record.getStatus()) + && !Objects.equals(VisaStatusEnum.COMPLETED, record.getStatus()), "当前单据状态下不允许操作"); + + if (Boolean.TRUE.equals(req.getClearAll())) { + deleteByVisaId(req.getVisaId(), Lists.newArrayList(VisaRelationFieldEnum.IM_GROUP_PARTICIPATE)); + } else { + List byCondition = findByCondition(VisaRelationDto.builder() + .visaId(req.getVisaId()) + .varName(VisaRelationFieldEnum.IM_GROUP_PARTICIPATE.name()) + .content(String.valueOf(req.getOperatorOuId())) + .contentExt(NumberUtil.isPositiveNumber(req.getOperatorWorkspaceId()) ? String.valueOf(req.getOperatorWorkspaceId()) : null) + .createBy(req.getOperatorPersonId()) + .build()); + removeByIds(byCondition.stream().map(ChangeRecordRelation::getVisaId).collect(Collectors.toList())); + } + + return true; + } + + @Override + public ImGroupTipsResp queryImGroupTips(ImGroupTipsQueryReq req) { + ChangeRecord record = changeRecordService.getById(req.getVisaId()); + AssertUtil.notNull(record, "未找到变洽签记录"); + + ImGroupTipsResp resp = new ImGroupTipsResp(); + if (Objects.equals(record.getStatus(), VisaStatusEnum.DECIDING_FROM_APPROVE)) { + record.setStatus(VisaStatusEnum.DECIDING); + } else if (Objects.equals(record.getStatus(), VisaStatusEnum.REPORT_FROM_APPROVE)) { + record.setStatus(VisaStatusEnum.TO_REPORT); + } + resp.setStatus(record.getStatus()); + resp.setStatusText(record.getStatus().getDesc()); + resp.setVisaType(record.getType()); + resp.setVisaTypeText(record.getType().getDesc()); + List imGroupParticipate = findByCondition(VisaRelationDto.builder() + .visaId(req.getVisaId()) + .varName(VisaRelationFieldEnum.IM_GROUP_PARTICIPATE.name()) + .build()); + + List buttons = new ArrayList<>(); + if (Boolean.TRUE.equals(req.getIsGroupOwner())) { + Map countMap = imGroupParticipate.stream() + .collect(Collectors.groupingBy(ChangeRecordRelation::getVarExt, Collectors.counting())); + Long agreeCount = countMap.getOrDefault(VisaButtonTypeEnum.AGREE.name(), 0L); + Long rejectCount = countMap.getOrDefault(VisaButtonTypeEnum.REJECT.name(), 0L); + resp.setTipsText(String.format(IM_GROUP_OWNER_TIPS, req.getGroupTotalPersonCount() - 1, + imGroupParticipate.size(), agreeCount, rejectCount)); + switch (record.getStatus()) { + case DECIDING: + if (req.getGroupTotalPersonCount() - 1 == agreeCount) { + // 全部同意 + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.TO_APPROVE.getDesc()) + .type(VisaButtonTypeEnum.TO_APPROVE.name()) + .build()); + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.EXECUTE.getDesc()) + .type(VisaButtonTypeEnum.EXECUTE.name()) + .build()); + } else { + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.FORBID.getDesc()) + .type(VisaButtonTypeEnum.FORBID.name()) + .build()); + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.REDECISION.getDesc()) + .type(VisaButtonTypeEnum.REDECISION.name()) + .build()); + } + break; + case EXECUTING: + if (req.getGroupTotalPersonCount() - 1 == agreeCount) { + // 全部同意 + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.TO_APPROVE.getDesc()) + .type(VisaButtonTypeEnum.TO_APPROVE.name()) + .build()); + } else { + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.FORBID.getDesc()) + .type(VisaButtonTypeEnum.FORBID.name()) + .build()); + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.REDECISION.getDesc()) + .type(VisaButtonTypeEnum.REDECISION.name()) + .build()); + } + break; + default: + break; + } + } else if (Objects.equals(VisaStatusEnum.DECIDING, record.getStatus()) + || Objects.equals(VisaStatusEnum.EXECUTING, record.getStatus())) { + // IM 群中非群主的人 + Optional any = imGroupParticipate.stream() + .filter(i -> Objects.equals(i.getCreateBy(), req.getPersonId())) + .filter(i -> Objects.equals(i.getContent(), String.valueOf(req.getOuId()))) + .findAny(); + PersonProfileDto profile = visaProfileGateway.getProfile(record.getCreateBy()); + if (any.isPresent()) { + ChangeRecordRelation operation = any.get(); + VisaButtonTypeEnum buttonType = VisaButtonTypeEnum.valueOf(operation.getVarExt()); + resp.setTipsText(String.format(IM_GROUP_PARTICIPATE_TIPS_OPERATED, buttonType.getDesc(), profile.getRealName(), record.getTopic())); + } else { + resp.setTipsText(String.format(IM_GROUP_PARTICIPATE_TIPS, profile.getRealName(), record.getTopic())); + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.REJECT.getDesc()) + .type(VisaButtonTypeEnum.REJECT.name()) + .build()); + buttons.add(ImGroupButton.builder() + .name(VisaButtonTypeEnum.AGREE.getDesc()) + .type(VisaButtonTypeEnum.AGREE.name()) + .build()); + + } + } else { + // 不显示横条, + resp.setStatus(null); + resp.setTipsText(null); + } + resp.setButtonList(buttons); + return resp; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void appendAttachment(ChangeRecordAppendAttachReq req) { + ChangeRecord visa = changeRecordService.getById(req.getVisaId()); + + AssertUtil.notNull(visa, "变洽签单据不存在"); + List existsAttach = findByCondition(VisaRelationDto.builder() + .visaId(req.getVisaId()) + .varName(VisaRelationFieldEnum.ATTACHMENT.name()) + .build()); + AssertUtil.isTrue(existsAttach.size() < 30, "已达到附件数量限制"); + AssertUtil.isTrue(existsAttach.size() + req.getAttach().size() <= 30, "新增的附件数量超过限制"); + + List attach = req.getAttach().stream() + .map(item -> { + ChangeRecordRelation changeRecordRelation = new ChangeRecordRelation(); + changeRecordRelation.setVisaId(req.getVisaId()); + changeRecordRelation.setVarName(VisaRelationFieldEnum.ATTACHMENT.name()); + changeRecordRelation.setVarType(VisaRelationVarTypeEnum.JSON.getType()); + changeRecordRelation.setContent(JSON.toJSONString(item)); + changeRecordRelation.setCreateBy(req.getOperatorPersonId()); + changeRecordRelation.setUpdateBy(req.getOperatorPersonId()); + return changeRecordRelation; + }) + .collect(Collectors.toList()); + saveBatch(attach); + + if (Objects.equals(PROCESSING.getStatus(), visa.getApprovalStatus())) { + FormVariablesUpdateDTO formUpdateDto = new FormVariablesUpdateDTO(); + formUpdateDto.setProcessInstanceId(visa.getApprovalId()); + List relations = findByCondition(VisaRelationDto.builder() + .visaId(req.getVisaId()) + .varName(VisaRelationFieldEnum.ATTACHMENT.name()) + .build()); + + List attachments = relations.stream().map(i -> JSON.parseObject(i.getContent(), UploadFieldDTO.class)).collect(Collectors.toList()); + formUpdateDto.setFormVariables(Maps.of(FORM_FIELD_ATTACH, JSON.toJSONString(attachments))); + workflowGateway.updateInstanceFormData(formUpdateDto); + } + //单独上传附件-追加操作日志 + this.addLogWhenAttachAppend(req.getAttach(), req.getVisaId(), req.getOperatorPersonId(), req.getOperatorOuId(), req.getWorkspaceId()); + } + + /** + * 单独上传附件-追加操作日志 + * @param attach 上传附件信息 + * @param visaId 变更签证Id + * @param personId 人员Id + * @param ouId 单位id + * @param workspaceId 项目Id + */ + private void addLogWhenAttachAppend(List attach, Long visaId, Long personId, Long ouId, Long workspaceId) { + if (CollectionUtils.isNotEmpty(attach)) { + List uploadAttachFileNameList = attach.stream().map(item -> item.getFileName()).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(uploadAttachFileNameList) + && Objects.nonNull(personId) + && Objects.nonNull(ouId) + && Objects.nonNull(workspaceId)) { + changeRecordLogService.batchAddLog(visaId, VisaLogTypeEnum.UPLOAD_FILE + , VisaLogParam.builder().personId(personId) + .ouId(ouId) + .workspaceId(workspaceId) + .uploadAttach(uploadAttachFileNameList).build()); + } + + } + } + + @Override + public boolean deleteByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return false; + } + return lambdaUpdate().in(ChangeRecordRelation::getId, ids) + .setSql("is_delete = id") + .update(); + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordServiceImpl.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordServiceImpl.java new file mode 100644 index 00000000..4f6b22aa --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/service/impl/ChangeRecordServiceImpl.java @@ -0,0 +1,2090 @@ +package cn.axzo.nanopart.visa.server.service.impl; + +import cn.axzo.apollo.api.req.QueryOrderToBasicReq; +import cn.axzo.apollo.api.res.ConstructionAreaInfo; +import cn.axzo.apollo.api.res.TaskOrderToBasicRes; +import cn.axzo.apollo.workspace.api.workspace.res.GetDetailRes; +import cn.axzo.apollo.workspace.api.workspace.res.ProjectBriefResp; +import cn.axzo.apollo.workspace.api.workspace.res.ProjectDetailRes; +import cn.axzo.basics.common.constant.enums.OrganizationalNodeTypeEnum; +import cn.axzo.basics.common.constant.enums.OrganizationalUnitTypeEnum; +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.basics.common.util.NumberUtil; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.basics.report.api.ReportServiceSDK; +import cn.axzo.basics.report.api.ReportUserContext; +import cn.axzo.basics.report.api.req.ReportFileReq; +import cn.axzo.basics.report.api.sheet.SheetWrapper; +import cn.axzo.digital.req.ListRectifyOrderReq; +import cn.axzo.digital.resp.RectifyOrderResp; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.framework.domain.web.result.PageData; +import cn.axzo.im.center.api.vo.PersonAccountAttribute; +import cn.axzo.im.center.api.vo.req.GroupCreateRequest; +import cn.axzo.im.center.api.vo.req.GroupDismissRequest; +import cn.axzo.im.center.api.vo.req.SendChatMessageRequest; +import cn.axzo.im.center.api.vo.resp.GroupCreateResponse; +import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.im.center.common.enums.GroupType; +import cn.axzo.maokai.api.vo.request.OrgNodeUserBriefInfoListReq; +import cn.axzo.maokai.api.vo.request.OrganizationalUnitQuery; +import cn.axzo.maokai.api.vo.response.OrgNodeUserBriefInfoResp; +import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO; +import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum; +import cn.axzo.msg.center.service.dto.PeerPerson; +import cn.axzo.msg.center.service.pending.request.CardSendRequest; +import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaChangeFieldEnum; +import cn.axzo.nanopart.visa.api.enums.VisaConfirmBizTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaLogTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum; +import cn.axzo.nanopart.visa.api.enums.VisaRelationVarTypeEnum; +import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum; +import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum; +import cn.axzo.nanopart.visa.api.request.ChangeRecordButtonOperationReq; +import cn.axzo.nanopart.visa.api.request.ChangeStatusRequest; +import cn.axzo.nanopart.visa.api.request.CheckVisaWithVisaIdReq; +import cn.axzo.nanopart.visa.api.request.GetBillIfRelatedReq; +import cn.axzo.nanopart.visa.api.request.ImGroupOperationClearReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeApproveOnlyReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeDiscussCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeExportReq; +import cn.axzo.nanopart.visa.api.request.VisaChangePageSearchReq; +import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq; +import cn.axzo.nanopart.visa.api.request.VisaInitiatorListReq; +import cn.axzo.nanopart.visa.api.request.VisaSearchReq; +import cn.axzo.nanopart.visa.api.response.VisaChangeDiscussCreateResp; +import cn.axzo.nanopart.visa.api.response.VisaChangeInitiatorResp; +import cn.axzo.nanopart.visa.api.response.VisaChangePageSearchResp; +import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse; +import cn.axzo.nanopart.visa.api.response.VisaSearchResp; +import cn.axzo.nanopart.visa.server.config.RefreshableConfiguration; +import cn.axzo.nanopart.visa.server.domain.ChangeRecord; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordBill; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordConfirm; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordLog; +import cn.axzo.nanopart.visa.server.domain.ChangeRecordRelation; +import cn.axzo.nanopart.visa.server.dto.VisaAddLogContext; +import cn.axzo.nanopart.visa.server.dto.VisaBillDto; +import cn.axzo.nanopart.visa.server.dto.VisaConfirmDto; +import cn.axzo.nanopart.visa.server.dto.VisaExportDto; +import cn.axzo.nanopart.visa.server.dto.VisaLogParam; +import cn.axzo.nanopart.visa.server.mapper.ChangeRecordDao; +import cn.axzo.nanopart.visa.server.rpc.ApolloConstructionAreaGateway; +import cn.axzo.nanopart.visa.server.rpc.ApolloTaskOrderApiGateway; +import cn.axzo.nanopart.visa.server.rpc.DrawingAnnotationApiGateway; +import cn.axzo.nanopart.visa.server.rpc.DrawingMajorGateway; +import cn.axzo.nanopart.visa.server.rpc.MsgCenterGateway; +import cn.axzo.nanopart.visa.server.rpc.OrganizationalUnitGateway; +import cn.axzo.nanopart.visa.server.rpc.RectifyApiGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaCooperateShipGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaOrganizationalNodeUserGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaProfileGateway; +import cn.axzo.nanopart.visa.server.rpc.VisaProjectApiGateway; +import cn.axzo.nanopart.visa.server.rpc.WorkflowGateway; +import cn.axzo.nanopart.visa.server.rpc.WorkspaceGateway; +import cn.axzo.nanopart.visa.server.service.ChangeRecordBillService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordConfirmService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordLogService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordRelationService; +import cn.axzo.nanopart.visa.server.service.ChangeRecordService; +import cn.axzo.nanopart.visa.server.service.VisaHelper; +import cn.axzo.nanopart.visa.server.utils.Constants; +import cn.axzo.orggateway.api.nodeuser.dto.OrgNodeUserDTO; +import cn.axzo.orggateway.api.nodeuser.req.ListOrgNodeUserReq; +import cn.axzo.orgmanax.dto.nodeuser.req.ListNodeUserReq; +import cn.axzo.workflow.common.model.dto.CooperationOrgDTO; +import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Pair; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.groovy.util.Maps; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_AMOUNT_CHANGE; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_ATTACH; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_CONTEXT; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_CONTEXT_DESCRIPTION; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_DESCRIPTION; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_HAPPEN_TIME; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_NO; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_PERSON; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_REASON; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_RELATION_AREA; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_RELATION_PROFESSIONAL; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_RELATION_PROJECT; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_RELATION_RECTIFICATION_ORDER; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_RELATION_TASK_ORDER; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_RELATION_VISA_ORDER; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_TOPIC; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_UNIT; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_UNIT_PERSON; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.FORM_FIELD_WORKSPACE_NAME; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_INITIATOR_WORKSPACE_ID; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_VISA_ID; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.IM_GROUP_BIZ_INFO_VISA_TYPE; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.MSG_CENTER_APP_CODE; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.WORKFLOW_PENDING_INITIATOR_NAME; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.WORKFLOW_PENDING_TOPIC; +import static cn.axzo.nanopart.visa.api.constant.VisaConstant.WORKFLOW_VAR_VISA_TYPE_KEY; +import static cn.axzo.nanopart.visa.server.utils.Constants.DATE_FORMAT; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; + +/** + * @author xudawei + * @date 2025/01/15 + * @desc 变更签证记录 + */ +@Slf4j +@Service +@RefreshScope +public class ChangeRecordServiceImpl extends ServiceImpl implements ChangeRecordService { + + @Resource + private ChangeRecordLogService changeRecordLogService; + @Resource + private ChangeRecordConfirmService changeRecordConfirmService; + @Resource + private ChangeRecordRelationService changeRecordRelationService; + + @Resource + private VisaProfileGateway visaProfileGateway; + + @Resource + private DrawingMajorGateway drawingMajorGateway; + + @Resource + private ApolloConstructionAreaGateway apolloConstructionAreaGateway; + + @Resource + private VisaProjectApiGateway visaProjectApiGateway; + + @Resource + private VisaOrganizationalNodeUserGateway nodeUserGateway; + + @Resource + private ReportServiceSDK reportServiceSDK; + + @Resource + private WorkflowGateway workflowGateway; + @Resource + private MsgCenterGateway msgCenterGateway; + + /** + * 最大变化金额 + */ + private final BigDecimal maxAmount = new BigDecimal("99999999.99"); + + /** + * 最小变化金额 + */ + private final BigDecimal minAmount = new BigDecimal("-99999999.99"); + + @Resource + private ChangeRecordBillService changeRecordBillService; + + @Resource + private WorkspaceGateway workspaceGateway; + + @Resource + private VisaHelper visaHelper; + + @Resource + private OrganizationalUnitGateway organizationalUnitGateway; + + @Resource + private VisaOrganizationalNodeUserGateway visaOrganizationalNodeUserGateway; + + @Resource + private RefreshableConfiguration refreshableConfiguration; + + @Resource + private VisaCooperateShipGateway visaCooperateShipGateway; + @Autowired + private ChangeRecordService changeRecordService; + @Resource + private RectifyApiGateway rectifyApiGateway; + @Resource + private ApolloTaskOrderApiGateway taskOrderApiGateway; + @Resource + private DrawingAnnotationApiGateway drawingAnnotationApiGateway; + @Value("${permission.data.flag:true}") + private boolean permissionDataFlag; + + /** + * 状态变更 + */ + @Override + public Boolean changeStatus(ChangeStatusRequest request) { + log.info("changeStatus: {}", JSON.toJSONString(request)); + request.check(); + ChangeRecord changeRecord = this.getById(request.getVisaId()); + // 1 构建前置状态与日志 + List preStatusList = this.buildPreStatus(request.getUpdateStatus()); + // 2 更新状态 + if (!this.updateVisaStatus(request.getVisaId(), preStatusList, request.getUpdateStatus())) { + return false; + } + // 3 新增日志 + this.addLogWhenChangeStatus(request, VisaLogParam.builder() + .personId(request.getPersonId()) + .ouId(request.getOuId()) + .workspaceId(request.getWorkspaceId()) + .newStatus(request.getUpdateStatus()) + .oldStatus(changeRecord.getStatus()) + .newAmountChange(request.getNewAmountChange()) + .oldAmountChange(request.getOldAmountChange()) + .uploadAttach(VisaLogParam.uploadAttachWithObject(request.getNewAttach(), request.getOldAttach())) + .deleteAttach(VisaLogParam.deleteAttachWithObject(request.getOldAttach(), request.getNewAttach())).build()); + return true; + } + + /** + * 更新DB变签状态 + */ + @Override + public boolean changeDbStatus(Long visaId, List statusList, VisaStatusEnum updateStatus) { + return this.updateVisaStatus(visaId, statusList, updateStatus); + } + + /** + * 更新变签状态 + */ + private boolean updateVisaStatus(Long visaId, List statusList, VisaStatusEnum updateStatus) { + boolean update = this.lambdaUpdate().eq(ChangeRecord::getId, visaId) + .eq(ChangeRecord::getIsDelete, 0) + .in(ChangeRecord::getStatus, statusList) + .set(ChangeRecord::getStatus, updateStatus) + .update(); + if (!update) { + //单据最新的状态是更新状态 + VisaDetailByIdResponse response = this.detailById(visaId); + if (response.getStatus().equals(updateStatus)) { + return false; + } + throw new ServiceException("更新状态失败"); + } + return update; + } + + /** + * 添加日志 + */ + private void addLogWhenChangeStatus(ChangeStatusRequest request, VisaLogParam param) { + log.info("addLogWhenChangeStatus: request:{},param:{}", JSON.toJSONString(request), JSON.toJSONString(param)); + List logList = Lists.newArrayList(); + // 1 由[决策中|执行中] -> [审批中] 执行变更签证属性变更 + if (Objects.nonNull(param.getOldStatus()) && Objects.nonNull(param.getNewStatus()) + && VisaStatusEnum.isEditForm(param.getOldStatus()) + && param.getNewStatus().equals(VisaStatusEnum.APPROVING) && request.isEditFormFlag()) { + logList.addAll(changeRecordLogService.buildLogList(request.getVisaId(), VisaLogTypeEnum.EDIT_FORM, param)); + } + // 2 状态变更,引发的操作日志记录 + if (Objects.nonNull(param.getNewStatus().getLogType())) { + logList.addAll(changeRecordLogService.buildLogList(request.getVisaId(), param.getNewStatus().getLogType(), param)); + } + // 3 记录操作日志 + changeRecordLogService.batchAddLog(logList); + } + + /** + * 更新前置状态 + */ + private List buildPreStatus(VisaStatusEnum updateStatus) { + List preStatusList; + switch (updateStatus) { + case TO_REPORT: + // 动作:暂存 + // 状态流转:[待提报|审批的待提报]-->[待提报|审批到待提报] + preStatusList = Lists.newArrayList(VisaStatusEnum.TO_REPORT, VisaStatusEnum.REPORT_FROM_APPROVE); + break; + case DECIDING: + // 动作:洽商 + // 状态流转:[待提报|审核不通过的待提报]-->[决策中] + preStatusList = Lists.newArrayList(VisaStatusEnum.TO_REPORT, VisaStatusEnum.REPORT_FROM_APPROVE); + break; + case EXECUTING: + // 动作:执行 + // 状态流转:[决策中|审核不通过的待提报]-->[执行中] + preStatusList = Lists.newArrayList(VisaStatusEnum.DECIDING, VisaStatusEnum.DECIDING_FROM_APPROVE); + break; + case APPROVING: + // 动作:发起审批 + // 状态流转:[待提报|审批不通过的待提报|决策中|审批不通过的决策中|执行中]-->[审批中] + preStatusList = Lists.newArrayList(VisaStatusEnum.TO_REPORT, VisaStatusEnum.REPORT_FROM_APPROVE, VisaStatusEnum.DECIDING, VisaStatusEnum.DECIDING_FROM_APPROVE, VisaStatusEnum.EXECUTING); + break; + case REPORT_FROM_APPROVE: + // 动作:审批不通过(已驳回/已撤回/已中止) + // 状态流转:[待提报|审批不通过的待提报|决策中|审批不通过的决策中|执行中]-->[审批中] + preStatusList = Lists.newArrayList(VisaStatusEnum.APPROVING); + break; + case DECIDING_FROM_APPROVE: + preStatusList = Lists.newArrayList(VisaStatusEnum.APPROVING); + break; + case FORBIDED: + // 动作:作废 + // 状态流转:[待提报|审核不通过的待提报|决策中|审核不通过的决策中|执行中|审批中]-->[审核不通过的决策中] + preStatusList = Lists.newArrayList(VisaStatusEnum.TO_REPORT, VisaStatusEnum.REPORT_FROM_APPROVE, VisaStatusEnum.DECIDING, VisaStatusEnum.DECIDING_FROM_APPROVE, VisaStatusEnum.EXECUTING, VisaStatusEnum.APPROVING); + break; + case COMPLETED: + // 动作:整个审批通过 + // 状态流转:[审批中]-->[已完成] + preStatusList = Lists.newArrayList(VisaStatusEnum.APPROVING); + break; + default: + throw new ServiceException("更新状态类型不匹配"); + } + return preStatusList; + } + + /** + * 变更签证详情 + * + * @param visaId 变更签证Id + * @return 变更签证详情 + */ + @Override + public VisaDetailByIdResponse detailById(Long visaId) { + if (Objects.isNull(visaId)) { + return VisaDetailByIdResponse.builder().build(); + } + ChangeRecord visa = this.getById(visaId); + if (Objects.isNull(visa) || Objects.isNull(visa.getId())) { + throw new ServiceException("未查询到单据信息"); + } + PersonProfileDto profile = visaProfileGateway.getProfile(visa.getCreateBy()); + + List changeRecordBills = changeRecordBillService.listById(visaId); + Map> billMap = changeRecordBills.stream() + .collect(Collectors.groupingBy(ChangeRecordBill::getBillType + , Collectors.mapping(item -> Objects.equals(0L, item.getBillId()) ? item.getBillNo() : item.getBillId().toString(), Collectors.toList()))); + + ProjectDetailRes projectDetailRes = visaProjectApiGateway.getProjectById(visa.getRelationProject()); + GetDetailRes workspace = workspaceGateway.getById(visa.getRelationWorkspaceId()); + + // 1 查询相关确认人 + Pair, ChangeRecordConfirm> pair = this.buildConfirmCreate(visaId); + + OrgNodeUserDTO orgNodeUserDTO = this.getOrgNodeUserDTO(pair.getValue().getPersonId(), pair.getValue().getOuId(), pair.getValue().getWorkspaceId()); + + return VisaDetailByIdResponse.builder() + .visaId(visaId) + .no(visa.getNo()) //单号 + .type(visa.getType()) //单据类型,DESIGN_CHANGE:设计变更,TECHNOLOGY_APPROVED:技术核定,PROJECT_VISA:工程签证 + .reason(visa.getReason()) //发生原因 + .createName(profile.getRealName()) //发起人姓名 + .createAvatarUrl(profile.getAvatarUrl()) // 发起人头像 + .createJobCode((Objects.nonNull(orgNodeUserDTO) && Objects.nonNull(orgNodeUserDTO.getJob())) ? orgNodeUserDTO.getJob().getCode() : "") // 发起人岗位code + .createJobName((Objects.nonNull(orgNodeUserDTO) && Objects.nonNull(orgNodeUserDTO.getJob())) ? orgNodeUserDTO.getJob().getName() : "") // 发起人岗位名称 + .createOuName((Objects.nonNull(orgNodeUserDTO) && Objects.nonNull(orgNodeUserDTO.getUnit())) ? orgNodeUserDTO.getUnit().getName() : "") //发起人单位名称 + .createOuId((Objects.nonNull(orgNodeUserDTO) && Objects.nonNull(orgNodeUserDTO.getUnit())) ? orgNodeUserDTO.getUnit().getId() : 0l) // 发起人单位 + .createBy(visa.getCreateBy()) //发起人Id + .happenTime(Objects.nonNull(visa.getHappenTime()) ? DateUtil.format(visa.getHappenTime(), DATE_FORMAT) : "") // 提出时间 + .approvalCompleteTime(Objects.nonNull(visa.getApprovalCompleteTime()) ? DateUtil.format(visa.getApprovalCompleteTime(), Constants.DATE_TIME_FORMAT) : "") //最终审批时间 + .relationProfessionalList(VisaDetailByIdResponse.RelationProfessionalDetail.buildProfessionWithObject(visa.getRelationProfessional()))//专业 + .relationProfessionalName(this.visaHelper.buildProfessionalName(visa.getRelationProject(), visa.getRelationProfessional())) //专业名称 + .relationAreaList(VisaDetailByIdResponse.RelationAreaDetail.buildAreaWithObject(visa.getRelationArea())) //区域 + .relationAreaName(this.visaHelper.buildAreaName(visa.getRelationArea())) // 区域名称 + .relationProject(visa.getRelationProject()) // 工程Id + .relationProjectName((Objects.nonNull(projectDetailRes) && Objects.nonNull(projectDetailRes.getProjectInfo())) ? projectDetailRes.getProjectInfo().getName() : "") // 工程名称 + .relationWorkspaceId(visa.getRelationWorkspaceId()) // 项目Id + .relationWorkspaceName(Objects.nonNull(workspace) ? workspace.getName() : "") // 项目名称 + .relationOuAndPersonList(changeRecordConfirmService.listRelationByVisaId(pair.getKey(), visa.getRelationWorkspaceId())) // 相关单位与确认人 + .relationTaskList(CollectionUtils.isEmpty(billMap.get(VisaBillTypeEnum.TASK)) ? Lists.newArrayList() : billMap.get(VisaBillTypeEnum.TASK)) // 关联任务单 + .relationRectifyList(CollectionUtils.isEmpty(billMap.get(VisaBillTypeEnum.RECTIFY)) ? Lists.newArrayList() : billMap.get(VisaBillTypeEnum.RECTIFY)) //关联整改单 + .relationVisaList(CollectionUtils.isEmpty(billMap.get(VisaBillTypeEnum.DESIGN_VISA)) ? Lists.newArrayList() : billMap.get(VisaBillTypeEnum.DESIGN_VISA)) // 关联变更单 + .relationInvalidVisaList(this.detailPageInvalidVisaIdList(billMap.get(VisaBillTypeEnum.DESIGN_VISA))) + .amountChange(this.buildAmountChange(visa.getAmountChange())) // 变更金额 + .changeContextAndDescriptionList(this.buildDesc(visaId)) //发生内容及说明 + .status(visa.getStatus()) //状态,TO_REPORT:待提报;DECIDING:决策中;EXECUTING:执行中;APPROVING:审批中;COMPLETED:已完成;FORBIDED:已废除;REPORT_FROM_APPROVE:审批到待提报;DECIDING_FROM_APPROVE:审批到决策中 EXECUTING + .attach(this.buildAttachUploadFile(visaId)) // 附件 + .imGroupId(visa.getImGroupId()) // 群组ID + .approvalId(visa.getApprovalId()) //最终审批ID + .approvalStatus(visa.getApprovalStatus()) //最终审批状态 + .topic(visa.getTopic()) // 主题 + .build(); + } + + /** + * 从visaIdList过滤无效的变更签证Id + */ + private List detailPageInvalidVisaIdList(List visaIdList) { + + if (CollectionUtils.isEmpty(visaIdList)) { + return Collections.emptyList(); + } + + VisaChangePageSearchReq req = new VisaChangePageSearchReq(); + req.setVisaIds(visaIdList.stream().map(item -> Long.valueOf(item)).collect(Collectors.toList())); + req.setStatuses(Lists.newArrayList(VisaStatusEnum.detailPageInvalidStatus())); + + List changeRecords = this.getFilter(req).list(); + if (CollectionUtils.isEmpty(changeRecords)) { + return Collections.emptyList(); + } + return changeRecords.stream().map(item -> item.getId().toString()).collect(Collectors.toList()); + } + + /** + * 构建变更金额 + */ + private String buildAmountChange(BigDecimal amountChange) { + if (Objects.isNull(amountChange)) { + return null; + } + if (BigDecimal.ZERO.compareTo(amountChange) == 0) { + return "0"; + } + return amountChange.toPlainString(); + } + + /** + * 构建确认人集合与发起人 + */ + private Pair, ChangeRecordConfirm> buildConfirmCreate(Long visaId) { + VisaConfirmDto dto = VisaConfirmDto.builder() + .visaId(visaId).build(); + List list = this.changeRecordConfirmService.findByCondition(dto); + List confirmList = list.stream().filter(item -> item.getBizType() == VisaConfirmBizTypeEnum.CONFIRM).collect(Collectors.toList()); + List createList = list.stream().filter(item -> item.getBizType() == VisaConfirmBizTypeEnum.CREATE).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(confirmList)) { + confirmList = Lists.newArrayList(); + } + if (CollectionUtils.isEmpty(createList)) { + return Pair.of(confirmList, ChangeRecordConfirm.builder().build()); + } + return Pair.of(confirmList, createList.get(0)); + } + + /** + * 综合获取基础信息 + */ + private OrgNodeUserDTO getOrgNodeUserDTO(Long personId, Long ouId, Long workspaceId) { + if (Objects.isNull(personId) || Objects.isNull(ouId) || Objects.isNull(workspaceId)) { + return null; + } + PageResp pageResp = visaOrganizationalNodeUserGateway.list(ListOrgNodeUserReq.builder() + .personId(personId) + .organizationalUnitId(ouId) + .workspaceId(workspaceId) + .needs(ListNodeUserReq.Needs.builder().job(true).unit(true).personProfile(true).build()).build()); + if (Objects.isNull(pageResp) || CollectionUtils.isEmpty(pageResp.getData())) { + return null; + } + return pageResp.getData().get(0); + } + + /** + * 构建上传文件对象集合 + */ + @Override + public List buildAttachUploadFile(Long visaId) { + if (Objects.isNull(visaId)) { + return Lists.newArrayList(); + } + List attachList = changeRecordRelationService.findByVisaAndVarName(visaId, VisaRelationFieldEnum.ATTACHMENT.name()); + return this.buildAttachUploadFileByRelation(attachList); + } + + /** + * 附件对象转换 + */ + @Override + public List buildAttachUploadFileByRelation(List attachList) { + if (CollectionUtils.isEmpty(attachList)) { + return Lists.newArrayList(); + } + List attachContentList = attachList.stream() + .filter(item -> StringUtils.hasText(item.getContent())) + .map(ChangeRecordRelation::getContent).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(attachContentList)) { + return Lists.newArrayList(); + } + return attachContentList.stream() + .filter(item -> StringUtils.hasText(item)) + .map(item -> JSON.parseObject(item, VisaDetailByIdResponse.VisaUploadFile.class)).collect(Collectors.toList()); + } + + /** + * 构建发生内容与说明 + */ + private List buildDesc(Long visaId) { + if (Objects.isNull(visaId)) { + return Lists.newArrayList(); + } + List relationList = this.changeRecordRelationService.findByVisaAndVarName(visaId, VisaRelationFieldEnum.CONTENT_DESCRIPTION.name()); + if (CollectionUtils.isEmpty(relationList)) { + return Lists.newArrayList(); + } + return relationList.stream().map(item -> JSON.parseObject(item.getContent(), VisaChangeTempCreateReq.ChangeContextAndDescription.class)).collect(Collectors.toList()); + } + + /** + * 创建变更签证 + * + * @param req 表单数据 + * @return 变更签证Id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long tempCreateVisaChangeRecord(VisaChangeTempCreateReq req) { + /*参数校验*/ + AssertUtil.isTrue(NumberUtil.isPositiveNumber(req.getRelationWorkspaceId()), "关联项目不能为空"); + AssertUtil.notNull(req.getType(), "请选择变更签证类型"); + AssertUtil.isTrue(NumberUtil.isPositiveNumber(req.getOperatorOuId()) + && NumberUtil.isPositiveNumber(req.getOperatorPersonId()) + && NumberUtil.isPositiveNumber(req.getOperatorNodeId()) + && NumberUtil.isPositiveNumber(req.getOperatorOuType()), + "操作人信息不能为空"); + AssertUtil.isTrue(CollectionUtils.isEmpty(req.getChangeContextAndDescriptionList()) || + req.getChangeContextAndDescriptionList().size() <= refreshableConfiguration.getVerifyChangeContextLimit(), + "发生内容及说明数量过多,请重新填写"); + + + ChangeRecord changeRecord; + List confirmPerson = Lists.newArrayList(); + VisaAddLogContext oldContext = null; + VisaAddLogContext newContext = null; + if (NumberUtil.isNotPositiveNumber(req.getId())) { + /*暂存(新增)*/ + /*保存主记录*/ + changeRecord = new ChangeRecord(); + BeanUtil.copyProperties(req, changeRecord); + changeRecord.setStatus(VisaStatusEnum.TO_REPORT); + changeRecord.setCreateBy(req.getOperatorPersonId()); + changeRecord.setUpdateBy(req.getOperatorPersonId()); + save(changeRecord); + // 发起人 + confirmPerson.add(buildCreateVisaConfirmDto(req, changeRecord)); + } else { + /*编辑*/ + changeRecord = getById(req.getId()); + AssertUtil.notNull(changeRecord, "记录不存在"); + // 编辑态下校验单据状态 + AssertUtil.isTrue(!StringUtils.hasText(changeRecord.getApprovalId()) + || (StringUtils.hasText(changeRecord.getApprovalId()) && !Objects.equals(VisaStatusEnum.APPROVING, changeRecord.getStatus())), + "当前单据审批中,不允许操作"); + + + oldContext = new VisaAddLogContext(); + oldContext.setStatus(changeRecord.getStatus()); + oldContext.setChangeRecord(BeanUtil.copyProperties(changeRecord, ChangeRecord.class)); + /*更新主记录*/ + BeanUtil.copyProperties(req, changeRecord); + changeRecord.setUpdateBy(req.getOperatorPersonId()); + updateById(changeRecord); + + /*更新关联附加信息,采用覆盖策略,删除原有信息,直接保存本次信息*/ + // 相关单位与人员(发起人不能被覆盖) + changeRecordConfirmService.deleteByVisaId(changeRecord.getId(), Lists.newArrayList(VisaConfirmBizTypeEnum.CONFIRM)); + // 更新发生内容及说明 + changeRecordRelationService.deleteByVisaId(changeRecord.getId(), Lists.newArrayList(VisaRelationFieldEnum.CONTENT_DESCRIPTION)); + // 更新关联单据 + changeRecordBillService.deleteByVisaId(changeRecord.getId()); + // 更新附件 + List oldVisaAttach = changeRecordRelationService.findByVisaAndVarName(changeRecord.getId(), VisaRelationFieldEnum.ATTACHMENT.name()); + oldContext.setRelationList(oldVisaAttach); + changeRecordRelationService.deleteByVisaId(changeRecord.getId(), Lists.newArrayList(VisaRelationFieldEnum.ATTACHMENT)); + } + /*保存附加关联信息*/ + // 相关单位与人员 + if (CollectionUtils.isNotEmpty(req.getRelationUnitAndPersonList())) { + confirmPerson.addAll(req.getRelationUnitAndPersonList().stream() + .map(p -> buildConfirmVisaConfirmDto(p, changeRecord, req.getOperatorPersonId())) + .collect(Collectors.toList())); + } + changeRecordConfirmService.addConfirm(confirmPerson); + // 保存发生内容及说明 + if (CollectionUtils.isNotEmpty(req.getChangeContextAndDescriptionList())) { + changeRecordRelationService.save(VisaRelationFieldEnum.CONTENT_DESCRIPTION, req, changeRecord); + } + // 保存关联单据 + if (CollUtil.isNotEmpty(req.getRelationOrderMap())) { + changeRecordBillService.addBill(buildVisaBillDto(req, changeRecord)); + } + // 保存附件 + if (CollectionUtils.isNotEmpty(req.getAttach())) { + changeRecordRelationService.save(VisaRelationFieldEnum.ATTACHMENT, req, changeRecord); + } + /*操作日志记录*/ + newContext = new VisaAddLogContext(); + newContext.setPersonId(req.getOperatorPersonId()); + newContext.setWorkspaceId(req.getRelationWorkspaceId()); + newContext.setOuId(req.getOperatorOuId()); + newContext.setStatus(changeRecord.getStatus()); + newContext.setChangeRecord(changeRecord); + visaHelper.addLogAndChangeStatus(oldContext, newContext); + return changeRecord.getId(); + } + + private static List buildVisaBillDto(VisaChangeTempCreateReq req, ChangeRecord changeRecord) { + return req.getRelationOrderMap().entrySet().stream() + .peek(os -> { + if (VisaBillTypeEnum.TASK.name().equals(os.getKey())) { + os.setValue(os.getValue().stream().filter(o -> StringUtils.hasText(o.getNo())).collect(Collectors.toList())); + } else { + os.setValue(os.getValue().stream().filter(o -> NumberUtil.isPositiveNumber(o.getId())).collect(Collectors.toList())); + } + }) + .flatMap(os -> os.getValue().stream() + .map(o -> VisaBillDto.builder() + .visaId(changeRecord.getId()) + .visaType(changeRecord.getType()) + .billId(o.getId()) + .billNo(o.getNo()) + .billType(VisaBillTypeEnum.valueOf(os.getKey())) + .createBy(req.getOperatorPersonId()) + .updateBy(req.getOperatorPersonId()) + .build())) + .collect(Collectors.toList()); + } + + /** + * 创建群聊洽商变更签证 + * + * @param req 表单数据 + * @return 变更签证Id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public VisaChangeDiscussCreateResp discussCreateVisaChangeRecord(VisaChangeDiscussCreateReq req) { + /*参数校验*/ + AssertUtil.notEmpty(req.getTopic(), "主题不能为空,请输入主题用作群聊名称"); + /*暂存数据*/ + Long visaId = tempCreateVisaChangeRecord(req); + /*创建群聊*/ + GroupCreateRequest request = new GroupCreateRequest(); + request.setGroupType(GroupType.VISA); + request.setBizCode(String.valueOf(visaId)); + request.setName(req.getTopic()); + // 群主 + request.setOwner(PersonAccountAttribute.builder() + .personId(String.valueOf(req.getOperatorPersonId())) + .ouId(req.getOperatorOuId()) + .workspaceId(req.getRelationWorkspaceId()) + .appType(AppTypeEnum.CMP) + .build()); + // 群成员 + if (CollectionUtils.isNotEmpty(req.getDiscussPersonInfoList())) { + request.setMembers(req.getDiscussPersonInfoList().stream() + .filter(i -> !i.getIsAdmin()) + .map(i -> PersonAccountAttribute.builder() + .personId(String.valueOf(i.getPersonId())) + .ouId(i.getUnitId()) + .workspaceId(i.getWorkspaceId()) + .appType(AppTypeEnum.CMP) + .build()) + .collect(Collectors.toSet())); + } + // 群头像 + request.setAvatar("https://axzo-obs-public.obs.cn-north-4.myhuaweicloud.com:443/obs-public/obs-public/visa-group.png"); + request.setMemberLimit(100L); + request.setBizGroupInfo(Maps.of(IM_GROUP_BIZ_INFO_VISA_ID, visaId, + IM_GROUP_BIZ_INFO_INITIATOR_WORKSPACE_ID, req.getRelationWorkspaceId(), + IM_GROUP_BIZ_INFO_VISA_TYPE, req.getType().name())); + GroupCreateResponse imGroup = msgCenterGateway.createImGroup(request); + AssertUtil.notNull(imGroup, "IM 群创建失败"); + ChangeRecord visa = updateImInfo(visaId, imGroup); + + sendCardAndMsgToImGroup(ChangeRecordButtonOperationReq.builder() + .operatorPersonId(req.getOperatorPersonId()) + .operatorOuId(req.getOperatorOuId()) + .operatorWorkspaceId(req.getRelationWorkspaceId()) + .build(), visa); + + // 更新主表状态 + changeStatus(ChangeStatusRequest.builder() + .visaId(visaId) + .updateStatus(VisaStatusEnum.DECIDING) + .personId(req.getOperatorPersonId()) + .ouId(req.getOperatorOuId()) + .workspaceId(req.getRelationWorkspaceId()) + .editFormFlag(true) + .build()); + return VisaChangeDiscussCreateResp.builder() + .visaId(visaId) + .imGroupId(imGroup.getTid()) + .imGroupName(req.getTopic()) + .build(); + } + + private ChangeRecord updateImInfo(Long visaId, GroupCreateResponse imGroup) { + ChangeRecord changeRecord = changeRecordService.getById(visaId); + AssertUtil.isNull(changeRecord.getImGroupId(), "已创建群聊,请勿重复创建"); + changeRecord.setImGroupId(imGroup.getTid()); + updateById(changeRecord); + return changeRecord; + } + + /** + * 创建审批变更签证 + * + * @param req 表单数据 + * @return 变更签证Id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long approveCreateVisaChangeRecord(VisaChangeApproveCreateReq req) { + /*校验*/ + validVisaChangeForm(req); + ChangeRecord changeRecord = this.getById(req.getId()); + ChangeRecord oldChangeRecord = BeanUtil.copyProperties(changeRecord, ChangeRecord.class); + List visaUploadFiles = this.buildAttachUploadFile(req.getId()); + + /*保存签证信息*/ + Long visaId = tempCreateVisaChangeRecord(req); + + /*提交审批*/ + String processInstanceId = createProcessInstance(req, visaId); + AssertUtil.isTrue(StringUtils.hasText(processInstanceId), "创建审批异常"); + + // 更新主表中的审批字段,记录变量表的审批的信息 + updateApprovalInfo(visaId, processInstanceId); + + this.changeStatusWhenApproveCreate(visaId, req, oldChangeRecord, visaUploadFiles); + + return visaId; + } + + /** + * 更改状态-申请审批时 + * + * @param visaId 变更签证id + * @param req 请求,主要是获取最新的属性,比如变更金额/附件 + * @param changeRecord 变更签证对象,主要获取老的属性,比如变更金额 + * @param visaUploadFiles 上传附件,主要是老的属性 + */ + public void changeStatusWhenApproveCreate(Long visaId, VisaChangeApproveCreateReq req, ChangeRecord changeRecord, List visaUploadFiles) { + ChangeStatusRequest changeStatusRequest = ChangeStatusRequest.builder() + .visaId(visaId) + .updateStatus(VisaStatusEnum.APPROVING) + .personId(req.getOperatorPersonId()) + .ouId(req.getOperatorOuId()) + .workspaceId(req.getRelationWorkspaceId()) + .editFormFlag(true) + .newAmountChange(req.getAmountChange()) + .oldAmountChange(changeRecord.getAmountChange()) + .build(); + + if (CollectionUtils.isNotEmpty(req.getAttach())) { + changeStatusRequest.setNewAttach(BeanUtil.copyToList(req.getAttach(), VisaDetailByIdResponse.VisaUploadFile.class)); + } + if (CollectionUtils.isNotEmpty(visaUploadFiles)) { + changeStatusRequest.setOldAttach(visaUploadFiles); + } + // 更新主表状态 + changeStatus(changeStatusRequest); + } + + private String createProcessInstance(VisaChangeApproveCreateReq req, Long visaId) { + BpmnProcessInstanceCreateDTO processDto = new BpmnProcessInstanceCreateDTO(); + // 发起时指定审批人 + processDto.setSpecifyAssignerMap(ImmutableMap.of(req.getActivityId(), buildApprovers(req.getApprovePersonInfoList()))); + // 设置变洽签主键 ID + processDto.setBusinessKey(String.valueOf(visaId)); + Map variables = new HashMap<>(); + variables.put(WORKFLOW_VAR_VISA_TYPE_KEY, req.getType().name()); + processDto.setVariables(variables); + processDto.setInitiator(BpmnTaskDelegateAssigner.builder() + .tenantId(String.valueOf(req.getRelationWorkspaceId())) + .ouId(String.valueOf(req.getOperatorOuId())) + .personId(String.valueOf(req.getOperatorPersonId())) + .build()); + processDto.setTenantId(String.valueOf(req.getRelationWorkspaceId())); + + Map formVariables = new HashMap<>(); + // 传递表单数据 + formVariables.put(FORM_FIELD_NO, req.getNo()); + final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); + formVariables.put(FORM_FIELD_HAPPEN_TIME, sdf.format(req.getHappenTime())); + formVariables.put(FORM_FIELD_TOPIC, req.getTopic()); + GetDetailRes workspaceRes = workspaceGateway.getById(req.getRelationWorkspaceId()); + formVariables.put(FORM_FIELD_WORKSPACE_NAME, Objects.isNull(workspaceRes) ? "" : workspaceRes.getName()); + ProjectDetailRes projectDetailRes = visaProjectApiGateway.getProjectById(req.getRelationProject()); + formVariables.put(FORM_FIELD_RELATION_PROJECT, projectDetailRes.getProjectInfo().getName()); + formVariables.put(FORM_FIELD_RELATION_PROFESSIONAL, visaHelper.buildProfessionalName(req.getRelationProject(), req.getRelationProfessional())); + formVariables.put(FORM_FIELD_REASON, req.getReason()); + formVariables.put(FORM_FIELD_RELATION_AREA, visaHelper.buildAreaName(req.getRelationArea())); + formVariables.put(FORM_FIELD_CONTEXT_DESCRIPTION, buildContextDescription(req.getChangeContextAndDescriptionList())); + formVariables.put(FORM_FIELD_UNIT_PERSON, buildUnitPerson(req.getRelationUnitAndPersonList(), req.getRelationWorkspaceId())); + + // 非必填项 + if (Objects.nonNull(req.getAmountChange())) { + formVariables.put(FORM_FIELD_AMOUNT_CHANGE, VisaHelper.buildChangeAmount(req.getAmountChange())); + } + + // 三种单据 + if (MapUtils.isNotEmpty(req.getRelationOrderMap())) { + List taskOrders = req.getRelationOrderMap().getOrDefault(VisaBillTypeEnum.TASK.name(), null); + if (CollectionUtils.isNotEmpty(taskOrders)) { + List> taskOrderMaps = new ArrayList<>(); + taskOrders.forEach(i -> { + Map taskOrderMap = new HashMap<>(); + taskOrderMap.put("id", i.getNo()); + taskOrderMap.put("no", i.getNo()); + taskOrderMap.put("name", i.getName()); + taskOrderMaps.add(taskOrderMap); + }); + formVariables.put(FORM_FIELD_RELATION_TASK_ORDER, taskOrderMaps); + } + + List rectificationOrders = req.getRelationOrderMap().getOrDefault(VisaBillTypeEnum.RECTIFY.name(), null); + if (CollectionUtils.isNotEmpty(rectificationOrders)) { + formVariables.put(FORM_FIELD_RELATION_RECTIFICATION_ORDER, rectificationOrders); + } + + List visaOrders = req.getRelationOrderMap().getOrDefault(VisaBillTypeEnum.DESIGN_VISA.name(), null); + if (CollectionUtils.isNotEmpty(visaOrders)) { + formVariables.put(FORM_FIELD_RELATION_VISA_ORDER, visaOrders); + } + } + formVariables.put(FORM_FIELD_ATTACH, req.getAttach()); + processDto.setStartFormVariables(formVariables); + processDto.setProcessDefinitionKey(req.getType().getProcessDefinitionKey()); + Map pendingVariables = new HashMap<>(); + PersonProfileDto profile = visaProfileGateway.getProfile(req.getOperatorPersonId()); + pendingVariables.put(WORKFLOW_PENDING_INITIATOR_NAME, profile.getRealName()); + pendingVariables.put(WORKFLOW_PENDING_TOPIC, req.getTopic()); + pendingVariables.put(IM_GROUP_BIZ_INFO_VISA_ID, visaId); + pendingVariables.put(IM_GROUP_BIZ_INFO_INITIATOR_WORKSPACE_ID, req.getRelationWorkspaceId()); + processDto.setPendingVariables(pendingVariables); + processDto.setCooperationOrg(new CooperationOrgDTO()); + return workflowGateway.processInstanceCreate(processDto); + } + + private List> buildUnitPerson(List list, Long workspaceId) { + if (CollectionUtils.isEmpty(list)) { + return Collections.emptyList(); + } + List ouIds = list.stream().map(VisaChangeTempCreateReq.RelationUnitAndPerson::getOuId).distinct().collect(Collectors.toList()); + Map unitMap = organizationalUnitGateway.list(OrganizationalUnitQuery.builder().unitIds(ouIds).build()) + .stream().collect(Collectors.toMap(OrganizationalUnitVO::getId, Function.identity(), (s, t) -> s)); + + Map> unitGroupBy = list.stream().collect(Collectors.groupingBy(VisaChangeTempCreateReq.RelationUnitAndPerson::getOuId, Collectors.toList())); + List> unitPersons = new ArrayList<>(); + + unitGroupBy.forEach((unitId, persons) -> { + Map unitPerson = new HashMap<>(); + OrganizationalUnitVO unitVo = unitMap.get(unitId); + unitPerson.put(FORM_FIELD_UNIT, Objects.isNull(unitVo) ? "" : unitVo.getName() + "(" + OrganizationalUnitTypeEnum.getByType(unitVo.getType()).getDesc() + ")"); + List personInfos = getPersonInfos(workspaceId, unitId, persons); + unitPerson.put(FORM_FIELD_PERSON, buildPersonInfosStr(personInfos)); + unitPersons.add(unitPerson); + }); + + return unitPersons; + } + + private String buildPersonInfosStr(List personInfos) { + StringBuilder personInfosStr = new StringBuilder(); + if (CollectionUtils.isEmpty(personInfos)) { + return personInfosStr.toString(); + } + + for (OrgNodeUserDTO info : personInfos) { + if (personInfosStr.length() > 0) { + personInfosStr.append(","); + } + personInfosStr.append(info.getPersonProfile().getRealName()) + .append(Objects.isNull(info.getJob()) ? "" : "-" + info.getJob().getName()); + } + return personInfosStr.toString(); + } + + private List getPersonInfos(Long workspaceId, Long unitId, List persons) { + PageResp pageResp = visaOrganizationalNodeUserGateway.list(ListOrgNodeUserReq.builder() + .personIds(persons.stream().map(VisaChangeTempCreateReq.RelationUnitAndPerson::getPersonId).collect(Collectors.toList())) + .organizationalUnitId(unitId) + .workspaceId(workspaceId) + .organizationalNodeIds(persons.stream().map(VisaChangeTempCreateReq.RelationUnitAndPerson::getNodeId).collect(Collectors.toList())) + .needs(ListNodeUserReq.Needs.builder().job(true).personProfile(true).build()).build()); + if (Objects.isNull(pageResp) || CollectionUtils.isEmpty(pageResp.getData())) { + return Collections.emptyList(); + } + return pageResp.getData(); + } + + private List> buildContextDescription(List list) { + if (CollectionUtils.isEmpty(list)) { + return Collections.emptyList(); + } + List> contextDescriptions = new ArrayList<>(); + list.forEach(i -> { + Map contextDescription = new HashMap<>(); + UploadFieldDTO uploadFieldDTO = new UploadFieldDTO(); + uploadFieldDTO.setFileKey(i.getFileKey()); + uploadFieldDTO.setFileName(i.getFileName()); + uploadFieldDTO.setFileUrl(i.getFileUrl()); + contextDescription.put(FORM_FIELD_CONTEXT, uploadFieldDTO); + contextDescription.put(FORM_FIELD_DESCRIPTION, i.getText()); + contextDescriptions.add(contextDescription); + }); + return contextDescriptions; + } + + /** + * 更新 + * + * @param visaId + * @param processInstanceId + */ + private void updateApprovalInfo(Long visaId, String processInstanceId) { + // 主表更新审批信息 + ChangeRecord changeRecord = new ChangeRecord(); + changeRecord.setId(visaId); + changeRecord.setApprovalId(processInstanceId); + changeRecord.setApprovalStatus(PROCESSING.getStatus()); + updateById(changeRecord); + + // 设置关联表的审批信息 + ChangeRecordRelation processRelation = new ChangeRecordRelation(); + processRelation.setVisaId(visaId); + processRelation.setVarName(VisaRelationFieldEnum.PROCESS_INSTANCE_OF_VISA.name()); + processRelation.setVarType(VisaRelationVarTypeEnum.STRING.getType()); + processRelation.setContent(processInstanceId); + processRelation.setContentExt(PROCESSING.getStatus()); + changeRecordRelationService.save(processRelation); + + // 更新单据关联状态为 true + changeRecordBillService.billRelationStatus(visaId, true); + + // 同步记录图纸绑定关系 + syncDrawAnnotationBindRelation(visaId); + } + + @Override + public void syncDrawAnnotationBindRelation(Long visaId) { + List relationDrawAnnotation = getRelationDrawAnnotation(visaId); + if (CollectionUtils.isNotEmpty(relationDrawAnnotation)) { + Set annotationIds = relationDrawAnnotation.stream() + .map(VisaChangeTempCreateReq.ChangeContextAndDescription::getAnnotationId) + .collect(Collectors.toSet()); + drawingAnnotationApiGateway.bindBusiness(visaId, annotationIds); + } + } + + @Override + public void syncDrawAnnotationUnBindRelation(Long visaId) { + List relationDrawAnnotation = getRelationDrawAnnotation(visaId); + if (CollectionUtils.isNotEmpty(relationDrawAnnotation)) { + Set annotationIds = relationDrawAnnotation.stream() + .map(VisaChangeTempCreateReq.ChangeContextAndDescription::getAnnotationId) + .collect(Collectors.toSet()); + drawingAnnotationApiGateway.unbindBusiness(visaId, annotationIds); + } + } + + private List buildApprovers(List approvePersonInfoList) { + List approvers = Lists.newArrayList(); + if (CollectionUtils.isEmpty(approvePersonInfoList)) { + return approvers; + } + + approvers.addAll(approvePersonInfoList.stream() + .map(i -> BpmnTaskDelegateAssigner.builder() + .personId(String.valueOf(i.getPersonId())) + .tenantId(String.valueOf(i.getWorkspaceId())) + .ouId(String.valueOf(i.getOuId())) + .build()) + .collect(Collectors.toList())); + return approvers; + } + + /** + * 分页查询 + * + * @param req 筛查条件 + * @return 分页数据 + */ + @Override + public PageData page(VisaChangePageSearchReq req) { + /*筛查条件校验与修正*/ + validAndAmendSearchCondition(req); + + if (permissionDataFlag) { + Set permissionVisaIdSet = this.fetchPermissionVisaIdSet(req); + if (CollectionUtils.isEmpty(permissionVisaIdSet)) { + return PageData.builder() + .page(Math.toIntExact(req.getPage())) + .pageSize(Math.toIntExact(req.getPageSize())) + .totalCount(0L) + .list(Lists.newArrayList()) + .build(); + } + } + + /*数据查询*/ + Page page = getFilter(req).page(new Page<>(req.getPage(), req.getPageSize())); + return PageData.builder() + .page(Math.toIntExact(page.getCurrent())) + .pageSize(Math.toIntExact(page.getSize())) + .totalCount(page.getTotal()) + .list(buildVisaChangePageSearchResp(page.getRecords(), req)) + .build(); + } + + @Override + public List listInitiator(VisaInitiatorListReq req) { + // 有权限查看的最大数据范围 + VisaChangePageSearchReq filter = VisaChangePageSearchReq.builder() + .workspaceId(req.getWorkspaceId()) + .currentPersonId(req.getCurrentPersonId()) + .currentWorkspaceId(req.getCurrentWorkspaceId()) + .currentOuId(req.getCurrentOuId()) + .build(); + if (permissionDataFlag) { + Set permissionVisaIdSet = this.fetchPermissionVisaIdSet(filter); + if (CollectionUtils.isEmpty(permissionVisaIdSet)) { + return Lists.newArrayList(); + } + } + List changeRecordList = getFilter(filter).list(); + if (CollectionUtils.isEmpty(changeRecordList)) { + return Collections.emptyList(); + } + + /*获取发起人*/ + List visaIdList = changeRecordList.stream().map(ChangeRecord::getId).distinct().collect(Collectors.toList()); + VisaConfirmDto visaConfirmDto = VisaConfirmDto.builder() + .bizType(VisaConfirmBizTypeEnum.CREATE) + .workspaceId(req.getWorkspaceId()) + .visaIds(visaIdList) + .build(); + List initiators = changeRecordConfirmService.findByCondition(visaConfirmDto); + if (CollectionUtils.isEmpty(initiators)) { + return Collections.emptyList(); + } + /*获取岗位*/ + List initiatorPersonIds = initiators.stream().map(ChangeRecordConfirm::getPersonId).distinct().collect(Collectors.toList()); + Map initiatorMap = getPersonBriefInfoMap(req.getWorkspaceId(), initiatorPersonIds); + AssertUtil.isTrue(CollUtil.isNotEmpty(initiatorMap), "发起人信息获取失败"); + /*组装数据*/ + return new ArrayList<>(initiators.stream() + .map(i -> buildInitiatorResp(initiatorMap, i)) + .collect(Collectors.toMap(i -> i.getPersonId() + ":" + i.getJobId(), Function.identity(), (v1, v2) -> v1)) + .values()); + } + + @Override + public void export(VisaChangeExportReq req) { + VisaChangePageSearchReq filter = req.getFilter(); + List fields = req.getFields(); + // 默认导出全部字段 + if (CollectionUtils.isEmpty(fields)) { + fields = Arrays.asList(VisaChangeFieldEnum.values()); + } + + filter.setCurrentPersonId(req.getPersonId()); + filter.setCurrentOuId(req.getOuId()); + filter.setCurrentWorkspaceId(req.getWorkspaceId()); + + if (permissionDataFlag) { + Set permissionVisaIdSet = this.fetchPermissionVisaIdSet(filter); + AssertUtil.notEmpty(permissionVisaIdSet, "没有数据需要导出"); + } + + /*获取数据*/ + List changeRecordList = getFilter(filter).list(); + AssertUtil.notEmpty(changeRecordList, "没有数据需要导出"); + + /*数据转换*/ + // 所属工程 + Map projectInfoMapById; + Map initiatorMap; + if (fields.contains(VisaChangeFieldEnum.RELATION_PROJECT)) { + List projectIdList = changeRecordList.stream().map(ChangeRecord::getRelationProject).collect(Collectors.toList()); + projectInfoMapById = getProjectInfoMapById(projectIdList); + } else { + projectInfoMapById = Collections.emptyMap(); + } + // 发起人 + if (fields.contains(VisaChangeFieldEnum.INITIATOR)) { + List initiatorPersonIdList = changeRecordList.stream().map(ChangeRecord::getCreateBy).distinct().collect(Collectors.toList()); + // Map + initiatorMap = getPersonBriefInfoMap(changeRecordList.get(0).getRelationWorkspaceId(), initiatorPersonIdList); + } else { + initiatorMap = Collections.emptyMap(); + } + + List data = changeRecordList.stream() + .map(item -> VisaExportDto.builder() + .no(item.getNo()) + .type(item.getType().getDesc()) + .status(item.getStatus().getDesc()) + .topic(item.getTopic()) + .relationProject(CollUtil.isNotEmpty(projectInfoMapById) ? projectInfoMapById.getOrDefault(item.getRelationProject(), "") : "") + .happenTime(item.getHappenTime()) + .amountChange(item.getAmountChange()) + .approvalCompleteTime(Objects.equals(VisaStatusEnum.COMPLETED, item.getStatus()) ? item.getApprovalCompleteTime() : null) + .initiator(CollUtil.isNotEmpty(initiatorMap) ? initiatorMap.getOrDefault(item.getCreateBy(), OrgNodeUserBriefInfoResp.builder().build()).getRealName() : "") + .build()) + .collect(Collectors.toList()); + + /*导出文excel并上传至云*/ + exportToExcel(req, fields, data); + } + + private void exportToExcel(VisaChangeExportReq req, List fields, List data) { + String scene = "变更签证表"; + String fileName = String.format("变更签证单%s", DateUtil.format(new Date(), "yyyyMMddHHmmss")); + String sheetName = "变更签证表"; +// String headerName = "变更签证单"; // 不同版本office打开表头合并单元格存在样式问题,暂不设置 + SheetWrapper sheetWrapper = SheetWrapper.builder() + .headClass(VisaExportDto.class) +// .title(headerName) + .sheetName(sheetName) + .includeColumnFieldNames(fields.stream() + .map(VisaChangeFieldEnum::getValue) + .collect(Collectors.toList())) + .sourceData(data) + .build(); + ReportFileReq reportFileReq = ReportFileReq.builder() + .convertData(false) + .bizScene(scene) + .fileName(fileName) + .reportPassword(null) + .isPassword(false) + .userContext(BeanUtil.copyProperties(req, ReportUserContext.class)) + .sheetWrappers(Lists.newArrayList(sheetWrapper)) + .build(); + reportServiceSDK.asyncUploadReportPsw(reportFileReq); + } + + private LambdaQueryChainWrapper getFilter(VisaChangePageSearchReq filter) { + + LambdaQueryChainWrapper queryChainWrapper = lambdaQuery() + .in(CollectionUtils.isNotEmpty(filter.getPermissionVisaIds()), ChangeRecord::getId, filter.getPermissionVisaIds()) + .eq(Objects.nonNull(filter.getVisaId()), ChangeRecord::getId, filter.getVisaId()) + .eq(NumberUtil.isPositiveNumber(filter.getWorkspaceId()), ChangeRecord::getRelationWorkspaceId, filter.getWorkspaceId()) + .eq(Objects.nonNull(filter.getType()), ChangeRecord::getType, filter.getType()) + .and(StringUtils.hasText(filter.getKeyword()), + wrapper -> wrapper.like(ChangeRecord::getTopic, filter.getKeyword()) + .or() + .eq(ChangeRecord::getNo, filter.getKeyword())) + .eq(NumberUtil.isPositiveNumber(filter.getProjectId()), ChangeRecord::getRelationProject, filter.getProjectId()) + .in(CollectionUtils.isNotEmpty(filter.getInitiators()), ChangeRecord::getCreateBy, filter.getInitiators()) + .in(CollectionUtils.isNotEmpty(filter.getStatuses()), ChangeRecord::getStatus, filter.getStatuses()) + .orderByDesc(ChangeRecord::getId); + if (CollectionUtils.isNotEmpty(filter.getAmountChange())) { + queryChainWrapper.between(ChangeRecord::getAmountChange, filter.getAmountChange().get(0), filter.getAmountChange().get(1)); + } + if (CollectionUtils.isNotEmpty(filter.getHappenTime())) { + queryChainWrapper.between(ChangeRecord::getHappenTime, filter.getHappenTime().get(0), filter.getHappenTime().get(1)); + } + if (CollectionUtils.isNotEmpty(filter.getApproveCompletedDate())) { + queryChainWrapper.between(ChangeRecord::getApprovalCompleteTime, filter.getApproveCompletedDate().get(0), filter.getApproveCompletedDate().get(1)); + queryChainWrapper.eq(ChangeRecord::getStatus, VisaStatusEnum.COMPLETED); + } + if (Objects.nonNull(filter) && Objects.nonNull(filter.getCurrentPersonId()) && !Objects.equals(filter.getCurrentPersonId(), 0L)) { + queryChainWrapper.apply(" CASE WHEN status = 'TO_REPORT' THEN create_by = " + filter.getCurrentPersonId() + " else true end"); + } + + // 单据选择器调用需要只包含已完成状态的记录 + if (Objects.nonNull(filter.getFrom()) && VisaChangePageSearchReq.FromEnum.SELECT.equals(filter.getFrom())) { + // 编辑状态下调用单据选择器需要将已选的可能作废的单据返回 + if (Objects.nonNull(filter.getVisaType())) { + queryChainWrapper.eq(ChangeRecord::getStatus, VisaStatusEnum.COMPLETED); +// queryChainWrapper.apply("CASE " + +// "WHEN STATUS = 'COMPLETED' THEN TRUE " + +// "ELSE FALSE " + +// "END"); + + queryChainWrapper.or().in(CollectionUtils.isNotEmpty(resetSelectVisaIds(filter)), ChangeRecord::getId, filter.getVisaIds()); + return queryChainWrapper; + } + } + queryChainWrapper.in(CollectionUtils.isNotEmpty(filter.getVisaIds()), ChangeRecord::getId, filter.getVisaIds()); + return queryChainWrapper; + } + + private static Collection resetSelectVisaIds(VisaChangePageSearchReq filter) { + if (StringUtils.hasText(filter.getKeyword()) || CollectionUtils.isNotEmpty(filter.getStatuses()) || CollectionUtils.isNotEmpty(filter.getInitiators())) { + filter.setVisaIds(Collections.emptyList()); + } + return filter.getVisaIds(); + } + + + /** + * 获取权限的变更签证Id集合 + */ + private Set fetchPermissionVisaIdSet(VisaChangePageSearchReq filter) { + Set changeApproveVisaIdSet = changeRecordConfirmService.permissionVisaData(Constants.CHANGE_APPROVED_DATA_OBJECT, filter.getCurrentPersonId(), filter.getCurrentWorkspaceId(), filter.getCurrentOuId(), VisaTypeEnum.fetchChangeApprovedSet()); + Set projectVisaIdSet = changeRecordConfirmService.permissionVisaData(Constants.PROJECT_VISA_DATA_OBJECT, filter.getCurrentPersonId(), filter.getCurrentWorkspaceId(), filter.getCurrentOuId(), VisaTypeEnum.fetchProjectVisaSet()); + HashSet permissionIdSet = Sets.newHashSet(); + permissionIdSet.addAll(changeApproveVisaIdSet); + permissionIdSet.addAll(projectVisaIdSet); + if (CollectionUtils.isEmpty(permissionIdSet)) { + return Sets.newHashSet(); + } + filter.setPermissionVisaIds(permissionIdSet); + return permissionIdSet; + } + + + private Map getPersonBriefInfoMap(Long workspaceId, List initiatorPersonIds) { + OrgNodeUserBriefInfoListReq nodeUserReq = OrgNodeUserBriefInfoListReq.builder() + .workspaceId(workspaceId) + .personIds(initiatorPersonIds) + .needJob(true) + .needNode(true) + .needUnit(true) + .needProfile(true) + // 考虑发起人离职、退场情况,故加上该参数 + .containsExited(true) + .build(); + List nodeUserResp = nodeUserGateway.listOrgNodeUsers(nodeUserReq); + if (CollectionUtils.isEmpty(nodeUserResp)) { + return Collections.emptyMap(); + } + // Map + return nodeUserResp.stream().collect(Collectors.toMap(OrgNodeUserBriefInfoResp::getPersonId, Function.identity(), (v1, v2) -> { + // 优先取exited=false的数据,若均为false,则取updateAt较大的数据 + if (Objects.nonNull(v1.getExited()) && Objects.nonNull(v2.getExited())) { + if (v1.getExited() && !v2.getExited()) { + return v2; + } else if (!v1.getExited() && v2.getExited()) { + return v1; + } + } + return v1.getUpdateAt().compareTo(v2.getUpdateAt()) > 0 ? v1 : v2; + })); + } + + private static VisaChangeInitiatorResp buildInitiatorResp(Map initiatorMap, ChangeRecordConfirm i) { + VisaChangeInitiatorResp.VisaChangeInitiatorRespBuilder initiatorBuilder = VisaChangeInitiatorResp.builder() + .personId(i.getPersonId()) + .ouId(i.getOuId()); + OrgNodeUserBriefInfoResp initiator = initiatorMap.get(i.getPersonId()); + if (Objects.isNull(initiator)) { + // 发起人可能已退场 + log.warn("获取发起人: {} 信息失败", i.getPersonId()); + return initiatorBuilder.build(); + } + return initiatorBuilder + .name(initiator.getRealName()) + .avatar(initiator.getProfile().getAvatarUrl()) + .jobId(initiator.getOrganizationalJobId()) + .jobName(initiator.getOrganizationalJobName()) + .jobCode(initiator.getOrganizationalJobCode()) + .nodeId(initiator.getOrganizationalNodeId()) + .unitOrTeamName(OrganizationalNodeTypeEnum.PROJECT_TEAM.getValue().equals(initiator.getOrganizationalNodeType()) ? initiator.getOrganizationalNodeName() : initiator.getOrganizationalUnitName()) + .build(); + } + + private List buildVisaChangePageSearchResp(List records, VisaChangePageSearchReq req) { + if (CollectionUtils.isEmpty(records)) { + return Collections.emptyList(); + } + /*获取工程信息*/ + List projectIdList = records.stream().map(ChangeRecord::getRelationProject).distinct().collect(Collectors.toList()); + Map projectIdNameMap = getProjectInfoMapById(projectIdList); + + /*获取变更单发起人*/ + List visaIdList = records.stream().map(ChangeRecord::getId).distinct().collect(Collectors.toList()); + // Map + Map visaIdInitiatorMap = getInitiatorMapByVisaId(visaIdList); + AssertUtil.isTrue(CollUtil.isNotEmpty(visaIdInitiatorMap), "发起人信息获取失败"); + + /*获取发起人岗位*/ + List initiatorPersonIdList = visaIdInitiatorMap.values().stream().map(ChangeRecordConfirm::getPersonId).distinct().collect(Collectors.toList()); + // Map + Map initiatorMap = getPersonBriefInfoMap(records.get(0).getRelationWorkspaceId(), initiatorPersonIdList); + AssertUtil.isTrue(CollUtil.isNotEmpty(initiatorMap), "发起人信息获取失败"); + + // 获取主单据信息 + Map billIfRelated; + if (req.getNeedRelatedBill()) { + AssertUtil.notNull(req.getVisaType(), "主单据类型不能为空"); + List billIds = records.stream().map(r -> String.valueOf(r.getId())).distinct().collect(Collectors.toList()); + GetBillIfRelatedReq relatedReq = GetBillIfRelatedReq.builder() + .visaType(req.getVisaType()) + .billType(VisaBillTypeEnum.DESIGN_VISA) + .billIds(billIds) + .build(); + billIfRelated = changeRecordBillService.getBillIfRelated(relatedReq); + } else { + billIfRelated = null; + } + // 操作列按钮 + Map> operateBtnMap = visaHelper.fetchBtnsByBizTypeAndStatus(records, req.getCurrentPersonId(), req.getCurrentOuId(), req.getCurrentWorkspaceId()); + + /*组装信息*/ + return records.stream() + .map(r -> { + VisaChangePageSearchResp resp = BeanUtil.copyProperties(r, VisaChangePageSearchResp.class); + // 工程 + resp.setRelationProjectName(projectIdNameMap.get(r.getRelationProject())); + // 发起人 + resp.setInitiator(buildInitiatorResp(initiatorMap, visaIdInitiatorMap.get(r.getId()))); + // 是否被其他单据关联 + resp.setIsRelated(req.getNeedRelatedBill() ? CollUtil.isNotEmpty(billIfRelated) && billIfRelated.get(String.valueOf(r.getId())) : null); + // 操作列按钮 + resp.setOperateBtnSet(CollUtil.isNotEmpty(operateBtnMap) ? operateBtnMap.get(r.getId()) : null); + return resp; + }) + .collect(Collectors.toList()); + } + + private Map getProjectInfoMapById(List projectIdList) { + List projectBriefByIds = visaProjectApiGateway.getProjectBriefByIds(projectIdList); + if (CollectionUtils.isEmpty(projectBriefByIds)) { + return Collections.emptyMap(); + } + // Map + return projectBriefByIds.stream().collect(Collectors.toMap(ProjectBriefResp::getId, ProjectBriefResp::getName)); + } + + private Map getInitiatorMapByVisaId(List visaIdList) { + VisaConfirmDto visaConfirmDto = VisaConfirmDto.builder() + .bizType(VisaConfirmBizTypeEnum.CREATE) + .visaIds(visaIdList) + .build(); + List initiators = changeRecordConfirmService.findByCondition(visaConfirmDto); + if (CollectionUtils.isEmpty(initiators)) { + return Collections.emptyMap(); + } + return initiators.stream().collect(Collectors.toMap(ChangeRecordConfirm::getVisaId, Function.identity())); + } + + private static void validAndAmendSearchCondition(VisaChangePageSearchReq req) { + if (CollectionUtils.isNotEmpty(req.getAmountChange())) { + AssertUtil.isFalse(req.getAmountChange().size() < 2, "请填写金额范围"); + BigDecimal min = req.getAmountChange().get(0); + BigDecimal max = req.getAmountChange().get(1); + AssertUtil.isTrue(Objects.nonNull(min) && Objects.nonNull(max), "请填写金额范围"); + BigDecimal zero = new BigDecimal("0"); + // 若元素不存在0,则最大最小值的符号必须相同 + if (!Objects.equals(min, zero) && !Objects.equals(max, zero)) { + AssertUtil.isTrue(Objects.equals(min.signum(), max.signum()), "请输入合法的变更金额范围"); + } + AssertUtil.isTrue(min.compareTo(max) <= 0, "最大金额不能小于最小金额"); + } + if (CollectionUtils.isNotEmpty(req.getHappenTime())) { + AssertUtil.isFalse(req.getHappenTime().size() < 2, "请填写提出时间范围"); + Date start = req.getHappenTime().get(0); + Date end = req.getHappenTime().get(1); + AssertUtil.isTrue(Objects.nonNull(start) && Objects.nonNull(end), "请填写提出时间范围"); + AssertUtil.isTrue(start.compareTo(end) <= 0, "提出结束时间不能小于开始时间"); + // 时间修正,补充时分秒数据,起始时间为00:00:00:00,结束时间为23:59:59 + req.getHappenTime().set(0, DateUtil.beginOfDay(start)); + req.getHappenTime().set(1, DateUtil.endOfDay(end)); + } + if (CollectionUtils.isNotEmpty(req.getApproveCompletedDate())) { + AssertUtil.isFalse(req.getApproveCompletedDate().size() < 2, "请填写审批完成时间范围"); + Date start = req.getApproveCompletedDate().get(0); + Date end = req.getApproveCompletedDate().get(1); + AssertUtil.isTrue(Objects.nonNull(start) && Objects.nonNull(end), "请填写审批完成时间范围"); + AssertUtil.isTrue(start.compareTo(end) <= 0, "审批完成结束时间不能小于开始时间"); + // 时间修正,补充时分秒数据,起始时间为00:00:00:00,结束时间为23:59:59 + req.getApproveCompletedDate().set(0, DateUtil.beginOfDay(start)); + req.getApproveCompletedDate().set(1, DateUtil.endOfDay(end)); + } + } + + private static VisaConfirmDto buildCreateVisaConfirmDto(VisaChangeTempCreateReq req, ChangeRecord changeRecord) { + return VisaConfirmDto.builder() + .visaId(changeRecord.getId()) + .type(SaasCooperateShipCooperateTypeEnum.getByCode(req.getOperatorOuType())) + .bizType(VisaConfirmBizTypeEnum.CREATE) + .visaType(changeRecord.getType()) + .personId(req.getOperatorPersonId()) + .ouId(req.getOperatorOuId()) + .workspaceId(req.getRelationWorkspaceId()) + .projectId(req.getRelationProject()) + .nodeId(req.getOperatorNodeId()) + .operator(req.getOperatorPersonId()) + .build(); + } + + private static VisaConfirmDto buildConfirmVisaConfirmDto(VisaChangeTempCreateReq.RelationUnitAndPerson person, ChangeRecord changeRecord, Long operator) { + return VisaConfirmDto.builder() + .visaId(changeRecord.getId()) + .type(person.getType()) + .bizType(VisaConfirmBizTypeEnum.CONFIRM) + .visaType(changeRecord.getType()) + .personId(person.getPersonId()) + .ouId(person.getOuId()) + .workspaceId(changeRecord.getRelationWorkspaceId()) + .projectId(changeRecord.getRelationProject()) + .nodeId(person.getNodeId()) + .operator(operator) + .build(); + } + + private void validVisaChangeForm(VisaChangeApproveCreateReq req) { + // 单号项目内唯一性 + ChangeRecord visaRecord = getVisaByWorkspaceIdAndNo(req.getRelationWorkspaceId(), req.getNo()); + // 编辑模式下需要排除自身 + if (Objects.nonNull(visaRecord) && !Objects.equals(visaRecord.getId(), req.getId())) { + throw new ServiceException("已存在相同单号的变更签证单据,请修改"); + } + // 工程存在性 + ProjectDetailRes projectById = visaProjectApiGateway.getProjectById(req.getRelationProject()); + AssertUtil.notNull(projectById, "工程不存在"); + AssertUtil.isTrue(Objects.equals(projectById.getWorkspaceInfo().getWorkspaceId(), req.getRelationWorkspaceId()), "工程所属项目与关联项目不匹配"); + // 发生区域存在性 + if (CollectionUtils.isNotEmpty(req.getRelationArea())) { + List areaByIds = apolloConstructionAreaGateway.getConstructionAreaByIds(VisaDetailByIdResponse.RelationAreaDetail.buildAreaWithId(req.getRelationArea())); + AssertUtil.isTrue(CollectionUtils.isNotEmpty(areaByIds) && areaByIds.size() == req.getRelationArea().size(), "存在发生区域已被删除"); + } + // 提出时间合法性 + Date now = new Date(); + AssertUtil.notNull(req.getHappenTime(), "请选择提交日期"); + AssertUtil.isTrue(DateUtil.isIn(req.getHappenTime(), DateUtil.beginOfDay(DateUtil.offsetMonth(now, -1)), DateUtil.endOfDay(now)), "提出时间不可早于一个月前/晚于当前日期,请重新选择"); + // 发生内容及说明合法性 + if (CollectionUtils.isNotEmpty(req.getChangeContextAndDescriptionList())) { + AssertUtil.isTrue(req.getChangeContextAndDescriptionList().size() <= refreshableConfiguration.getVerifyChangeContextLimit(), "发生内容及说明数量过多,请重新填写"); + req.getChangeContextAndDescriptionList().forEach(c -> { + AssertUtil.isTrue(1 == c.getType() || 2 == c.getType(), "请选择发生内容类型"); + AssertUtil.isFalse(!StringUtils.hasText(c.getText()) && !StringUtils.hasText(c.getFileUrl()), "存在为空的发生内容及说明,请填写"); + }); + } + // 相关单位及人员存在性 + if (CollectionUtils.isNotEmpty(req.getRelationUnitAndPersonList())) { + Map> cooperateTypeMap = req.getRelationUnitAndPersonList().stream() + .peek(r -> { + AssertUtil.notNull(r.getType(), "相关单位类型错误"); + AssertUtil.isTrue(NumberUtil.isPositiveNumber(r.getOuId()), "请选择" + r.getType().getDesc()); + AssertUtil.isTrue(NumberUtil.isPositiveNumber(r.getNodeId()), "请选择" + r.getType().getDesc() + "确认人所属部门"); + AssertUtil.isTrue(NumberUtil.isPositiveNumber(r.getPersonId()), "请选择" + r.getType().getDesc() + "确认人"); + }) + .collect(Collectors.groupingBy(VisaChangeTempCreateReq.RelationUnitAndPerson::getType)); + // 获取项目下所有为退场人员 + OrgNodeUserBriefInfoListReq infoListReq = OrgNodeUserBriefInfoListReq.builder() + .workspaceId(req.getRelationWorkspaceId()) + .orgNodeIds(req.getRelationUnitAndPersonList().stream().map(VisaChangeTempCreateReq.RelationUnitAndPerson::getNodeId).distinct().collect(Collectors.toList())) + .personIds(req.getRelationUnitAndPersonList().stream().map(VisaChangeTempCreateReq.RelationUnitAndPerson::getPersonId).distinct().collect(Collectors.toList())) + .build(); + List nodeUserBriefInfoResps = nodeUserGateway.listOrgNodeUsers(infoListReq); + AssertUtil.notEmpty(nodeUserBriefInfoResps, "所有单位确认人均已退场"); + cooperateTypeMap.forEach((k, v) -> { + AssertUtil.isTrue(v.size() <= refreshableConfiguration.getVerifyPersonLimit(), String.format(k.getDesc() + "确认人,最多支持%d个", refreshableConfiguration.getVerifyPersonLimit())); + boolean noExit = v.stream().map(VisaChangeTempCreateReq.RelationUnitAndPerson::getPersonId) + .allMatch(p -> nodeUserBriefInfoResps.stream().map(OrgNodeUserBriefInfoResp::getPersonId).anyMatch(u -> Objects.equals(u, p))); + AssertUtil.isTrue(noExit, String.format(k.getDesc() + "确认人,存在已退场人员,请重新选择")); + }); + } + // 金额变化合法性 + if (Objects.equals(req.getType(), VisaTypeEnum.PROJECT_VISA) && Objects.isNull(req.getAmountChange())) { + throw new ServiceException("请选择金额变化"); + } + if (Objects.nonNull(req.getAmountChange())) { + AssertUtil.isTrue(req.getAmountChange().compareTo(maxAmount) <= 0 && req.getAmountChange().compareTo(minAmount) >= 0, "金额变化超出范围"); + } + // 关联单据合法性 + if (CollUtil.isNotEmpty(req.getRelationOrderMap())) { + req.getRelationOrderMap().forEach((key, value) -> { + if (CollectionUtils.isNotEmpty(value)) { + // 关联单据数量限制 + AssertUtil.isTrue(value.size() <= refreshableConfiguration.getVerifyVisaLimit(), + String.format("最多可关联%d个%s单,请重新选择", refreshableConfiguration.getVerifyVisaLimit(), VisaBillTypeEnum.valueOf(key).getDesc())); + if (Objects.equals(VisaBillTypeEnum.TASK.name(), key)) { + /*任务单*/ + List taskNos = value.stream() + .map(VisaChangeTempCreateReq.OrderSimpleModel::getNo) + .filter(StringUtils::hasText) + .collect(Collectors.toList()); + if (CollUtil.isNotEmpty(taskNos)) { + // 存在性 + QueryOrderToBasicReq queryOrderToBasicReq = new QueryOrderToBasicReq(); + queryOrderToBasicReq.setWorkspaceId(req.getRelationWorkspaceId()); + queryOrderToBasicReq.setTaskNos(taskNos); + queryOrderToBasicReq.setPage(1); + queryOrderToBasicReq.setPageSize(taskNos.size()); + List taskOrders = taskOrderApiGateway.pageOrderByForBasic(queryOrderToBasicReq).getData(); + AssertUtil.isTrue(CollectionUtils.isNotEmpty(taskOrders) && taskOrders.size() == taskNos.size(), "关联任务单单据不存在,请检查后重试"); + validTaskBillIfRelated(taskNos, req.getType()); + } + } else if (Objects.equals(VisaBillTypeEnum.RECTIFY.name(), key)) { + /*整改单*/ + List billIds = value.stream() + .map(v -> String.valueOf(v.getId())) + .filter(StringUtils::hasText) + .collect(Collectors.toList()); + if (CollUtil.isNotEmpty(billIds)) { + // 存在性 + ListRectifyOrderReq rectifyOrderReq = ListRectifyOrderReq.builder() + .ids(value.stream().map(VisaChangeTempCreateReq.OrderSimpleModel::getId).collect(Collectors.toSet())) + .build(); + List rectifyOrders = rectifyApiGateway.listRectifyOrders(rectifyOrderReq); + AssertUtil.isTrue(CollectionUtils.isNotEmpty(rectifyOrders) && rectifyOrders.size() == billIds.size(), "关联整改单单据不存在,请检查后重试"); + validBillIfRelated(VisaBillTypeEnum.RECTIFY, billIds, req.getType()); + } + } else if (Objects.equals(VisaBillTypeEnum.DESIGN_VISA.name(), key)) { + /*变更单*/ + AssertUtil.isTrue(VisaTypeEnum.TECHNOLOGY_APPROVED.equals(req.getType()) || VisaTypeEnum.PROJECT_VISA.equals(req.getType()), "变更单关联单据仅支持技术核定/工程签证"); + // 关联单据合法性,是否存在,状态是否合法 + List billIds = value.stream() + .map(v -> String.valueOf(v.getId())) + .filter(StringUtils::hasText) + .collect(Collectors.toList()); + if (CollUtil.isNotEmpty(billIds)) { + List relationVisa = listByIds(billIds); + AssertUtil.isTrue(CollectionUtils.isNotEmpty(relationVisa) && relationVisa.size() == billIds.size(), "关联的变更单单据不存在,请检查后重试"); + relationVisa.forEach(r -> { + AssertUtil.isTrue(Objects.equals(r.getRelationWorkspaceId(), req.getRelationWorkspaceId()), "关联的单据所属项目不匹配"); + AssertUtil.isTrue(VisaStatusEnum.COMPLETED.equals(r.getStatus()), String.format("%s单据%s状态为%s,不允许被关联", VisaBillTypeEnum.DESIGN_VISA.getDesc(), r.getNo(), r.getStatus().getDesc())); + }); + validBillIfRelated(VisaBillTypeEnum.DESIGN_VISA, billIds, req.getType()); + } + } else { + throw new ServiceException("不支持的关联单据类型"); + } + } + }); + } + } + + private ChangeRecord getVisaByWorkspaceIdAndNo(Long workspaceId, String no) { + return lambdaQuery().eq(ChangeRecord::getNo, no) + .eq(ChangeRecord::getRelationWorkspaceId, workspaceId) + .eq(ChangeRecord::getIsDelete, 0) + .last("LIMIT 1") + .one(); + } + + private void validBillIfRelated(VisaBillTypeEnum billType, List value, VisaTypeEnum visaType) { + AssertUtil.notNull(billType, "关联单据类型不能为空"); + AssertUtil.notEmpty(value, "关联单据Id不能为空"); + AssertUtil.notNull(visaType, "单据类型不能为空"); + List mainBill = changeRecordBillService.getRelatedMainBill(billType, value); + mainBill.stream() + .collect(Collectors.groupingBy(ChangeRecordBill::getBillId)) + .forEach((k, v) -> v.stream() + .filter(m -> Objects.equals(m.getVisaType(), visaType)) + .findAny() + .ifPresent(m -> { + log.info("{} bill {} is related by {} visa {}", m.getBillType(), m.getBillId(), m.getVisaType(), m.getVisaId()); + throw new ServiceException(String.format("%s已被其他%s单关联,请重新选择", m.getBillType().getDesc(), visaType.getDesc())); + })); + } + + private void validTaskBillIfRelated(List taskNos, VisaTypeEnum visaType) { + AssertUtil.notEmpty(taskNos, "关联单据Id不能为空"); + AssertUtil.notNull(visaType, "单据类型不能为空"); + List mainBill = changeRecordBillService.getRelatedMainBill(VisaBillTypeEnum.TASK, taskNos); + if (CollectionUtils.isEmpty(mainBill)) { + return; + } + mainBill.stream() + .collect(Collectors.groupingBy(ChangeRecordBill::getBillNo)) + .forEach((k, v) -> v.stream() + .filter(m -> Objects.equals(m.getVisaType(), visaType)) + .findAny() + .ifPresent(m -> { + log.info("{} bill {} is related by {} visa {}", m.getBillType(), m.getBillId(), m.getVisaType(), m.getVisaId()); + throw new ServiceException(String.format("%s已被其他%s单关联,请重新选择", m.getBillType().getDesc(), visaType.getDesc())); + })); + } + + /** + * 通过变更签证Id获取Entity + * + * @param visaId 变更签证Id + * @return 变更签证Entity + */ + public ChangeRecord getById(Long visaId) { + if (Objects.isNull(visaId)) { + return ChangeRecord.builder().build(); + } + return this.lambdaQuery() + .eq(ChangeRecord::getId, visaId) + .eq(ChangeRecord::getIsDelete, 0).one(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void forbid(ChangeRecordButtonOperationReq req) { + ChangeRecord record = getById(req.getVisaId()); + AssertUtil.notNull(record, "未找到变洽签单据"); + AssertUtil.isTrue(!Objects.equals(record.getStatus(), VisaStatusEnum.TO_REPORT) + && !Objects.equals(record.getStatus(), VisaStatusEnum.COMPLETED), "当前单据状态不支持该操作"); + + //校验操作人 + validOperationPermission(req.getVisaId(), req.getOperatorPersonId(), req.getOperatorOuId(), req.getOperatorWorkspaceId()); + + ChangeStatusRequest changeStatusRequest = new ChangeStatusRequest(); + changeStatusRequest.setVisaId(req.getVisaId()); + changeStatusRequest.setUpdateStatus(VisaStatusEnum.FORBIDED); + changeStatusRequest.setPersonId(req.getOperatorPersonId()); + changeStatusRequest.setOuId(req.getOperatorOuId()); + changeStatusRequest.setWorkspaceId(req.getOperatorWorkspaceId()); + changeStatus(changeStatusRequest); + if (Objects.nonNull(record.getImGroupId())) { + GroupDismissRequest request = new GroupDismissRequest(); + request.setTid(record.getImGroupId()); + // 解散 IM 群聊 + msgCenterGateway.dismissImGroup(request); + log.info("dismiss im group success"); + } + if (StringUtils.hasText(record.getApprovalId())) { + BpmnProcessInstanceAbortDTO abort = new BpmnProcessInstanceAbortDTO(); + abort.setProcessInstanceId(record.getApprovalId()); + abort.setReason("变更签证单据被废止,中止审批流"); + abort.setAssigner(BpmnTaskDelegateAssigner.builder() + .personId(String.valueOf(req.getOperatorPersonId())) + .ouId(String.valueOf(req.getOperatorOuId())) + .tenantId(String.valueOf(req.getOperatorWorkspaceId())) + .build()); + Boolean result = workflowGateway.processInstanceAbort(abort); + log.info("invoke abort process instance result:{}", result); + } + // 取消已关联整改单、任务单、变更单 + changeRecordBillService.deleteByVisaId(record.getId()); + // 取消关联的图纸 + deleteRelateDrawAnnotation(record.getId()); + // 同步取消图纸关联至图纸方 + syncDrawAnnotationUnBindRelation(record.getId()); + } + + /** + * 取消变更签证单关联的图纸 + * + * @param visaId 变更签证Id + */ + private boolean deleteRelateDrawAnnotation(Long visaId) { + List contentDescription = changeRecordRelationService.findByVisaAndVarName(visaId, VisaRelationFieldEnum.CONTENT_DESCRIPTION.name()); + if (CollectionUtils.isNotEmpty(contentDescription)) { + List annotation = contentDescription.stream() + .filter(item -> { + try { + VisaChangeTempCreateReq.ChangeContextAndDescription parseDescription = JSONObject.parseObject(item.getContent(), VisaChangeTempCreateReq.ChangeContextAndDescription.class); + return Objects.nonNull(parseDescription) && Objects.equals(parseDescription.getType(), 2); + } catch (Exception e) { + throw new ServiceException("发生内容说明解析失败"); + } + }) + .collect(Collectors.toList()); + return changeRecordRelationService.deleteByIds(annotation.stream().map(ChangeRecordRelation::getId).collect(Collectors.toList())); + } + return true; + } + + /** + * 获取变更签证单关联的图纸 + * + * @param visaId 变更签证Id + * @return 签证单关联的图纸批注 + */ + private List getRelationDrawAnnotation(Long visaId) { + List contentDescription = changeRecordRelationService.findByVisaAndVarName(visaId, VisaRelationFieldEnum.CONTENT_DESCRIPTION.name()); + if (CollectionUtils.isNotEmpty(contentDescription)) { + return contentDescription.stream() + .map(item -> { + try { + return JSONObject.parseObject(item.getContent(), VisaChangeTempCreateReq.ChangeContextAndDescription.class); + } catch (Exception e) { + throw new ServiceException("发生内容说明解析失败"); + } + }) + .filter(item -> Objects.nonNull(item) && Objects.equals(item.getType(), 2)) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + private void validOperationPermission(Long visaId, Long personId, Long ouId, Long workspaceId) { + List confirmUsers = changeRecordConfirmService.findByCondition(VisaConfirmDto.builder() + .visaId(visaId) + .bizType(VisaConfirmBizTypeEnum.CREATE) + .build()); + + AssertUtil.isTrue(CollectionUtils.isNotEmpty(confirmUsers) && confirmUsers.size() == 1, "单据发起人信息数据异常"); + + ChangeRecordConfirm creator = confirmUsers.get(0); + AssertUtil.isTrue(Objects.equals(creator.getPersonId(), personId) + && Objects.equals(creator.getOuId(), ouId) + && Objects.equals(creator.getWorkspaceId(), workspaceId), "不是单据发起人,不能操作"); + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long approveCreateOnlyVisaChangeRecord(VisaChangeApproveOnlyReq req) { + ChangeRecord visa = getById(req.getVisaId()); + req.setOperatorWorkspaceId(visa.getRelationWorkspaceId()); + validOperationPermission(req.getVisaId(), req.getOperatorPersonId(), req.getOperatorOuId(), req.getOperatorWorkspaceId()); + + AssertUtil.isTrue(!StringUtils.hasText(visa.getApprovalId()) + || (StringUtils.hasText(visa.getApprovalId()) && !Objects.equals(VisaStatusEnum.APPROVING, visa.getStatus())), + "当前单据审批中,不允许操作"); + + // 组装 REQ 参数 + VisaChangeApproveCreateReq createReq = buildVisaChangeApproveCreateReqByVisaId(req.getVisaId()); + AssertUtil.notNull(createReq, "未找到变洽签单据"); + + // 校验 + validVisaChangeForm(createReq); + createReq.setActivityId(req.getActivityId()); + createReq.setApprovePersonInfoList(req.getApprovePersonInfoList()); + createReq.setOperatorOuId(req.getOperatorOuId()); + createReq.setOperatorPersonId(req.getOperatorPersonId()); + createReq.setRelationWorkspaceId(req.getOperatorWorkspaceId()); + + // 更新主表状态 + changeStatus(ChangeStatusRequest.builder() + .visaId(visa.getId()) + .updateStatus(VisaStatusEnum.APPROVING) + .personId(req.getOperatorPersonId()) + .ouId(req.getOperatorOuId()) + .workspaceId(req.getOperatorWorkspaceId()) + .editFormFlag(false) + .build()); + + String processInstanceId = createProcessInstance(createReq, req.getVisaId()); + // 更新主表中的审批字段,记录变量表的审批的信息 + updateApprovalInfo(visa.getId(), processInstanceId); + return req.getVisaId(); + } + + @Override + public void reDecision(ChangeRecordButtonOperationReq req) { + ChangeRecord visa = changeRecordService.getById(req.getVisaId()); + AssertUtil.notNull(visa, "未找到变洽签单据"); + AssertUtil.isTrue(!Objects.equals(visa.getStatus(), VisaStatusEnum.APPROVING) + && !Objects.equals(visa.getStatus(), VisaStatusEnum.COMPLETED), "单据未在决策中或执行中,不支持该操作"); + validOperationPermission(req.getVisaId(), req.getOperatorPersonId(), req.getOperatorOuId(), req.getOperatorWorkspaceId()); + + ImGroupOperationClearReq build = new ImGroupOperationClearReq(); + build.setVisaId(req.getVisaId()); + build.setImGroupId(req.getImGroupId()); + build.setClearAll(true); + changeRecordRelationService.clearImOperationParticipate(build); + + // 重新发送卡片 + sendCardAndMsgToImGroup(req, visa); + } + + private void sendCardAndMsgToImGroup(ChangeRecordButtonOperationReq req, ChangeRecord visa) { + CardSendRequest cardRequest = new CardSendRequest(); + cardRequest.setAppCode(MSG_CENTER_APP_CODE); + cardRequest.setTemplateCode(refreshableConfiguration.getImGroupCardTemplateCode()); + cardRequest.setBizCode(IM_GROUP_BIZ_INFO_VISA_ID + ":" + visa.getId()); + cardRequest.setSender(PeerPerson.create(req.getOperatorPersonId(), req.getOperatorOuId(), req.getOperatorWorkspaceId())); + cardRequest.setImSenderAccountAppType(AppTypeEnum.CMP); + HashSet imReceiveAccounts = Sets.newHashSet(String.valueOf(visa.getImGroupId())); + cardRequest.setImReceiveAccounts(imReceiveAccounts); + + OrganizationalUnitVO unitInfo = organizationalUnitGateway.getUnitInfo(req.getOperatorOuId()); + PersonProfileDto profile = visaProfileGateway.getProfile(req.getOperatorPersonId()); + + cardRequest.setBizParam(new JSONObject(Maps.of( + "topic", visa.getTopic(), + "reason", visa.getReason(), + "initiatorName", profile.getRealName(), + "initiatorUnitName", unitInfo.getName(), + "visaType", visa.getType().getDesc(), + "amountChange", VisaHelper.buildChangeAmount(visa.getAmountChange()) + ))); + msgCenterGateway.sendCardToCroup(cardRequest); + + // 普通群消息 + SendChatMessageRequest msgRequest = new SendChatMessageRequest(); + msgRequest.setAsTextMessage(visa.getTopic() + ",请各位审阅"); + msgRequest.setImReceiveAccounts(imReceiveAccounts); + msgRequest.setSender(PersonAccountAttribute.builder() + .personId(String.valueOf(req.getOperatorPersonId())) + .ouId(req.getOperatorOuId()) + .workspaceId(req.getOperatorWorkspaceId()) + .appType(AppTypeEnum.CMP) + .build()); + msgRequest.setBizId(IM_GROUP_BIZ_INFO_VISA_ID + ":" + visa.getId()); + msgCenterGateway.sendMsgToGroup(msgRequest); + } + + @Override + public void doExecute(ChangeRecordButtonOperationReq req) { + ChangeRecord visa = changeRecordService.getById(req.getVisaId()); + AssertUtil.notNull(visa, "未找到变洽签单据"); + AssertUtil.isTrue(Objects.equals(visa.getStatus(), VisaStatusEnum.DECIDING) + || Objects.equals(visa.getStatus(), VisaStatusEnum.DECIDING_FROM_APPROVE), "单据未在决策中,不支持该操作"); + validOperationPermission(req.getVisaId(), req.getOperatorPersonId(), req.getOperatorOuId(), req.getOperatorWorkspaceId()); + + changeStatus(ChangeStatusRequest.builder() + .visaId(req.getVisaId()) + .personId(req.getOperatorPersonId()) + .ouId(req.getOperatorOuId()) + .workspaceId(req.getOperatorWorkspaceId()) + .updateStatus(VisaStatusEnum.EXECUTING) + .editFormFlag(false) + .build()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(ChangeRecordButtonOperationReq req) { + ChangeRecord record = this.getById(req.getVisaId()); + if (Objects.isNull(record)) { + log.info("no record id = {} to be deleted", req.getVisaId()); + return; + } + // 删除主记录 + lambdaUpdate() + .eq(ChangeRecord::getId, req.getVisaId()) + .setSql("is_delete = id") + .set(ChangeRecord::getUpdateBy, req.getOperatorPersonId()) + .update(); + // 删除相关单位确认人、变更说明、附件、关联单据等 + changeRecordConfirmService.deleteByVisaId(req.getVisaId(), + Arrays.asList(VisaConfirmBizTypeEnum.CONFIRM, VisaConfirmBizTypeEnum.CREATE)); + changeRecordRelationService.deleteByVisaId(req.getVisaId(), + Arrays.asList(VisaRelationFieldEnum.ATTACHMENT, VisaRelationFieldEnum.CONTENT_DESCRIPTION)); + changeRecordBillService.deleteByVisaId(req.getVisaId()); + // 同步取消图纸关联至图纸方 + syncDrawAnnotationUnBindRelation(record.getId()); + } + + /** + * 通过变更单id构建审批提交对象 + * + * @param visaId 变更单id + * @return VisaChangeApproveCreateReq + */ + private VisaChangeApproveCreateReq buildVisaChangeApproveCreateReqByVisaId(Long visaId) { + if (NumberUtil.isNotPositiveNumber(visaId)) { + return null; + } + ChangeRecord changeRecord = getById(visaId); + if (Objects.isNull(changeRecord)) { + return null; + } + VisaChangeApproveCreateReq req = new VisaChangeApproveCreateReq(); + // 基本信息 + BeanUtil.copyProperties(changeRecord, req); + // 工程名称 + ProjectDetailRes projectById = visaProjectApiGateway.getProjectById(changeRecord.getRelationProject()); + req.setRelationProjectName(projectById.getProjectInfo().getName()); + // 相关单位及人员 + VisaConfirmDto confirmDto = VisaConfirmDto.builder().visaId(changeRecord.getId()).bizType(VisaConfirmBizTypeEnum.CONFIRM).build(); + List confirms = changeRecordConfirmService.findByCondition(confirmDto); + if (CollectionUtils.isNotEmpty(confirms)) { + req.setRelationUnitAndPersonList(confirms.stream() + .map(item -> BeanUtil.copyProperties(item, VisaChangeTempCreateReq.RelationUnitAndPerson.class)) + .collect(Collectors.toList())); + } + // 发生内容说明 + List contentDescription = changeRecordRelationService.findByVisaAndVarName(changeRecord.getId(), VisaRelationFieldEnum.CONTENT_DESCRIPTION.name()); + if (CollectionUtils.isNotEmpty(contentDescription)) { + req.setChangeContextAndDescriptionList(contentDescription.stream() + .map(item -> { + try { + return JSONObject.parseObject(item.getContent(), VisaChangeTempCreateReq.ChangeContextAndDescription.class); + } catch (Exception e) { + throw new ServiceException("发生内容说明解析失败"); + } + }) + .collect(Collectors.toList())); + } + // 关联单据 + List bills = changeRecordBillService.listById(changeRecord.getId()); + // 实时构建单据名称 TODO 此处应该可以使用异步进行优化 + if (CollectionUtils.isNotEmpty(bills)) { + Map> relationOrderMap = com.google.common.collect.Maps.newHashMap(); + bills.stream().collect(Collectors.groupingBy(b -> b.getBillType().name(), + Collectors.mapping(Function.identity(), Collectors.toList()))) + .forEach((k, v) -> { + if (VisaBillTypeEnum.TASK.name().equals(k)) { + // 获取任务单数据 + QueryOrderToBasicReq queryOrderToBasicReq = new QueryOrderToBasicReq(); + queryOrderToBasicReq.setWorkspaceId(req.getRelationWorkspaceId()); + queryOrderToBasicReq.setTaskNos(v.stream().map(ChangeRecordBill::getBillNo).collect(Collectors.toList())); + queryOrderToBasicReq.setPage(1); + queryOrderToBasicReq.setPageSize(v.size()); + List orderData = taskOrderApiGateway.pageOrderByForBasic(queryOrderToBasicReq).getData(); + if (CollUtil.isNotEmpty(orderData)) { + Map taskOrderMap = orderData.stream() + .collect(Collectors.toMap(TaskOrderToBasicRes::getTaskNo, Function.identity())); + // 构建关联任务单 + relationOrderMap.put(k, v.stream() + .map(item -> VisaChangeTempCreateReq.OrderSimpleModel.builder() + .id(item.getBillId()) + .no(item.getBillNo()) + .name(Optional.ofNullable(taskOrderMap.get(item.getBillNo())) + .map(TaskOrderToBasicRes::getTaskName) + .orElse("")) + .build()) + .collect(Collectors.toList())); + } + } else if (VisaBillTypeEnum.RECTIFY.name().equals(k)) { + // 获取整改单数据 + ListRectifyOrderReq rectifyOrderReq = ListRectifyOrderReq.builder() + .ids(v.stream().map(ChangeRecordBill::getBillId).collect(Collectors.toSet())) + .build(); + List rectifyData = rectifyApiGateway.listRectifyOrders(rectifyOrderReq); + if (CollUtil.isNotEmpty(rectifyData)) { + Map rectifyOrderMap = rectifyData.stream() + .collect(Collectors.toMap(RectifyOrderResp::getId, Function.identity(), (v1, v2) -> v1)); + // 构建关联整改单 + relationOrderMap.put(k, v.stream() + .map(item -> VisaChangeTempCreateReq.OrderSimpleModel.builder() + .id(item.getBillId()) + .no(item.getBillNo()) + .name(Optional.ofNullable(rectifyOrderMap.get(item.getBillId())) + .map(RectifyOrderResp::getBizName) + .orElse("")) + .build()) + .collect(Collectors.toList())); + } + } else if (VisaBillTypeEnum.DESIGN_VISA.name().equals(k)) { + // 获取设计单数据 + List visaDesignData = listByIds(v.stream().map(ChangeRecordBill::getBillId).collect(Collectors.toSet())); + Map changeRecordMap = visaDesignData.stream() + .collect(Collectors.toMap(ChangeRecord::getId, Function.identity(), (v1, v2) -> v1)); + if (CollUtil.isNotEmpty(visaDesignData)) { + // 构建关联设计单 + relationOrderMap.put(k, v.stream() + .map(item -> VisaChangeTempCreateReq.OrderSimpleModel.builder() + .id(item.getBillId()) + .no(item.getBillNo()) + .name(Optional.ofNullable(changeRecordMap.get(item.getBillId())) + .map(ChangeRecord::getTopic) + .orElse("")) + .build()) + .collect(Collectors.toList()) + ); + } + } + }); + req.setRelationOrderMap(relationOrderMap); + } + // 附件 + List attach = changeRecordRelationService.findByVisaAndVarName(changeRecord.getId(), VisaRelationFieldEnum.ATTACHMENT.name()); + if (CollectionUtils.isNotEmpty(attach)) { + req.setAttach(attach.stream() + .map(item -> { + try { + return JSONObject.parseObject(item.getContent(), UploadFieldDTO.class); + } catch (Exception e) { + throw new ServiceException("附件解析失败"); + } + }) + .collect(Collectors.toList())); + } + return req; + } + + /** + * 根据读取详情中的变签-校验 + */ + @Override + public void checkVisaWithVisaId(CheckVisaWithVisaIdReq req) { + if (NumberUtil.isPositiveNumber(req.getVisaId())) { + VisaDetailByIdResponse response = this.detailById(req.getVisaId()); + AssertUtil.notNull(response, "单据不存在"); + req.check(response); + this.validVisaChangeForm(Objects.requireNonNull(this.buildVisaChangeApproveCreateReqByVisaId(req.getVisaId()))); + } else { + req.check(); + this.validVisaChangeForm(BeanUtil.copyProperties(req.getCreatFormData(), VisaChangeApproveCreateReq.class)); + } + } + + + /** + * 查询变更签证集合 + */ + @Override + public PageData list(VisaSearchReq req) { + //1 校验 + req.check(); + //2 根据条件获取visa_change_record_bill(变更签证记录单据关系表)中的visaId集合 + Set visaIds = this.fetchVisaIds(req); + + if (CollectionUtils.isEmpty(visaIds) && Objects.isNull(req.getVisaId())) { + return PageData.builder() + .page(Math.toIntExact(req.getPage())) + .pageSize(Math.toIntExact(req.getPageSize())) + .totalCount(0L) + .list(Lists.newArrayList()) + .build(); + } + + //3 查询变更签证主表(visa_change_record) + Page page = this.getFilter(VisaChangePageSearchReq.builder() + .visaIds(visaIds) + .visaId(req.getVisaId()) + .workspaceId(req.getWorkspaceId()).build()).page(new Page<>(req.getPage(), req.getPageSize())); + //4 构建返回数据 + return buildPageData(page); + } + + /** + * 构建返回数据 + */ + private PageData buildPageData(Page page) { + return PageData.builder() + .page(Math.toIntExact(page.getCurrent())) + .pageSize(Math.toIntExact(page.getSize())) + .totalCount(page.getTotal()) + .list(CollectionUtils.isEmpty(page.getRecords()) ? Lists.newArrayList() : page.getRecords().stream().map(item -> { + VisaSearchResp visaSearchResp = BeanUtil.copyProperties(item, VisaSearchResp.class); + visaSearchResp.setVisaId(item.getId()); + visaSearchResp.setRelationArea(VisaDetailByIdResponse.RelationAreaDetail.buildAreaWithObject(item.getRelationArea())); + visaSearchResp.setRelationProfessional(VisaDetailByIdResponse.RelationProfessionalDetail.buildProfessionWithObject(item.getRelationProfessional())); + visaSearchResp.setAmountChange(Objects.nonNull(item.getAmountChange()) ? item.getAmountChange().toPlainString() : ""); + return visaSearchResp; + }).collect(Collectors.toList())) + .build(); + } + + /** + * 根据条件获取visa_change_record_bill(变更签证记录单据关系表)中的visaId集合 + */ + private Set fetchVisaIds(VisaSearchReq req) { + Set visaIds = Sets.newHashSet(); + if (Objects.nonNull(req.getBillId()) || Objects.nonNull(req.getBillNo()) || Objects.nonNull(req.getBillType())) { + List billList = this.changeRecordBillService.findByCondition(VisaBillDto.builder() + .billId(req.getBillId()) + .billNo(req.getBillNo()) + .billType(req.getBillType()) + .relationStatus(true).build()); + if (CollectionUtils.isNotEmpty(billList)) { + visaIds = billList.stream().map(item -> item.getVisaId()).collect(Collectors.toSet()); + } + } + return visaIds; + } + +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/utils/Constants.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/utils/Constants.java new file mode 100644 index 00000000..4ed08cdf --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/utils/Constants.java @@ -0,0 +1,118 @@ +package cn.axzo.nanopart.visa.server.utils; + +import cn.axzo.orggateway.api.nodeuser.dto.OrgNodeUserDTO; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.List; +import java.util.Objects; + +/** + * @author xudawei@axzo.cn + * @date 2025/2/7 + * @description 静态变量 + */ +public class Constants { + + /** + * 操作日志-记录时间相同时,排序 + */ + /** + * 表单编辑-排序 + */ + public static final Integer EDIT_FORM_ORDER_BY = 1; + /** + * 金额变更-排序 + */ + public static final Integer AMOUNT_CHANGE_ORDER_BY = 2; + /** + * 新增附件-排序 + */ + public static final Integer ADD_UPLOAD_FILE_ORDER_BY = 3; + /** + * 删除附件-排序 + */ + public static final Integer DELETE_UPLOAD_FILE_ORDER_BY = 4; + /** + * 更改状态-排序 + */ + public static final Integer CHANGE_STATUS_FILE_ORDER_BY = 5; + + /** + * 单个节点审批通过排序 + */ + public static final Integer ONE_APPROVED_ORDER_BY = 6; + + /** + * 全部节点审批通过排序 + */ + public static final Integer APPROVE_COMPLETED_ORDER_BY = 7; + + /** + * 审批相关-排序 + */ + public static final Integer APPROVE_ORDER_BY = 6; + + /** + * 变更核定数据权限的code + */ + public static final String CHANGE_APPROVED_DATA_OBJECT = "change_approved"; + + /** + * 工程签证数据权限的code + */ + public static final String PROJECT_VISA_DATA_OBJECT = "project_visa"; + + /** + * 工程签证数据权限的code + */ + public static final String SPLIT_TREE_NODE_CHAT = "-"; + + /** + * 变更签证日志 + */ + public static final String VISA_LOG_SNAPSHOT_SCENE = "visa_log"; + + /** + * 变更签证日志-快照 + */ + public static final String VISA_LOG_SNAPSHOT_TAG_SNAPSHOT = "visa_log_snapshot"; + + /** + * 变更签证日志-快照 + */ + public static final String VISA_LOG_SNAPSHOT_LEVEL = "INFO"; + + /** + * 年月日格式 + */ + public static final String DATE_FORMAT = "yyyy.MM.dd"; + + /** + * 年月日 时分秒格式 + */ + public static final String DATE_TIME_FORMAT = "yyyy.MM.dd HH:mm:ss"; + + /** + * 班组长岗位code + */ + public static String PROJECT_TEAM_LEADER_JOB_CODE = "projTeamLeader"; + + + /** + * 有班组时,优先取班组岗位 + */ + public static OrgNodeUserDTO nodeUserDTOByList(List orgNodeUserDTOS) { + OrgNodeUserDTO nodeUserDTO = CollectionUtils.isEmpty(orgNodeUserDTOS) ? OrgNodeUserDTO.builder().build() : orgNodeUserDTOS.get(0); + if (CollectionUtils.isNotEmpty(orgNodeUserDTOS)) { + for (OrgNodeUserDTO orgNodeUserDTO : orgNodeUserDTOS) { + if (Objects.nonNull(orgNodeUserDTO.getJob()) + && StringUtils.hasText(orgNodeUserDTO.getJob().getCode()) + && orgNodeUserDTO.getJob().getCode().equals(Constants.PROJECT_TEAM_LEADER_JOB_CODE)) { + nodeUserDTO = orgNodeUserDTO; + } + } + } + return nodeUserDTO; + } +} diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/utils/RpcUtil.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/utils/RpcUtil.java new file mode 100644 index 00000000..c1f7c49a --- /dev/null +++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/utils/RpcUtil.java @@ -0,0 +1,37 @@ +package cn.axzo.nanopart.visa.server.utils; + +import cn.axzo.apollo.core.web.Result; +import cn.axzo.framework.domain.web.result.ApiListResult; +import cn.hutool.core.lang.Assert; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.function.Supplier; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2025/1/20 14:52 + */ +@Slf4j +public class RpcUtil extends cn.axzo.pokonyan.util.RpcUtil { + + public static T rpcResultProcessor(Supplier> supplier, String operationType, Object... param) { + log.info(operationType + "-Param: " + JSONUtil.toJsonStr(param)); + Result result = printLatency(supplier, operationType); + log.info(operationType + "-Result: " + JSONUtil.toJsonStr(result)); + Assert.notNull(result, "服务调用异常"); + Assert.isTrue(result.getCode() == 200, "服务调用异常:" + result.getMsg()); + return result.getData(); + } + + public static List rpcApiListResultProcessor(Supplier> supplier, String operationType, Object... param) { + log.info(operationType + "-Param: " + JSONUtil.toJsonStr(param)); + ApiListResult result = printLatency(supplier, operationType); + log.info(operationType + "-Result: " + JSONUtil.toJsonStr(result)); + Assert.notNull(result, "服务调用异常"); + Assert.isTrue(result.getCode() == 200, "服务调用异常:" + result.getMsg()); + return result.getData(); + } +}