feat: 产品中心

This commit is contained in:
曹鹏飞 2025-05-26 11:33:10 +08:00
parent 1c3554a35e
commit 14535f8906
26 changed files with 543 additions and 187 deletions

View File

@ -27,6 +27,7 @@ import com.nflg.mobilebroken.repository.service.ILanguageService;
import com.nflg.mobilebroken.repository.service.IWebComponentService; import com.nflg.mobilebroken.repository.service.IWebComponentService;
import com.nflg.mobilebroken.repository.service.IWebComponentTranslateService; import com.nflg.mobilebroken.repository.service.IWebComponentTranslateService;
import com.nflg.mobilebroken.starter.annotation.MethodInfoMark; import com.nflg.mobilebroken.starter.annotation.MethodInfoMark;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -55,6 +56,7 @@ import java.util.stream.Collectors;
* *
* @author 曹鹏飞 * @author 曹鹏飞
*/ */
@Slf4j
@RestController @RestController
@RequestMapping("/multilingual") @RequestMapping("/multilingual")
public class MultilingualController extends ControllerBase { public class MultilingualController extends ControllerBase {
@ -204,8 +206,15 @@ public class MultilingualController extends ControllerBase {
List<Map<String, Object>> datas = EecExcelUtil.readTo(file.getInputStream()); List<Map<String, Object>> datas = EecExcelUtil.readTo(file.getInputStream());
List<Language> languages = languageService.getLanguages(); List<Language> languages = languageService.getLanguages();
datas.forEach(d -> { datas.forEach(d -> {
String moduleCode = (String) d.get("模块编号");
String pageCode = (String) d.get("页面编号");
String componentCode = (String) d.get("组件编号"); String componentCode = (String) d.get("组件编号");
WebComponent webComponent = webComponentService.lambdaQuery().eq(WebComponent::getComponentCode, componentCode).one(); log.info("模块编号:{},页面编号:{},组件编号:{}",moduleCode,pageCode,componentCode);
WebComponent webComponent = webComponentService.lambdaQuery()
.eq(WebComponent::getModuleCode, moduleCode)
.eq(WebComponent::getPageCode, pageCode)
.eq(WebComponent::getComponentCode, componentCode)
.one();
if (Objects.nonNull(webComponent)) { if (Objects.nonNull(webComponent)) {
d.remove("模块"); d.remove("模块");
d.remove("模块编号"); d.remove("模块编号");

View File

@ -1,7 +1,9 @@
package com.nflg.mobilebroken.admin.controller; package com.nflg.mobilebroken.admin.controller;
import com.nflg.mobilebroken.admin.service.DeviceQRCodeService; import com.nflg.mobilebroken.admin.service.DeviceQRCodeService;
import com.nflg.mobilebroken.common.pojo.ApiResult;
import com.nflg.mobilebroken.common.util.VUtils; import com.nflg.mobilebroken.common.util.VUtils;
import com.nflg.mobilebroken.repository.service.ITicketCallService;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@ -21,6 +23,9 @@ public class TestController extends ControllerBase{
@Resource @Resource
private DeviceQRCodeService deviceQRCodeService; private DeviceQRCodeService deviceQRCodeService;
@Resource
private ITicketCallService ticketCallService;
/** /**
* 显示二维码 * 显示二维码
* @param deviceNo 设备编号 * @param deviceNo 设备编号
@ -42,4 +47,9 @@ public class TestController extends ControllerBase{
VUtils.trueThrowBusinessError(true).throwMessage("生成二维码出错"); VUtils.trueThrowBusinessError(true).throwMessage("生成二维码出错");
} }
} }
@GetMapping("test")
public ApiResult<Boolean> test(@RequestParam Integer userId){
return ApiResult.success(ticketCallService.isInCall(userId));
}
} }

View File

@ -54,7 +54,6 @@ import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -131,11 +130,11 @@ public class TicketController extends ControllerBase {
@Resource @Resource
private UniPushService uniPushService; private UniPushService uniPushService;
// @Resource @Resource
// private ITicketCallService ticketCallService; private ITicketCallService ticketCallService;
//
// @Resource @Resource
// private ITicketCallJoinService ticketCallJoinService; private ITicketCallJoinService ticketCallJoinService;
/** /**
* 获取问题类型 * 获取问题类型
@ -1235,7 +1234,7 @@ public class TicketController extends ControllerBase {
Integer handlerUserId = Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).findFirst().get(); Integer handlerUserId = Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).findFirst().get();
VUtils.trueThrowBusinessError(!Objects.equals(AdminUserUtil.getUserId(), handlerUserId)) VUtils.trueThrowBusinessError(!Objects.equals(AdminUserUtil.getUserId(), handlerUserId))
.throwMessage("不是工单主负责人无权限呼叫"); .throwMessage("不是工单主负责人无权限呼叫");
// VUtils.trueThrowBusinessError(ticketCallService.isInCall(ticket.getUserId())).throwMessage("对方正在通话中"); VUtils.trueThrowBusinessError(ticketCallService.isInCall(ticket.getUserId())).throwMessage("对方正在通话中");
AdminUser adminUser = adminUserService.getById(handlerUserId); AdminUser adminUser = adminUserService.getById(handlerUserId);
uniPushService.send(new UniPushMessage() uniPushService.send(new UniPushMessage()
.setSenderId("admin-uid-" + handlerUserId) .setSenderId("admin-uid-" + handlerUserId)
@ -1248,13 +1247,14 @@ public class TicketController extends ControllerBase {
.setUserId(adminUser.getId()) .setUserId(adminUser.getId())
.setUserName(adminUser.getUserName()) .setUserName(adminUser.getUserName())
.setUserAvatar(adminUser.getAvatar()) .setUserAvatar(adminUser.getAvatar())
.setCategory("call") .setCategory("ticketCall")
.setFrom("admin") .setFrom("admin")
) )
) )
); );
ssePushService.sendTicketCallToApp(adminUser, ticket.getUserId(), ticketId); ssePushService.sendTicketCallToApp(adminUser, ticket.getUserId(), ticketId);
// ticketCallService.add(ticketId, handlerUserId, ticket.getUserId(), Constant.FROM_ADMIN); ticketCallService.add(ticketId, handlerUserId, ticket.getUserId(), Constant.FROM_ADMIN);
ticketEventPublisher.publishTicketCallBeginEvent(ticketId, adminUser.getUserName());
return ApiResult.success(); return ApiResult.success();
} }
@ -1266,34 +1266,32 @@ public class TicketController extends ControllerBase {
@PostMapping("addCallUser") @PostMapping("addCallUser")
public ApiResult<Void> addCallUser(@Valid @RequestBody CallUserAddRequest request){ public ApiResult<Void> addCallUser(@Valid @RequestBody CallUserAddRequest request){
AdminUser adminUser = adminUserService.getById(AdminUserUtil.getUserId()); AdminUser adminUser = adminUserService.getById(AdminUserUtil.getUserId());
// TicketCall ticketCall = ticketCallService.lambdaQuery() TicketCall ticketCall = ticketCallService.lambdaQuery()
// .eq(TicketCall::getTicketId, request.getTicketId()) .eq(TicketCall::getTicketId, request.getTicketId())
// .eq(TicketCall::getState, 1) .eq(TicketCall::getState, 1)
// .one(); .one();
// VUtils.trueThrowBusinessError(Objects.isNull(ticketCall)).throwMessage("未在通话中"); VUtils.trueThrowBusinessError(Objects.isNull(ticketCall)).throwMessage("未在通话中");
request.getUserIds().forEach(userId->{ request.getUserIds().forEach(userId->{
AdminUser adminUser1 = adminUserService.getById(userId); AdminUser adminUser1 = adminUserService.getById(userId);
if (Objects.nonNull(adminUser1)) { if (Objects.nonNull(adminUser1)) {
CompletableFuture.runAsync(()->{ uniPushService.send(new UniPushMessage()
uniPushService.send(new UniPushMessage() .setSenderId("admin-uid-" + adminUser.getId())
.setSenderId("admin-uid-" + adminUser.getId()) .setReceiverId("admin-uid-" + userId)
.setReceiverId("admin-uid-" + userId) .setSendData(new UniPushMessageBody()
.setSendData(new UniPushMessageBody() .setTitle("视频通话")
.setTitle("视频通话") .setContent(adminUser.getUserName() + "请求与您视频通话")
.setContent(adminUser.getUserName() + "请求与您视频通话") .setPayload(new UniPushMessageCallPayload()
.setPayload(new UniPushMessageCallPayload() .setTicketId(request.getTicketId())
.setTicketId(request.getTicketId()) .setUserId(adminUser.getId())
.setUserId(adminUser.getId()) .setUserName(adminUser.getUserName())
.setUserName(adminUser.getUserName()) .setUserAvatar(adminUser.getAvatar())
.setUserAvatar(adminUser.getAvatar()) .setCategory("ticketCall")
.setCategory("call") .setFrom("admin")
.setFrom("admin") )
) )
) );
); ssePushService.sendTicketCallToAdmin(adminUser, userId, request.getTicketId());
ssePushService.sendTicketCallToAdmin(adminUser, userId, request.getTicketId()); ticketCallJoinService.add(ticketCall.getId(), userId, Constant.FROM_ADMIN);
// ticketCallJoinService.add(ticketCall.getId(), userId, Constant.FROM_ADMIN);
});
} }
}); });
return ApiResult.success(); return ApiResult.success();
@ -1309,9 +1307,9 @@ public class TicketController extends ControllerBase {
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在"); VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState())) VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState()))
.throwMessage("当前工单状态不允许请求通话"); .throwMessage("当前工单状态不允许请求通话");
// VUtils.trueThrowBusinessError(ticketCallService.isInCall(AdminUserUtil.getUserId())) VUtils.trueThrowBusinessError(ticketCallService.isInCall(AdminUserUtil.getUserId()))
// .throwMessage("您已加入别的通话中"); .throwMessage("您已加入别的通话中");
// ticketCallJoinService.join(ticketId, AdminUserUtil.getUserId(),Constant.FROM_ADMIN); ticketCallJoinService.join(ticketId, AdminUserUtil.getUserId(),Constant.FROM_ADMIN);
return ApiResult.success(); return ApiResult.success();
} }
@ -1321,10 +1319,11 @@ public class TicketController extends ControllerBase {
*/ */
@PostMapping("call/hangUp") @PostMapping("call/hangUp")
public ApiResult<Void> hangUp(@Valid @RequestBody TicketCallHangUpRequest request) { public ApiResult<Void> hangUp(@Valid @RequestBody TicketCallHangUpRequest request) {
// TicketCall ticketCall = ticketCallService.lambdaQuery() TicketCall ticketCall = ticketCallService.lambdaQuery()
// .eq(TicketCall::getTicketId, request.getTicketId()) .eq(TicketCall::getTicketId, request.getTicketId())
// .ne(TicketCall::getState, 2) .ne(TicketCall::getState, 2)
// .one(); .one();
boolean flag=false;
if (request.getReject()) { if (request.getReject()) {
AdminUser adminUser = adminUserService.getById(AdminUserUtil.getUserId()); AdminUser adminUser = adminUserService.getById(AdminUserUtil.getUserId());
if (StrUtil.equals(request.getFrom(), "app")) { if (StrUtil.equals(request.getFrom(), "app")) {
@ -1339,39 +1338,65 @@ public class TicketController extends ControllerBase {
.setUserId(adminUser.getId()) .setUserId(adminUser.getId())
.setUserName(adminUser.getUserName()) .setUserName(adminUser.getUserName())
.setUserAvatar(adminUser.getAvatar()) .setUserAvatar(adminUser.getAvatar())
.setCategory("callHangUp") .setCategory("ticketCallHangUp")
.setFrom("admin") .setFrom("admin")
) )
) )
); );
ssePushService.sendTicketCallHangUpToApp(request.getTicketId(), request.getFromUserId(), adminUser); ssePushService.sendTicketCallHangUpToApp(request.getTicketId(), request.getFromUserId(), adminUser);
// ticketCallService.hangUp(request.getTicketId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, true); flag=ticketCallJoinService.hangUp(request.getTicketId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, true);
}else if (StrUtil.equals(request.getFrom(), "admin")) { }else if (StrUtil.equals(request.getFrom(), "admin")) {
uniPushService.send(new UniPushMessage() if (Objects.equals(request.getFromUserId(), AdminUserUtil.getUserId())){
.setSenderId("admin-uid-" + adminUser.getId()) Ticket ticket = ticketService.getById(request.getTicketId());
.setReceiverId("admin-uid-" + request.getFromUserId()) int handlerId= Integer.parseInt(StrUtil.split(ticket.getHandle(), ",").stream().findFirst().get());
.setSendData(new UniPushMessageBody() uniPushService.send(new UniPushMessage()
.setTitle("拒绝视频通话") .setSenderId("admin-uid-" + adminUser.getId())
.setContent(adminUser.getUserName() + "拒绝与您视频通话") .setReceiverId("admin-uid-" + handlerId)
.setPayload(new UniPushMessageCallPayload() .setSendData(new UniPushMessageBody()
.setTicketId(request.getTicketId()) .setTitle("挂断视频通话")
.setUserId(adminUser.getId()) .setContent(adminUser.getUserName() + "挂断了与您的视频通话")
.setUserName(adminUser.getUserName()) .setPayload(new UniPushMessageCallPayload()
.setUserAvatar(adminUser.getAvatar()) .setTicketId(request.getTicketId())
.setCategory("callHangUp") .setUserId(adminUser.getId())
.setFrom("admin") .setUserName(adminUser.getUserName())
) .setUserAvatar(adminUser.getAvatar())
) .setCategory("ticketCallCancel")
); .setFrom("admin")
ssePushService.sendTicketCallHangUpToAdmin(request.getTicketId(), request.getFromUserId(), adminUser); )
// ticketCallJoinService.hangUp(ticketCall.getId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, true); )
);
ssePushService.sendTicketCallCancelToAdmin(request.getTicketId(), handlerId, adminUser);
flag=ticketCallService.hangUp(request.getTicketId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, true);
}else {
uniPushService.send(new UniPushMessage()
.setSenderId("admin-uid-" + adminUser.getId())
.setReceiverId("admin-uid-" + request.getFromUserId())
.setSendData(new UniPushMessageBody()
.setTitle("拒绝视频通话")
.setContent(adminUser.getUserName() + "拒绝与您视频通话")
.setPayload(new UniPushMessageCallPayload()
.setTicketId(request.getTicketId())
.setUserId(adminUser.getId())
.setUserName(adminUser.getUserName())
.setUserAvatar(adminUser.getAvatar())
.setCategory("ticketCallHangUp")
.setFrom("admin")
)
)
);
ssePushService.sendTicketCallHangUpToAdmin(request.getTicketId(), request.getFromUserId(), adminUser);
flag=ticketCallJoinService.hangUp(ticketCall.getId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, true);
}
} }
}else { }else {
// if (StrUtil.equals(request.getFrom(), "app")) { if (StrUtil.equals(request.getFrom(), "app") || Objects.equals(request.getFromUserId(), AdminUserUtil.getUserId())) {
// ticketCallService.hangUp(request.getTicketId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, false); flag=ticketCallService.hangUp(request.getTicketId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, false);
// }else { }else {
// ticketCallJoinService.hangUp(ticketCall.getId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, false); flag=ticketCallJoinService.hangUp(ticketCall.getId(), AdminUserUtil.getUserId(), Constant.FROM_ADMIN, false);
// } }
}
if (flag){
ticketEventPublisher.publishTicketCallEndEvent(request.getTicketId());
} }
return ApiResult.success(); return ApiResult.success();
} }

View File

@ -0,0 +1,48 @@
package com.nflg.mobilebroken.admin.event;
import com.nflg.mobilebroken.admin.service.SsePushService;
import com.nflg.mobilebroken.common.constant.TicketState;
import com.nflg.mobilebroken.common.pojo.dto.ChatMessageDTO;
import com.nflg.mobilebroken.repository.service.TicketChatService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import java.time.Instant;
public class TicketCallBeginEvent extends ApplicationEvent implements ApplicationContextAware {
private final Integer ticketId;
private final String userName;
private SsePushService ssePushService;
private TicketChatService ticketChatService;
public TicketCallBeginEvent(Object source, Integer ticketId, String userName) {
super(source);
this.ticketId = ticketId;
this.userName = userName;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ssePushService = applicationContext.getBean(SsePushService.class);
this.ticketChatService = applicationContext.getBean(TicketChatService.class);
}
public void send(){
ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("call")
.setTicketState(TicketState.Processing.getState())
.setSenderId(0)
.setSenderName("通话助手")
.setContent(userName+"发起了视频通话")
.setCreateTime(Instant.now());
ticketChatService.addMessage(ticketId, message);
//推送消息
ssePushService.sendTicketMessageToAdmin(ticketId,message);
ssePushService.sendTicketMessageToApp(ticketId,message);
}
}

View File

@ -0,0 +1,46 @@
package com.nflg.mobilebroken.admin.event;
import com.nflg.mobilebroken.admin.service.SsePushService;
import com.nflg.mobilebroken.common.constant.TicketState;
import com.nflg.mobilebroken.common.pojo.dto.ChatMessageDTO;
import com.nflg.mobilebroken.repository.service.TicketChatService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import java.time.Instant;
public class TicketCallEndEvent extends ApplicationEvent implements ApplicationContextAware {
private final Integer ticketId;
private SsePushService ssePushService;
private TicketChatService ticketChatService;
public TicketCallEndEvent(Object source, Integer ticketId) {
super(source);
this.ticketId = ticketId;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ssePushService = applicationContext.getBean(SsePushService.class);
this.ticketChatService = applicationContext.getBean(TicketChatService.class);
}
public void send(){
ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("call")
.setTicketState(TicketState.Processing.getState())
.setSenderId(0)
.setSenderName("通话助手")
.setContent("视频通话已结束")
.setCreateTime(Instant.now());
ticketChatService.addMessage(ticketId, message);
//推送消息
ssePushService.sendTicketMessageToAdmin(ticketId,message);
ssePushService.sendTicketMessageToApp(ticketId,message);
}
}

View File

@ -1,9 +1,6 @@
package com.nflg.mobilebroken.admin.listener; package com.nflg.mobilebroken.admin.listener;
import com.nflg.mobilebroken.admin.event.TicketAssignedEvent; import com.nflg.mobilebroken.admin.event.*;
import com.nflg.mobilebroken.admin.event.TicketCloseEvent;
import com.nflg.mobilebroken.admin.event.TicketCompleteEvent;
import com.nflg.mobilebroken.admin.event.TicketReplyEvent;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -34,4 +31,16 @@ public class TicketEventListener {
public void handleTicketCloseEvent(TicketCloseEvent event) { public void handleTicketCloseEvent(TicketCloseEvent event) {
event.send(); event.send();
} }
@Async
@EventListener
public void handleTicketCallBeginEvent(TicketCallBeginEvent event) {
event.send();
}
@Async
@EventListener
public void handleTicketCallEndEvent(TicketCallEndEvent event) {
event.send();
}
} }

View File

@ -1,9 +1,6 @@
package com.nflg.mobilebroken.admin.publisher; package com.nflg.mobilebroken.admin.publisher;
import com.nflg.mobilebroken.admin.event.TicketAssignedEvent; import com.nflg.mobilebroken.admin.event.*;
import com.nflg.mobilebroken.admin.event.TicketCloseEvent;
import com.nflg.mobilebroken.admin.event.TicketCompleteEvent;
import com.nflg.mobilebroken.admin.event.TicketReplyEvent;
import com.nflg.mobilebroken.repository.entity.Ticket; import com.nflg.mobilebroken.repository.entity.Ticket;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
@ -44,4 +41,16 @@ public class TicketEventPublisher {
event.setApplicationContext(applicationContext); event.setApplicationContext(applicationContext);
eventPublisher.publishEvent(event); eventPublisher.publishEvent(event);
} }
public void publishTicketCallBeginEvent(Integer ticketId, String userName) {
TicketCallBeginEvent event = new TicketCallBeginEvent(this, ticketId,userName);
event.setApplicationContext(applicationContext);
eventPublisher.publishEvent(event);
}
public void publishTicketCallEndEvent(Integer ticketId) {
TicketCallEndEvent event = new TicketCallEndEvent(this, ticketId);
event.setApplicationContext(applicationContext);
eventPublisher.publishEvent(event);
}
} }

View File

@ -37,10 +37,11 @@ public class SsePushService {
TicketMessagePushRequest request=new TicketMessagePushRequest() TicketMessagePushRequest request=new TicketMessagePushRequest()
.setTicketId(ticketId) .setTicketId(ticketId)
.setMessage(buildMessage(ticketId,message)); .setMessage(buildMessage(ticketId,message));
log.debug("发送SSE消息{}", JSONUtil.toJsonStr(request));
ApiResult<?> result = sendMessage(request,"admin"); ApiResult<?> result = sendMessage(request,"admin");
log.debug("发送消息结果:{}", JSONUtil.toJsonStr(result)); log.debug("发送SSE消息结果:{}", JSONUtil.toJsonStr(result));
} catch (Exception e) { } catch (Exception e) {
log.error("发送消息出错", e); log.error("发送SSE消息出错", e);
} }
} }
@ -111,6 +112,7 @@ public class SsePushService {
.setType("ticketMessage") .setType("ticketMessage")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketMessage")
.setData(new ChatMessageVO() .setData(new ChatMessageVO()
.setId(message.getId()) .setId(message.getId())
.setFrom(message.getFrom()) .setFrom(message.getFrom())
@ -146,6 +148,7 @@ public class SsePushService {
.setType("ticketMessageWithdraw") .setType("ticketMessageWithdraw")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketMessageWithdraw")
.setData(messageId) .setData(messageId)
); );
} }
@ -158,6 +161,7 @@ public class SsePushService {
.setType("ticketCall") .setType("ticketCall")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketCall")
.setData(new UniPushMessageCallPayload() .setData(new UniPushMessageCallPayload()
.setTicketId(ticketId) .setTicketId(ticketId)
.setUserId(user.getId()) .setUserId(user.getId())
@ -182,6 +186,7 @@ public class SsePushService {
.setType("ticketCall") .setType("ticketCall")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketCall")
.setData(new UniPushMessageCallPayload() .setData(new UniPushMessageCallPayload()
.setTicketId(ticketId) .setTicketId(ticketId)
.setUserId(user.getId()) .setUserId(user.getId())
@ -211,6 +216,7 @@ public class SsePushService {
.setType("ticketCallHangUp") .setType("ticketCallHangUp")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketCallHangUp")
.setData(new UniPushMessageCallPayload() .setData(new UniPushMessageCallPayload()
.setTicketId(ticketId) .setTicketId(ticketId)
.setUserId(user.getId()) .setUserId(user.getId())
@ -235,6 +241,32 @@ public class SsePushService {
.setType("ticketCallHangUp") .setType("ticketCallHangUp")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketCallHangUp")
.setData(new UniPushMessageCallPayload()
.setTicketId(ticketId)
.setUserId(user.getId())
.setUserName(user.getUserName())
.setUserAvatar(user.getAvatar())
.setFrom("admin")
)
)
);
ApiResult<?> result = sendMessageByUser(request, "admin");
log.debug("发送消息结果:{}", JSONUtil.toJsonStr(result));
} catch (Exception e) {
log.error("发送消息出错", e);
}
}
public void sendTicketCallCancelToAdmin(@NotNull Integer ticketId, @NotNull Integer userId, AdminUser user) {
try {
PushUserMessageRequest request = new PushUserMessageRequest()
.setUserId(userId)
.setMessage(new PushMessageDTO()
.setType("ticketCallCancel")
.setData(new PushMessageDataBody()
.setTargetId(ticketId)
.setType("ticketCallCancel")
.setData(new UniPushMessageCallPayload() .setData(new UniPushMessageCallPayload()
.setTicketId(ticketId) .setTicketId(ticketId)
.setUserId(user.getId()) .setUserId(user.getId())

View File

@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.nflg.mobilebroken.cfs.publisher.TicketEventPublisher; import com.nflg.mobilebroken.cfs.publisher.TicketEventPublisher;
import com.nflg.mobilebroken.cfs.service.SsePushService; import com.nflg.mobilebroken.cfs.service.SsePushService;
import com.nflg.mobilebroken.common.constant.Constant;
import com.nflg.mobilebroken.common.constant.TicketState; import com.nflg.mobilebroken.common.constant.TicketState;
import com.nflg.mobilebroken.common.pojo.ApiResult; import com.nflg.mobilebroken.common.pojo.ApiResult;
import com.nflg.mobilebroken.common.pojo.PageData; import com.nflg.mobilebroken.common.pojo.PageData;
@ -92,11 +93,11 @@ public class TicketController extends ControllerBase {
@Resource @Resource
private UniPushService uniPushService; private UniPushService uniPushService;
// @Resource @Resource
// private ITicketCallService ticketCallService; private ITicketCallService ticketCallService;
//
// @Resource @Resource
// private ITicketCallJoinService ticketCallJoinService; private ITicketCallJoinService ticketCallJoinService;
/** /**
* 搜索设备 * 搜索设备
@ -489,7 +490,7 @@ public class TicketController extends ControllerBase {
VUtils.trueThrowBusinessError(!Objects.equals(AppUserUtil.getUserId(), ticket.getUserId())) VUtils.trueThrowBusinessError(!Objects.equals(AppUserUtil.getUserId(), ticket.getUserId()))
.throwMessage("不是工单创建人无权限呼叫"); .throwMessage("不是工单创建人无权限呼叫");
Integer handlerUserId = Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).findFirst().get(); Integer handlerUserId = Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).findFirst().get();
// VUtils.trueThrowBusinessError(ticketCallService.isInCall(handlerUserId)).throwMessage("对方正在通话中"); VUtils.trueThrowBusinessError(ticketCallService.isInCall(handlerUserId)).throwMessage("对方正在通话中");
AppUser appUser = appUserService.getById(ticket.getUserId()); AppUser appUser = appUserService.getById(ticket.getUserId());
uniPushService.send(new UniPushMessage() uniPushService.send(new UniPushMessage()
.setSenderId("app-uid-" + ticket.getUserId()) .setSenderId("app-uid-" + ticket.getUserId())
@ -502,13 +503,14 @@ public class TicketController extends ControllerBase {
.setUserId(appUser.getId()) .setUserId(appUser.getId())
.setUserName(appUser.getName()) .setUserName(appUser.getName())
.setUserAvatar(appUser.getAvatar()) .setUserAvatar(appUser.getAvatar())
.setCategory("call") .setCategory("ticketCall")
.setFrom("app") .setFrom("app")
) )
) )
); );
ssePushService.sendTicketCallToAdmin(appUser, handlerUserId, ticketId); ssePushService.sendTicketCallToAdmin(appUser, handlerUserId, ticketId);
// ticketCallService.add(ticketId, AppUserUtil.getUserId(), handlerUserId, Constant.FROM_APP); ticketCallService.add(ticketId, AppUserUtil.getUserId(), handlerUserId, Constant.FROM_APP);
ticketEventPublisher.publishTicketCallBeginEvent(ticketId,appUser.getName());
return ApiResult.success(); return ApiResult.success();
} }
@ -522,9 +524,9 @@ public class TicketController extends ControllerBase {
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在"); VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState())) VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState()))
.throwMessage("当前工单状态不允许通话"); .throwMessage("当前工单状态不允许通话");
// VUtils.trueThrowBusinessError(ticketCallService.isInCall(AppUserUtil.getUserId())) VUtils.trueThrowBusinessError(ticketCallService.isInCall(AppUserUtil.getUserId()))
// .throwMessage("您已加入别的通话中"); .throwMessage("您已加入别的通话中");
// ticketCallJoinService.join(ticketId, AppUserUtil.getUserId(),Constant.FROM_APP); ticketCallJoinService.join(ticketId, AppUserUtil.getUserId(),Constant.FROM_APP);
return ApiResult.success(); return ApiResult.success();
} }
@ -534,26 +536,54 @@ public class TicketController extends ControllerBase {
*/ */
@PostMapping("call/hangUp") @PostMapping("call/hangUp")
public ApiResult<Void> hangUp(@Valid @RequestBody TicketCallHangUpRequest request) { public ApiResult<Void> hangUp(@Valid @RequestBody TicketCallHangUpRequest request) {
boolean flag=false;
if (request.getReject()) { if (request.getReject()) {
AppUser appUser = appUserService.getById(AppUserUtil.getUserId()); if (StrUtil.equals(request.getFrom(), Constant.FROM_APP) && Objects.equals(AppUserUtil.getUserId(), request.getFromUserId())) {
uniPushService.send(new UniPushMessage() AppUser appUser = appUserService.getById(AppUserUtil.getUserId());
.setSenderId("app-uid-" + appUser.getId()) Ticket ticket = ticketService.getById(request.getTicketId());
.setReceiverId("admin-uid-" + request.getFromUserId()) int handlerId= Integer.parseInt(StrUtil.split(ticket.getHandle(), ",").stream().findFirst().get());
.setSendData(new UniPushMessageBody() uniPushService.send(new UniPushMessage()
.setTitle("拒绝视频通话") .setSenderId("app-uid-" + appUser.getId())
.setContent(appUser.getName() + "拒绝与您视频通话") .setReceiverId("admin-uid-" + handlerId)
.setPayload(new UniPushMessageCallPayload() .setSendData(new UniPushMessageBody()
.setTicketId(request.getTicketId()) .setTitle("挂断视频通话")
.setUserId(appUser.getId()) .setContent(appUser.getName() + "挂断了与您的视频通话")
.setUserName(appUser.getName()) .setPayload(new UniPushMessageCallPayload()
.setUserAvatar(appUser.getAvatar()) .setTicketId(request.getTicketId())
.setCategory("callHangUp") .setUserId(appUser.getId())
.setFrom("app") .setUserName(appUser.getName())
) .setUserAvatar(appUser.getAvatar())
) .setCategory("ticketCallCancel")
); .setFrom("app")
ssePushService.sendTicketCallHangUpToAdmin(request.getTicketId(), request.getFromUserId(), appUser); )
// ticketCallService.hangUp(request.getTicketId(), AppUserUtil.getUserId(), Constant.FROM_APP, true); )
);
ssePushService.sendTicketCallCancelToAdmin(request.getTicketId(), handlerId, appUser);
flag=ticketCallService.hangUp(request.getTicketId(), AppUserUtil.getUserId(), Constant.FROM_APP, true);
}else {
AppUser appUser = appUserService.getById(AppUserUtil.getUserId());
uniPushService.send(new UniPushMessage()
.setSenderId("app-uid-" + appUser.getId())
.setReceiverId("admin-uid-" + request.getFromUserId())
.setSendData(new UniPushMessageBody()
.setTitle("拒绝视频通话")
.setContent(appUser.getName() + "拒绝与您视频通话")
.setPayload(new UniPushMessageCallPayload()
.setTicketId(request.getTicketId())
.setUserId(appUser.getId())
.setUserName(appUser.getName())
.setUserAvatar(appUser.getAvatar())
.setCategory("ticketCallHangUp")
.setFrom("app")
)
)
);
ssePushService.sendTicketCallHangUpToAdmin(request.getTicketId(), request.getFromUserId(), appUser);
flag=ticketCallJoinService.hangUp(request.getTicketId(), AppUserUtil.getUserId(), Constant.FROM_APP, true);
}
}
if (flag){
ticketEventPublisher.publishTicketCallEndEvent(request.getTicketId());
} }
return ApiResult.success(); return ApiResult.success();
} }

View File

@ -0,0 +1,48 @@
package com.nflg.mobilebroken.cfs.event;
import com.nflg.mobilebroken.cfs.service.SsePushService;
import com.nflg.mobilebroken.common.constant.TicketState;
import com.nflg.mobilebroken.common.pojo.dto.ChatMessageDTO;
import com.nflg.mobilebroken.repository.service.TicketChatService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import java.time.Instant;
public class TicketCallBeginEvent extends ApplicationEvent implements ApplicationContextAware {
private final Integer ticketId;
private final String userName;
private SsePushService ssePushService;
private TicketChatService ticketChatService;
public TicketCallBeginEvent(Object source, Integer ticketId, String userName) {
super(source);
this.ticketId = ticketId;
this.userName = userName;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ssePushService = applicationContext.getBean(SsePushService.class);
this.ticketChatService = applicationContext.getBean(TicketChatService.class);
}
public void send(){
ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("call")
.setTicketState(TicketState.Processing.getState())
.setSenderId(0)
.setSenderName("通话助手")
.setContent(userName+"发起了视频通话")
.setCreateTime(Instant.now());
ticketChatService.addMessage(ticketId, message);
//推送消息
ssePushService.sendTicketMessageToAdmin(ticketId,message);
ssePushService.sendTicketMessageToApp(ticketId,message);
}
}

View File

@ -0,0 +1,46 @@
package com.nflg.mobilebroken.cfs.event;
import com.nflg.mobilebroken.cfs.service.SsePushService;
import com.nflg.mobilebroken.common.constant.TicketState;
import com.nflg.mobilebroken.common.pojo.dto.ChatMessageDTO;
import com.nflg.mobilebroken.repository.service.TicketChatService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import java.time.Instant;
public class TicketCallEndEvent extends ApplicationEvent implements ApplicationContextAware {
private final Integer ticketId;
private SsePushService ssePushService;
private TicketChatService ticketChatService;
public TicketCallEndEvent(Object source,Integer ticketId) {
super(source);
this.ticketId = ticketId;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ssePushService = applicationContext.getBean(SsePushService.class);
this.ticketChatService = applicationContext.getBean(TicketChatService.class);
}
public void send(){
ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("call")
.setTicketState(TicketState.Processing.getState())
.setSenderId(0)
.setSenderName("通话助手")
.setContent("视频通话已结束")
.setCreateTime(Instant.now());
ticketChatService.addMessage(ticketId, message);
//推送消息
ssePushService.sendTicketMessageToAdmin(ticketId,message);
ssePushService.sendTicketMessageToApp(ticketId,message);
}
}

View File

@ -37,4 +37,16 @@ public class TicketEventListener {
public void handleTicketCloseEvent(TicketCloseEvent event) { public void handleTicketCloseEvent(TicketCloseEvent event) {
event.send(); event.send();
} }
@Async
@EventListener
public void handleTicketCallBeginEvent(TicketCallBeginEvent event) {
event.send();
}
@Async
@EventListener
public void handleTicketCallEndEvent(TicketCallEndEvent event) {
event.send();
}
} }

View File

@ -52,4 +52,16 @@ public class TicketEventPublisher {
event.setApplicationContext(applicationContext); event.setApplicationContext(applicationContext);
eventPublisher.publishEvent(event); eventPublisher.publishEvent(event);
} }
public void publishTicketCallBeginEvent(Integer ticketId, String userName) {
TicketCallBeginEvent event = new TicketCallBeginEvent(this, ticketId,userName);
event.setApplicationContext(applicationContext);
eventPublisher.publishEvent(event);
}
public void publishTicketCallEndEvent(Integer ticketId) {
TicketCallEndEvent event = new TicketCallEndEvent(this, ticketId);
event.setApplicationContext(applicationContext);
eventPublisher.publishEvent(event);
}
} }

View File

@ -103,6 +103,7 @@ public class SsePushService {
.setType("ticketMessage") .setType("ticketMessage")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketMessage")
.setData(new ChatMessageVO() .setData(new ChatMessageVO()
.setId(message.getId()) .setId(message.getId())
.setFrom(message.getFrom()) .setFrom(message.getFrom())
@ -136,7 +137,10 @@ public class SsePushService {
private PushMessageDTO buildWithdrawMessage(Integer ticketId,String messageId) { private PushMessageDTO buildWithdrawMessage(Integer ticketId,String messageId) {
return new PushMessageDTO() return new PushMessageDTO()
.setType("ticketMessageWithdraw") .setType("ticketMessageWithdraw")
.setData(new PushMessageDataBody().setTargetId(ticketId).setData(messageId)); .setData(new PushMessageDataBody()
.setType("ticketMessageWithdraw")
.setTargetId(ticketId)
.setData(messageId));
} }
public void sendTicketCallToAdmin(AppUser appUser, Integer userId, Integer ticketId) { public void sendTicketCallToAdmin(AppUser appUser, Integer userId, Integer ticketId) {
@ -146,6 +150,7 @@ public class SsePushService {
.setType("ticketCall") .setType("ticketCall")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketCall")
.setData(new UniPushMessageCallPayload() .setData(new UniPushMessageCallPayload()
.setTicketId(ticketId) .setTicketId(ticketId)
.setUserId(appUser.getId()) .setUserId(appUser.getId())
@ -176,6 +181,32 @@ public class SsePushService {
.setType("ticketCallHangUp") .setType("ticketCallHangUp")
.setData(new PushMessageDataBody() .setData(new PushMessageDataBody()
.setTargetId(ticketId) .setTargetId(ticketId)
.setType("ticketCallHangUp")
.setData(new UniPushMessageCallPayload()
.setTicketId(ticketId)
.setUserId(user.getId())
.setUserName(user.getName())
.setUserAvatar(user.getAvatar())
.setFrom("app")
)
)
);
ApiResult<?> result = sendMessageByUser(request, "admin");
log.debug("发送消息结果:{}", JSONUtil.toJsonStr(result));
} catch (Exception e) {
log.error("发送消息出错", e);
}
}
public void sendTicketCallCancelToAdmin(@NotNull Integer ticketId, @NotNull Integer userId, AppUser user) {
try {
PushUserMessageRequest request = new PushUserMessageRequest()
.setUserId(userId)
.setMessage(new PushMessageDTO()
.setType("ticketCallCancel")
.setData(new PushMessageDataBody()
.setTargetId(ticketId)
.setType("ticketCallCancel")
.setData(new UniPushMessageCallPayload() .setData(new UniPushMessageCallPayload()
.setTicketId(ticketId) .setTicketId(ticketId)
.setUserId(user.getId()) .setUserId(user.getId())

View File

@ -9,5 +9,10 @@ public class PushMessageDataBody {
private Integer targetId; private Integer targetId;
/**
* 消息类型
*/
private String type;
private Object data; private Object data;
} }

View File

@ -12,6 +12,8 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@Accessors(chain = true) @Accessors(chain = true)
public class UserSseEmitter extends SseEmitter { public class UserSseEmitter extends SseEmitter {
private String from;
private Integer userId; private Integer userId;
private Integer ticketId; private Integer ticketId;
@ -19,4 +21,8 @@ public class UserSseEmitter extends SseEmitter {
public UserSseEmitter(){ public UserSseEmitter(){
super(0L); super(0L);
} }
public String getUser(){
return from + "-" + userId;
}
} }

View File

@ -1,25 +0,0 @@
package com.nflg.mobilebroken.push.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许所有路径
.allowedOrigins("*") // 允许所有来源
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的HTTP方法
.allowedHeaders("*") // 允许所有请求头
.allowCredentials(true) // 允许携带凭证如cookies
.maxAge(3600); // 预检请求的缓存时间
}
};
}
}

View File

@ -6,9 +6,11 @@ import com.nflg.mobilebroken.common.pojo.request.PushUserMessageRequest;
import com.nflg.mobilebroken.common.pojo.request.TicketMessagePushRequest; import com.nflg.mobilebroken.common.pojo.request.TicketMessagePushRequest;
import com.nflg.mobilebroken.common.util.AdminUserUtil; import com.nflg.mobilebroken.common.util.AdminUserUtil;
import com.nflg.mobilebroken.common.util.AppUserUtil; import com.nflg.mobilebroken.common.util.AppUserUtil;
import com.nflg.mobilebroken.common.util.IdUtil;
import com.nflg.mobilebroken.push.service.impl.APPSSEManagerService; import com.nflg.mobilebroken.push.service.impl.APPSSEManagerService;
import com.nflg.mobilebroken.push.service.impl.AdminSSEManagerService; import com.nflg.mobilebroken.push.service.impl.AdminSSEManagerService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@ -44,11 +46,14 @@ public class SSEController {
@PostMapping("app/push/ticket/message") @PostMapping("app/push/ticket/message")
public ApiResult<Void> pushToApp(@Valid @RequestBody @NotNull TicketMessagePushRequest request){ public ApiResult<Void> pushToApp(@Valid @RequestBody @NotNull TicketMessagePushRequest request){
try { try {
MDC.put("traceId", IdUtil.getIdStr());
appsseManagerService.sendTicketMessage(request.getTicketId(),request.getMessage()); appsseManagerService.sendTicketMessage(request.getTicketId(),request.getMessage());
return ApiResult.success(); return ApiResult.success();
} catch (IOException e) { } catch (IOException e) {
log.error("发送SSE消息出错", e); log.error("发送SSE消息出错", e);
return ApiResult.error(STATE.BusinessError,e.getMessage()); return ApiResult.error(STATE.BusinessError,e.getMessage());
}finally {
MDC.remove("traceId");
} }
} }
@ -59,11 +64,14 @@ public class SSEController {
@PostMapping("app/push/user/message") @PostMapping("app/push/user/message")
public ApiResult<Void> appMessageByUser(@Valid @RequestBody @NotNull PushUserMessageRequest request){ public ApiResult<Void> appMessageByUser(@Valid @RequestBody @NotNull PushUserMessageRequest request){
try { try {
MDC.put("traceId", IdUtil.getIdStr());
appsseManagerService.sendByUser(request.getUserId(),request.getMessage()); appsseManagerService.sendByUser(request.getUserId(),request.getMessage());
return ApiResult.success(); return ApiResult.success();
} catch (Exception e) { } catch (Exception e) {
log.error("发送SSE消息出错", e); log.error("发送SSE消息出错", e);
return ApiResult.error(STATE.BusinessError,e.getMessage()); return ApiResult.error(STATE.BusinessError,e.getMessage());
}finally {
MDC.remove("traceId");
} }
} }
@ -82,11 +90,14 @@ public class SSEController {
@PostMapping("admin/push/ticket/message") @PostMapping("admin/push/ticket/message")
public ApiResult<Void> adminPushTicketMessage(@Valid @RequestBody @NotNull TicketMessagePushRequest request){ public ApiResult<Void> adminPushTicketMessage(@Valid @RequestBody @NotNull TicketMessagePushRequest request){
try { try {
MDC.put("traceId", IdUtil.getIdStr());
adminSSEManagerService.sendTicketMessage(request.getTicketId(),request.getMessage()); adminSSEManagerService.sendTicketMessage(request.getTicketId(),request.getMessage());
return ApiResult.success(); return ApiResult.success();
} catch (IOException e) { } catch (IOException e) {
log.error("发送SSE消息出错", e); log.error("发送SSE消息出错", e);
return ApiResult.error(STATE.BusinessError,e.getMessage()); return ApiResult.error(STATE.BusinessError,e.getMessage());
}finally {
MDC.remove("traceId");
} }
} }
@ -97,11 +108,14 @@ public class SSEController {
@PostMapping("admin/push/user/message") @PostMapping("admin/push/user/message")
public ApiResult<Void> adminMessageByUser(@Valid @RequestBody @NotNull PushUserMessageRequest request){ public ApiResult<Void> adminMessageByUser(@Valid @RequestBody @NotNull PushUserMessageRequest request){
try { try {
MDC.put("traceId", IdUtil.getIdStr());
adminSSEManagerService.sendByUser(request.getUserId(),request.getMessage()); adminSSEManagerService.sendByUser(request.getUserId(),request.getMessage());
return ApiResult.success(); return ApiResult.success();
} catch (Exception e) { } catch (Exception e) {
log.error("发送SSE消息出错", e); log.error("发送SSE消息出错", e);
return ApiResult.error(STATE.BusinessError,e.getMessage()); return ApiResult.error(STATE.BusinessError,e.getMessage());
}finally {
MDC.remove("traceId");
} }
} }
} }

View File

@ -57,30 +57,30 @@ public class SSEManagerBase {
protected SseEmitter connect(Integer ticketId,Integer userId) { protected SseEmitter connect(Integer ticketId,Integer userId) {
check(); check();
log.info(from+"SSE连接:用户id:"+userId+",工单id:"+ticketId); log.info(from + "SSE连接:用户id:" + userId + ",工单id:" + ticketId);
UserSseEmitter emitter = new UserSseEmitter(userId,ticketId); UserSseEmitter emitter = new UserSseEmitter(from, userId, ticketId);
SSE_EMITTERS.add(emitter); SSE_EMITTERS.add(emitter);
ScheduledFuture<?> heartbeatTask = startHeartbeat(emitter); ScheduledFuture<?> heartbeatTask = startHeartbeat(emitter);
emitter.onError((ex) -> { emitter.onError((ex) -> {
remove(userId, emitter,heartbeatTask); remove(emitter,heartbeatTask);
log.error("SSE异常:"+userId, ex); log.error("SSE异常({}):{}", ex.getMessage(),emitter.getUser());
}); });
emitter.onTimeout(() -> { emitter.onTimeout(() -> {
remove(userId, emitter,heartbeatTask); remove(emitter,heartbeatTask);
log.error("SSE超时:"+userId); log.error("SSE超时:"+userId);
}); });
emitter.onCompletion(() -> { emitter.onCompletion(() -> {
remove(userId, emitter,heartbeatTask); remove(emitter,heartbeatTask);
log.error("SSE完成:"+userId); log.error("SSE完成:"+userId);
}); });
try { try {
emitter.send(SseEmitter.event().data("已连接").reconnectTime(5000)); emitter.send(SseEmitter.event().data("已连接").reconnectTime(5000));
}catch (ClientAbortException e){ }catch (ClientAbortException e){
log.error("客户端断开连接:{}", userId); log.error("客户端断开连接:{}", userId);
emitter.complete(); emitter.completeWithError(e);
} }
catch (IOException e) { catch (IOException e) {
log.error("sse发送数据出错", e); log.error("sse发送数据出错:{}", e.getMessage());
emitter.completeWithError(e); emitter.completeWithError(e);
} }
return emitter; return emitter;
@ -99,10 +99,10 @@ public class SSEManagerBase {
try { try {
emitter.send(SseEmitter.event().name(dto.getType()).data(dto.getData())); emitter.send(SseEmitter.event().name(dto.getType()).data(dto.getData()));
} catch (ClientAbortException e) { } catch (ClientAbortException e) {
log.error("客户端断开连接:{}", emitter.getUserId()); log.error("客户端断开连接:{}", emitter.getUser());
emitter.complete(); emitter.completeWithError(e);
} catch (IOException e) { } catch (IOException e) {
log.error("sse发送数据出错", e); log.error("sse发送数据出错:{}", e.getMessage());
emitter.completeWithError(e); emitter.completeWithError(e);
} }
}); });
@ -111,7 +111,7 @@ public class SSEManagerBase {
protected void sendByUser(Integer userId, PushMessageDTO dto) { protected void sendByUser(Integer userId, PushMessageDTO dto) {
log.info(StrUtil.format(from + "SSE发送消息,用户id: {},内容: {}", userId, dto)); log.info(StrUtil.format(from + "SSE发送消息,用户id: {},内容: {}", userId, dto));
List<UserSseEmitter> emitters = SSE_EMITTERS.stream() List<UserSseEmitter> emitters = SSE_EMITTERS.stream()
.filter(s -> Objects.equals(s.getUserId(), userId)) .filter(s -> StrUtil.equals(s.getFrom(), from) && Objects.equals(s.getUserId(), userId))
.collect(Collectors.toList()); .collect(Collectors.toList());
if (CollectionUtil.isEmpty(emitters)) { if (CollectionUtil.isEmpty(emitters)) {
log.info(StrUtil.format(from + "用户未连接:{}", userId)); log.info(StrUtil.format(from + "用户未连接:{}", userId));
@ -122,26 +122,30 @@ public class SSEManagerBase {
emitter.send(SseEmitter.event().name(dto.getType()).data(dto.getData())); emitter.send(SseEmitter.event().name(dto.getType()).data(dto.getData()));
log.info("发送成功"); log.info("发送成功");
} catch (ClientAbortException e) { } catch (ClientAbortException e) {
log.error("客户端断开连接:{}", emitter.getUserId()); log.error("客户端断开连接:{}", emitter.getUser());
emitter.complete(); emitter.completeWithError(e);
}catch (IOException e) { }catch (IOException e) {
log.error("sse发送数据出错", e); log.error("sse发送数据出错:{}", e.getMessage());
emitter.completeWithError(e); emitter.completeWithError(e);
} }
}); });
} }
private void remove(Integer userId,UserSseEmitter emitter,ScheduledFuture<?> heartbeatTask){ private void remove(UserSseEmitter emitter,ScheduledFuture<?> heartbeatTask){
heartbeatTask.cancel(false); heartbeatTask.cancel(true);
SSE_EMITTERS.remove(emitter); SSE_EMITTERS.remove(emitter);
emitter.complete(); emitter.complete();
emitter=null;
} }
private ScheduledFuture<?> startHeartbeat(UserSseEmitter emitter) { private ScheduledFuture<?> startHeartbeat(UserSseEmitter emitter) {
return taskScheduler.scheduleAtFixedRate(() -> { return taskScheduler.scheduleAtFixedRate(() -> {
try { try {
emitter.send(SseEmitter.event().data("ping")); if (Objects.nonNull(emitter)) {
emitter.send(SseEmitter.event().data("ping"));
}
} catch (IOException e) { } catch (IOException e) {
log.error("sse发送ping数据出错({}):{}", emitter.getUser(),e.getMessage());
emitter.completeWithError(e); emitter.completeWithError(e);
} }
}, 30_000); }, 30_000);

View File

@ -22,7 +22,7 @@ public interface ITicketCallJoinService extends IService<TicketCallJoin> {
boolean allIsHangUp(Integer callId); boolean allIsHangUp(Integer callId);
void hangUp(Integer callId, Integer userId, String from, boolean reject); boolean hangUp(Integer callId, Integer userId, String from, boolean reject);
void add(Integer callId, Integer userId, String from); void add(Integer callId, Integer userId, String from);

View File

@ -19,5 +19,5 @@ public interface ITicketCallService extends IService<TicketCall> {
void add(Integer ticketId, Integer callerUserId, Integer calledUserId, String from); void add(Integer ticketId, Integer callerUserId, Integer calledUserId, String from);
void hangUp(@NotNull Integer ticketId, Integer userId, String from, boolean reject); boolean hangUp(@NotNull Integer ticketId, Integer userId, String from, boolean reject);
} }

View File

@ -42,7 +42,7 @@ public class TicketCallJoinServiceImpl extends ServiceImpl<TicketCallJoinMapper,
.eq(TicketCall::getTicketId, ticketId) .eq(TicketCall::getTicketId, ticketId)
.ne(TicketCall::getState, 2) .ne(TicketCall::getState, 2)
.one(); .one();
VUtils.trueThrowBusinessError(Objects.isNull(ticketCall)).throwMessage("加入失败"); VUtils.trueThrowBusinessError(Objects.isNull(ticketCall)).throwMessage("加入失败,通话已结束");
TicketCallJoin ticketCallJoin = lambdaQuery() TicketCallJoin ticketCallJoin = lambdaQuery()
.eq(TicketCallJoin::getCallId, ticketCall.getId()) .eq(TicketCallJoin::getCallId, ticketCall.getId())
.eq(TicketCallJoin::getUserId, userId) .eq(TicketCallJoin::getUserId, userId)
@ -66,13 +66,14 @@ public class TicketCallJoinServiceImpl extends ServiceImpl<TicketCallJoinMapper,
@Transactional @Transactional
@Override @Override
public void hangUp(Integer callId, Integer userId, String from, boolean reject) { public boolean hangUp(Integer callId, Integer userId, String from, boolean reject) {
TicketCallJoin ticketCallJoin = lambdaQuery() TicketCallJoin ticketCallJoin = lambdaQuery()
.eq(TicketCallJoin::getCallId, callId) .eq(TicketCallJoin::getCallId, callId)
.ne(TicketCallJoin::getState, 2) .ne(TicketCallJoin::getState, 2)
.eq(TicketCallJoin::getUserId, userId) .eq(TicketCallJoin::getUserId, userId)
.eq(TicketCallJoin::getFrom, from) .eq(TicketCallJoin::getFrom, from)
.one(); .one();
boolean flag = false;
if (Objects.nonNull(ticketCallJoin)) { if (Objects.nonNull(ticketCallJoin)) {
ticketCallJoin.setState(2); ticketCallJoin.setState(2);
if (!reject) { if (!reject) {
@ -96,8 +97,10 @@ public class TicketCallJoinServiceImpl extends ServiceImpl<TicketCallJoinMapper,
.set(!reject,TicketCall::getHangupTime, LocalDateTime.now()) .set(!reject,TicketCall::getHangupTime, LocalDateTime.now())
.eq(TicketCall::getId, callId) .eq(TicketCall::getId, callId)
.update(); .update();
flag = true;
} }
} }
return flag;
} }
@Override @Override
@ -113,6 +116,7 @@ public class TicketCallJoinServiceImpl extends ServiceImpl<TicketCallJoinMapper,
.setUserId(userId) .setUserId(userId)
.setFrom(from) .setFrom(from)
.setState(0) .setState(0)
.setCreateTime(LocalDateTime.now())
); );
} }
} }

View File

@ -59,11 +59,12 @@ public class TicketCallServiceImpl extends ServiceImpl<TicketCallMapper, TicketC
} }
@Override @Override
public void hangUp(Integer ticketId, Integer userId, String from, boolean reject) { public boolean hangUp(Integer ticketId, Integer userId, String from, boolean reject) {
TicketCall ticketCall = lambdaQuery() TicketCall ticketCall = lambdaQuery()
.eq(TicketCall::getTicketId, ticketId) .eq(TicketCall::getTicketId, ticketId)
.ne(TicketCall::getState, 2) .ne(TicketCall::getState, 2)
.one(); .one();
boolean flag = false;
if (Objects.nonNull(ticketCall)){ if (Objects.nonNull(ticketCall)){
if ((Objects.equals(ticketCall.getCallerUserId(), userId) && StrUtil.equals(ticketCall.getFrom(), Constant.FROM_APP)) if ((Objects.equals(ticketCall.getCallerUserId(), userId) && StrUtil.equals(ticketCall.getFrom(), Constant.FROM_APP))
|| ticketCallJoinService.allIsHangUp(ticketCall.getId())){ || ticketCallJoinService.allIsHangUp(ticketCall.getId())){
@ -74,7 +75,8 @@ public class TicketCallServiceImpl extends ServiceImpl<TicketCallMapper, TicketC
} }
updateById(ticketCall); updateById(ticketCall);
} }
ticketCallJoinService.hangUp(ticketCall.getId(), userId, from, reject); flag=ticketCallJoinService.hangUp(ticketCall.getId(), userId, from, reject);
} }
return flag;
} }
} }

View File

@ -2,10 +2,11 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nflg.mobilebroken.repository.mapper.TicketCallJoinMapper"> <mapper namespace="com.nflg.mobilebroken.repository.mapper.TicketCallJoinMapper">
<select id="isInCall" resultType="boolean"> <select id="isInCall" resultType="boolean">
select exists(select 1 SELECT COUNT(*) > 0
from ticket_call tc FROM ticket_call tc
left join ticket_call_join tcj on tc.id = tcj.call_id INNER JOIN ticket_call_join tcj ON tc.id = tcj.call_id
where tc.state = 1 WHERE tc.state = 1
and tcj.user_id = #{userId}) AND tcj.state = 1
AND tcj.user_id = #{userId}
</select> </select>
</mapper> </mapper>

View File

@ -1,25 +0,0 @@
package com.nflg.mobilebroken.starter.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许所有路径
.allowedOrigins("*") // 允许所有来源
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的HTTP方法
.allowedHeaders("*") // 允许所有请求头
.allowCredentials(true) // 允许携带凭证如cookies
.maxAge(3600); // 预检请求的缓存时间
}
};
}
}

View File

@ -43,6 +43,9 @@ public class RedisConfig {
template.setKeySerializer(new StringRedisSerializer()); template.setKeySerializer(new StringRedisSerializer());
// 设置 Value 的序列化器 // 设置 Value 的序列化器
template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// 设置 Hash Key 的序列化器
template.setHashKeySerializer(new StringRedisSerializer());
// 设置 Hash Value 的序列化器
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template; return template;
} }