diff --git a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/config/TaskSchedulerConfig.java b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/config/TaskSchedulerConfig.java index 45e75780..82bb646a 100644 --- a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/config/TaskSchedulerConfig.java +++ b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/config/TaskSchedulerConfig.java @@ -1,5 +1,6 @@ package com.nflg.mobilebroken.admin.config; +import com.nflg.mobilebroken.starter.decorator.MdcTaskDecorator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; @@ -34,6 +35,19 @@ public class TaskSchedulerConfig { executor.setThreadNamePrefix("sse-send-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); + executor.setTaskDecorator(new MdcTaskDecorator()); + return executor; + } + + @Bean(name = "httpExecutor") + public Executor httpExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(4); + executor.setMaxPoolSize(20); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("http-async-"); + executor.initialize(); + executor.setTaskDecorator(new MdcTaskDecorator()); return executor; } } diff --git a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java index d88b51fa..bdb05dbb 100644 --- a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java +++ b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java @@ -1387,7 +1387,6 @@ public class TicketController extends ControllerBase { */ @GetMapping("call") public ApiResult call(@Valid @RequestParam @NotNull Integer ticketId) { - Ticket ticket = ticketService.getById(ticketId); VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在"); VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState())) @@ -1446,7 +1445,7 @@ public class TicketController extends ControllerBase { ssePushService.sendTicketCallToAdmin(adminUser, receiveUserId, Long.valueOf(ticketId)); } ticketEventPublisher.publishTicketCallBeginEvent(ticketId, adminUser.getUserName()); - stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, receiveUserFrom + "-" + receiveUserId); + stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, receiveUserFrom + "-uid-" + receiveUserId); stringRedisTemplate.expire(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, 1, TimeUnit.MINUTES); return ApiResult.success(); } @@ -1493,7 +1492,7 @@ public class TicketController extends ControllerBase { ); ssePushService.sendTicketCallToAdmin(adminUser, userId, request.getTicketId()); // ticketCallJoinService.add(ticketCall.getId(), userId, Constant.FROM_ADMIN); - stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_ADMIN + "-" + userId); + stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_ADMIN + "-uid-" + userId); stringRedisTemplate.expire(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), 1, TimeUnit.MINUTES); } } @@ -1561,7 +1560,7 @@ public class TicketController extends ControllerBase { .setTitle("视频通话结束") .setTicketId(String.valueOf(request.getTicketId())) .setTicketType(ticket.getType()) - .setUserId(Integer.valueOf(StrUtil.split(uid, "-").get(1))) + .setUserId(Integer.valueOf(StrUtil.split(uid, "-").get(2))) .setCategory("ticketCallEnd") .setFrom("admin") ) @@ -1589,7 +1588,7 @@ public class TicketController extends ControllerBase { ) ); } - stringRedisTemplate.opsForSet().remove(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_ADMIN + "-" + AdminUserUtil.getUserId()); + stringRedisTemplate.opsForSet().remove(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_ADMIN + "-uid-" + AdminUserUtil.getUserId()); } taskScheduler.schedule(() -> { ticketEventPublisher.publishTicketCallEndEvent(ticket); diff --git a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/config/TaskSchedulerConfig.java b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/config/TaskSchedulerConfig.java new file mode 100644 index 00000000..b10b121f --- /dev/null +++ b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/config/TaskSchedulerConfig.java @@ -0,0 +1,28 @@ +package com.nflg.mobilebroken.cfs.config; + +import com.nflg.mobilebroken.starter.decorator.MdcTaskDecorator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@EnableAsync +@Configuration +@EnableScheduling +public class TaskSchedulerConfig { + + @Bean(name = "httpExecutor") + public Executor httpExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(4); + executor.setMaxPoolSize(20); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("http-async-"); + executor.initialize(); + executor.setTaskDecorator(new MdcTaskDecorator()); + return executor; + } +} diff --git a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TicketController.java b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TicketController.java index ffd58782..c81d4ecf 100644 --- a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TicketController.java +++ b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TicketController.java @@ -655,7 +655,7 @@ public class TicketController extends ControllerBase { ssePushService.sendTicketCallToAdmin(appUser, handlerUserId, ticketId); // ticketCallService.add(ticketId, AppUserUtil.getUserId(),Constant.FROM_APP, handlerUserId, Constant.FROM_ADMIN); ticketEventPublisher.publishTicketCallBeginEvent(ticketId, appUser.getName()); - stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, Constant.FROM_ADMIN + "-" + handlerUserId); + stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, Constant.FROM_ADMIN + "-uid-" + handlerUserId); stringRedisTemplate.expire(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, 1, TimeUnit.MINUTES); return ApiResult.success(); } @@ -716,7 +716,7 @@ public class TicketController extends ControllerBase { .setTitle("视频通话结束") .setTicketId(String.valueOf(request.getTicketId())) .setTicketType(ticket.getType()) - .setUserId(Integer.valueOf(StrUtil.split(uid, "-").get(1))) + .setUserId(Integer.valueOf(StrUtil.split(uid, "-").get(2))) .setCategory("ticketCallEnd") .setFrom("app") ) @@ -742,7 +742,7 @@ public class TicketController extends ControllerBase { ) ) ); - stringRedisTemplate.opsForSet().remove(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_APP + "-" + AppUserUtil.getUserId()); + stringRedisTemplate.opsForSet().remove(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_APP + "-uid-" + AppUserUtil.getUserId()); } taskScheduler.schedule(() -> { ticketEventPublisher.publishTicketCallEndEvent(ticket); diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/TicketAddRequest.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/TicketAddRequest.java index f7a0c082..404c3e3e 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/TicketAddRequest.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/TicketAddRequest.java @@ -48,8 +48,7 @@ public class TicketAddRequest { /** * 类型,0:移动破;1:工服 */ - @NotNull - private Integer type; + private Integer type = 0; /** * 紧急程度,0:非紧急;1:普通;2:紧急 diff --git a/nflg-mobilebroken-gongfu/src/main/java/com/nflg/mobilebroken/gongfu/config/TaskSchedulerConfig.java b/nflg-mobilebroken-gongfu/src/main/java/com/nflg/mobilebroken/gongfu/config/TaskSchedulerConfig.java index 7848abc5..514f09b4 100644 --- a/nflg-mobilebroken-gongfu/src/main/java/com/nflg/mobilebroken/gongfu/config/TaskSchedulerConfig.java +++ b/nflg-mobilebroken-gongfu/src/main/java/com/nflg/mobilebroken/gongfu/config/TaskSchedulerConfig.java @@ -1,5 +1,6 @@ package com.nflg.mobilebroken.gongfu.config; +import com.nflg.mobilebroken.starter.decorator.MdcTaskDecorator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; @@ -34,6 +35,19 @@ public class TaskSchedulerConfig { executor.setThreadNamePrefix("sse-send-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); + executor.setTaskDecorator(new MdcTaskDecorator()); + return executor; + } + + @Bean(name = "httpExecutor") + public Executor httpExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(4); + executor.setMaxPoolSize(20); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("http-async-"); + executor.initialize(); + executor.setTaskDecorator(new MdcTaskDecorator()); return executor; } } diff --git a/nflg-mobilebroken-gongfu/src/main/java/com/nflg/mobilebroken/gongfu/controller/TicketController.java b/nflg-mobilebroken-gongfu/src/main/java/com/nflg/mobilebroken/gongfu/controller/TicketController.java index 49853421..9ec34595 100644 --- a/nflg-mobilebroken-gongfu/src/main/java/com/nflg/mobilebroken/gongfu/controller/TicketController.java +++ b/nflg-mobilebroken-gongfu/src/main/java/com/nflg/mobilebroken/gongfu/controller/TicketController.java @@ -1374,7 +1374,7 @@ public class TicketController extends ControllerBase { ssePushService.sendTicketCallToAdmin(adminUser, receiveUserId, ticketId); } ticketEventPublisher.publishTicketCallBeginEvent(ticketId, adminUser.getUserName()); - stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, receiveUserFrom + "-" + receiveUserId); + stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, receiveUserFrom + "-uid-" + receiveUserId); stringRedisTemplate.expire(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, 1, TimeUnit.MINUTES); return ApiResult.success(); } @@ -1421,7 +1421,7 @@ public class TicketController extends ControllerBase { ); ssePushService.sendTicketCallToAdmin(adminUser, userId, request.getTicketId()); // ticketCallJoinService.add(ticketCall.getId(), userId, Constant.FROM_ADMIN); - stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_ADMIN + "-" + userId); + stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_ADMIN + "-uid-" + userId); stringRedisTemplate.expire(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), 1, TimeUnit.MINUTES); } } @@ -1483,7 +1483,7 @@ public class TicketController extends ControllerBase { // .setPayload(new UniPushMessageCallPayload() // .setTitle("视频通话结束") // .setTicketId(request.getTicketId()) -// .setUserId(Integer.valueOf(StrUtil.split(uid, "-").get(1))) +// .setUserId(Integer.valueOf(StrUtil.split(uid, "-").get(2))) // .setCategory("ticketCallEnd") // .setFrom("admin") // ) diff --git a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/decorator/MdcTaskDecorator.java b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/decorator/MdcTaskDecorator.java new file mode 100644 index 00000000..3e876c3b --- /dev/null +++ b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/decorator/MdcTaskDecorator.java @@ -0,0 +1,30 @@ +package com.nflg.mobilebroken.starter.decorator; + +import lombok.NonNull; +import org.slf4j.MDC; +import org.springframework.core.task.TaskDecorator; + +import java.util.Map; + +public class MdcTaskDecorator implements TaskDecorator { + + @Override + public Runnable decorate(@NonNull Runnable runnable) { + // 1. 获取主线程(父线程)当前的 MDC 上下文副本 + Map contextMap = MDC.getCopyOfContextMap(); + + return () -> { + try { + // 2. 将父线程的上下文 设置到子线程中 + if (contextMap != null) { + MDC.setContextMap(contextMap); + } + // 3. 执行真正的任务 + runnable.run(); + } finally { + // 4. 务必清除 MDC,避免线程复用时内存泄漏或数据污染 + MDC.clear(); + } + }; + } +} diff --git a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/filter/AppVersionFilter.java b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/filter/AppVersionFilter.java new file mode 100644 index 00000000..4370db3c --- /dev/null +++ b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/filter/AppVersionFilter.java @@ -0,0 +1,63 @@ +package com.nflg.mobilebroken.starter.filter; + +import cn.hutool.core.comparator.VersionComparator; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nflg.mobilebroken.common.constant.STATE; +import com.nflg.mobilebroken.common.pojo.ApiResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +@Slf4j +@Order(0) +@Component +public class AppVersionFilter extends OncePerRequestFilter { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static final String MIN_SUPPER_VERSION = "1.0.9"; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { + String appPlatform = request.getHeader("App-Platform"); + response.setStatus(HttpServletResponse.SC_OK); + if (StrUtil.isBlank(appPlatform)) { + log.error("请求头中未找到App-Platform"); + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + out(response, ApiResult.error(STATE.ServiceConnectRefused, "请更新版本!")); + } else { + if (appPlatform.startsWith("pc")) { + filterChain.doFilter(request, response); + } else { + String appVersion = request.getHeader("App-Version"); + if (StrUtil.isBlank(appVersion)) { + log.error("请求头中未找到App-Version"); + out(response, ApiResult.error(STATE.ServiceConnectRefused, "请更新版本!")); + } else if (VersionComparator.INSTANCE.compare(appVersion, MIN_SUPPER_VERSION) < 0) { + out(response, ApiResult.error(STATE.ServiceConnectRefused, "版本太低,请更新版本!")); + } else { + filterChain.doFilter(request, response); + } + } + } + } + + private void out(HttpServletResponse response, ApiResult result) throws IOException { + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding("UTF-8"); + PrintWriter writer = response.getWriter(); + writer.write(objectMapper.writeValueAsString(result)); + writer.flush(); + } +} diff --git a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/filter/TraceIdFilter.java b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/filter/TraceIdFilter.java index 0f84e979..ec1ac384 100644 --- a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/filter/TraceIdFilter.java +++ b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/filter/TraceIdFilter.java @@ -9,6 +9,7 @@ import com.nflg.mobilebroken.common.util.SaTokenAdminUtil; import com.nflg.mobilebroken.common.util.SaTokenAppUtil; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; +import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.ContentCachingRequestWrapper; @@ -26,6 +27,7 @@ import java.util.Arrays; import java.util.List; @Slf4j +@Order(1) @Component public class TraceIdFilter extends OncePerRequestFilter { @@ -48,13 +50,11 @@ public class TraceIdFilter extends OncePerRequestFilter { // 存入MDC和响应头 MDC.put(Constant.TRACE_ID, traceId); responseWrapper.addHeader(TRACE_ID_HEADER, traceId); - filterChain.doFilter(requestWrapper, responseWrapper); } finally { logRequest(requestWrapper); logResponse(responseWrapper); responseWrapper.copyBodyToResponse(); - // 请求结束时清除MDC MDC.remove(Constant.TRACE_ID); } diff --git a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/UniPushService.java b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/UniPushService.java index 126ec887..111656c1 100644 --- a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/UniPushService.java +++ b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/UniPushService.java @@ -11,9 +11,12 @@ import com.nflg.mobilebroken.common.util.AppUserUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; +import java.util.concurrent.CompletableFuture; + @Slf4j @Component public class UniPushService { @@ -21,11 +24,13 @@ public class UniPushService { @Value("${uniapp.cloud.push.url}") private String url; - public void send(UniPushMessage message) { + @Async("httpExecutor") + public CompletableFuture send(UniPushMessage message) { log.info("发送uniapp消息:{}", JSONUtil.toJsonStr(message)); RestTemplate restTemplate = new RestTemplate(); ResponseEntity response = restTemplate.postForEntity(url, message, String.class); log.info("发送uniapp消息结果:{}", response.getBody()); + return CompletableFuture.completedFuture(null); } public void sendTicketMessage(String from, String to, ChatMessageDTO data) {