Compare commits

...

3 Commits

Author SHA1 Message Date
曹鹏飞 e0c62ae8f2 feat: bug-528 app端添加管理功能
调整视频通话逻辑
2025-08-06 19:21:08 +08:00
曹鹏飞 a81d362c96 feat: bug-528 app端添加管理功能
管理端工单详情接口返回数据添加创建人来源字段
2025-08-06 13:58:15 +08:00
曹鹏飞 90d6a3c78f feat: bug-528 app端添加管理功能
修复管理端用户登录无法搜索到自己创建的订单的问题
2025-08-06 09:56:40 +08:00
11 changed files with 110 additions and 59 deletions

View File

@ -639,6 +639,7 @@ public class TicketController extends ControllerBase {
.setAccidentLevel(ticket.getAccidentLevel())
.setUserIsHandle(handleIds.contains(AdminUserUtil.getUserId()))
.setUserIsCQM(cqms.contains(AdminUserUtil.getUserId()))
.setUserPlatform(ticket.getUserPlatform())
.setEvaluate(getTicketEvaluate(ticket.getId()));
return ApiResult.success(vo);
}
@ -744,6 +745,9 @@ public class TicketController extends ControllerBase {
.throwMessage("当前工单状态不允许发送消息");
List<Integer> adminUsers=adminUserService.getTickerMangagers();
adminUsers.addAll(StrUtil.split(ticket.getHandle(),",").stream().map(Integer::parseInt).collect(Collectors.toList()));
if (StrUtil.equals(Constant.FROM_ADMIN,ticket.getUserPlatform())){
adminUsers.add(ticket.getUserId());
}
VUtils.trueThrowBusinessError(adminUsers.stream()
.noneMatch(uid -> Objects.equals(uid, AdminUserUtil.getUserId())))
.throwMessage("你无权发送消息");
@ -1245,13 +1249,33 @@ public class TicketController extends ControllerBase {
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState()))
.throwMessage("当前工单状态不允许请求通话");
Integer handlerUserId = Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).findFirst().get();
if (StrUtil.equals(Constant.FROM_APP,ticket.getUserPlatform())) {
VUtils.trueThrowBusinessError(!Objects.equals(AdminUserUtil.getUserId(), handlerUserId))
.throwMessage("不是工单主负责人无权限呼叫");
}else {
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getUserId(), AdminUserUtil.getUserId()) && !Objects.equals(AdminUserUtil.getUserId(), handlerUserId))
.throwMessage("无权限呼叫");
}
VUtils.trueThrowBusinessError(ticketCallService.isInCall(ticket.getUserId())).throwMessage("对方正在通话中");
boolean userIdCreate=StrUtil.equals(ticket.getUserPlatform(),Constant.FROM_ADMIN) && Objects.equals(ticket.getUserId(), AdminUserUtil.getUserId());
Integer sendUserId=0,receiveUserId=0;
String sendUserFrom,receiveUserFrom;
if (userIdCreate){
sendUserId=ticket.getUserId();
receiveUserId=handlerUserId;
sendUserFrom=Constant.FROM_ADMIN;
receiveUserFrom=Constant.FROM_ADMIN;
}else {
sendUserId=handlerUserId;
receiveUserId=ticket.getUserId();
sendUserFrom=Constant.FROM_ADMIN;
receiveUserFrom=Constant.FROM_APP;
}
ticketCallService.add(ticketId, sendUserId,sendUserFrom, receiveUserId, receiveUserFrom);
AdminUser adminUser = adminUserService.getById(handlerUserId);
uniPushService.send(new UniPushMessage()
.setSenderId("admin-uid-" + handlerUserId)
.setReceiverId("app-uid-" + ticket.getUserId())
.setReceiverId(ticket.getUserPlatform()+"-uid-" + ticket.getUserId())
.setSendData(new UniPushMessageBody()
.setTitle("视频通话")
.setContent(adminUser.getUserName() + "请求与您视频通话")
@ -1265,8 +1289,11 @@ public class TicketController extends ControllerBase {
)
)
);
if (StrUtil.equals(Constant.FROM_APP,ticket.getUserPlatform())) {
ssePushService.sendTicketCallToApp(adminUser, ticket.getUserId(), ticketId);
ticketCallService.add(ticketId, handlerUserId, ticket.getUserId(), Constant.FROM_ADMIN);
}else {
ssePushService.sendTicketCallToAdmin(adminUser, ticket.getUserId(), ticketId);
}
ticketEventPublisher.publishTicketCallBeginEvent(ticketId, adminUser.getUserName());
return ApiResult.success();
}
@ -1325,7 +1352,7 @@ public class TicketController extends ControllerBase {
ticketCallJoinService.join(ticketId, AdminUserUtil.getUserId(),Constant.FROM_ADMIN);
ssePushService.sendTicketCallJoinedToAdmin(AdminUserUtil.getUserId(), ticketId);
uniPushService.send(new UniPushMessage()
.setSenderId("admin-uid-" + AdminUserUtil.getUserId())
.setSenderId("admin-uid-" + AdminUserUtil.getUserId())//不重要
.setReceiverId("admin-uid-" + AdminUserUtil.getUserId())
.setSendData(new UniPushMessageBody()
.setTitle("视频通话")
@ -1350,7 +1377,9 @@ public class TicketController extends ControllerBase {
TicketCall ticketCall = ticketCallService.lambdaQuery()
.eq(TicketCall::getTicketId, request.getTicketId())
.ne(TicketCall::getState, 2)
.last("limit 1")
.one();
if (Objects.isNull(ticketCall)) return ApiResult.success();
boolean flag=false;
if (request.getReject()) {
AdminUser adminUser = adminUserService.getById(AdminUserUtil.getUserId());

View File

@ -13,6 +13,7 @@ import com.nflg.mobilebroken.repository.service.ITicketCallService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@ -23,6 +24,7 @@ import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@ConditionalOnProperty(name = "shengwang.sync.enable", havingValue = "true", matchIfMissing = true)
@Component
@Slf4j
public class ShengWangScheduledTasks {
@ -62,7 +64,7 @@ public class ShengWangScheduledTasks {
ticketCallService.updateById(ticketCall);
ticketCallJoinService.lambdaUpdate()
.set(TicketCallJoin::getState, 2)
.set(TicketCallJoin::getHangupTime, LocalDateTime.now())
// .set(TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, ticketCall.getId())
.update();
});
@ -80,7 +82,7 @@ public class ShengWangScheduledTasks {
if (CollectionUtil.isEmpty(channelUsers)) {
ticketCallJoinService.lambdaUpdate()
.set(TicketCallJoin::getState, 2)
.set(TicketCallJoin::getHangupTime, LocalDateTime.now())
// .set(TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, ticketCall.getId())
.update();
} else {
@ -94,7 +96,7 @@ public class ShengWangScheduledTasks {
if (CollectionUtil.isNotEmpty(userIds)) {
ticketCallJoinService.lambdaUpdate()
.set(TicketCallJoin::getState, 2)
.set(TicketCallJoin::getHangupTime, LocalDateTime.now())
// .set(TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, ticketCall.getId())
.eq(TicketCallJoin::getFrom, Constant.FROM_APP)
.notIn(TicketCallJoin::getUserId, userIds)
@ -110,7 +112,7 @@ public class ShengWangScheduledTasks {
if (CollectionUtil.isNotEmpty(userIds)) {
ticketCallJoinService.lambdaUpdate()
.set(TicketCallJoin::getState, 2)
.set(TicketCallJoin::getHangupTime, LocalDateTime.now())
// .set(TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, ticketCall.getId())
.eq(TicketCallJoin::getFrom, Constant.FROM_ADMIN)
.notIn(TicketCallJoin::getUserId, userIds)

View File

@ -507,7 +507,7 @@ public class TicketController extends ControllerBase {
VUtils.trueThrowBusinessError(ticketCallService.isInCall(handlerUserId)).throwMessage("对方正在通话中");
AppUser appUser = appUserService.getById(ticket.getUserId());
uniPushService.send(new UniPushMessage()
.setSenderId("app-uid-" + ticket.getUserId())
.setSenderId(ticket.getUserPlatform()+"-uid-" + ticket.getUserId())
.setReceiverId("admin-uid-" + handlerUserId)
.setSendData(new UniPushMessageBody()
.setTitle("视频通话")
@ -523,7 +523,7 @@ public class TicketController extends ControllerBase {
)
);
ssePushService.sendTicketCallToAdmin(appUser, handlerUserId, ticketId);
ticketCallService.add(ticketId, AppUserUtil.getUserId(), handlerUserId, Constant.FROM_APP);
ticketCallService.add(ticketId, AppUserUtil.getUserId(),Constant.FROM_APP, handlerUserId, Constant.FROM_ADMIN);
ticketEventPublisher.publishTicketCallBeginEvent(ticketId,appUser.getName());
return ApiResult.success();
}
@ -538,13 +538,17 @@ public class TicketController extends ControllerBase {
VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在");
VUtils.trueThrowBusinessError(!Objects.equals(ticket.getState(), TicketState.Processing.getState()))
.throwMessage("当前工单状态不允许通话");
VUtils.trueThrowBusinessError(!StrUtil.equals(ticket.getUserPlatform(), AppUserUtil.getFrom())
|| Objects.equals(AppUserUtil.getUserId(), ticket.getUserId()))
.throwMessage("不是创建人无法加入通话");
VUtils.trueThrowBusinessError(ticketCallService.isInCall(AppUserUtil.getUserId()))
.throwMessage("您已加入别的通话中");
ticketCallJoinService.join(ticketId, AppUserUtil.getUserId(), Constant.FROM_APP);
ssePushService.sendTicketCallJoinedToApp(AppUserUtil.getUserId(), ticketId);
Integer handlerUserId = Arrays.stream(ticket.getHandle().split(",")).map(Integer::parseInt).findFirst().get();
uniPushService.send(new UniPushMessage()
.setSenderId("app-uid-" + AppUserUtil.getUserId())
.setReceiverId("app-uid-" + AppUserUtil.getUserId())
.setSenderId(ticket.getUserPlatform()+"-uid-" + ticket.getUserId())
.setReceiverId("admin-uid-" + handlerUserId)
.setSendData(new UniPushMessageBody()
.setTitle("视频通话")
.setContent("您已加入别的通话中")
@ -572,7 +576,7 @@ public class TicketController extends ControllerBase {
Ticket ticket = ticketService.getById(request.getTicketId());
int handlerId= Integer.parseInt(StrUtil.split(ticket.getHandle(), ",").stream().findFirst().get());
uniPushService.send(new UniPushMessage()
.setSenderId("app-uid-" + appUser.getId())
.setSenderId(ticket.getUserPlatform()+"-uid-" + appUser.getId())
.setReceiverId("admin-uid-" + handlerId)
.setSendData(new UniPushMessageBody()
.setTitle("挂断视频通话")
@ -591,8 +595,9 @@ public class TicketController extends ControllerBase {
flag=ticketCallService.hangUp(request.getTicketId(), AppUserUtil.getUserId(), Constant.FROM_APP, true);
}else {
AppUser appUser = appUserService.getById(AppUserUtil.getUserId());
Ticket ticket = ticketService.getById(request.getTicketId());
uniPushService.send(new UniPushMessage()
.setSenderId("app-uid-" + appUser.getId())
.setSenderId(ticket.getUserPlatform()+"-uid-" + appUser.getId())
.setReceiverId("admin-uid-" + request.getFromUserId())
.setSendData(new UniPushMessageBody()
.setTitle("拒绝视频通话")

View File

@ -32,6 +32,11 @@ public class TicketInfoVO {
//创建人头像
private String createUserAvatar;
/**
* 用户来源平台app或者admin
*/
private String userPlatform;
//提交时间
private LocalDateTime createTime;

View File

@ -1,5 +1,6 @@
package com.nflg.mobilebroken.push;
import cn.hutool.core.util.IdUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -22,6 +23,8 @@ public class UserSseEmitter extends SseEmitter {
private Integer ticketId;
private String id = IdUtil.getSnowflakeNextIdStr();
private ScheduledFuture<?> heartbeatFuture;
public UserSseEmitter(){
@ -34,8 +37,8 @@ public class UserSseEmitter extends SseEmitter {
this.ticketId = ticketId;
}
public String getUser(){
return from + "-" + userId;
public String getLable() {
return from + "-" + userId + ",连接id:" + id;
}
public void startHeartbeat(TaskScheduler taskScheduler) {
@ -44,7 +47,7 @@ public class UserSseEmitter extends SseEmitter {
send(SseEmitter.event().data("ping"));
} catch (IOException e) {
heartbeatFuture.cancel(true);
log.error("sse发送ping数据出错({}):{}", getUser(),e.getMessage());
log.error("sse发送ping数据出错({}):{}", getLable(),e.getMessage());
completeWithError(e);
}
}, 30_000);

View File

@ -34,53 +34,53 @@ public class SSEManagerBase {
protected String from;
protected void check(){
VUtils.trueThrow(IS_SHUTDOWN).throwMessage(STATE.ServiceConnectRefused,"SSE服务已关闭");
protected void check() {
VUtils.trueThrow(IS_SHUTDOWN).throwMessage(STATE.ServiceConnectRefused, "SSE服务已关闭");
}
@PreDestroy
protected void shutdown() {
IS_SHUTDOWN=true;
IS_SHUTDOWN = true;
log.warn("准备关闭SSE服务");
SSE_EMITTERS.forEach(emitter->{
SSE_EMITTERS.forEach(emitter -> {
try {
emitter.send("因SSE服务关闭,连接即将断开");
emitter.complete();
}catch (Exception ex){
log.error("SSE发送消息失败",ex);
} catch (Exception ex) {
log.error("SSE发送消息失败", ex);
emitter.completeWithError(ex);
}
});
log.warn("SSE服务已关闭");
}
protected SseEmitter connect(Integer ticketId,Integer userId) {
protected SseEmitter connect(Integer ticketId, Integer userId) {
check();
log.info(from + "SSE连接:用户id:" + userId + ",工单id:" + ticketId);
UserSseEmitter emitter = new UserSseEmitter(from, userId, ticketId);
log.info(from + "SSE连接:用户id:" + userId + ",工单id:" + ticketId + ",连接id:" + emitter.getId());
emitter.startHeartbeat(taskScheduler);
SSE_EMITTERS.add(emitter);
emitter.onError((ex) -> {
remove(emitter);
log.error("SSE异常({}):{}", ex.getMessage(),emitter.getUser());
log.error("SSE异常({}):{}", ex.getMessage(), emitter.getLable());
});
emitter.onTimeout(() -> {
remove(emitter);
log.error("SSE超时:"+userId);
log.error("SSE超时:" + userId);
});
emitter.onCompletion(() -> {
remove(emitter);
log.error("SSE完成:"+userId);
log.error("SSE完成:" + userId);
});
try {
log.info("发送连接成功信息:" + emitter.getLable());
emitter.send(SseEmitter.event().data("已连接").reconnectTime(5000));
}catch (ClientAbortException e){
log.error("客户端断开连接:{}", userId);
emitter.completeWithError(e);
}
catch (IOException e) {
} catch (ClientAbortException e) {
log.error("断开已连接:{}", e.getMessage());
emitter.complete();
} catch (IOException e) {
log.error("sse发送数据出错:{}", e.getMessage());
emitter.completeWithError(e);
emitter.complete();
}
return emitter;
}
@ -90,19 +90,21 @@ public class SSEManagerBase {
List<UserSseEmitter> emitters = SSE_EMITTERS.stream()
.filter(s -> Objects.equals(s.getTicketId(), ticketId))
.collect(Collectors.toList());
if (CollectionUtil.isEmpty(emitters)){
if (CollectionUtil.isEmpty(emitters)) {
log.info(StrUtil.format(from + "没有用户连接工单:{}", ticketId));
return;
}
emitters.forEach(emitter -> {
try {
log.info("发送给用户:" + emitter.getLable());
emitter.send(SseEmitter.event().name(dto.getType()).data(dto.getData()));
log.info("发送成功");
} catch (ClientAbortException e) {
log.error("客户端断开连接:{}", emitter.getUser());
emitter.completeWithError(e);
log.error("发送失败,{}", e.getMessage());
emitter.complete();
} catch (IOException e) {
log.error("sse发送数据出错:{}", e.getMessage());
emitter.completeWithError(e);
log.error("发送失败,sse发送数据出错:{}", e.getMessage());
emitter.complete();
}
});
}
@ -118,21 +120,22 @@ public class SSEManagerBase {
}
emitters.forEach(emitter -> {
try {
log.info("发送给用户:" + emitter.getLable());
emitter.send(SseEmitter.event().name(dto.getType()).data(dto.getData()));
log.info("发送成功");
} catch (ClientAbortException e) {
log.error("客户端断开连接:{}", emitter.getUser());
emitter.completeWithError(e);
}catch (IOException e) {
log.error("sse发送数据出错:{}", e.getMessage());
emitter.completeWithError(e);
log.error("发送失败,{}", e.getMessage());
emitter.complete();
} catch (IOException e) {
log.error("发送失败,sse发送数据出错:{}", e.getMessage());
emitter.complete();
}
});
}
private void remove(UserSseEmitter emitter){
private void remove(UserSseEmitter emitter) {
SSE_EMITTERS.remove(emitter);
emitter.complete();
emitter=null;
emitter = null;
}
}

View File

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

View File

@ -41,6 +41,7 @@ public class TicketCallJoinServiceImpl extends ServiceImpl<TicketCallJoinMapper,
TicketCall ticketCall = ticketCallService.lambdaQuery()
.eq(TicketCall::getTicketId, ticketId)
.ne(TicketCall::getState, 2)
.last("limit 1")
.one();
VUtils.trueThrowBusinessError(Objects.isNull(ticketCall)).throwMessage("加入失败,通话已结束");
TicketCallJoin ticketCallJoin = lambdaQuery()
@ -72,18 +73,19 @@ public class TicketCallJoinServiceImpl extends ServiceImpl<TicketCallJoinMapper,
.ne(TicketCallJoin::getState, 2)
.eq(TicketCallJoin::getUserId, userId)
.eq(TicketCallJoin::getFrom, from)
.last("limit 1")
.one();
boolean flag = false;
if (Objects.nonNull(ticketCallJoin)) {
ticketCallJoin.setState(2);
if (!reject) {
if (reject) {
ticketCallJoin.setHangupTime(LocalDateTime.now());
}
updateById(ticketCallJoin);
if (StrUtil.equals(from, Constant.FROM_APP)) {
lambdaUpdate()
.set(TicketCallJoin::getState, 2)
.set(!reject, TicketCallJoin::getHangupTime, LocalDateTime.now())
.set(reject, TicketCallJoin::getHangupTime, LocalDateTime.now())
.eq(TicketCallJoin::getCallId, callId)
.ne(TicketCallJoin::getFrom, from)
.update();
@ -94,7 +96,7 @@ public class TicketCallJoinServiceImpl extends ServiceImpl<TicketCallJoinMapper,
.exists()){
ticketCallService.lambdaUpdate()
.set(TicketCall::getState, 2)
.set(!reject,TicketCall::getHangupTime, LocalDateTime.now())
.set(reject,TicketCall::getHangupTime, LocalDateTime.now())
.eq(TicketCall::getId, callId)
.update();
flag = true;

View File

@ -41,17 +41,17 @@ public class TicketCallServiceImpl extends ServiceImpl<TicketCallMapper, TicketC
@Transactional
@Override
public void add(Integer ticketId, Integer callerUserId, Integer calledUserId, String from) {
public void add(Integer ticketId, Integer callerUserId,String callerUserFrom, Integer calledUserId, String calledUserFrom) {
TicketCall ticketCall = new TicketCall()
.setTicketId(ticketId)
.setCallerUserId(callerUserId)
.setState(0)
.setFrom(from)
.setFrom(callerUserFrom)
.setCreateTime(LocalDateTime.now());
save(ticketCall);
ticketCallJoinService.save(new TicketCallJoin()
.setCallId(ticketCall.getId())
.setFrom(StrUtil.equals(from, Constant.FROM_APP) ? Constant.FROM_ADMIN : Constant.FROM_APP)
.setFrom(calledUserFrom)
.setUserId(calledUserId)
.setState(0)
.setCreateTime(LocalDateTime.now())
@ -63,6 +63,7 @@ public class TicketCallServiceImpl extends ServiceImpl<TicketCallMapper, TicketC
TicketCall ticketCall = lambdaQuery()
.eq(TicketCall::getTicketId, ticketId)
.ne(TicketCall::getState, 2)
.last("limit 1")
.one();
boolean flag = false;
if (Objects.nonNull(ticketCall)){
@ -70,7 +71,7 @@ public class TicketCallServiceImpl extends ServiceImpl<TicketCallMapper, TicketC
|| ticketCallJoinService.allIsHangUp(ticketCall.getId())){
ticketCall.setState(2);
ticketCallJoinService.hangUpAll(ticketCall.getId());
if (!reject) {
if (reject) {
ticketCall.setHangupTime(LocalDateTime.now());
}
updateById(ticketCall);

View File

@ -39,7 +39,7 @@
<where>
t.state!=4
<if test="!request.ticketManager">
AND (FIND_IN_SET(#{userId},t.handle)>0 OR fun_inAduit(t.id,#{userId})=1)
AND (FIND_IN_SET(#{userId},t.handle)>0 OR fun_inAduit(t.id,#{userId})=1 OR (t.user_platform='admin' AND t.user_id=#{userId}))
</if>
<if test="request.state!=null">
AND t.state=#{request.state}

View File

@ -24,9 +24,10 @@
<module>nflg-mobilebroken-product</module>
</modules>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>11</java.version>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>