feat: bug-632 视频通话逻辑调整

This commit is contained in:
曹鹏飞 2025-08-27 16:50:33 +08:00
parent bb415c9b70
commit 691fe40131
14 changed files with 662 additions and 440 deletions

View File

@ -1,20 +1,13 @@
package com.nflg.mobilebroken.admin.event; package com.nflg.mobilebroken.admin.event;
import cn.hutool.core.collection.CollectionUtil;
import com.nflg.mobilebroken.admin.service.ShengWangService; import com.nflg.mobilebroken.admin.service.ShengWangService;
import com.nflg.mobilebroken.admin.service.SsePushService; 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 com.nflg.mobilebroken.repository.service.TicketChatService;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import java.io.IOException;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
public class TicketCallEndEvent extends ApplicationEvent implements ApplicationContextAware { public class TicketCallEndEvent extends ApplicationEvent implements ApplicationContextAware {
private final Integer ticketId; private final Integer ticketId;
@ -32,23 +25,24 @@ public class TicketCallEndEvent extends ApplicationEvent implements ApplicationC
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ssePushService = applicationContext.getBean(SsePushService.class); this.ssePushService = applicationContext.getBean(SsePushService.class);
this.ticketChatService = applicationContext.getBean(TicketChatService.class); this.ticketChatService = applicationContext.getBean(TicketChatService.class);
this.shengWangService = applicationContext.getBean(ShengWangService.class);
} }
public void send() throws IOException, InterruptedException { // public void send() throws IOException, InterruptedException {
TimeUnit.SECONDS.sleep(2); // TimeUnit.SECONDS.sleep(2);
if (CollectionUtil.isEmpty(shengWangService.getChannelUsers("ticket-" + ticketId))) { // if (CollectionUtil.isEmpty(shengWangService.getChannelUsers("ticket-" + ticketId))) {
ChatMessageDTO message = new ChatMessageDTO() // ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr()) // .setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("call") // .setFrom("call")
.setTicketState(TicketState.Processing.getState()) // .setTicketState(TicketState.Processing.getState())
.setSenderId(0) // .setSenderId(0)
.setSenderName("通话助手") // .setSenderName("通话助手")
.setContent("视频通话已结束") // .setContent("视频通话已结束")
.setCreateTime(Instant.now()); // .setCreateTime(Instant.now());
ticketChatService.addMessage(ticketId, message); // ticketChatService.addMessage(ticketId, message);
//推送消息 // //推送消息
ssePushService.sendTicketMessageToAdmin(ticketId, message); // ssePushService.sendTicketMessageToAdmin(ticketId, message);
ssePushService.sendTicketMessageToApp(ticketId, message); // ssePushService.sendTicketMessageToApp(ticketId, message);
} // }
} // }
} }

View File

@ -5,8 +5,6 @@ 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;
import java.io.IOException;
@Component @Component
public class TicketEventListener { public class TicketEventListener {
@ -40,9 +38,9 @@ public class TicketEventListener {
event.send(); event.send();
} }
@Async // @Async
@EventListener // @EventListener
public void handleTicketCallEndEvent(TicketCallEndEvent event) throws IOException, InterruptedException { // public void handleTicketCallEndEvent(TicketCallEndEvent event) throws IOException, InterruptedException {
event.send(); // event.send();
} // }
} }

View File

@ -45,6 +45,10 @@ public class ShengWangService {
@Value("${shengwang.rtc.customerSecret}") @Value("${shengwang.rtc.customerSecret}")
private String customerSecret; private String customerSecret;
/**
* 获取所有频道
* @return 频道列表
*/
public ShengWangChannelDTO getAllChannels() throws IOException, InterruptedException { public ShengWangChannelDTO getAllChannels() throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.GET() .GET()
@ -59,6 +63,26 @@ public class ShengWangService {
return result.getData(); return result.getData();
} }
/**
* 查询频道用户列表
* @param channelName 频道名称
* @return 用户列表
*/
public ShengWangChannelUserListDTO getChannelUsers(String channelName) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.GET()
.header("Authorization", generateAuthorization(customerKey, customerSecret))
.uri(URI.create(baseUrl + "/v1/channel/user/" + appId + "/" + channelName))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
log.debug("getChannelUsers Response:" + response.body());
VUtils.trueThrowBusinessError(response.statusCode() != 200).throwMessage("请求失败");
ShengWangResponse<ShengWangChannelUserListDTO> result = objectMapper.readValue(response.body(), new TypeReference<>() {
});
VUtils.trueThrowBusinessError(!result.isSuccess()).throwMessage("查询用户列表失败");
return result.getData();
}
/** /**
* 创建规则 * 创建规则
* @param channelName 频道名称 * @param channelName 频道名称
@ -141,18 +165,4 @@ public class ShengWangService {
// 创建 authorization header // 创建 authorization header
return "Basic " + base64Credentials; return "Basic " + base64Credentials;
} }
public List<Integer> getChannelUsers(String channelName) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.GET()
.header("Authorization", generateAuthorization(customerKey, customerSecret))
.uri(URI.create(baseUrl + "/v1/channel/user/" + appId + "/" + channelName))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
log.debug("getChannelUsers Response:" + response.body());
VUtils.trueThrowBusinessError(response.statusCode() != 200).throwMessage("请求失败");
ShengWangChannelUserResponseDTO result = objectMapper.readValue(response.body(), ShengWangChannelUserResponseDTO.class);
VUtils.trueThrowBusinessError(!result.getSuccess()).throwMessage("获取频道用户失败");
return result.getData().isChannel_exist()?result.getData().getUsers():null;
}
} }

View File

@ -304,4 +304,28 @@ public class SsePushService {
log.error("发送消息出错", e); log.error("发送消息出错", e);
} }
} }
public void sendTicketCallEnd(Integer ticketId) {
try {
TicketMessagePushRequest request = new TicketMessagePushRequest()
.setTicketId(ticketId)
.setMessage(new PushMessageDTO()
.setType("ticketCallEnd")
.setData(new PushMessageDataBody()
.setTargetId(ticketId)
.setType("ticketCallEnd")
.setData(new UniPushMessageCallPayload()
.setTicketId(ticketId)
.setFrom("admin")
)
)
);
ApiResult<?> result = sendMessage(request, "app");
log.debug("发送消息结果:{}", JSONUtil.toJsonStr(result));
result = sendMessage(request, "admin");
log.debug("发送消息结果:{}", JSONUtil.toJsonStr(result));
} catch (Exception e) {
log.error("发送消息出错", e);
}
}
} }

View File

@ -1,28 +1,16 @@
package com.nflg.mobilebroken.admin.task; package com.nflg.mobilebroken.admin.task;
import cn.hutool.core.collection.CollectionUtil;
import com.nflg.mobilebroken.admin.service.ShengWangService; import com.nflg.mobilebroken.admin.service.ShengWangService;
import com.nflg.mobilebroken.common.constant.Constant;
import com.nflg.mobilebroken.common.pojo.dto.ShengWangChannelDTO;
import com.nflg.mobilebroken.common.pojo.dto.ShengWangChannelInfoDTO;
import com.nflg.mobilebroken.common.util.IdUtil;
import com.nflg.mobilebroken.repository.entity.TicketCall;
import com.nflg.mobilebroken.repository.entity.TicketCallJoin;
import com.nflg.mobilebroken.repository.service.ITicketCallJoinService; import com.nflg.mobilebroken.repository.service.ITicketCallJoinService;
import com.nflg.mobilebroken.repository.service.ITicketCallService; import com.nflg.mobilebroken.repository.service.ITicketCallService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@ConditionalOnProperty(name = "shengwang.sync.enable", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(name = "shengwang.sync.enable", havingValue = "true", matchIfMissing = true)
@Component @Component
@ -45,83 +33,83 @@ public class ShengWangScheduledTasks {
@Transactional @Transactional
@Scheduled(cron = "0 0/1 * * * ?") @Scheduled(cron = "0 0/1 * * * ?")
public void syncState() throws Exception { public void syncState() throws Exception {
MDC.put("traceId", IdUtil.getIdStr()); // MDC.put("traceId", IdUtil.getIdStr());
log.info("开始执行声网通话状态同步"); // log.info("开始执行声网通话状态同步");
ShengWangChannelDTO dto = shengWangService.getAllChannels(); // ShengWangChannelDTO dto = shengWangService.getAllChannels();
log.info("共{}个频道", dto.getTotalSize()); // log.info("共{}个频道", dto.getTotalSize());
log.info("将不存在列表中的通话设置为已结束状态"); // log.info("将不存在列表中的通话设置为已结束状态");
List<TicketCall> ticketCalls = ticketCallService.lambdaQuery() // List<TicketCall> ticketCalls = ticketCallService.lambdaQuery()
.ne(TicketCall::getState, 2) // .ne(TicketCall::getState, 2)
.notIn(dto.getTotalSize() > 0, TicketCall::getTicketId, dto.getChannels() // .notIn(dto.getTotalSize() > 0, TicketCall::getTicketId, dto.getChannels()
.stream() // .stream()
.map(o -> Integer.valueOf(o.getChannelName().replace("ticket", ""))) // .map(o -> Integer.valueOf(o.getChannelName().replace("ticket", "")))
.collect(Collectors.toList())) // .collect(Collectors.toList()))
.list(); // .list();
if (CollectionUtil.isNotEmpty(ticketCalls)) { // if (CollectionUtil.isNotEmpty(ticketCalls)) {
ticketCalls.forEach(ticketCall -> { // ticketCalls.forEach(ticketCall -> {
ticketCall.setState(2); // ticketCall.setState(2);
ticketCall.setHangupTime(LocalDateTime.now()); // ticketCall.setHangupTime(LocalDateTime.now());
ticketCallService.updateById(ticketCall); // ticketCallService.updateById(ticketCall);
ticketCallJoinService.lambdaUpdate() // ticketCallJoinService.lambdaUpdate()
.set(TicketCallJoin::getState, 2) // .set(TicketCallJoin::getState, 2)
// .set(TicketCallJoin::getHangupTime, LocalDateTime.now()) //// .set(TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, ticketCall.getId()) // .eq(TicketCallJoin::getCallId, ticketCall.getId())
.update(); // .update();
}); // });
} // }
for (ShengWangChannelInfoDTO info : dto.getChannels()) { // for (ShengWangChannelInfoDTO info : dto.getChannels()) {
log.info("处理频道:{}", info.getChannelName()); // log.info("处理频道:{}", info.getChannelName());
TicketCall ticketCall = ticketCallService.lambdaQuery() // TicketCall ticketCall = ticketCallService.lambdaQuery()
.eq(TicketCall::getTicketId, Integer.valueOf(info.getChannelName().replace("ticket", ""))) // .eq(TicketCall::getTicketId, Integer.valueOf(info.getChannelName().replace("ticket", "")))
.eq(TicketCall::getState, 1) // .eq(TicketCall::getState, 1)
.one(); // .one();
if (Objects.nonNull(ticketCall)) { // if (Objects.nonNull(ticketCall)) {
log.info("获取频道用户"); // log.info("获取频道用户");
List<Integer> channelUsers = shengWangService.getChannelUsers(info.getChannelName()); // List<Integer> channelUsers = shengWangService.getChannelUsers(info.getChannelName());
log.info("频道用户:{}", channelUsers); // log.info("频道用户:{}", channelUsers);
if (CollectionUtil.isEmpty(channelUsers)) { // if (CollectionUtil.isEmpty(channelUsers)) {
ticketCallJoinService.lambdaUpdate() // ticketCallJoinService.lambdaUpdate()
.set(TicketCallJoin::getState, 2) // .set(TicketCallJoin::getState, 2)
// .set(TicketCallJoin::getHangupTime, LocalDateTime.now()) //// .set(TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, ticketCall.getId()) // .eq(TicketCallJoin::getCallId, ticketCall.getId())
.update(); // .update();
} else { // } else {
//用户端 // //用户端
List<Integer> userIds = channelUsers.stream() // List<Integer> userIds = channelUsers.stream()
.map(String::valueOf) // .map(String::valueOf)
.filter(userId -> String.valueOf(userId).startsWith("1")) // .filter(userId -> String.valueOf(userId).startsWith("1"))
.map(this::getUserId) // .map(this::getUserId)
.collect(Collectors.toList()); // .collect(Collectors.toList());
log.info("客户端用户:{}", userIds); // log.info("客户端用户:{}", userIds);
if (CollectionUtil.isNotEmpty(userIds)) { // if (CollectionUtil.isNotEmpty(userIds)) {
ticketCallJoinService.lambdaUpdate() // ticketCallJoinService.lambdaUpdate()
.set(TicketCallJoin::getState, 2) // .set(TicketCallJoin::getState, 2)
// .set(TicketCallJoin::getHangupTime, LocalDateTime.now()) //// .set(TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, ticketCall.getId()) // .eq(TicketCallJoin::getCallId, ticketCall.getId())
.eq(TicketCallJoin::getFrom, Constant.FROM_APP) // .eq(TicketCallJoin::getFrom, Constant.FROM_APP)
.notIn(TicketCallJoin::getUserId, userIds) // .notIn(TicketCallJoin::getUserId, userIds)
.update(); // .update();
} // }
//管理端 // //管理端
userIds = channelUsers.stream() // userIds = channelUsers.stream()
.map(String::valueOf) // .map(String::valueOf)
.filter(userId -> String.valueOf(userId).startsWith("2")) // .filter(userId -> String.valueOf(userId).startsWith("2"))
.map(this::getUserId) // .map(this::getUserId)
.collect(Collectors.toList()); // .collect(Collectors.toList());
log.info("管理端用户:{}", userIds); // log.info("管理端用户:{}", userIds);
if (CollectionUtil.isNotEmpty(userIds)) { // if (CollectionUtil.isNotEmpty(userIds)) {
ticketCallJoinService.lambdaUpdate() // ticketCallJoinService.lambdaUpdate()
.set(TicketCallJoin::getState, 2) // .set(TicketCallJoin::getState, 2)
// .set(TicketCallJoin::getHangupTime, LocalDateTime.now()) //// .set(TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, ticketCall.getId()) // .eq(TicketCallJoin::getCallId, ticketCall.getId())
.eq(TicketCallJoin::getFrom, Constant.FROM_ADMIN) // .eq(TicketCallJoin::getFrom, Constant.FROM_ADMIN)
.notIn(TicketCallJoin::getUserId, userIds) // .notIn(TicketCallJoin::getUserId, userIds)
.update(); // .update();
} // }
} // }
} // }
} // }
log.info("执行声网通话状态同步完毕"); // log.info("执行声网通话状态同步完毕");
} }
private Integer getUserId(String userId){ private Integer getUserId(String userId){

View File

@ -5,6 +5,7 @@ import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; 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.ShengWangService;
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.Constant;
import com.nflg.mobilebroken.common.constant.TicketState; import com.nflg.mobilebroken.common.constant.TicketState;
@ -13,10 +14,7 @@ import com.nflg.mobilebroken.common.pojo.PageData;
import com.nflg.mobilebroken.common.pojo.dto.*; import com.nflg.mobilebroken.common.pojo.dto.*;
import com.nflg.mobilebroken.common.pojo.request.*; import com.nflg.mobilebroken.common.pojo.request.*;
import com.nflg.mobilebroken.common.pojo.vo.*; import com.nflg.mobilebroken.common.pojo.vo.*;
import com.nflg.mobilebroken.common.util.AppUserUtil; import com.nflg.mobilebroken.common.util.*;
import com.nflg.mobilebroken.common.util.MultilingualUtil;
import com.nflg.mobilebroken.common.util.PageUtil;
import com.nflg.mobilebroken.common.util.VUtils;
import com.nflg.mobilebroken.repository.entity.*; import com.nflg.mobilebroken.repository.entity.*;
import com.nflg.mobilebroken.repository.service.*; import com.nflg.mobilebroken.repository.service.*;
import com.nflg.mobilebroken.starter.service.UniPushService; import com.nflg.mobilebroken.starter.service.UniPushService;
@ -28,8 +26,10 @@ import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -99,6 +99,9 @@ public class TicketController extends ControllerBase {
// @Resource // @Resource
// private ITicketCallJoinService ticketCallJoinService; // private ITicketCallJoinService ticketCallJoinService;
@Resource
private ShengWangService shengWangService;
/** /**
* 搜索设备 * 搜索设备
* @param request 搜索条件 * @param request 搜索条件
@ -525,6 +528,8 @@ public class TicketController extends ControllerBase {
ssePushService.sendTicketCallToAdmin(appUser, handlerUserId, ticketId); ssePushService.sendTicketCallToAdmin(appUser, handlerUserId, ticketId);
// ticketCallService.add(ticketId, AppUserUtil.getUserId(),Constant.FROM_APP, handlerUserId, Constant.FROM_ADMIN); // ticketCallService.add(ticketId, AppUserUtil.getUserId(),Constant.FROM_APP, handlerUserId, Constant.FROM_ADMIN);
ticketEventPublisher.publishTicketCallBeginEvent(ticketId,appUser.getName()); ticketEventPublisher.publishTicketCallBeginEvent(ticketId,appUser.getName());
stringRedisTemplate.opsForSet().add(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, Constant.FROM_ADMIN + "-" + handlerUserId);
stringRedisTemplate.expire(Constant.REDIS_KEY_TICKET_CALL_WAIT + ticketId, 1, TimeUnit.MINUTES);
return ApiResult.success(); return ApiResult.success();
} }
@ -533,7 +538,7 @@ public class TicketController extends ControllerBase {
* @param ticketId 工单编号 * @param ticketId 工单编号
*/ */
@GetMapping("call/joinChannel") @GetMapping("call/joinChannel")
public ApiResult<Void> joinChannel(@Valid @RequestParam @NotNull Integer ticketId){ public ApiResult<Void> joinChannel(@Valid @RequestParam @NotNull Integer ticketId) throws IOException, InterruptedException {
Ticket ticket = ticketService.getById(ticketId); Ticket ticket = ticketService.getById(ticketId);
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()))
@ -560,6 +565,24 @@ public class TicketController extends ControllerBase {
// ) // )
// ) // )
// ); // );
ShengWangChannelUserListDTO channelUsers = shengWangService.getChannelUsers("ticket" + ticketId);
VUtils.trueThrowBusinessError(!channelUsers.isChannelEexist()).throwMessage("通话已结束");
VUtils.trueThrowBusinessError(channelUsers.getUsers().contains(ShengWangUtil.getUserId(AppUserUtil.getUserId(), AppUserUtil.getFrom())))
.throwMessage("你已在通话中");
ssePushService.sendTicketCallJoined(AppUserUtil.getUserId(), ticketId, AppUserUtil.getFrom());
uniPushService.send(new UniPushMessage()
.setSenderId(AppUserUtil.getFrom() + "-uid-" + AppUserUtil.getUserId())//不重要
.setReceiverId(AppUserUtil.getFrom() + "-uid-" + AppUserUtil.getUserId())
.setSendData(new UniPushMessageBody()
.setTitle("视频通话")
.setPayload(new UniPushMessageCallPayload()
.setTicketId(ticketId)
.setUserId(AppUserUtil.getUserId())
.setCategory("ticketCallJoined")
.setFrom("app")
)
)
);
return ApiResult.success(); return ApiResult.success();
} }
@ -568,56 +591,91 @@ public class TicketController extends ControllerBase {
* @param request 请求信息 * @param request 请求信息
*/ */
@PostMapping("call/hangUp") @PostMapping("call/hangUp")
public ApiResult<Void> hangUp(@Valid @RequestBody TicketCallHangUpRequest request) { public ApiResult<Void> hangUp(@Valid @RequestBody TicketCallHangUpRequest request) throws IOException, InterruptedException {
// TicketCall ticketCall = ticketCallService.getLast(request.getTicketId()); // TicketCall ticketCall = ticketCallService.getLast(request.getTicketId());
if (request.getReject()) { // if (request.getReject()) {
// if (Objects.isNull(ticketCall)) return ApiResult.success(); //// if (Objects.isNull(ticketCall)) return ApiResult.success();
if (StrUtil.equals(request.getFrom(), Constant.FROM_APP) && Objects.equals(AppUserUtil.getUserId(), request.getFromUserId())) { // if (StrUtil.equals(request.getFrom(), Constant.FROM_APP) && Objects.equals(AppUserUtil.getUserId(), request.getFromUserId())) {
AppUser appUser = appUserService.getById(AppUserUtil.getUserId()); // AppUser appUser = appUserService.getById(AppUserUtil.getUserId());
Ticket ticket = ticketService.getById(request.getTicketId()); // Ticket ticket = ticketService.getById(request.getTicketId());
int handlerId = Integer.parseInt(StrUtil.split(ticket.getHandle(), ",").stream().findFirst().get()); // int handlerId = Integer.parseInt(StrUtil.split(ticket.getHandle(), ",").stream().findFirst().get());
uniPushService.send(new UniPushMessage() // uniPushService.send(new UniPushMessage()
.setSenderId(ticket.getUserPlatform() + "-uid-" + appUser.getId()) // .setSenderId(ticket.getUserPlatform() + "-uid-" + appUser.getId())
.setReceiverId("admin-uid-" + handlerId) // .setReceiverId("admin-uid-" + handlerId)
.setSendData(new UniPushMessageBody() // .setSendData(new UniPushMessageBody()
.setTitle("挂断视频通话") // .setTitle("挂断视频通话")
.setContent(appUser.getName() + "挂断了与您的视频通话") // .setContent(appUser.getName() + "挂断了与您的视频通话")
.setPayload(new UniPushMessageCallPayload() // .setPayload(new UniPushMessageCallPayload()
.setTicketId(request.getTicketId()) // .setTicketId(request.getTicketId())
.setUserId(appUser.getId()) // .setUserId(appUser.getId())
.setUserName(appUser.getName()) // .setUserName(appUser.getName())
.setUserAvatar(appUser.getAvatar()) // .setUserAvatar(appUser.getAvatar())
.setCategory("ticketCallCancel") // .setCategory("ticketCallCancel")
.setFrom("app") // .setFrom("app")
) // )
) // )
); // );
ssePushService.sendTicketCallCancelToAdmin(request.getTicketId(), handlerId, appUser); // ssePushService.sendTicketCallCancelToAdmin(request.getTicketId(), handlerId, appUser);
} else { // } else {
AppUser appUser = appUserService.getById(AppUserUtil.getUserId()); // AppUser appUser = appUserService.getById(AppUserUtil.getUserId());
Ticket ticket = ticketService.getById(request.getTicketId()); // Ticket ticket = ticketService.getById(request.getTicketId());
uniPushService.send(new UniPushMessage() // uniPushService.send(new UniPushMessage()
.setSenderId(ticket.getUserPlatform() + "-uid-" + appUser.getId()) // .setSenderId(ticket.getUserPlatform() + "-uid-" + appUser.getId())
.setReceiverId("admin-uid-" + request.getFromUserId()) // .setReceiverId("admin-uid-" + request.getFromUserId())
.setSendData(new UniPushMessageBody() // .setSendData(new UniPushMessageBody()
.setTitle("拒绝视频通话") // .setTitle("拒绝视频通话")
.setContent(appUser.getName() + "拒绝与您视频通话") // .setContent(appUser.getName() + "拒绝与您视频通话")
.setPayload(new UniPushMessageCallPayload() // .setPayload(new UniPushMessageCallPayload()
.setTicketId(request.getTicketId()) // .setTicketId(request.getTicketId())
.setUserId(appUser.getId()) // .setUserId(appUser.getId())
.setUserName(appUser.getName()) // .setUserName(appUser.getName())
.setUserAvatar(appUser.getAvatar()) // .setUserAvatar(appUser.getAvatar())
.setCategory("ticketCallHangUp") // .setCategory("ticketCallHangUp")
.setFrom("app") // .setFrom("app")
) // )
) // )
); // );
ssePushService.sendTicketCallHangUpToAdmin(request.getTicketId(), request.getFromUserId(), appUser); // ssePushService.sendTicketCallHangUpToAdmin(request.getTicketId(), request.getFromUserId(), appUser);
// }
// }
//// if (ticketCallJoinService.hangUp(ticketCall.getId(), AppUserUtil.getUserId(), Constant.FROM_APP, request.getReject())) {
// ticketEventPublisher.publishTicketCallEndEvent(request.getTicketId());
//// }
stringRedisTemplate.opsForSet().remove(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId(), Constant.FROM_ADMIN + "-" + AdminUserUtil.getUserId());
if (Objects.equals(0L, stringRedisTemplate.opsForSet().size(Constant.REDIS_KEY_TICKET_CALL_WAIT + request.getTicketId()))) {
ShengWangChannelUserListDTO channelUserListDTO = shengWangService.getChannelUsers("ticket" + request.getTicketId());
if (!channelUserListDTO.isChannelEexist() || channelUserListDTO.getUsers().size() <= 1) {
ssePushService.sendTicketCallEnd(request.getTicketId());
Integer userId = CollectionUtil.get(channelUserListDTO.getUsers(), 0);
if (Objects.nonNull(userId)) {
String from = userId.toString().startsWith("1") ? "app" : "admin";
userId = Integer.valueOf(userId.toString().substring(1));
uniPushService.send(new UniPushMessage()
.setSenderId(from + "-uid-" + userId)//不重要
.setReceiverId(from + "-uid-" + userId)
.setSendData(new UniPushMessageBody()
.setTitle("视频通话结束")
.setPayload(new UniPushMessageCallPayload()
.setTicketId(request.getTicketId())
.setUserId(userId)
.setCategory("ticketCallEnd")
.setFrom("app")
)
)
);
}
} }
} }
// if (ticketCallJoinService.hangUp(ticketCall.getId(), AppUserUtil.getUserId(), Constant.FROM_APP, request.getReject())) {
ticketEventPublisher.publishTicketCallEndEvent(request.getTicketId());
// }
return ApiResult.success(); return ApiResult.success();
} }
/**
* 获取声网频道用户列表
* @param ticketId 工单ID
* @return 用户列表
*/
@GetMapping("getShengWangChannelUsers")
public ApiResult<List<Integer>> getShengWangChannelUsers(Integer ticketId) throws IOException, InterruptedException {
return ApiResult.success(shengWangService.getChannelUsers("ticket" + ticketId).getUsers());
}
} }

