From 4dfe32bff807f047ac91ffaf2a45a49959544328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E9=B9=8F=E9=A3=9E?= Date: Wed, 26 Feb 2025 20:22:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20sse=E6=9C=8D=E5=8A=A1=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/controller/TicketController.java | 35 +++---- .../admin/service/SsePushService.java | 16 ++- .../service/impl/SSEINotifyPushService.java | 19 ++-- .../cfs/controller/TestController.java | 20 ++-- .../cfs/controller/TiketController.java | 6 +- .../cfs/service/SsePushService.java | 12 +-- .../common/constant/Constant.java | 4 + .../common/pojo/dto/SSEMessageDTO.java | 4 - .../common/pojo/request/PushRequest.java | 8 +- nflg-mobilebroken-push/pom.xml | 6 +- .../push/config/ThreadPoolConfig.java | 20 ++++ .../push/controller/AnalysisController.java | 55 +++++++++++ .../push/controller/SSEController.java | 12 +-- .../push/service/SSEManagerBase.java | 99 +++++++++++++------ .../push/service/SSEManagerService.java | 13 +-- .../service/impl/APPSSEManagerService.java | 55 ++--------- .../service/impl/AdminSSEManagerService.java | 55 ++--------- .../push/task/SSEScheduledTasks.java | 41 -------- .../src/main/resources/application.properties | 3 + .../repository/mapper/AdminUserMapper.java | 2 + .../repository/service/IAdminUserService.java | 2 + .../service/impl/AdminUserServiceImpl.java | 5 + .../service/impl/TicketServiceImpl.java | 31 +++--- .../main/resources/mapper/AdminUserMapper.xml | 10 ++ .../main/resources/mapper/TicketMapper.xml | 4 +- 25 files changed, 262 insertions(+), 275 deletions(-) create mode 100644 nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/config/ThreadPoolConfig.java create mode 100644 nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/controller/AnalysisController.java delete mode 100644 nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/task/SSEScheduledTasks.java 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 b1d1f529..e19a2e73 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 @@ -210,7 +210,7 @@ public class TicketController extends ControllerBase { .setCreateTime(Instant.now()); ticketChatService.addMessage(ticket.getId(), message); //推送消息 - ssePushService.sendTicketMessageToApp(ticket.getUserId(),message); + ssePushService.sendTicketMessageToApp(ticket.getId(),message); return ApiResult.success(); } @@ -226,15 +226,20 @@ public class TicketController extends ControllerBase { } /** - * 添加工单处理人 + * 添加/删除工单处理人 * @param request 请求参数 */ @PostMapping("addTicketHandle") @MethodInfoMark(value = "添加工单处理人", menuName = "工单管理") @ApiMark(moduleName = "工单管理", apiName = "添加工单处理人") public ApiResult addTicketHandle(@Valid @RequestBody TicketHandleAddRequest request){ - Ticket ticket=ticketService.addTicketHandle(request); - ticketEventPublisher.publishTicketAssignedEvent(ticket,request.getUserIds()); + Ticket ticket=ticketService.getById(request.getTicketId()); + List handleIds= Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).collect(Collectors.toList()); + Ticket ticket1=ticketService.addTicketHandle(request); + request.getUserIds().removeAll(handleIds); + if(CollectionUtil.isNotEmpty(request.getUserIds())) { + ticketEventPublisher.publishTicketAssignedEvent(ticket1, request.getUserIds()); + } return ApiResult.success(); } @@ -259,13 +264,7 @@ public class TicketController extends ControllerBase { .setCreateTime(Instant.now()); ticketChatService.addMessage(id, message); //推送消息 - ssePushService.sendTicketMessageToApp(ticket.getUserId(),message); - List handles = Arrays.stream(ticket.getHandle().split(",")) - .map(Integer::parseInt).collect(Collectors.toList()); - handles.remove(AdminUserUtil.getUserId()); - if (CollectionUtil.isNotEmpty(handles)){ - handles.forEach(uid->ssePushService.sendTicketMessageToAdmin(uid,message)); - } + ssePushService.sendTicketMessageToAdmin(id,message); } return ApiResult.success(); } @@ -290,13 +289,7 @@ public class TicketController extends ControllerBase { .setCreateTime(Instant.now()); ticketChatService.addMessage(request.getTicketId(), message); //推送消息 - ssePushService.sendTicketMessageToApp(ticket.getUserId(),message); - List handles = Arrays.stream(ticket.getHandle().split(",")) - .map(Integer::parseInt).collect(Collectors.toList()); - handles.remove(AdminUserUtil.getUserId()); - if (CollectionUtil.isNotEmpty(handles)){ - handles.forEach(uid->ssePushService.sendTicketMessageToAdmin(uid,message)); - } + ssePushService.sendTicketMessageToAdmin(ticket.getId(),message); return ApiResult.success(); } @@ -556,11 +549,7 @@ public class TicketController extends ControllerBase { } ticketChatService.addMessage(request.getTicketId(), message); //推送消息 - ssePushService.sendTicketMessageToApp(ticket.getUserId(),message); - handles.remove(AdminUserUtil.getUserId()); - if (CollectionUtil.isNotEmpty(handles)){ - handles.forEach(uid->ssePushService.sendTicketMessageToAdmin(uid,message)); - } + ssePushService.sendTicketMessageToAdmin(request.getTicketId(),message); ticketEventPublisher.publishTicketReplyEvent(ticket); return ApiResult.success(); } diff --git a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/service/SsePushService.java b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/service/SsePushService.java index d47667db..c4b4fd42 100644 --- a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/service/SsePushService.java +++ b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/service/SsePushService.java @@ -4,7 +4,6 @@ import cn.hutool.core.date.DatePattern; import cn.hutool.json.JSONUtil; import com.nflg.mobilebroken.common.pojo.ApiResult; import com.nflg.mobilebroken.common.pojo.dto.ChatMessageDTO; -import com.nflg.mobilebroken.common.pojo.dto.SSEMessageDTO; import com.nflg.mobilebroken.common.pojo.request.PushRequest; import com.nflg.mobilebroken.common.pojo.vo.ChatMessageVO; import com.nflg.mobilebroken.common.util.MultilingualUtil; @@ -27,9 +26,9 @@ public class SsePushService { @Value("${sse.url}") private String sseUrl; - public void sendTicketMessageToAdmin(Integer userId, ChatMessageDTO message){ + public void sendTicketMessageToAdmin(Integer ticketId, ChatMessageDTO message){ try { - PushRequest request=new PushRequest().setUserId(userId).setMessage(buildMessage(message)); + PushRequest request=new PushRequest().setTicketId(ticketId).setMessage(buildMessage(message)); RestTemplate restTemplate = new RestTemplate(); ResponseEntity response = restTemplate.postForEntity(sseUrl+"/sse/admin/push",request, ApiResult.class); log.debug("发送消息结果:{}", JSONUtil.toJsonStr(response.getBody())); @@ -38,9 +37,9 @@ public class SsePushService { } } - public void sendTicketMessageToApp(Integer userId, ChatMessageDTO message){ + public void sendTicketMessageToApp(Integer ticketId, ChatMessageDTO message){ try { - PushRequest request=new PushRequest().setUserId(userId).setMessage(buildMessage(message)); + PushRequest request=new PushRequest().setTicketId(ticketId).setMessage(buildMessage(message)); RestTemplate restTemplate = new RestTemplate(); ResponseEntity response = restTemplate.postForEntity(sseUrl+"/sse/app/push",request, ApiResult.class); log.debug("发送消息结果:{}", JSONUtil.toJsonStr(response.getBody())); @@ -49,11 +48,11 @@ public class SsePushService { } } - private SSEMessageDTO buildMessage(ChatMessageDTO message){ + private ChatMessageVO buildMessage(ChatMessageDTO message){ String zone = MultilingualUtil.getZone(); ZoneId zoneId = ZoneId.of(zone); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN).withZone(zoneId); - ChatMessageVO messageVO = new ChatMessageVO() + return new ChatMessageVO() .setId(message.getId()) .setFrom(message.getFrom()) .setSenderId(message.getSenderId()) @@ -75,8 +74,5 @@ public class SsePushService { .setAttachments(message.getQuote().getAttachments()) .setImages(message.getQuote().getImages()) .setCreateTime(formatter.format(message.getQuote().getCreateTime()))); - return new SSEMessageDTO() - .setType(1) - .setData(messageVO); } } diff --git a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/service/impl/SSEINotifyPushService.java b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/service/impl/SSEINotifyPushService.java index 89800457..e8aea669 100644 --- a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/service/impl/SSEINotifyPushService.java +++ b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/service/impl/SSEINotifyPushService.java @@ -1,8 +1,6 @@ package com.nflg.mobilebroken.admin.service.impl; import com.nflg.mobilebroken.common.constant.Constant; -import com.nflg.mobilebroken.common.pojo.dto.NotifyDTO; -import com.nflg.mobilebroken.common.pojo.dto.SSEMessageDTO; import com.nflg.mobilebroken.common.pojo.dto.UserDTO; import com.nflg.mobilebroken.starter.service.INotifyPushService; import com.nflg.mobilebroken.starter.service.SSEManagerService; @@ -12,7 +10,6 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.io.IOException; import java.util.Objects; @Service @@ -28,14 +25,14 @@ public class SSEINotifyPushService implements INotifyPushService { @Override public void push(UserDTO user, String subject, String content) { - try { - SSEMessageDTO message = new SSEMessageDTO() - .setType(2) - .setData(new NotifyDTO().setSubject(subject).setContent(content)); - sseManagerService.send(user.getId(), message); - } catch (IOException e) { - log.error("发送SSE失败", e); - } +// try { +// SSEMessageDTO message = new SSEMessageDTO() +// .setType(2) +// .setData(new NotifyDTO().setSubject(subject).setContent(content)); +// sseManagerService.send(user.getId(), message); +// } catch (IOException e) { +// log.error("发送SSE失败", e); +// } } @Override diff --git a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TestController.java b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TestController.java index e7c28fd3..c7c97e80 100644 --- a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TestController.java +++ b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TestController.java @@ -2,8 +2,6 @@ package com.nflg.mobilebroken.cfs.controller; import com.nflg.mobilebroken.cfs.publisher.TicketEventPublisher; import com.nflg.mobilebroken.common.pojo.ApiResult; -import com.nflg.mobilebroken.common.pojo.dto.NotifyDTO; -import com.nflg.mobilebroken.common.pojo.dto.SSEMessageDTO; import com.nflg.mobilebroken.common.util.MultilingualUtil; import com.nflg.mobilebroken.repository.entity.Ticket; import com.nflg.mobilebroken.repository.service.ITicketService; @@ -11,11 +9,9 @@ import com.nflg.mobilebroken.starter.service.impl.APPSSEManagerService; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; -import java.io.IOException; @RestController @Slf4j @@ -31,14 +27,14 @@ public class TestController extends ControllerBase { @Resource private ITicketService ticketService; - @GetMapping("sse/send") - public ApiResult sendSse(@RequestParam String userId, @RequestParam String message) throws IOException { - SSEMessageDTO messageDTO = new SSEMessageDTO() - .setType(2) - .setData(new NotifyDTO().setSubject("消息测试").setContent("消息内容")); - sseManagerService.send(Integer.valueOf(userId), messageDTO); - return ApiResult.success(); - } +// @GetMapping("sse/send") +// public ApiResult sendSse(@RequestParam String userId, @RequestParam String message) throws IOException { +// SSEMessageDTO messageDTO = new SSEMessageDTO() +// .setType(2) +// .setData(new NotifyDTO().setSubject("消息测试").setContent("消息内容")); +// sseManagerService.send(Integer.valueOf(userId), messageDTO); +// return ApiResult.success(); +// } @GetMapping("sss") public ApiResult sss(){ diff --git a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TiketController.java b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TiketController.java index e4290aac..97a37d4d 100644 --- a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TiketController.java +++ b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TiketController.java @@ -331,11 +331,7 @@ public class TiketController extends ControllerBase { } ticketChatService.addMessage(request.getTicketId(), message); //推送消息 - List handles=Arrays.stream(ticket.getHandle().split(",")) - .map(Integer::parseInt).collect(Collectors.toList()); - if (CollectionUtil.isNotEmpty(handles)){ - handles.forEach(uid->ssePushService.sendTicketMessageToAdmin(uid,message)); - } + ssePushService.sendTicketMessageToAdmin(ticket.getId(),message); ticketEventPublisher.publishTicketReplyEvent(ticket, MultilingualUtil.getLanguage(), MultilingualUtil.getZone()); return ApiResult.success(); } diff --git a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/service/SsePushService.java b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/service/SsePushService.java index 4a87af94..7956b70c 100644 --- a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/service/SsePushService.java +++ b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/service/SsePushService.java @@ -4,7 +4,6 @@ import cn.hutool.core.date.DatePattern; import cn.hutool.json.JSONUtil; import com.nflg.mobilebroken.common.pojo.ApiResult; import com.nflg.mobilebroken.common.pojo.dto.ChatMessageDTO; -import com.nflg.mobilebroken.common.pojo.dto.SSEMessageDTO; import com.nflg.mobilebroken.common.pojo.request.PushRequest; import com.nflg.mobilebroken.common.pojo.vo.ChatMessageVO; import com.nflg.mobilebroken.common.util.MultilingualUtil; @@ -27,9 +26,9 @@ public class SsePushService { @Value("${sse.url}") private String sseUrl; - public void sendTicketMessageToAdmin(Integer userId, ChatMessageDTO message){ + public void sendTicketMessageToAdmin(Integer ticketId, ChatMessageDTO message){ try { - PushRequest request=new PushRequest().setUserId(userId).setMessage(buildMessage(message)); + PushRequest request=new PushRequest().setTicketId(ticketId).setMessage(buildMessage(message)); RestTemplate restTemplate = new RestTemplate(); ResponseEntity response = restTemplate.postForEntity(sseUrl+"/sse/admin/push",request, ApiResult.class); log.debug("发送消息结果:{}", JSONUtil.toJsonStr(response.getBody())); @@ -38,11 +37,11 @@ public class SsePushService { } } - private SSEMessageDTO buildMessage(ChatMessageDTO message){ + private ChatMessageVO buildMessage(ChatMessageDTO message){ String zone = MultilingualUtil.getZone(); ZoneId zoneId = ZoneId.of(zone); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN).withZone(zoneId); - ChatMessageVO messageVO = new ChatMessageVO() + return new ChatMessageVO() .setId(message.getId()) .setFrom(message.getFrom()) .setSenderId(message.getSenderId()) @@ -64,8 +63,5 @@ public class SsePushService { .setAttachments(message.getQuote().getAttachments()) .setImages(message.getQuote().getImages()) .setCreateTime(formatter.format(message.getQuote().getCreateTime()))); - return new SSEMessageDTO() - .setType(1) - .setData(messageVO); } } \ No newline at end of file diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/constant/Constant.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/constant/Constant.java index 2695674e..ed132cf0 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/constant/Constant.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/constant/Constant.java @@ -1,5 +1,7 @@ package com.nflg.mobilebroken.common.constant; +import java.util.List; + public class Constant { public static final String DEFAULT_LANGUAGE_CODE="cn"; @@ -101,4 +103,6 @@ public class Constant { public static final String DICTIONARY_ITEM_ACCOUNT_HAS_EXPIRED_PRIMARY="AccountHasExpiredPrimary"; public static final String DICTIONARY_ITEM_ACCOUNT_HAS_EXPIRED="AccountHasExpired"; + + public static final List ROLE_CODE_TICKET_MANAGERS = List.of(TITLE_DIRECTOROF_BUSINESS_UNIT,TITLE_TECHNICAL_MANAGER,TITLE_SALES_MANAGER,TITLE_TEST_MANAGER,TITLE_QUALITY_MANAGER,DICTIONARY_TYPE_TITLE_CQM); } diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/dto/SSEMessageDTO.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/dto/SSEMessageDTO.java index b405ffc6..4f64cc75 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/dto/SSEMessageDTO.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/dto/SSEMessageDTO.java @@ -9,10 +9,6 @@ import javax.validation.constraints.NotNull; @Accessors(chain = true) public class SSEMessageDTO { - //类型,1:工单会话消息,2:消息提醒 - @NotNull - private int type; - //消息内容 @NotNull private Object data; diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/PushRequest.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/PushRequest.java index e27f3a19..f7f94417 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/PushRequest.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/PushRequest.java @@ -1,6 +1,6 @@ package com.nflg.mobilebroken.common.pojo.request; -import com.nflg.mobilebroken.common.pojo.dto.SSEMessageDTO; +import com.nflg.mobilebroken.common.pojo.vo.ChatMessageVO; import lombok.Data; import lombok.experimental.Accessors; @@ -11,12 +11,12 @@ import javax.validation.constraints.NotNull; @Accessors(chain = true) public class PushRequest { - //用户id + //工单id @NotNull - private Integer userId; + private Integer ticketId; //消息 @NotNull @Valid - private SSEMessageDTO message; + private ChatMessageVO message; } diff --git a/nflg-mobilebroken-push/pom.xml b/nflg-mobilebroken-push/pom.xml index 785fe915..fa341dc7 100644 --- a/nflg-mobilebroken-push/pom.xml +++ b/nflg-mobilebroken-push/pom.xml @@ -27,9 +27,9 @@ spring-webmvc - cn.dev33 - sa-token-spring-boot-starter - + cn.dev33 + sa-token-spring-boot-starter + cn.dev33 sa-token-jwt diff --git a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/config/ThreadPoolConfig.java b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/config/ThreadPoolConfig.java new file mode 100644 index 00000000..f3ce6949 --- /dev/null +++ b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/config/ThreadPoolConfig.java @@ -0,0 +1,20 @@ +package com.nflg.mobilebroken.push.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +public class ThreadPoolConfig { + + @Bean + public ThreadPoolTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(25); + executor.setThreadNamePrefix("SseHeartbeat-"); + executor.initialize(); + return executor; + } +} \ No newline at end of file diff --git a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/controller/AnalysisController.java b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/controller/AnalysisController.java new file mode 100644 index 00000000..a53c79f7 --- /dev/null +++ b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/controller/AnalysisController.java @@ -0,0 +1,55 @@ +package com.nflg.mobilebroken.push.controller; + +import com.nflg.mobilebroken.push.service.impl.APPSSEManagerService; +import com.nflg.mobilebroken.push.service.impl.AdminSSEManagerService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 统计分析相关 + */ +@RestController +@RequestMapping("/analysis") +public class AnalysisController{ + + @Resource + private APPSSEManagerService appSSEManagerService; + + @Resource + private AdminSSEManagerService adminSSEManagerService; + + /** + * 获取当前已连接SSE的客户端列表 + * @return 当前已连接SSE的客户端列表 + */ + @GetMapping("getAppSSEConnects") + public Map getAppSSEConnects(){ + Map> map= appSSEManagerService.getMap(); + Map countMap=new HashMap<>(); + map.forEach((k,v)->{ + countMap.put(k,v.size()); + }); + return countMap; + } + + /** + * 获取当前已连接SSE的管理端列表 + * @return 当前已连接SSE的管理端列表 + */ + @GetMapping("getAdminSSEConnects") + public Map getAdminSSEConnects(){ + Map> map= adminSSEManagerService.getMap(); + Map countMap=new HashMap<>(); + map.forEach((k,v)->{ + countMap.put(k,v.size()); + }); + return countMap; + } +} \ No newline at end of file diff --git a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/controller/SSEController.java b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/controller/SSEController.java index 6a881b63..6d9c5cc6 100644 --- a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/controller/SSEController.java +++ b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/controller/SSEController.java @@ -32,8 +32,8 @@ public class SSEController { * 客户端账号建立sse连接 */ @GetMapping(value = "app/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public SseEmitter appConnect() { - return appsseManagerService.connect(AppUserUtil.getUserId()); + public SseEmitter appConnect(@Valid @RequestParam @NotNull Integer ticketId) { + return appsseManagerService.connect(ticketId,AppUserUtil.getUserId()); } /** @@ -43,7 +43,7 @@ public class SSEController { @PostMapping("app/push") public ApiResult pushtToApp(@Valid @RequestBody @NotNull PushRequest request){ try { - appsseManagerService.send(request.getUserId(),request.getMessage()); + appsseManagerService.send(request.getTicketId(),request.getMessage()); return ApiResult.success(); } catch (IOException e) { log.error("发送SSE消息出错", e); @@ -55,8 +55,8 @@ public class SSEController { * 管理端账号建立sse连接 */ @GetMapping(value = "admin/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public SseEmitter adminConnect() { - return adminSSEManagerService.connect(AdminUserUtil.getUserId()); + public SseEmitter adminConnect(@Valid @RequestParam @NotNull Integer ticketId) { + return adminSSEManagerService.connect(ticketId,AdminUserUtil.getUserId()); } /** @@ -66,7 +66,7 @@ public class SSEController { @PostMapping("admin/push") public ApiResult pushtToAdmin(@Valid @RequestBody @NotNull PushRequest request){ try { - adminSSEManagerService.send(request.getUserId(),request.getMessage()); + adminSSEManagerService.send(request.getTicketId(),request.getMessage()); return ApiResult.success(); } catch (IOException e) { log.error("发送SSE消息出错", e); diff --git a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/SSEManagerBase.java b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/SSEManagerBase.java index 3ac0af89..38b559bc 100644 --- a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/SSEManagerBase.java +++ b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/SSEManagerBase.java @@ -1,73 +1,114 @@ package com.nflg.mobilebroken.push.service; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; import com.nflg.mobilebroken.common.constant.STATE; +import com.nflg.mobilebroken.common.pojo.vo.ChatMessageVO; import com.nflg.mobilebroken.common.util.VUtils; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.TaskScheduler; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; import java.io.IOException; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.stream.Collectors; @Slf4j public class SSEManagerBase { + @Resource + private TaskScheduler taskScheduler; + private static boolean IS_SHUTDOWN = false; + @Getter + protected final Map> map = new ConcurrentHashMap<>(); + + protected String from; + protected void check(){ VUtils.trueThrow(IS_SHUTDOWN).throwMessage(STATE.ServiceConnectRefused,"SSE服务已关闭"); } - protected static void shutdown(Map emitters) { + @PreDestroy + protected void shutdown() { IS_SHUTDOWN=true; log.warn("准备关闭SSE服务"); - emitters.forEach((k,v)->{ + map.values().stream().flatMap(List::stream).collect(Collectors.toList()).forEach(emitter->{ try { - v.send("因SSE服务关闭,连接即将断开"); + emitter.send("因SSE服务关闭,连接即将断开"); + emitter.complete(); }catch (Exception ex){ - log.error("SSE发送消息失败:"+k,ex); + log.error("SSE发送消息失败",ex); + emitter.completeWithError(ex); } - v.complete(); }); log.warn("SSE服务已关闭"); } - protected static void close(SseEmitter emitter){ - emitter.complete(); - } - - protected SseEmitter connect(Integer userId, Map emitters) { + protected SseEmitter connect(Integer ticketId,Integer userId) { + check(); + log.info(from+"SSE连接:工单id:"+ticketId+",用户id:"+userId); SseEmitter emitter = new SseEmitter(Long.MAX_VALUE); - SseEmitter old=emitters.put(userId, emitter); - if (Objects.nonNull(old)){ - log.warn("停止旧连接:"+userId); - try { - old.send(SseEmitter.event().name("被踢下线").data("你已在其他地方连接")); - old.complete(); - } catch (Exception e) { - old.completeWithError(e); - } - } + List emitters=map.getOrDefault(userId, Collections.synchronizedList(new ArrayList<>())); + emitters.add(emitter); + ScheduledFuture heartbeatTask = startHeartbeat(emitter); emitter.onError((ex) -> { - emitters.remove(userId); - emitter.complete(); + remove(userId, emitters, emitter,heartbeatTask); log.error("SSE异常:"+userId, ex); }); emitter.onTimeout(() -> { - emitters.remove(userId); - emitter.complete(); + remove(userId, emitters, emitter,heartbeatTask); log.error("SSE超时:"+userId); }); emitter.onCompletion(() -> { - emitters.remove(userId); - emitter.complete(); + remove(userId, emitters, emitter,heartbeatTask); log.error("SSE完成:"+userId); }); try { - emitter.send(SseEmitter.event().data("已连接").reconnectTime(5000)); + emitter.send(SseEmitter.event().comment("已连接").reconnectTime(5000)); } catch (IOException e) { log.error("sse发送数据出错", e); + emitter.completeWithError(e); } return emitter; } + + protected void send(Integer ticketId, ChatMessageVO message) throws IOException { + log.info(StrUtil.format(from+"SSE发送消息,工单id: {},内容: {}", ticketId, message)); + List emitters = map.get(ticketId); + VUtils.trueThrowBusinessError(Objects.isNull(emitters)).throwMessage("没有用户连接工单:"+ticketId); + emitters.forEach(emitter-> { + try { + emitter.send(SseEmitter.event().name("ticketMessage").data(message)); + } catch (IOException e) { + log.error("sse发送数据出错", e); + emitter.completeWithError(e); + } + }); + } + + private void remove(Integer userId,List emitters,SseEmitter emitter,ScheduledFuture heartbeatTask){ + heartbeatTask.cancel(false); + emitters.remove(emitter); + if (CollectionUtil.isEmpty(emitters)){ + map.remove(userId); + } + emitter.complete(); + } + + private ScheduledFuture startHeartbeat(SseEmitter emitter) { + return taskScheduler.scheduleAtFixedRate(() -> { + try { + emitter.send(SseEmitter.event().comment("ping")); + } catch (IOException e) { + emitter.completeWithError(e); + } + }, 60_000); + } } \ No newline at end of file diff --git a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/SSEManagerService.java b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/SSEManagerService.java index 905dff94..99928fea 100644 --- a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/SSEManagerService.java +++ b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/SSEManagerService.java @@ -1,20 +1,13 @@ package com.nflg.mobilebroken.push.service; -import com.nflg.mobilebroken.common.pojo.dto.SSEMessageDTO; +import com.nflg.mobilebroken.common.pojo.vo.ChatMessageVO; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; -import java.util.Collection; public interface SSEManagerService { - SseEmitter connect(Integer userId); + SseEmitter connect(Integer ticketId,Integer userId); - void send(Integer userId, SSEMessageDTO message) throws IOException; - - void close(Integer userId); - - void shutdown(); - - Collection getUserIds(); + void send(Integer ticketId, ChatMessageVO message) throws IOException; } diff --git a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/impl/APPSSEManagerService.java b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/impl/APPSSEManagerService.java index d9c8ad91..43aeaf16 100644 --- a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/impl/APPSSEManagerService.java +++ b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/impl/APPSSEManagerService.java @@ -1,68 +1,31 @@ package com.nflg.mobilebroken.push.service.impl; -import cn.hutool.core.util.StrUtil; -import com.nflg.mobilebroken.common.pojo.dto.SSEMessageDTO; -import com.nflg.mobilebroken.common.util.VUtils; +import com.nflg.mobilebroken.common.pojo.vo.ChatMessageVO; import com.nflg.mobilebroken.push.service.SSEManagerBase; import com.nflg.mobilebroken.push.service.SSEManagerService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import javax.annotation.PreDestroy; +import javax.annotation.PostConstruct; import java.io.IOException; -import java.util.Collection; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; @Service @Slf4j public class APPSSEManagerService extends SSEManagerBase implements SSEManagerService { - public static final Map EMITTERS = new ConcurrentHashMap<>(); - - @Override - public SseEmitter connect(Integer userId) { - check(); - log.info("APP端SSE已连接:"+userId); - return connect(userId,EMITTERS); + @PostConstruct + public void init() { + from="APP端"; } @Override - public void send(Integer userId, SSEMessageDTO message) throws IOException { - log.info(StrUtil.format("APP端SSE发送消息,用户id: {},内容: {}", userId, message)); - SseEmitter emitter = EMITTERS.get(userId); - VUtils.trueThrowBusinessError(Objects.isNull(emitter)).throwMessage("用户未连接:"+userId); - emitter.send(message); + public SseEmitter connect(Integer ticketId,Integer userId) { + return super.connect(ticketId,userId); } @Override - public void close(Integer userId) { - log.info("APP端SSE连接主动关闭:"+userId); - close(EMITTERS.remove(userId)); - } - - @Override - public void shutdown() { - shutdown(EMITTERS); - } - - @Override - public Collection getUserIds() { - return EMITTERS.keySet(); - } - - @PreDestroy - public void cleanup() { - log.info("释放SSE连接"); - for (SseEmitter emitter : EMITTERS.values()) { - try { - emitter.complete(); - } catch (Exception e) { - emitter.completeWithError(e); - } - } - EMITTERS.clear(); + public void send(Integer ticketId, ChatMessageVO message) throws IOException { + super.send(ticketId, message); } } \ No newline at end of file diff --git a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/impl/AdminSSEManagerService.java b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/impl/AdminSSEManagerService.java index 33da938a..5b2eddd1 100644 --- a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/impl/AdminSSEManagerService.java +++ b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/service/impl/AdminSSEManagerService.java @@ -1,68 +1,31 @@ package com.nflg.mobilebroken.push.service.impl; -import cn.hutool.core.util.StrUtil; -import com.nflg.mobilebroken.common.pojo.dto.SSEMessageDTO; -import com.nflg.mobilebroken.common.util.VUtils; +import com.nflg.mobilebroken.common.pojo.vo.ChatMessageVO; import com.nflg.mobilebroken.push.service.SSEManagerBase; import com.nflg.mobilebroken.push.service.SSEManagerService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import javax.annotation.PreDestroy; +import javax.annotation.PostConstruct; import java.io.IOException; -import java.util.Collection; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; @Service @Slf4j public class AdminSSEManagerService extends SSEManagerBase implements SSEManagerService { - public static final Map EMITTERS = new ConcurrentHashMap<>(); - - @Override - public SseEmitter connect(Integer userId) { - check(); - log.info("管理端SSE已连接:"+userId); - return connect(userId,EMITTERS); + @PostConstruct + public void init() { + from="管理端"; } @Override - public void send(Integer userId, SSEMessageDTO message) throws IOException { - log.info(StrUtil.format("管理端SSE发送消息,用户id: {},内容: {}", userId, message)); - SseEmitter emitter = EMITTERS.get(userId); - VUtils.trueThrowBusinessError(Objects.isNull(emitter)).throwMessage("用户未连接:"+userId); - emitter.send(message); + public SseEmitter connect(Integer ticketId,Integer userId) { + return super.connect(ticketId,userId); } @Override - public void close(Integer userId) { - close(EMITTERS.remove(userId)); - log.info("管理端SSE连接主动关闭:"+userId); - } - - @Override - public void shutdown() { - shutdown(EMITTERS); - } - - @Override - public Collection getUserIds() { - return EMITTERS.keySet(); - } - - @PreDestroy - public void cleanup() { - log.info("释放SSE连接"); - for (SseEmitter emitter : EMITTERS.values()) { - try { - emitter.complete(); - } catch (Exception e) { - emitter.completeWithError(e); - } - } - EMITTERS.clear(); + public void send(Integer ticketId, ChatMessageVO message) throws IOException { + super.send(ticketId, message); } } \ No newline at end of file diff --git a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/task/SSEScheduledTasks.java b/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/task/SSEScheduledTasks.java deleted file mode 100644 index ee6f4992..00000000 --- a/nflg-mobilebroken-push/src/main/java/com/nflg/mobilebroken/push/task/SSEScheduledTasks.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.nflg.mobilebroken.push.task; - -import com.nflg.mobilebroken.push.service.impl.APPSSEManagerService; -import com.nflg.mobilebroken.push.service.impl.AdminSSEManagerService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -import java.util.Iterator; -import java.util.Map; - -@Component -@Slf4j -public class SSEScheduledTasks { - - /** - * 发送SSE心跳消息 - * 每分钟执行一次 - */ - @Scheduled(fixedDelay=60000) - public void sendHeart() { - log.info("发送SSE心跳消息开始"); - send(APPSSEManagerService.EMITTERS.entrySet().iterator()); - send(AdminSSEManagerService.EMITTERS.entrySet().iterator()); - log.info("发送SSE心跳消息结束"); - } - - private void send(Iterator iterator) { - while (iterator.hasNext()) { - SseEmitter emitter = ((Map.Entry) iterator.next()).getValue(); - try { - emitter.send(SseEmitter.event().data("心跳")); - } catch (Exception e) { - log.error("发送心跳消息失败", e); - emitter.complete(); - iterator.remove(); - } - } - } -} diff --git a/nflg-mobilebroken-push/src/main/resources/application.properties b/nflg-mobilebroken-push/src/main/resources/application.properties index 5cc7c6e0..d84da407 100644 --- a/nflg-mobilebroken-push/src/main/resources/application.properties +++ b/nflg-mobilebroken-push/src/main/resources/application.properties @@ -2,6 +2,9 @@ spring.application.name=push spring.profiles.active=dev server.port=8084 +server.tomcat.threads.max=1000 +server.tomcat.max-connections=1000 + # sa-token?? sa-token.is-log=true sa-token.token-name=authorization diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/mapper/AdminUserMapper.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/mapper/AdminUserMapper.java index 22d8a283..82180517 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/mapper/AdminUserMapper.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/mapper/AdminUserMapper.java @@ -19,4 +19,6 @@ public interface AdminUserMapper extends BaseMapper { List getByRoleCode(String roleCode); List getSimples(List userIds); + + List getTickerMangagers(List titleCodes); } diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAdminUserService.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAdminUserService.java index 2a459a3e..a9ad7f62 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAdminUserService.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAdminUserService.java @@ -50,4 +50,6 @@ public interface IAdminUserService extends IService { List getSimples(List userIds); void deleteAccount(Integer id); + + List getTickerMangagers(); } diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/AdminUserServiceImpl.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/AdminUserServiceImpl.java index eee1e6fb..c7478836 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/AdminUserServiceImpl.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/AdminUserServiceImpl.java @@ -301,6 +301,11 @@ public class AdminUserServiceImpl extends ServiceImpl getTickerMangagers() { + return baseMapper.getTickerMangagers(Constant.ROLE_CODE_TICKET_MANAGERS); + } + private String getDepartmentName(Long departmentId) { TBaseDepartment department = departmentService.lambdaQuery() .eq(TBaseDepartment::getId, departmentId) diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/TicketServiceImpl.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/TicketServiceImpl.java index 1cf0c819..b2c84c11 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/TicketServiceImpl.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/TicketServiceImpl.java @@ -113,9 +113,9 @@ public class TicketServiceImpl extends ServiceImpl impleme Ticket ticket = getById(request.getTicketId()); VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("未找到工单"); VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.PendingProcessing.getState())).throwMessage("工单状态异常"); - VUtils.trueThrowBusinessError(adminUserService.getCQM().stream() - .noneMatch(u -> Objects.equals(u.getId(), AdminUserUtil.getUserId()))) - .throwMessage("你不是CQM,无权分派工单"); + List tickerMangagers = adminUserService.getTickerMangagers(); + VUtils.trueThrowBusinessError(tickerMangagers.stream().noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId()))) + .throwMessage("你无权分派工单"); ticket.setUrgency(TicketUrgency.findByValue(request.getUrgency()).getState()); ticket.setQuestion(request.getQuestion()); ticket.setState(TicketState.Processing.getState()); @@ -166,9 +166,10 @@ public class TicketServiceImpl extends ServiceImpl impleme Ticket ticket=getById(id); VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState())) .throwMessage("工单状态不允许完成"); - VUtils.trueThrowBusinessError(Arrays.stream(ticket.getHandle().split(",")) - .noneMatch(uid->StrUtil.equals(uid, AdminUserUtil.getUserId().toString()))) - .throwMessage("你无权操作该工单"); + List tickerMangagers = adminUserService.getTickerMangagers(); + tickerMangagers.addAll(Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).collect(Collectors.toList())); + VUtils.trueThrowBusinessError(tickerMangagers.stream().noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId()))) + .throwMessage("你无权添加处理人"); ticket.setState(TicketState.ProcessingCompleted.getState()); ticket.setCurrentHandle(AdminUserUtil.getUserId()); ticket.setUpdateTime(LocalDateTime.now()); @@ -186,9 +187,10 @@ public class TicketServiceImpl extends ServiceImpl impleme .eq(TicketEvaluate::getTicketId, request.getTicketId()) .exists()) .throwMessage("工单尚未评价,不能关闭"); - VUtils.trueThrowBusinessError(adminUserService.getCQM().stream() - .noneMatch(u -> Objects.equals(u.getId(), AdminUserUtil.getUserId()))) - .throwMessage("你不是CQM,无权关闭工单"); + List tickerMangagers = adminUserService.getTickerMangagers(); + tickerMangagers.addAll(Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).collect(Collectors.toList())); + VUtils.trueThrowBusinessError(tickerMangagers.stream().noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId()))) + .throwMessage("你无权关闭工单"); ticket.setState(TicketState.Closed.getState()); ticket.setSolution(request.getSolution()); ticket.setSolutionAttachments(StrUtil.join(",", request.getAttachments())); @@ -268,13 +270,12 @@ public class TicketServiceImpl extends ServiceImpl impleme Ticket ticket = getById(request.getTicketId()); VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("未找到工单"); VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState())).throwMessage("工单状态异常"); - VUtils.trueThrowBusinessError(!Objects.equals(ticket.getCqm(), AdminUserUtil.getUserId()) - && Arrays.stream(ticket.getHandle().split(",")) - .map(Integer::parseInt) - .noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId()))) + List tickerMangagers = adminUserService.getTickerMangagers(); + tickerMangagers.addAll(Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).collect(Collectors.toList())); + VUtils.trueThrowBusinessError(tickerMangagers.stream().noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId()))) .throwMessage("你无权添加处理人"); - ticket.setHandle(ticket.getHandle().concat(",").concat(StrUtil.join(",", request.getUserIds()))); - ticket.setHandleName(ticket.getHandleName().concat(",").concat(StrUtil.join(",", adminUserService.listByIds(request.getUserIds()).stream().map(AdminUser::getUserName).collect(Collectors.toList())))); + ticket.setHandle(StrUtil.join(",", request.getUserIds())); + ticket.setHandleName(StrUtil.join(",", adminUserService.listByIds(request.getUserIds()).stream().map(AdminUser::getUserName).collect(Collectors.toList()))); ticket.setUpdateTime(LocalDateTime.now()); updateById(ticket); return ticket; diff --git a/nflg-mobilebroken-repository/src/main/resources/mapper/AdminUserMapper.xml b/nflg-mobilebroken-repository/src/main/resources/mapper/AdminUserMapper.xml index 812d2367..6015b350 100644 --- a/nflg-mobilebroken-repository/src/main/resources/mapper/AdminUserMapper.xml +++ b/nflg-mobilebroken-repository/src/main/resources/mapper/AdminUserMapper.xml @@ -19,4 +19,14 @@ #{userId} + + diff --git a/nflg-mobilebroken-repository/src/main/resources/mapper/TicketMapper.xml b/nflg-mobilebroken-repository/src/main/resources/mapper/TicketMapper.xml index ba0372d0..fd58df07 100644 --- a/nflg-mobilebroken-repository/src/main/resources/mapper/TicketMapper.xml +++ b/nflg-mobilebroken-repository/src/main/resources/mapper/TicketMapper.xml @@ -93,7 +93,7 @@ LEFT JOIN app_user u ON t.user_id=u.id LEFT JOIN t_base_area a1 ON u.area_id=a1.id LEFT JOIN app_area a2 ON u.area_id=a2.id - LEFT JOIN ticket_follow tf ON t.id=tf.ticket_id AND tf.from=0 + INNER JOIN ticket_follow tf ON t.id=tf.ticket_id AND tf.from=0 WHERE tf.user_id=#{userId} AND t.state!=4 ORDER BY t.id DESC @@ -106,7 +106,7 @@ LEFT JOIN app_user u ON t.user_id=u.id LEFT JOIN t_base_area a1 ON u.area_id=a1.id LEFT JOIN app_area a2 ON u.area_id=a2.id - LEFT JOIN ticket_follow tf ON t.id=tf.ticket_id AND tf.from=0 + LEFT JOIN ticket_follow tf ON t.id=tf.ticket_id AND tf.user_id=#{userId} AND tf.from=0 WHERE t.state!=4 AND u.company_id IN #{companyId}