This commit is contained in:
2025-12-22 13:08:08 +08:00
parent 85e4513284
commit f0a6e03989
26 changed files with 2023 additions and 627 deletions

View File

@@ -0,0 +1,134 @@
package org.xyzh.api.workcase.constant;
/**
* @description 工单模块常量类
* @filename WorkcaseConstant.java
* @author cascade
* @copyright xyzh
* @since 2025-12-22
*/
public class WorkcaseConstant {
private WorkcaseConstant() {
}
// ========================= Redis Key 前缀 ==========================
/**
* 聊天室Redis key前缀
*/
public static final String REDIS_CHAT_PREFIX = "chat:room:";
/**
* 聊天室在线用户 chat:room:online:{roomId}
*/
public static final String REDIS_CHAT_ONLINE = REDIS_CHAT_PREFIX + "online:";
/**
* 聊天室消息锁 chat:room:lock:{roomId}
*/
public static final String REDIS_CHAT_LOCK = REDIS_CHAT_PREFIX + "lock:";
/**
* 聊天室最后消息时间 chat:room:lasttime:{roomId}
*/
public static final String REDIS_CHAT_LASTTIME = REDIS_CHAT_PREFIX + "lasttime:";
/**
* 聊天室列表更新通知频道
*/
public static final String REDIS_CHAT_LIST_UPDATE = "chat:list:update";
/**
* 会议Redis key前缀
*/
public static final String REDIS_MEET_PREFIX = "meet:";
// ========================= 用户类型 ==========================
/**
* 用户类型:来客
*/
public static final String USER_TYPE_GUEST = "guest";
/**
* 用户类型:客服
*/
public static final String USER_TYPE_STAFF = "staff";
/**
* 用户类型AI助手
*/
public static final String USER_TYPE_AI = "ai";
// ========================= 状态常量 ==========================
/**
* 聊天室状态:活跃
*/
public static final String ROOM_STATUS_ACTIVE = "active";
/**
* 聊天室状态:已关闭
*/
public static final String ROOM_STATUS_CLOSED = "closed";
/**
* 成员状态:活跃
*/
public static final String MEMBER_STATUS_ACTIVE = "active";
/**
* 成员状态:已离开
*/
public static final String MEMBER_STATUS_LEFT = "left";
/**
* 成员状态:被移除
*/
public static final String MEMBER_STATUS_REMOVED = "removed";
/**
* 消息状态:已发送
*/
public static final String MESSAGE_STATUS_SENT = "sent";
/**
* 消息状态:已撤回
*/
public static final String MESSAGE_STATUS_RECALLED = "recalled";
// ========================= 会议状态 ==========================
/**
* 会议状态:已计划
*/
public static final String MEETING_STATUS_SCHEDULED = "scheduled";
/**
* 会议状态:进行中
*/
public static final String MEETING_STATUS_ONGOING = "ongoing";
/**
* 会议状态:已结束
*/
public static final String MEETING_STATUS_ENDED = "ended";
// ========================= 客服状态 ==========================
/**
* 客服状态:在线
*/
public static final String SERVICE_STATUS_ONLINE = "online";
/**
* 客服状态:离线
*/
public static final String SERVICE_STATUS_OFFLINE = "offline";
/**
* 客服状态:忙碌
*/
public static final String SERVICE_STATUS_BUSY = "busy";
}

View File

@@ -1,5 +1,7 @@
package org.xyzh.api.workcase.dto;
import java.util.Date;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -53,7 +55,7 @@ public class TbChatRoomDTO extends BaseDTO {
private Integer unreadCount;
@Schema(description = "最后消息时间")
private String lastMessageTime;
private Date lastMessageTime;
@Schema(description = "最后一条消息内容")
private String lastMessage;
@@ -62,5 +64,5 @@ public class TbChatRoomDTO extends BaseDTO {
private String closedBy;
@Schema(description = "关闭时间")
private String closedTime;
private Date closedTime;
}

View File

@@ -1,5 +1,7 @@
package org.xyzh.api.workcase.dto;
import java.util.Date;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -25,15 +27,12 @@ public class TbChatRoomMemberDTO extends BaseDTO {
@Schema(description = "用户ID来客ID或员工ID")
private String userId;
@Schema(description = "用户类型guest-来客 agent-客服 ai-AI助手")
@Schema(description = "用户类型guest-来客 staff-客服 ai-AI助手")
private String userType;
@Schema(description = "用户名称")
private String userName;
@Schema(description = "角色owner-创建者 admin-管理员 member-普通成员")
private String role;
@Schema(description = "状态active-活跃 left-已离开 removed-被移除")
private String status;
@@ -41,14 +40,14 @@ public class TbChatRoomMemberDTO extends BaseDTO {
private Integer unreadCount;
@Schema(description = "最后阅读时间")
private String lastReadTime;
private Date lastReadTime;
@Schema(description = "最后阅读的消息ID")
private String lastReadMsgId;
@Schema(description = "加入时间")
private String joinTime;
private Date joinTime;
@Schema(description = "离开时间")
private String leaveTime;
private Date leaveTime;
}

View File

@@ -1,6 +1,8 @@
package org.xyzh.api.workcase.dto;
import java.util.Date;
import java.util.List;
import com.alibaba.fastjson2.JSONObject;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -15,7 +17,7 @@ import lombok.Data;
*/
@Data
@Schema(description = "聊天消息表对象")
public class TbChatMessageDTO extends BaseDTO {
public class TbChatRoomMessageDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "消息ID")
@@ -61,5 +63,5 @@ public class TbChatMessageDTO extends BaseDTO {
private Integer readCount;
@Schema(description = "发送时间")
private String sendTime;
private Date sendTime;
}

View File

@@ -1,5 +1,7 @@
package org.xyzh.api.workcase.dto;
import java.util.Date;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -32,10 +34,10 @@ public class TbMeetingParticipantDTO extends BaseDTO {
private String userName;
@Schema(description = "加入时间")
private String joinTime;
private Date joinTime;
@Schema(description = "离开时间")
private String leaveTime;
private Date leaveTime;
@Schema(description = "参与时长(秒)")
private Integer durationSeconds;

View File

@@ -1,6 +1,6 @@
package org.xyzh.api.workcase.dto;
import java.time.OffsetDateTime;
import java.util.Date;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -46,10 +46,10 @@ public class TbMeetingTranscriptionDTO extends BaseDTO {
private Double confidence;
@Schema(description = "语音开始时间")
private OffsetDateTime speechStartTime;
private Date speechStartTime;
@Schema(description = "语音结束时间")
private OffsetDateTime speechEndTime;
private Date speechEndTime;
@Schema(description = "语音时长(毫秒)")
private Integer durationMs;

View File

@@ -1,5 +1,7 @@
package org.xyzh.api.workcase.dto;
import java.util.Date;
import com.alibaba.fastjson2.JSONObject;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -60,10 +62,10 @@ public class TbVideoMeetingDTO extends BaseDTO {
private Integer maxParticipants;
@Schema(description = "实际开始时间")
private String actualStartTime;
private Date actualStartTime;
@Schema(description = "实际结束时间")
private String actualEndTime;
private Date actualEndTime;
@Schema(description = "会议时长(秒)")
private Integer durationSeconds;

View File

@@ -0,0 +1,201 @@
package org.xyzh.api.workcase.service;
import org.xyzh.api.workcase.dto.TbChatRoomDTO;
import org.xyzh.api.workcase.dto.TbChatRoomMemberDTO;
import org.xyzh.api.workcase.dto.TbChatRoomMessageDTO;
import org.xyzh.api.workcase.dto.TbCustomerServiceDTO;
import org.xyzh.api.workcase.vo.ChatRoomVO;
import org.xyzh.api.workcase.vo.ChatMemberVO;
import org.xyzh.api.workcase.vo.ChatRoomMessageVO;
import org.xyzh.api.workcase.vo.CustomerServiceVO;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageRequest;
/**
* @description 聊天室服务接口,管理聊天室、成员和消息
* @filename ChatRoomService.java
* @author cascade
* @copyright xyzh
* @since 2025-12-22
*/
public interface ChatRoomService {
// ========================= 聊天室管理 ==========================
/**
* @description 创建聊天室
* @param chatRoom 聊天室信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbChatRoomDTO> createChatRoom(TbChatRoomDTO chatRoom);
/**
* @description 更新聊天室
* @param chatRoom 聊天室信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbChatRoomDTO> updateChatRoom(TbChatRoomDTO chatRoom);
/**
* @description 关闭聊天室
* @param roomId 聊天室ID
* @param closedBy 关闭人
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> closeChatRoom(String roomId, String closedBy);
/**
* @description 删除聊天室
* @param roomId 聊天室ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> deleteChatRoom(String roomId);
/**
* @description 根据ID获取聊天室
* @param roomId 聊天室ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbChatRoomDTO> getChatRoomById(String roomId);
/**
* @description 获取聊天室列表/分页
* @param pageRequest 分页请求
* @author cascade
* @since 2025-12-22
*/
ResultDomain<ChatRoomVO> getChatRoomPage(PageRequest<TbChatRoomDTO> pageRequest);
// ========================= 聊天室成员管理 ==========================
/**
* @description 添加聊天室成员
* @param member 成员信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbChatRoomMemberDTO> addChatRoomMember(TbChatRoomMemberDTO member);
/**
* @description 移除聊天室成员
* @param memberId 成员ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> removeChatRoomMember(String memberId);
/**
* @description 更新成员信息(如角色、状态)
* @param member 成员信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbChatRoomMemberDTO> updateChatRoomMember(TbChatRoomMemberDTO member);
/**
* @description 获取聊天室成员列表
* @param roomId 聊天室ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<ChatMemberVO> getChatRoomMemberList(String roomId);
/**
* @description 更新成员已读状态
* @param memberId 成员ID
* @param lastReadMsgId 最后已读消息ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> updateMemberReadStatus(String memberId, String lastReadMsgId);
// ========================= 聊天消息管理 ==========================
/**
* @description 发送消息
* @param message 消息内容
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbChatRoomMessageDTO> sendMessage(TbChatRoomMessageDTO message);
/**
* @description 获取聊天室消息列表/分页
* @param pageRequest 分页请求
* @author cascade
* @since 2025-12-22
*/
ResultDomain<ChatRoomMessageVO> getChatMessagePage(PageRequest<TbChatRoomMessageDTO> pageRequest);
/**
* @description 删除消息
* @param messageId 消息ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> deleteMessage(String messageId);
// ========================= 客服人员管理 ==========================
/**
* @description 添加客服人员配置
* @param customerService 客服人员信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbCustomerServiceDTO> addCustomerService(TbCustomerServiceDTO customerService);
/**
* @description 更新客服人员配置
* @param customerService 客服人员信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbCustomerServiceDTO> updateCustomerService(TbCustomerServiceDTO customerService);
/**
* @description 删除客服人员配置
* @param userId 员工ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> deleteCustomerService(String userId);
/**
* @description 获取客服人员列表/分页
* @param pageRequest 分页请求
* @author cascade
* @since 2025-12-22
*/
ResultDomain<CustomerServiceVO> getCustomerServicePage(PageRequest<TbCustomerServiceDTO> pageRequest);
/**
* @description 更新客服人员在线状态
* @param userId 员工ID
* @param status 状态online-在线 busy-忙碌 offline-离线
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> updateCustomerServiceStatus(String userId, String status);
/**
* @description 获取可接待的客服人员(在线且工作量未满)
* @author cascade
* @since 2025-12-22
*/
ResultDomain<CustomerServiceVO> getAvailableCustomerServices();
/**
* @description 自动分配客服人员
* @param roomId 聊天室ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<CustomerServiceVO> assignCustomerService(String roomId);
}

View File

@@ -0,0 +1,186 @@
package org.xyzh.api.workcase.service;
import org.xyzh.api.workcase.dto.TbVideoMeetingDTO;
import org.xyzh.api.workcase.dto.TbMeetingParticipantDTO;
import org.xyzh.api.workcase.dto.TbMeetingTranscriptionDTO;
import org.xyzh.api.workcase.vo.VideoMeetingVO;
import org.xyzh.api.workcase.vo.MeetingParticipantVO;
import org.xyzh.api.workcase.vo.MeetingTranscriptionVO;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageRequest;
/**
* @description 视频会议服务接口管理Jitsi Meet会议、参与者和转录
* @filename MeetService.java
* @author cascade
* @copyright xyzh
* @since 2025-12-22
*/
public interface MeetService {
// ========================= 会议管理 ==========================
/**
* @description 创建视频会议
* @param meeting 会议信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbVideoMeetingDTO> createMeeting(TbVideoMeetingDTO meeting);
/**
* @description 更新会议信息
* @param meeting 会议信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbVideoMeetingDTO> updateMeeting(TbVideoMeetingDTO meeting);
/**
* @description 开始会议
* @param meetingId 会议ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbVideoMeetingDTO> startMeeting(String meetingId);
/**
* @description 结束会议
* @param meetingId 会议ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbVideoMeetingDTO> endMeeting(String meetingId);
/**
* @description 删除会议
* @param meetingId 会议ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> deleteMeeting(String meetingId);
/**
* @description 根据ID获取会议
* @param meetingId 会议ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbVideoMeetingDTO> getMeetingById(String meetingId);
/**
* @description 获取会议列表/分页
* @param pageRequest 分页请求
* @author cascade
* @since 2025-12-22
*/
ResultDomain<VideoMeetingVO> getMeetingPage(PageRequest<TbVideoMeetingDTO> pageRequest);
/**
* @description 生成会议加入链接/iframe URL
* @param meetingId 会议ID
* @param userId 用户ID
* @param userName 用户名称
* @author cascade
* @since 2025-12-22
*/
ResultDomain<String> generateMeetingJoinUrl(String meetingId, String userId, String userName);
/**
* @description 生成会议JWT Token
* @param meetingId 会议ID
* @param userId 用户ID
* @param isModerator 是否主持人
* @author cascade
* @since 2025-12-22
*/
ResultDomain<String> generateMeetingToken(String meetingId, String userId, boolean isModerator);
// ========================= 参与者管理 ==========================
/**
* @description 参与者加入会议
* @param participant 参与者信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbMeetingParticipantDTO> joinMeeting(TbMeetingParticipantDTO participant);
/**
* @description 参与者离开会议
* @param participantId 参与者ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> leaveMeeting(String participantId);
/**
* @description 获取会议参与者列表
* @param meetingId 会议ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<MeetingParticipantVO> getMeetingParticipantList(String meetingId);
/**
* @description 更新参与者信息
* @param participant 参与者信息
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbMeetingParticipantDTO> updateParticipant(TbMeetingParticipantDTO participant);
/**
* @description 设置参与者为主持人
* @param participantId 参与者ID
* @param isModerator 是否主持人
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> setModerator(String participantId, boolean isModerator);
// ========================= 转录管理 ==========================
/**
* @description 添加转录记录
* @param transcription 转录内容
* @author cascade
* @since 2025-12-22
*/
ResultDomain<TbMeetingTranscriptionDTO> addTranscription(TbMeetingTranscriptionDTO transcription);
/**
* @description 获取会议转录列表/分页
* @param pageRequest 分页请求
* @author cascade
* @since 2025-12-22
*/
ResultDomain<MeetingTranscriptionVO> getTranscriptionPage(PageRequest<TbMeetingTranscriptionDTO> pageRequest);
/**
* @description 获取会议完整转录文本
* @param meetingId 会议ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<String> getFullTranscriptionText(String meetingId);
/**
* @description 删除转录记录
* @param transcriptionId 转录ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<Boolean> deleteTranscription(String transcriptionId);
// ========================= 会议统计 ==========================
/**
* @description 获取会议统计信息(参与人数、时长等)
* @param meetingId 会议ID
* @author cascade
* @since 2025-12-22
*/
ResultDomain<VideoMeetingVO> getMeetingStatistics(String meetingId);
}

View File

@@ -35,9 +35,6 @@ public class ChatMemberVO extends BaseVO {
@Schema(description = "用户头像")
private String userAvatar;
@Schema(description = "角色owner-创建者 admin-管理员 member-普通成员")
private String role;
@Schema(description = "状态active-活跃 left-已离开 removed-被移除")
private String status;

View File

@@ -16,7 +16,7 @@ import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "聊天消息VO")
public class ChatMessageVO extends BaseVO {
public class ChatRoomMessageVO extends BaseVO {
private static final long serialVersionUID = 1L;
@Schema(description = "消息ID")

View File

@@ -4,7 +4,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.vo.BaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.OffsetDateTime;
import java.util.Date;
import com.alibaba.fastjson2.annotation.JSONField;
/**
* 会议参与记录VO
@@ -30,11 +31,13 @@ public class MeetingParticipantVO extends BaseVO {
@Schema(description = "用户名称")
private String userName;
@Schema(description = "加入时间")
private OffsetDateTime joinTime;
@Schema(description = "加入时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date joinTime;
@Schema(description = "离开时间")
private OffsetDateTime leaveTime;
@Schema(description = "离开时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date leaveTime;
@Schema(description = "参与时长(秒)")
private Integer durationSeconds;

View File

@@ -4,7 +4,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.vo.BaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.OffsetDateTime;
import java.util.Date;
import com.alibaba.fastjson2.annotation.JSONField;
/**
* 会议转录记录VO
@@ -42,11 +43,13 @@ public class MeetingTranscriptionVO extends BaseVO {
@Schema(description = "识别置信度0-1")
private Double confidence;
@Schema(description = "语音开始时间")
private OffsetDateTime speechStartTime;
@Schema(description = "语音开始时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date speechStartTime;
@Schema(description = "语音结束时间")
private OffsetDateTime speechEndTime;
@Schema(description = "语音结束时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date speechEndTime;
@Schema(description = "语音时长(毫秒)")
private Integer durationMs;

View File

@@ -62,11 +62,11 @@ public class VideoMeetingVO extends BaseVO {
@Schema(description = "实际开始时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
private Date actualStartTime;
@Schema(description = "实际结束时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
private Date actualEndTime;
@Schema(description = "会议时长(秒)")
private Integer durationSeconds;