视频会议
This commit is contained in:
@@ -41,6 +41,14 @@ type cloudLawCreateMeetingRequest struct {
|
|||||||
CallType string `json:"callType"`
|
CallType string `json:"callType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type cloudLawJoinMeetingRequest struct {
|
||||||
|
ReceiverID int64 `json:"receiverId"`
|
||||||
|
ReceiverType string `json:"receiverType"`
|
||||||
|
ReceiverPhone string `json:"receiverPhone"`
|
||||||
|
ReceiverName string `json:"receiverName"`
|
||||||
|
CallID string `json:"callId"`
|
||||||
|
}
|
||||||
|
|
||||||
func (ctl *MeetingController) requireCloudLawInternalToken(c *gin.Context) bool {
|
func (ctl *MeetingController) requireCloudLawInternalToken(c *gin.Context) bool {
|
||||||
expected := ""
|
expected := ""
|
||||||
if ctl.meetingConfig != nil {
|
if ctl.meetingConfig != nil {
|
||||||
@@ -270,6 +278,48 @@ func (ctl *MeetingController) CloudLawGetMeeting(c *gin.Context) {
|
|||||||
utils.ResponseOK(c, cloudLawMeetingPayload(room, onlineCount))
|
utils.ResponseOK(c, cloudLawMeetingPayload(room, onlineCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctl *MeetingController) CloudLawJoinMeeting(c *gin.Context) {
|
||||||
|
if !ctl.requireCloudLawInternalToken(c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := c.Param("code")
|
||||||
|
if code == "" {
|
||||||
|
utils.ResponseBadRequest(c, "会议号不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req cloudLawJoinMeetingRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
utils.ResponseBadRequest(c, "参数校验失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.ReceiverPhone = strings.TrimSpace(req.ReceiverPhone)
|
||||||
|
req.ReceiverName = strings.TrimSpace(req.ReceiverName)
|
||||||
|
if req.ReceiverPhone == "" {
|
||||||
|
utils.ResponseBadRequest(c, "receiverPhone不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, created, err := ctl.meetingService.EnsureCloudLawUserByPhone(c.Request.Context(), req.ReceiverPhone, req.ReceiverName)
|
||||||
|
if err != nil {
|
||||||
|
ctl.handleError(c, err, "创建云律会议接单人失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
room, participant, _, err := ctl.meetingService.JoinRoomForInternal(c.Request.Context(), user.ID, code)
|
||||||
|
if err != nil {
|
||||||
|
ctl.handleError(c, err, "云律接单人加入会议失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
payload := cloudLawMeetingPayload(room, 0)
|
||||||
|
payload["callId"] = req.CallID
|
||||||
|
payload["receiverId"] = req.ReceiverID
|
||||||
|
payload["receiverType"] = req.ReceiverType
|
||||||
|
payload["echoChatParticipantUserId"] = strconv.FormatInt(user.ID, 10)
|
||||||
|
payload["echoChatParticipantUserCreated"] = created
|
||||||
|
if participant != nil {
|
||||||
|
payload["participantId"] = strconv.FormatInt(participant.ID, 10)
|
||||||
|
}
|
||||||
|
utils.ResponseOK(c, payload)
|
||||||
|
}
|
||||||
|
|
||||||
func (ctl *MeetingController) CloudLawEndMeeting(c *gin.Context) {
|
func (ctl *MeetingController) CloudLawEndMeeting(c *gin.Context) {
|
||||||
if !ctl.requireCloudLawInternalToken(c) {
|
if !ctl.requireCloudLawInternalToken(c) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ func RegisterRoutes(
|
|||||||
registerCloudLawRoutes := func(group *gin.RouterGroup) {
|
registerCloudLawRoutes := func(group *gin.RouterGroup) {
|
||||||
group.POST("/meetings", ctrl.CloudLawCreateMeeting)
|
group.POST("/meetings", ctrl.CloudLawCreateMeeting)
|
||||||
group.GET("/meetings/:code", ctrl.CloudLawGetMeeting)
|
group.GET("/meetings/:code", ctrl.CloudLawGetMeeting)
|
||||||
|
group.POST("/meetings/:code/join", ctrl.CloudLawJoinMeeting)
|
||||||
group.POST("/meetings/:code/end", ctrl.CloudLawEndMeeting)
|
group.POST("/meetings/:code/end", ctrl.CloudLawEndMeeting)
|
||||||
}
|
}
|
||||||
registerCloudLawRoutes(r.Group("/api/v1/cloud-law"))
|
registerCloudLawRoutes(r.Group("/api/v1/cloud-law"))
|
||||||
|
|||||||
@@ -355,7 +355,62 @@ func (s *MeetingService) CreateRoom(ctx context.Context, hostID int64, req *dto.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *MeetingService) CreateRoomForInternal(ctx context.Context, hostID int64, req *dto.CreateMeetingRoomRequest) (*model.MeetingRoom, *model.MeetingParticipant, string, error) {
|
func (s *MeetingService) CreateRoomForInternal(ctx context.Context, hostID int64, req *dto.CreateMeetingRoomRequest) (*model.MeetingRoom, *model.MeetingParticipant, string, error) {
|
||||||
return s.createRoom(ctx, hostID, req, false, false, "service.meeting_service.CreateRoomForInternal")
|
return s.createRoom(ctx, hostID, req, false, true, "service.meeting_service.CreateRoomForInternal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MeetingService) JoinRoomForInternal(ctx context.Context, userID int64, code string) (*model.MeetingRoom, *model.MeetingParticipant, string, error) {
|
||||||
|
funcName := "service.meeting_service.JoinRoomForInternal"
|
||||||
|
room, err := s.roomDAO.GetByCode(ctx, code)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", err
|
||||||
|
}
|
||||||
|
if room == nil {
|
||||||
|
return nil, nil, "", ErrMeetingNotFound
|
||||||
|
}
|
||||||
|
if room.Status == constants.MeetingStatusEnded {
|
||||||
|
return nil, nil, "", ErrMeetingEnded
|
||||||
|
}
|
||||||
|
if existing, pErr := s.participantDAO.GetByRoomAndUser(ctx, room.ID, userID); pErr != nil {
|
||||||
|
return nil, nil, "", pErr
|
||||||
|
} else if existing != nil && existing.IsActive() {
|
||||||
|
routerID, _ := s.mediaOrchestrator.ResolveRouterID(code)
|
||||||
|
return room, existing, routerID, nil
|
||||||
|
}
|
||||||
|
activeCount, err := s.participantDAO.CountActiveByRoom(ctx, room.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", err
|
||||||
|
}
|
||||||
|
if int(activeCount) >= room.MaxMembers {
|
||||||
|
return nil, nil, "", ErrMeetingFull
|
||||||
|
}
|
||||||
|
participant, err := s.participantDAO.JoinRoom(ctx, room.ID, userID, constants.MeetingRoleParticipant)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, dao.ErrAlreadyInMeeting) {
|
||||||
|
existing, getErr := s.participantDAO.GetByRoomAndUser(ctx, room.ID, userID)
|
||||||
|
if getErr != nil {
|
||||||
|
return nil, nil, "", getErr
|
||||||
|
}
|
||||||
|
routerID, _ := s.mediaOrchestrator.ResolveRouterID(code)
|
||||||
|
return room, existing, routerID, nil
|
||||||
|
}
|
||||||
|
return nil, nil, "", err
|
||||||
|
}
|
||||||
|
if s.lifecycleSvc != nil {
|
||||||
|
s.lifecycleSvc.CancelEmptyTTL(ctx, code)
|
||||||
|
}
|
||||||
|
routerID, _ := s.mediaOrchestrator.ResolveRouterID(code)
|
||||||
|
if routerID == "" {
|
||||||
|
newID, mediaErr := s.mediaOrchestrator.CreateRouter(ctx, code)
|
||||||
|
if mediaErr != nil {
|
||||||
|
if _, leaveErr := s.participantDAO.LeaveRoom(ctx, room.ID, userID, constants.MeetingLeftReasonSelf); leaveErr != nil {
|
||||||
|
logs.Warn(ctx, funcName, "内部入会补偿 LeaveRoom 失败", zap.Int64("room_id", room.ID), zap.Int64("user_id", userID), zap.Error(leaveErr))
|
||||||
|
}
|
||||||
|
return nil, nil, "", ErrMediaServiceUnavailable
|
||||||
|
}
|
||||||
|
routerID = newID
|
||||||
|
}
|
||||||
|
logs.Info(ctx, funcName, "云律接单人加入会议成功", zap.String("room_code", code), zap.Int64("user_id", userID))
|
||||||
|
return room, participant, routerID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MeetingService) createRoom(ctx context.Context, hostID int64, req *dto.CreateMeetingRoomRequest, enforceSingleActive bool, addHostParticipant bool, funcName string) (*model.MeetingRoom, *model.MeetingParticipant, string, error) {
|
func (s *MeetingService) createRoom(ctx context.Context, hostID int64, req *dto.CreateMeetingRoomRequest, enforceSingleActive bool, addHostParticipant bool, funcName string) (*model.MeetingRoom, *model.MeetingParticipant, string, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user