View File

@ -1,20 +1,13 @@
package com.nflg.mobilebroken.cfs.event; package com.nflg.mobilebroken.cfs.event;
import cn.hutool.core.collection.CollectionUtil;
import com.nflg.mobilebroken.cfs.service.ShengWangService; import com.nflg.mobilebroken.cfs.service.ShengWangService;
import com.nflg.mobilebroken.cfs.service.SsePushService; 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 com.nflg.mobilebroken.repository.service.TicketChatService;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import java.io.IOException;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
public class TicketCallEndEvent extends ApplicationEvent implements ApplicationContextAware { public class TicketCallEndEvent extends ApplicationEvent implements ApplicationContextAware {
private final Integer ticketId; private final Integer ticketId;
@ -35,21 +28,21 @@ public class TicketCallEndEvent extends ApplicationEvent implements ApplicationC
this.shengWangService = applicationContext.getBean(ShengWangService.class); this.shengWangService = applicationContext.getBean(ShengWangService.class);
} }
public void send() throws IOException, InterruptedException { // public void send() throws IOException, InterruptedException {
TimeUnit.SECONDS.sleep(2); // TimeUnit.SECONDS.sleep(2);
if (CollectionUtil.isEmpty(shengWangService.getChannelUsers("ticket-" + ticketId))) { // if (CollectionUtil.isEmpty(shengWangService.getChannelUsers("ticket-" + ticketId))) {
ChatMessageDTO message = new ChatMessageDTO() // ChatMessageDTO message = new ChatMessageDTO()
.setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr()) // .setId(cn.hutool.core.util.IdUtil.getSnowflakeNextIdStr())
.setFrom("call") // .setFrom("call")
.setTicketState(TicketState.Processing.getState()) // .setTicketState(TicketState.Processing.getState())
.setSenderId(0) // .setSenderId(0)
.setSenderName("通话助手") // .setSenderName("通话助手")
.setContent("视频通话已结束") // .setContent("视频通话已结束")
.setCreateTime(Instant.now()); // .setCreateTime(Instant.now());
ticketChatService.addMessage(ticketId, message); // ticketChatService.addMessage(ticketId, message);
//推送消息 // //推送消息
ssePushService.sendTicketMessageToAdmin(ticketId, message); // ssePushService.sendTicketMessageToAdmin(ticketId, message);
ssePushService.sendTicketMessageToApp(ticketId, message); // ssePushService.sendTicketMessageToApp(ticketId, message);
} // }
} // }
} }

