Commit 63e13557 by zhangxingmin

push

parent e00f6e6d
...@@ -31,16 +31,17 @@ public class ApiCoSessionController implements ApiCoSessionFeignClient { ...@@ -31,16 +31,17 @@ public class ApiCoSessionController implements ApiCoSessionFeignClient {
private ApiCoSessionService apiCoSessionService; private ApiCoSessionService apiCoSessionService;
/** /**
* 客户创建会话(生成共享码) * 创建协同会话
* @param request * @param request 创建会话请求
* @return * @return
*/ */
@Override
public Result<CreateResponse> create(CreateRequest request) { public Result<CreateResponse> create(CreateRequest request) {
return apiCoSessionService.create(request); return apiCoSessionService.create(request);
} }
/** /**
* 顾问加入会话(输入共享码加入房间,可以多次加入) * 加入协同会话
* @param request * @param request
* @return * @return
*/ */
......
...@@ -14,6 +14,7 @@ import com.yd.communication.service.model.CoSession; ...@@ -14,6 +14,7 @@ import com.yd.communication.service.model.CoSession;
public interface ApiCoSessionService { public interface ApiCoSessionService {
Result<CreateResponse> create(CreateRequest request); Result<CreateResponse> create(CreateRequest request);
Result<JoinResponse> join(JoinRequest request); Result<JoinResponse> join(JoinRequest request);
......
...@@ -4,10 +4,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; ...@@ -4,10 +4,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yd.common.enums.CommonEnum; import com.yd.common.enums.CommonEnum;
import com.yd.common.exception.BusinessException; import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result; import com.yd.common.result.Result;
import com.yd.common.utils.ByteArrayMultipartFile;
import com.yd.common.utils.RandomStringGenerator; import com.yd.common.utils.RandomStringGenerator;
import com.yd.common.utils.RedisUtil; import com.yd.common.utils.RedisUtil;
import com.yd.communication.api.service.ApiCoSessionService; import com.yd.communication.api.service.ApiCoSessionService;
import com.yd.communication.api.utils.ClientInfoUtils; import com.yd.communication.api.utils.ClientInfoUtils;
import com.yd.communication.api.utils.QRCodeUtils;
import com.yd.communication.feign.dto.RoomRedisInfoDTO; import com.yd.communication.feign.dto.RoomRedisInfoDTO;
import com.yd.communication.feign.enums.CoSessionStatusEnum; import com.yd.communication.feign.enums.CoSessionStatusEnum;
import com.yd.communication.feign.enums.ControlHolderTypeEnum; import com.yd.communication.feign.enums.ControlHolderTypeEnum;
...@@ -25,11 +27,17 @@ import com.yd.communication.service.service.ICoOperationLogService; ...@@ -25,11 +27,17 @@ import com.yd.communication.service.service.ICoOperationLogService;
import com.yd.communication.service.service.ICoSessionService; import com.yd.communication.service.service.ICoSessionService;
import com.yd.communication.service.service.IRecordingTaskService; import com.yd.communication.service.service.IRecordingTaskService;
import com.yd.communication.service.utils.RandomUtil; import com.yd.communication.service.utils.RandomUtil;
import com.yd.oss.feign.client.ApiOssFeignClient;
import com.yd.oss.feign.request.ApiUploadFileRequest;
import com.yd.oss.feign.response.ApiUploadResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.UUID; import java.util.UUID;
...@@ -50,40 +58,11 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService { ...@@ -50,40 +58,11 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService {
@Resource @Resource
private RedisUtil redisUtil; private RedisUtil redisUtil;
/** @Resource
* 客户创建会话(生成共享码) private ApiOssFeignClient apiOssFeignClient;
*/
@Override @Value("${co.room.base-url:https://default-domain.com/co/join}")
@Transactional(rollbackFor = Exception.class) private String roomBaseUrl;
public Result<CreateResponse> create(CreateRequest request) {
log.info("【创建会话】收到创建请求, request={}", request);
try {
CoSession session = createSession(
request.getScope(),
request.getResourceType(),
request.getResourceId(),
request.getResourceInit(),
request.getOwnerId(),
request.getOwnerType(),
request.getUserId(),
request.getToken()
);
if (session == null) {
log.warn("【创建会话】创建失败,返回空会话");
return Result.success();
}
CreateResponse response = new CreateResponse();
response.setRoomId(session.getRoomId());
response.setRoomPwd(session.getRoomPwd());
response.setSessionBizId(session.getCoSessionBizId());
response.setStatus(session.getStatus());
log.info("【创建会话】成功,roomId={}, roomPwd={}, sessionBizId={}", session.getRoomId(), session.getRoomPwd(), session.getCoSessionBizId());
return Result.success(response);
} catch (Exception e) {
log.error("【创建会话】异常", e);
throw e;
}
}
/** /**
* 客户创建会话(生成共享码)——内部方法 * 客户创建会话(生成共享码)——内部方法
...@@ -133,7 +112,9 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService { ...@@ -133,7 +112,9 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService {
roomRedisInfoDTO.setUserId(userId); roomRedisInfoDTO.setUserId(userId);
roomRedisInfoDTO.setControlHolderType(session.getControlHolderType()); roomRedisInfoDTO.setControlHolderType(session.getControlHolderType());
roomRedisInfoDTO.setControlHolderId(session.getControlHolderId()); roomRedisInfoDTO.setControlHolderId(session.getControlHolderId());
redisUtil.setCacheObject(RedisEnum.ROOM.getPrefix() + roomId, roomRedisInfoDTO, roomRedisInfoDTO.setRoomId(roomId);
roomRedisInfoDTO.setRoomPwd(roomPwd);
redisUtil.setCacheObject(RedisEnum.ROOM.getPrefix() + roomId + ":" + roomPwd, roomRedisInfoDTO,
RedisEnum.ROOM.getTimeout(), RedisEnum.ROOM.getTimeUnit()); RedisEnum.ROOM.getTimeout(), RedisEnum.ROOM.getTimeUnit());
log.info("【创建会话-内部】Redis缓存已设置, key={}", RedisEnum.ROOM.getPrefix() + roomId); log.info("【创建会话-内部】Redis缓存已设置, key={}", RedisEnum.ROOM.getPrefix() + roomId);
...@@ -163,7 +144,95 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService { ...@@ -163,7 +144,95 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService {
} }
/** /**
* 顾问加入会话(输入共享码加入房间,可以多次加入) * 创建协同会话
* @param request
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Result<CreateResponse> create(CreateRequest request) {
log.info("【创建协同会话】收到创建请求, request={}", request);
try {
// 1. 创建会话
CoSession session = createSession(
request.getScope(),
request.getResourceType(),
request.getResourceId(),
request.getResourceInit(),
request.getOwnerId(),
request.getOwnerType(),
request.getUserId(),
request.getToken()
);
if (session == null) {
log.warn("【创建协同会话-生成二维码】创建会话失败");
return Result.success();
}
// 2. 构建房间链接(从配置读取)
String roomLink = roomBaseUrl + "?roomId=" + session.getRoomId() + "&pwd=" + session.getRoomPwd();
log.info("【创建协同会话】生成房间链接: {}", roomLink);
// 3. 生成二维码图片字节数组(PNG格式)
byte[] qrCodeBytes = QRCodeUtils.generateQRCode(roomLink, 300, 300);
// 4. 将字节数组转换为 MultipartFile
String fileName = session.getCoSessionBizId() + ".png";
MultipartFile multipartFile = new ByteArrayMultipartFile(
qrCodeBytes, // 图片字节数组
"file", // 字段名,与 Feign @RequestPart("file") 匹配
fileName, // 原始文件名
"image/png" // 内容类型
);
// 5. 构建 OSS 上传请求参数
ApiUploadFileRequest ossRequest = new ApiUploadFileRequest();
ossRequest.setObjectBizId(session.getCoSessionBizId()); // 必填,使用会话业务ID
ossRequest.setObjectType("co_session_qr"); // 自定义对象类型
ossRequest.setObjectName("协同会话二维码");
ossRequest.setProjectBizId(request.getResourceId()); // 可关联资源ID
ossRequest.setTenantBizId(request.getOwnerId()); // 可关联所有者
// 6. 调用 Feign 上传
Result<ApiUploadResponse> uploadResult = apiOssFeignClient.uploadFileBody(multipartFile, ossRequest);
if (uploadResult == null || uploadResult.getCode() != 200 || uploadResult.getData() == null) {
log.error("【创建协同会话】OSS上传失败,result={}", uploadResult);
throw new BusinessException("二维码上传失败:" + (uploadResult != null ? uploadResult.getMsg() : "未知错误"));
}
// 7. 获取文件访问 URL(假设 ApiUploadResponse 包含 url 字段)
ApiUploadResponse uploadData = uploadResult.getData();
String roomQrCodeUrl = uploadData.getUrl(); // 若字段名为 fileUrl 则对应调整
log.info("【创建协同会话】OSS上传成功,文件URL: {}", roomQrCodeUrl);
//8. 更新会话信息表数据
session.setRoomQrCode(roomQrCodeUrl);
session.setRoomLink(roomLink);
iCoSessionService.saveOrUpdate(session);
//设置房间链接失效时间(用于扫码和链接直接访问,加载页面后调用校验接口判断是否失效)
redisUtil.setCacheObject(RedisEnum.ROOM_LINK.getPrefix() + session.getRoomId() + ":" + session.getRoomPwd(), session.getRoomLink(),
RedisEnum.ROOM_LINK.getTimeout(), RedisEnum.ROOM_LINK.getTimeUnit());
//9. 组装响应
CreateResponse response = new CreateResponse();
response.setRoomId(session.getRoomId());
response.setRoomPwd(session.getRoomPwd());
response.setSessionBizId(session.getCoSessionBizId());
response.setStatus(session.getStatus());
response.setRoomLink(roomLink);
response.setRoomQrCode(roomQrCodeUrl);
log.info("【创建协同会话】成功,roomId={}, sessionBizId={}", session.getRoomId(), session.getCoSessionBizId());
return Result.success(response);
} catch (Exception e) {
log.error("【创建协同会话】异常", e);
throw new BusinessException("创建协同会话失败:" + e.getMessage());
}
}
/**
* 加入协同会话
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
...@@ -171,6 +240,7 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService { ...@@ -171,6 +240,7 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService {
log.info("【加入会话】收到加入请求, request={}", request); log.info("【加入会话】收到加入请求, request={}", request);
try { try {
CoSession session = joinSession( CoSession session = joinSession(
request.getRoomId(),
request.getRoomPwd(), request.getRoomPwd(),
request.getParticipantId(), request.getParticipantId(),
request.getParticipantType() request.getParticipantType()
...@@ -206,19 +276,30 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService { ...@@ -206,19 +276,30 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService {
} }
/** /**
* 顾问加入会话(内部方法) * 顾问加入会话
* @param roomId
* @param roomPwd
* @param participantId
* @param participantType
* @return
*/ */
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public CoSession joinSession(String roomPwd, String participantId, String participantType) { public CoSession joinSession(String roomId,String roomPwd, String participantId, String participantType) {
log.info("【加入会话-内部】开始, roomPwd={}, participantId={}, participantType={}", roomPwd, participantId, participantType); log.info("【加入会话-内部】开始, roomId={}, roomPwd={}, participantId={}, participantType={}", roomId, roomPwd, participantId, participantType);
try { try {
//校验房间链接是否失效
String roomLink = redisUtil.getCacheObject(RedisEnum.ROOM_LINK.getPrefix() + roomId + ":" + roomPwd);
if (StringUtils.isBlank(roomLink)) {
throw new BusinessException("访问链接已失效,请重新生成");
}
LambdaQueryWrapper<CoSession> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<CoSession> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CoSession::getRoomPwd, roomPwd) wrapper.eq(CoSession::getRoomId, roomId)
.eq(CoSession::getRoomPwd, roomPwd)
.last(" limit 1 "); .last(" limit 1 ");
CoSession session = iCoSessionService.getOne(wrapper); CoSession session = iCoSessionService.getOne(wrapper);
if (session == null) { if (session == null) {
log.error("【加入会话-内部】未找到会话,roomPwd={}", roomPwd); log.error("【加入会话-内部】未找到会话,roomId={}, roomPwd={}", roomId, roomPwd);
throw new BusinessException("房间密码(共享码)错误,或客户未发起会话"); throw new BusinessException("房间号或者密码错误,无法加入");
} }
log.info("【加入会话-内部】找到会话, roomId={}, status={}, currentHolder={}:{}", log.info("【加入会话-内部】找到会话, roomId={}, status={}, currentHolder={}:{}",
session.getRoomId(), session.getStatus(), session.getControlHolderType(), session.getControlHolderId()); session.getRoomId(), session.getStatus(), session.getControlHolderType(), session.getControlHolderId());
......
package com.yd.communication.api.utils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class QRCodeUtils {
/**
* 生成二维码图片的字节数组(PNG格式)
*
* @param content 二维码内容(如URL)
* @param width 图片宽度(像素)
* @param height 图片高度(像素)
* @return PNG图片的字节数组
*/
public static byte[] generateQRCode(String content, int width, int height) {
try {
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.MARGIN, 1); // 边距
QRCodeWriter writer = new QRCodeWriter();
BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", baos);
return baos.toByteArray();
} catch (Exception e) {
log.error("生成二维码失败,content={}, width={}, height={}", content, width, height, e);
throw new RuntimeException("生成二维码失败", e);
}
}
}
\ No newline at end of file
...@@ -25,7 +25,7 @@ import org.springframework.web.bind.annotation.*; ...@@ -25,7 +25,7 @@ import org.springframework.web.bind.annotation.*;
public interface ApiCoSessionFeignClient { public interface ApiCoSessionFeignClient {
/** /**
* 客户创建协同会话(生成共享码) * 创建协同会话
* *
* @param request 创建会话请求 * @param request 创建会话请求
* @return 会话信息 * @return 会话信息
......
...@@ -10,4 +10,6 @@ public class RedisConstants { ...@@ -10,4 +10,6 @@ public class RedisConstants {
*/ */
public static final String ROOM_KEY_PREFIX = "room:"; public static final String ROOM_KEY_PREFIX = "room:";
public static final String ROOM_LINK_KEY_PREFIX = "room:link:";
} }
...@@ -13,6 +13,16 @@ public class RoomRedisInfoDTO implements Serializable { ...@@ -13,6 +13,16 @@ public class RoomRedisInfoDTO implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 协同房间号(房间ID)
*/
private String roomId;
/**
* 协同房间密码(输入密码进入房间,协同共享码,进入这个协同房间号的密码)
*/
private String roomPwd;
/**
* 控制权持有者类型:owner(资源所有者类型)/participant(参与者类型) * 控制权持有者类型:owner(资源所有者类型)/participant(参与者类型)
*/ */
private String controlHolderType; private String controlHolderType;
......
...@@ -10,7 +10,9 @@ import java.util.concurrent.TimeUnit; ...@@ -10,7 +10,9 @@ import java.util.concurrent.TimeUnit;
public enum RedisEnum { public enum RedisEnum {
//协同房间缓存信息redis参数 //协同房间缓存信息redis参数
ROOM(RedisConstants.ROOM_KEY_PREFIX,600000000,TimeUnit.MINUTES), ROOM(RedisConstants.ROOM_KEY_PREFIX,120,TimeUnit.MINUTES),
//房间链接缓存信息redis参数
ROOM_LINK(RedisConstants.ROOM_KEY_PREFIX,120,TimeUnit.MINUTES),
; ;
//缓存key前缀 //缓存key前缀
......
...@@ -11,12 +11,12 @@ import javax.validation.constraints.NotBlank; ...@@ -11,12 +11,12 @@ import javax.validation.constraints.NotBlank;
@Data @Data
public class JoinRequest { public class JoinRequest {
// /** /**
// * 协同房间号(房间ID) * 协同房间号(房间ID)
// * 客户创建会话时生成的唯一房间标识 * 客户创建会话时生成的唯一房间标识
// * 示例:room_abc12345 * 示例:room_abc12345
// */ */
// private String roomId; private String roomId;
/** /**
* 协同房间密码(共享码) * 协同房间密码(共享码)
......
...@@ -27,4 +27,14 @@ public class CreateResponse { ...@@ -27,4 +27,14 @@ public class CreateResponse {
* 会话状态:0-进行中,1-已结束,2-已超时 * 会话状态:0-进行中,1-已结束,2-已超时
*/ */
private String status; private String status;
/**
* 协同房间二维码(扫码进入房间)
*/
private String roomQrCode;
/**
* 协同房间链接(访问链接进入房间)
*/
private String roomLink;
} }
\ No newline at end of file
...@@ -74,6 +74,15 @@ ...@@ -74,6 +74,15 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
</dependency>
<!-- &lt;!&ndash; 阿里云 Java SDK 核心库 &ndash;&gt;--> <!-- &lt;!&ndash; 阿里云 Java SDK 核心库 &ndash;&gt;-->
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>com.aliyun</groupId>--> <!-- <groupId>com.aliyun</groupId>-->
......
...@@ -97,12 +97,24 @@ public class CoSession implements Serializable { ...@@ -97,12 +97,24 @@ public class CoSession implements Serializable {
private String roomId; private String roomId;
/** /**
* 协同房间密码(协同共享码,进入这个协同房间号的密码) * 协同房间密码(输入密码进入房间,协同共享码,进入这个协同房间号的密码)
*/ */
@TableField("room_pwd") @TableField("room_pwd")
private String roomPwd; private String roomPwd;
/** /**
* 协同房间二维码(扫码进入房间)
*/
@TableField("room_qr_code")
private String roomQrCode;
/**
* 协同房间链接(访问链接进入房间)
*/
@TableField("room_link")
private String roomLink;
/**
* RTC频道前缀,便于区分业务 * RTC频道前缀,便于区分业务
*/ */
@TableField("channel_prefix") @TableField("channel_prefix")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment