diff --git a/urbanLifelineServ/common/common-core/src/main/java/org/xyzh/common/core/domain/ResultDomain.java b/urbanLifelineServ/common/common-core/src/main/java/org/xyzh/common/core/domain/ResultDomain.java index 1655d2df..d599003a 100644 --- a/urbanLifelineServ/common/common-core/src/main/java/org/xyzh/common/core/domain/ResultDomain.java +++ b/urbanLifelineServ/common/common-core/src/main/java/org/xyzh/common/core/domain/ResultDomain.java @@ -54,6 +54,12 @@ public class ResultDomain implements Serializable { this.message = message; this.success = false; this.pageDomain = pageDomain; + if(pageDomain != null) this.dataList = pageDomain.getDataList(); + } + + public void setPageDomain(PageDomain pageDomain){ + this.pageDomain = pageDomain; + if(pageDomain != null) this.dataList = pageDomain.getDataList(); } // 静态工厂方法 - 推荐使用(简洁、清晰) @@ -88,6 +94,7 @@ public class ResultDomain implements Serializable { result.success = true; result.message = message; result.pageDomain = pageDomain; + if(pageDomain != null) result.dataList = pageDomain.getDataList(); result.code = HttpStatus.OK.value(); return result; } diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseChatContorller.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseChatContorller.java index 4eff66f3..98d9cd9e 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseChatContorller.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseChatContorller.java @@ -92,27 +92,17 @@ public class WorkcaseChatContorller { @Operation(summary = "关闭聊天室") @PreAuthorize("hasAuthority('workcase:room:close')") @PostMapping("/room/{roomId}/close") - public ResultDomain closeChatRoom(@PathVariable String roomId, @RequestParam String closedBy) { + public ResultDomain closeChatRoom(@PathVariable(value = "roomId") String roomId, @RequestParam String closedBy) { return chatRoomService.closeChatRoom(roomId, closedBy); } @Operation(summary = "获取聊天室详情") @PreAuthorize("hasAuthority('workcase:room:view')") @GetMapping("/room/{roomId}") - public ResultDomain getChatRoomById(@PathVariable String roomId) { + public ResultDomain getChatRoomById(@PathVariable(value = "roomId") String roomId) { return chatRoomService.getChatRoomById(roomId); } - @Operation(summary = "绑定工单到聊天室") - @PreAuthorize("hasAuthority('workcase:room:update')") - @PostMapping("/room/{roomId}/bind-workcase") - public ResultDomain bindWorkcaseToRoom(@PathVariable String roomId, @RequestParam String workcaseId) { - TbChatRoomDTO chatRoom = new TbChatRoomDTO(); - chatRoom.setRoomId(roomId); - chatRoom.setWorkcaseId(workcaseId); - return chatRoomService.updateChatRoom(chatRoom); - } - @Operation(summary = "分页查询聊天室") @PreAuthorize("hasAuthority('workcase:room:view')") @PostMapping("/room/page") @@ -146,14 +136,14 @@ public class WorkcaseChatContorller { @Operation(summary = "移除聊天室成员") @PreAuthorize("hasAuthority('workcase:room:member')") @DeleteMapping("/room/member/{memberId}") - public ResultDomain removeChatRoomMember(@PathVariable String memberId) { + public ResultDomain removeChatRoomMember(@PathVariable(value = "memberId") String memberId) { return chatRoomService.removeChatRoomMember(memberId); } @Operation(summary = "获取聊天室成员列表") @PreAuthorize("hasAuthority('workcase:room:member')") @GetMapping("/room/{roomId}/members") - public ResultDomain getChatRoomMemberList(@PathVariable String roomId) { + public ResultDomain getChatRoomMemberList(@PathVariable(value = "roomId") String roomId) { return chatRoomService.getChatRoomMemberList(roomId); } @@ -191,7 +181,7 @@ public class WorkcaseChatContorller { @Operation(summary = "删除聊天室消息") @PreAuthorize("hasAuthority('workcase:room:message')") @DeleteMapping("/room/message/{messageId}") - public ResultDomain deleteRoomMessage(@PathVariable String messageId) { + public ResultDomain deleteRoomMessage(@PathVariable(value = "messageId") String messageId) { return chatRoomService.deleteMessage(messageId); } @@ -223,7 +213,7 @@ public class WorkcaseChatContorller { @Operation(summary = "删除客服人员") @DeleteMapping("/customer-service/{userId}") - public ResultDomain deleteCustomerService(@PathVariable String userId) { + public ResultDomain deleteCustomerService(@PathVariable(value = "userId") String userId) { return chatRoomService.deleteCustomerService(userId); } @@ -241,8 +231,8 @@ public class WorkcaseChatContorller { } @Operation(summary = "更新客服在线状态") - @PostMapping("/customer-service/{userId}/status") - public ResultDomain updateCustomerServiceStatus(@PathVariable String userId, @RequestParam String status) { + @PostMapping("/customer-service/{userId}/status/{status}") + public ResultDomain updateCustomerServiceStatus(@PathVariable(value = "userId") String userId, @PathVariable(value = "status") String status) { return chatRoomService.updateCustomerServiceStatus(userId, status); } @@ -254,7 +244,7 @@ public class WorkcaseChatContorller { @Operation(summary = "自动分配客服") @PostMapping("/room/{roomId}/assign") - public ResultDomain assignCustomerService(@PathVariable String roomId) { + public ResultDomain assignCustomerService(@PathVariable(value = "roomId") String roomId) { return chatRoomService.assignCustomerService(roomId); } diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseController.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseController.java index 12958bfc..37b46241 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseController.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseController.java @@ -72,7 +72,7 @@ public class WorkcaseController { @Operation(summary = "删除工单") @PreAuthorize("hasAuthority('workcase:ticket:update')") @DeleteMapping("/{workcaseId}") - public ResultDomain deleteWorkcase(@PathVariable String workcaseId) { + public ResultDomain deleteWorkcase(@PathVariable(value = "workcaseId") String workcaseId) { TbWorkcaseDTO workcase = new TbWorkcaseDTO(); workcase.setWorkcaseId(workcaseId); return workcaseService.deleteWorkcase(workcase); @@ -81,7 +81,7 @@ public class WorkcaseController { @Operation(summary = "获取工单详情") @PreAuthorize("hasAuthority('workcase:ticket:view')") @GetMapping("/{workcaseId}") - public ResultDomain getWorkcaseById(@PathVariable String workcaseId) { + public ResultDomain getWorkcaseById(@PathVariable(value = "workcaseId") String workcaseId) { return workcaseService.getWorkcaseById(workcaseId); } @@ -159,7 +159,7 @@ public class WorkcaseController { @Operation(summary = "删除工单处理过程") @PreAuthorize("hasAuthority('workcase:ticket:process')") @DeleteMapping("/process/{processId}") - public ResultDomain deleteWorkcaseProcess(@PathVariable String processId) { + public ResultDomain deleteWorkcaseProcess(@PathVariable(value = "processId") String processId) { TbWorkcaseProcessDTO process = new TbWorkcaseProcessDTO(); process.setProcessId(processId); return workcaseService.deleteWorkcaseProcess(process); @@ -219,7 +219,7 @@ public class WorkcaseController { @Operation(summary = "删除工单设备") @PreAuthorize("hasAuthority('workcase:ticket:device')") @DeleteMapping("/device/{workcaseId}/{device}") - public ResultDomain deleteWorkcaseDevice(@PathVariable String workcaseId, @PathVariable String device) { + public ResultDomain deleteWorkcaseDevice(@PathVariable(value = "workcaseId") String workcaseId, @PathVariable(value = "device") String device) { TbWorkcaseDeviceDTO deviceDTO = new TbWorkcaseDeviceDTO(); deviceDTO.setWorkcaseId(workcaseId); deviceDTO.setDevice(device); diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/ChatRoomServiceImpl.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/ChatRoomServiceImpl.java index e387278e..86203509 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/ChatRoomServiceImpl.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/ChatRoomServiceImpl.java @@ -558,14 +558,7 @@ public class ChatRoomServiceImpl implements ChatRoomService { filter.setStatus("online"); List list = customerServiceMapper.selectCustomerServiceList(filter); - // 过滤工作量未满的客服 - List availableList = list.stream() - .filter(cs -> cs.getCurrentWorkload() == null || - cs.getMaxConcurrent() == null || - cs.getCurrentWorkload() < cs.getMaxConcurrent()) - .toList(); - - return ResultDomain.success("查询成功", availableList); + return ResultDomain.success("查询成功", list); } @Override diff --git a/urbanLifelineWeb/packages/workcase/src/api/workcase/workcaseChat.ts b/urbanLifelineWeb/packages/workcase/src/api/workcase/workcaseChat.ts index 021cb420..516d088b 100644 --- a/urbanLifelineWeb/packages/workcase/src/api/workcase/workcaseChat.ts +++ b/urbanLifelineWeb/packages/workcase/src/api/workcase/workcaseChat.ts @@ -156,9 +156,7 @@ export const workcaseChatAPI = { * 更新客服在线状态 */ async updateCustomerServiceStatus(userId: string, status: string): Promise> { - const response = await api.post(`${this.baseUrl}/customer-service/${userId}/status`, null, { - params: { status } - }) + const response = await api.post(`${this.baseUrl}/customer-service/${userId}/status/${status}`) return response.data }, diff --git a/urbanLifelineWeb/packages/workcase_wechat/api/workcase/workcaseChat.ts b/urbanLifelineWeb/packages/workcase_wechat/api/workcase/workcaseChat.ts index fe485699..0f6075a4 100644 --- a/urbanLifelineWeb/packages/workcase_wechat/api/workcase/workcaseChat.ts +++ b/urbanLifelineWeb/packages/workcase_wechat/api/workcase/workcaseChat.ts @@ -45,170 +45,6 @@ const BASE_URL = 'http://localhost:8180' export const workcaseChatAPI = { baseUrl: '/urban-lifeline/workcase/chat', - // ====================== AI对话管理 ====================== - - /** - * 创建对话 - * @param param agentId和userId必传 - */ - createChat(param: CreateChatParam): Promise> { - return request({ url: this.baseUrl, method: 'POST', data: param }) - }, - - /** - * 更新对话 - */ - updateChat(chat: TbChat): Promise> { - return request({ url: this.baseUrl, method: 'PUT', data: chat }) - }, - - /** - * 查询对话列表 - * @param param userId必传 - */ - getChatList(param: ChatListParam): Promise> { - return request({ url: `${this.baseUrl}/list`, method: 'POST', data: param }) - }, - - /** - * 获取对话消息列表 - * @param param chatId必传 - */ - getChatMessageList(param: ChatMessageListParam): Promise> { - return request({ url: `${this.baseUrl}/message/list`, method: 'POST', data: param }) - }, - - /** - * 准备流式对话会话 - * @param param chatId和message必传 - */ - prepareChatMessageSession(param: PrepareChatParam): Promise> { - return request({ url: `${this.baseUrl}/prepare`, method: 'POST', data: param }) - }, - - /** - * 流式对话(SSE)- 返回EventSource URL - */ - getStreamUrl(sessionId: string): string { - return `${this.baseUrl}/stream/${sessionId}` - }, - - /** - * 建立SSE流式对话连接 - * @param sessionId 会话ID(必传) - * @param callbacks 回调函数 - * @returns SSETask 可用于中止请求 - */ - streamChat(sessionId: string, callbacks: SSECallbacks): SSETask { - const url = `${BASE_URL}${this.baseUrl}/stream/${sessionId}` - const token = uni.getStorageSync('token') || '' - - const requestTask = uni.request({ - url: url, - method: 'GET', - header: { - 'Accept': 'text/event-stream', - 'Authorization': token ? `Bearer ${token}` : '' - }, - enableChunked: true, - success: (res: any) => { - console.log('SSE请求完成:', res) - // 处理非200状态码 - if (res.statusCode !== 200) { - console.error('SSE请求状态码异常:', res.statusCode) - let errorMsg = '抱歉,服务暂时不可用,请稍后重试。' - if (res.statusCode === 401) { - errorMsg = '登录已过期,请重新登录。' - } else if (res.statusCode === 403) { - errorMsg = '无权限访问,请联系管理员。' - } else if (res.statusCode === 404) { - errorMsg = '会话不存在或已过期,请重新发起对话。' - } else if (res.statusCode >= 500) { - errorMsg = '服务器异常,请稍后重试。' - } - callbacks.onError?.(errorMsg) - } - callbacks.onComplete?.() - }, - fail: (err: any) => { - console.error('SSE请求失败:', err) - callbacks.onError?.('网络连接失败,请稍后重试。') - callbacks.onComplete?.() - } - }) - - // 监听分块数据 - requestTask.onChunkReceived((res: any) => { - try { - const decoder = new TextDecoder('utf-8') - const text = decoder.decode(new Uint8Array(res.data)) - - const lines = text.split('\n') - for (const line of lines) { - if (line.startsWith('data:')) { - const dataStr = line.substring(5).trim() - if (dataStr && dataStr !== '[DONE]') { - try { - const data: SSEMessageData = JSON.parse(dataStr) - const event = data.event - - if (event === 'message' || event === 'agent_message') { - callbacks.onMessage?.(data) - } else if (event === 'message_end') { - callbacks.onEnd?.(data.task_id || '') - } else if (event === 'error') { - callbacks.onError?.(data.message || '发生错误,请稍后重试。') - } - } catch (e) { - console.log('解析SSE数据失败:', dataStr) - } - } - } - } - } catch (e) { - console.error('处理分块数据失败:', e) - } - }) - - return { - abort: () => { - requestTask.abort() - } - } - }, - - /** - * 停止对话 - * @param param taskId, agentId, userId必传 - */ - stopChat(param: StopChatParam): Promise> { - return request({ url: `${this.baseUrl}/stop/${param.taskId}`, method: 'POST', data: param }) - }, - - /** - * 评价对话消息 - * @param param agentId, chatId, messageId, comment, userId必传 - */ - commentChatMessage(param: CommentMessageParam): Promise> { - return request({ url: `${this.baseUrl}/comment?messageId=${param.messageId}&comment=${param.comment}`, method: 'POST', data: param }) - }, - - // ====================== 对话分析 ====================== - - /** - * 分析对话(AI预填工单信息) - */ - analyzeChat(chatId: string): Promise> { - return request({ url: `${this.baseUrl}/analyze/${chatId}`, method: 'GET' }) - }, - - /** - * 总结对话 - */ - summaryChat(chatId: string): Promise> { - return request({ url: `${this.baseUrl}/summary/${chatId}`, method: 'POST' }) - }, - // ====================== ChatRoom聊天室管理 ====================== /** @@ -326,7 +162,7 @@ export const workcaseChatAPI = { * 更新客服在线状态 */ updateCustomerServiceStatus(userId: string, status: string): Promise> { - return request({ url: `${this.baseUrl}/customer-service/${userId}/status?status=${status}`, method: 'POST' }) + return request({ url: `${this.baseUrl}/customer-service/${userId}/status/${status}`, method: 'POST' }) }, /** diff --git a/urbanLifelineWeb/packages/workcase_wechat/components/WorkcaseCreator/WorkcaseCreator.scss b/urbanLifelineWeb/packages/workcase_wechat/components/WorkcaseCreator/WorkcaseCreator.scss index 6b6c6d12..36e932b0 100644 --- a/urbanLifelineWeb/packages/workcase_wechat/components/WorkcaseCreator/WorkcaseCreator.scss +++ b/urbanLifelineWeb/packages/workcase_wechat/components/WorkcaseCreator/WorkcaseCreator.scss @@ -238,11 +238,11 @@ } .submit-btn { - background-color: #5B8FF9; - color: #FFFFFF; +background-color: #5B8FF9; +color: #FFFFFF; } -.submit-btn[disabled] { - background-color: #CCCCCC; - color: #999999; +.submit-btn.is-disabled { +background-color: #CCCCCC; +color: #999999; } \ No newline at end of file diff --git a/urbanLifelineWeb/packages/workcase_wechat/components/WorkcaseCreator/WorkcaseCreator.uvue b/urbanLifelineWeb/packages/workcase_wechat/components/WorkcaseCreator/WorkcaseCreator.uvue index f5d88250..fcc69ab5 100644 --- a/urbanLifelineWeb/packages/workcase_wechat/components/WorkcaseCreator/WorkcaseCreator.uvue +++ b/urbanLifelineWeb/packages/workcase_wechat/components/WorkcaseCreator/WorkcaseCreator.uvue @@ -62,7 +62,7 @@ - + diff --git a/urbanLifelineWeb/packages/workcase_wechat/pages/chatRoom/chatRoom/chatRoom.scss b/urbanLifelineWeb/packages/workcase_wechat/pages/chatRoom/chatRoom/chatRoom.scss index 47224038..d61c33c7 100644 --- a/urbanLifelineWeb/packages/workcase_wechat/pages/chatRoom/chatRoom/chatRoom.scss +++ b/urbanLifelineWeb/packages/workcase_wechat/pages/chatRoom/chatRoom/chatRoom.scss @@ -58,9 +58,9 @@ justify-content: flex-end; gap: 6px; // 小程序需要为右侧胶囊按钮留出空间 - /* #ifdef MP-WEIXIN */ - padding-right: 100px; - /* #endif */ + // /* #ifdef MP-WEIXIN */ + // padding-right: 100px; + // /* #endif */ } .nav-btn { @@ -100,11 +100,185 @@ color: #fff; } -.chat-area { +// 工具栏(人员数量 + 按钮) +.room-toolbar { + position: fixed; + left: 0; + right: 0; + height: 44px; + background: #fff; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 0 24rpx; + box-sizing: border-box; + z-index: 99; + border-bottom: 1rpx solid #f0f0f0; +} + +.member-count { + padding: 8rpx 20rpx; + border: 2rpx solid #ff4d4f; + border-radius: 8rpx; +} + +.member-count-text { + font-size: 24rpx; + color: #ff4d4f; + font-weight: 500; +} + +.toolbar-right { + display: flex; + flex-direction: row; + align-items: center; + gap: 16rpx; +} + +.toolbar-btn { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + padding: 8rpx 24rpx; + height: 56rpx; + border: 2rpx solid #ff4d4f; + border-radius: 8rpx; + background: #fff; + box-sizing: border-box; + margin: 0; + line-height: 1; +} + +.toolbar-btn::after { + border: none; +} + +.toolbar-btn-text { + color: #ff4d4f; + font-size: 24rpx; + font-weight: 500; + line-height: 1; +} + +.toolbar-btn.meeting-btn { + background: #fff; + border-color: #ff4d4f; +} + +.toolbar-btn.meeting-btn .meeting-text { + color: #ff4d4f; +} + +// 人员弹窗 +.members-popup-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.3); + z-index: 200; +} + +.members-popup { + position: absolute; + left: 24rpx; + width: 300rpx; + background: #fff; + border-radius: 16rpx; + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15); + padding: 16rpx 0; + max-height: 400rpx; + overflow-y: auto; +} + +.members-list { + display: flex; + flex-direction: column; +} + +.member-item { + display: flex; + flex-direction: row; + align-items: center; + padding: 16rpx 24rpx; + gap: 16rpx; +} + +.member-avatar { + width: 56rpx; + height: 56rpx; + border-radius: 50%; + background: linear-gradient(145deg, #e8f7ff 0%, #c5e4ff 100%); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.member-avatar-text { + font-size: 24rpx; + font-weight: 600; + color: #173294; +} + +.member-name { flex: 1; - margin-top: 176rpx; + font-size: 26rpx; + color: #333; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.member-status { + width: 16rpx; + height: 16rpx; + border-radius: 50%; + flex-shrink: 0; +} + +.member-status.online { + background: #52c41a; +} + +.member-status.offline { + background: #d9d9d9; +} + +.members-empty { + padding: 32rpx 24rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.members-empty-text { + font-size: 26rpx; + color: #999; +} + +.loading-more { + padding: 20rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.loading-more-text { + font-size: 24rpx; + color: #999; +} + +.chat-area { + position: fixed; + left: 0; + right: 0; + bottom: 120rpx; padding: 24rpx; - padding-bottom: 180rpx; + overflow-y: auto; } .message-list { diff --git a/urbanLifelineWeb/packages/workcase_wechat/pages/chatRoom/chatRoom/chatRoom.uvue b/urbanLifelineWeb/packages/workcase_wechat/pages/chatRoom/chatRoom/chatRoom.uvue index 619eb926..bd10385f 100644 --- a/urbanLifelineWeb/packages/workcase_wechat/pages/chatRoom/chatRoom/chatRoom.uvue +++ b/urbanLifelineWeb/packages/workcase_wechat/pages/chatRoom/chatRoom/chatRoom.uvue @@ -9,21 +9,52 @@ {{ roomName }} - - - - + + + + + + 暂无人员在线 + + + + {{ member.userName?.charAt(0) || '客' }} + + {{ member.userName || '未知' }} + + + + + + :style="{ top: (headerPaddingTop + 88) + 'px' }" + @scrolltoupper="loadMoreMessages" + upper-threshold="50"> + + + 加载中... + + + 没有更多消息了 + - @@ -77,9 +108,10 @@