View File

@ -5,8 +5,6 @@ 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;
import java.io.IOException;
@Component @Component
public class TicketEventListener { public class TicketEventListener {
@ -46,9 +44,9 @@ public class TicketEventListener {
event.send(); event.send();
} }
@Async // @Async
@EventListener // @EventListener
public void handleTicketCallEndEvent(TicketCallEndEvent event) throws IOException, InterruptedException { // public void handleTicketCallEndEvent(TicketCallEndEvent event) throws IOException, InterruptedException {
event.send(); // event.send();
} // }
} }

View File

@ -60,6 +60,26 @@ public class ShengWangService {
return result.getData(); return result.getData();
} }
/**
* 查询频道用户列表
* @param channelName 频道名称
* @return 用户列表
*/
public ShengWangChannelUserListDTO getChannelUsers(String channelName) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.GET()
.header("Authorization", generateAuthorization(customerKey, customerSecret))
.uri(URI.create(baseUrl + "/v1/channel/user/" + appId + "/" + channelName))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
log.debug("getChannelUsers Response:" + response.body());
VUtils.trueThrowBusinessError(response.statusCode() != 200).throwMessage("请求失败");
ShengWangResponse<ShengWangChannelUserListDTO> result = objectMapper.readValue(response.body(), new TypeReference<>() {
});
VUtils.trueThrowBusinessError(!result.isSuccess()).throwMessage("查询用户列表失败");
return result.getData();
}
/** /**
* 创建规则 * 创建规则
* @param channelName 频道名称 * @param channelName 频道名称
@ -142,18 +162,4 @@ public class ShengWangService {
// 创建 authorization header // 创建 authorization header
return "Basic " + base64Credentials; return "Basic " + base64Credentials;
} }
public List<Integer> getChannelUsers(String channelName) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.GET()
.header("Authorization", generateAuthorization(customerKey, customerSecret))
.uri(URI.create(baseUrl + "/v1/channel/user/" + appId + "/" + channelName))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
log.debug("getChannelUsers Response:" + response.body());
VUtils.trueThrowBusinessError(response.statusCode() != 200).throwMessage("请求失败");
ShengWangChannelUserResponseDTO result = objectMapper.readValue(response.body(), ShengWangChannelUserResponseDTO.class);
VUtils.trueThrowBusinessError(!result.getSuccess()).throwMessage("获取频道用户失败");
return result.getData().isChannel_exist() ? result.getData().getUsers() : null;
}
} }

View File

@ -18,6 +18,7 @@ import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.time.ZoneId; import java.time.ZoneId;
@ -244,4 +245,50 @@ public class SsePushService {
log.error("发送消息出错", e); log.error("发送消息出错", e);
} }
} }
public void sendTicketCallJoined(Integer userId, @Valid @NotNull Integer ticketId, String to) {
try {
PushUserMessageRequest request = new PushUserMessageRequest()
.setUserId(userId)
.setMessage(new PushMessageDTO()
.setType("ticketCallJoined")
.setData(new PushMessageDataBody()
.setTargetId(ticketId)
.setType("ticketCallJoined")
.setData(new UniPushMessageCallPayload()
.setTicketId(ticketId)
.setFrom("app")
)
)
);
ApiResult<?> result = sendMessageByUser(request, to);
log.debug("发送消息结果:{}", JSONUtil.toJsonStr(result));
} catch (Exception e) {
log.error("发送消息出错", e);
}
}
public void sendTicketCallEnd(Integer ticketId) {
try {
TicketMessagePushRequest request = new TicketMessagePushRequest()
.setTicketId(ticketId)
.setMessage(new PushMessageDTO()
.setType("ticketCallEnd")
.setData(new PushMessageDataBody()
.setTargetId(ticketId)
.setType("ticketCallEnd")
.setData(new UniPushMessageCallPayload()
.setTicketId(ticketId)
.setFrom("app")
)
)
);
ApiResult<?> result = sendMessage(request, "app");
log.debug("发送消息结果:{}", JSONUtil.toJsonStr(result));
result = sendMessage(request, "admin");
log.debug("发送消息结果:{}", JSONUtil.toJsonStr(result));
} catch (Exception e) {
log.error("发送消息出错", e);
}
}
} }

View File

@ -135,4 +135,6 @@ public class Constant {
public static final String DICTIONARY_PRODUCT_MODULE = "ProductModule"; public static final String DICTIONARY_PRODUCT_MODULE = "ProductModule";
public static final String TRACE_ID = "traceId"; public static final String TRACE_ID = "traceId";
public static final String REDIS_KEY_TICKET_CALL_WAIT = "ticket:called:wait:";
} }

View File

@ -0,0 +1,26 @@
package com.nflg.mobilebroken.common.pojo.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
@Data
public class ShengWangChannelUserListDTO {
/**
* 指定频道是否存在
*/
@JsonProperty("channel_exist")
private boolean channelEexist;
/**
* 频道内的总人数
*/
private Integer total;
/**
* 频道内所有用户的用户ID
*/
private List<Integer> users;
}

View File

@ -0,0 +1,18 @@
package com.nflg.mobilebroken.common.util;
import cn.hutool.core.util.StrUtil;
import com.nflg.mobilebroken.common.constant.Constant;
public class ShengWangUtil {
public static Integer getUserId(Integer userId, String from) {
switch (from) {
case Constant.FROM_APP:
return Integer.valueOf("1" + StrUtil.padPre(String.valueOf(userId), 9, '0'));
case Constant.FROM_ADMIN:
return Integer.valueOf("2" + StrUtil.padPre(String.valueOf(userId), 9, '0'));
default:
return null;
}
}
}