Merge branch 'feature/REQ-3045' into dev

This commit is contained in:
yanglin 2024-10-11 16:33:41 +08:00
commit 1cc3b3f146
7 changed files with 206 additions and 14 deletions

View File

@ -1,12 +1,16 @@
package cn.axzo.msg.center.message.controller;
import cn.axzo.msg.center.dal.MessageTemplateButtonV3Dao;
import cn.axzo.msg.center.dal.MessageTemplateV3Dao;
import cn.axzo.msg.center.domain.entity.MessageTemplateButtonV3;
import cn.axzo.msg.center.domain.entity.MessageTemplateV3;
import cn.axzo.msg.center.service.domain.UrlConfig;
import cn.axzo.msg.center.service.enums.MessageCategoryEnum;
import cn.axzo.msg.center.service.enums.RouterCategoryEnum;
import cn.axzo.msg.center.service.pending.client.WorkflowButtonSyncClient;
import cn.axzo.msg.center.service.pending.request.WorkflowSyncButtonsRequest;
import cn.axzo.msg.center.service.pending.request.WorkflowSyncButtonsRequest.WorkflowButton;
import cn.axzo.msg.center.utils.AsyncRunTasks;
import cn.azxo.framework.common.model.CommonResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -14,6 +18,8 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
@ -26,31 +32,59 @@ import static java.util.stream.Collectors.toMap;
@RequiredArgsConstructor
public class WorkflowButtonSyncClientController implements WorkflowButtonSyncClient {
private final MessageTemplateV3Dao messageTemplateV3Dao;
private final MessageTemplateButtonV3Dao messageTemplateButtonV3Dao;
@Override
public CommonResponse<Void> syncButtons(WorkflowSyncButtonsRequest request) {
public CommonResponse<Void> syncButtons(WorkflowSyncButtonsRequest request) throws Exception {
Map<String, MessageTemplateV3> code2Template = messageTemplateV3Dao.lambdaQuery()
.list().stream()
.collect(toMap(MessageTemplateV3::getCode, identity()));
Map<String, WorkflowButton> code2FlowButton =
request.getButtons().stream()
.collect(toMap(WorkflowButton::getCode, identity(),
(oldValue, newValue) -> oldValue));
List<MessageTemplateButtonV3> buttons =
messageTemplateButtonV3Dao.lambdaQuery().list();
int nThreads = 10;
AsyncRunTasks tasks = new AsyncRunTasks(Executors.newFixedThreadPool(nThreads));
Semaphore semaphore = new Semaphore(nThreads);
for (MessageTemplateButtonV3 button : buttons) {
WorkflowButton workflowButton = code2FlowButton.get(button.getCode());
if (workflowButton == null) continue;
MessageTemplateButtonV3 update = new MessageTemplateButtonV3();
update.setId(button.getId());
if (button.getCategory() == RouterCategoryEnum.JUMP) {
UrlConfig urlConfig = new UrlConfig();
urlConfig.applyUrlAsDefaults(workflowButton.getUrl());
update.setUrlConfig(urlConfig);
} else {
update.setApiUrl(workflowButton.getUrl());
}
messageTemplateButtonV3Dao.updateById(update);
semaphore.acquire();
tasks.runAsync(() -> {
try {
sync(code2Template, code2FlowButton, button);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
semaphore.release();
}
});
}
tasks.awaitTermination();
return CommonResponse.success();
}
private void sync(Map<String, MessageTemplateV3> code2Template,
Map<String, WorkflowButton> code2FlowButton,
MessageTemplateButtonV3 button) {
MessageTemplateV3 template = code2Template.get(button.getTemplateCode());
if (template.getMsgCategory() != MessageCategoryEnum.APPROVAL_PENDING_MESSAGE)
return;
WorkflowButton workflowButton = code2FlowButton.get(button.getCode());
if (workflowButton == null) return;
MessageTemplateButtonV3 update = new MessageTemplateButtonV3();
update.setId(button.getId());
if (button.getCategory() == RouterCategoryEnum.JUMP) {
UrlConfig urlConfig = new UrlConfig();
urlConfig.applyUrlAsDefaults(workflowButton.getUrl());
update.setUrlConfig(urlConfig);
} else {
update.setApiUrl(workflowButton.getUrl());
}
messageTemplateButtonV3Dao.updateById(update);
}
}

View File

@ -0,0 +1,31 @@
package cn.axzo.msg.center.utils;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
/**
* @author yanglin
*/
public class AsyncRunTasks extends AsyncTasks<Void> {
private final ExecutorService executor;
/**
* use common pool
*/
public AsyncRunTasks() {
this(null);
}
public AsyncRunTasks(ExecutorService executor) {
this.executor = executor;
}
public void runAsync(Runnable runnable) {
CompletableFuture<Void> future = executor == null
? CompletableFuture.runAsync(runnable)
: CompletableFuture.runAsync(runnable, executor);
add(future);
}
}

View File

@ -0,0 +1,34 @@
package cn.axzo.msg.center.utils;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;
/**
* @author yanglin
*/
public class AsyncSupplyTasks extends AsyncTasks<Object> {
private final ExecutorService executor;
/**
* use common pool
*/
public AsyncSupplyTasks() {
this(null);
}
public AsyncSupplyTasks(ExecutorService executor) {
this.executor = executor;
}
public <U> FutureWrapper<U> supplyAsync(Supplier<U> supplier) {
CompletableFuture<U> future = executor == null
? CompletableFuture.supplyAsync(supplier)
: CompletableFuture.supplyAsync(supplier, executor);
//noinspection unchecked
add((CompletableFuture<Object>) future);
return new FutureWrapper<>(future);
}
}

View File

@ -0,0 +1,49 @@
package cn.axzo.msg.center.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author yanglin
*/
public class AsyncTasks<T> {
private final List<CompletableFuture<T>> futures = Collections.synchronizedList(new ArrayList<>());
private final boolean removeFutureWhenComplete;
public AsyncTasks() {
this(true);
}
AsyncTasks(boolean removeFutureWhenComplete) {
this.removeFutureWhenComplete = removeFutureWhenComplete;
}
public void add(CompletableFuture<T> future) {
futures.add(future);
if (removeFutureWhenComplete)
future.whenComplete((unused, throwable) -> futures.remove(future));
}
CompletableFuture<T> getFuture(int index) {
return futures.get(index);
}
public void awaitTermination() {
collectAll().join();
}
public void awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
collectAll().get(timeout, unit);
}
private CompletableFuture<Void> collectAll() {
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}
}

View File

@ -0,0 +1,39 @@
package cn.axzo.msg.center.utils;
import cn.axzo.basics.common.exception.ServiceException;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author yanglin
*/
@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class FutureWrapper<T> {
private final CompletableFuture<T> future;
public T get() {
try {
return future.get();
} catch (InterruptedException | ExecutionException e) {
log.error("执行异常", e);
throw new ServiceException("执行异常");
}
}
public T get(long timeout, TimeUnit unit) {
try {
return future.get(timeout, unit);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
log.error("执行异常", e);
throw new ServiceException("执行异常");
}
}
}

View File

@ -18,6 +18,6 @@ import javax.validation.Valid;
public interface WorkflowButtonSyncClient {
@PostMapping(value = "/pending-message/workflow/syncButtons", produces = {MediaType.APPLICATION_JSON_VALUE})
CommonResponse<Void> syncButtons(@RequestBody @Valid WorkflowSyncButtonsRequest request);
CommonResponse<Void> syncButtons(@RequestBody @Valid WorkflowSyncButtonsRequest request) throws Exception;
}

View File

@ -23,6 +23,11 @@ public class WorkflowSyncButtonsRequest {
private String code;
private RouterCategoryEnum category;
private String url;
@Override
public String toString() {
return code;
}
}
}