diff --git a/src/main/java/com/dd/admin/business/chat/controller/ChatController.java b/src/main/java/com/dd/admin/business/chat/controller/ChatController.java index 6c61768..5842149 100644 --- a/src/main/java/com/dd/admin/business/chat/controller/ChatController.java +++ b/src/main/java/com/dd/admin/business/chat/controller/ChatController.java @@ -3,8 +3,10 @@ package com.dd.admin.business.chat.controller; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.extra.pinyin.PinyinUtil; -import com.dd.admin.business.chat.domain.AuthorChat; -import com.dd.admin.business.chat.domain.MessageBean; +import com.dd.admin.business.api.domain.UnReadCountBean; +import com.dd.admin.business.chat.domain.*; +import com.dd.admin.common.aop.operationLog.aop.OperLog; +import com.dd.admin.common.aop.operationLog.aop.OperType; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import org.springframework.web.bind.annotation.*; import io.swagger.annotations.Api; @@ -16,8 +18,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import javax.validation.constraints.NotBlank; import com.dd.admin.business.chat.entity.Chat; -import com.dd.admin.business.chat.domain.ChatVo; -import com.dd.admin.business.chat.domain.ChatDto; import com.dd.admin.business.chat.service.ChatService; import java.util.*; @@ -52,6 +52,16 @@ public class ChatController { return calendar.getTimeInMillis(); } + + @ApiOperation(value = "查询客服未读消息数量") + @ApiOperationSupport(order = 1) + @GetMapping("/admin/chat/getUnReadCount") + @OperLog(operModule = "查询客服未读消息数量",operType = OperType.OTHER,operDesc = "查询客服未读消息数量") + public ResultBean getUnReadCount() { + Integer chatUnReadCount = chatService.selectUnReadCount("8"); + return ResultBean.success(chatUnReadCount); + }; + @ApiOperation(value = "作者列表") @ApiOperationSupport(order = 2) @GetMapping("/admin/chat/authorList") @@ -98,6 +108,17 @@ public class ChatController { } + + @ApiOperation(value = "读取聊天消息") + @ApiOperationSupport(order = 4) + @PostMapping("/admin/chat/readAuthorMessage") + @OperLog(operModule = "读取回复消息", operType = OperType.OTHER, operDesc = "读取聊天消息") + public ResultBean readReplyMessage(@RequestBody AuthorParam authorParam) { + chatService.readMessage(authorParam.getAuthorId(),"8"); + return ResultBean.success("noAlert"); + } + + @ApiOperation(value = "-分页列表") @ApiOperationSupport(order = 1) @GetMapping("/admin/chat/page") diff --git a/src/main/java/com/dd/admin/business/chat/domain/AuthorParam.java b/src/main/java/com/dd/admin/business/chat/domain/AuthorParam.java new file mode 100644 index 0000000..a9fb76d --- /dev/null +++ b/src/main/java/com/dd/admin/business/chat/domain/AuthorParam.java @@ -0,0 +1,12 @@ +package com.dd.admin.business.chat.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AuthorParam { + private String authorId; +} diff --git a/src/main/java/com/dd/admin/business/chat/mapper/xml/ChatMapper.xml b/src/main/java/com/dd/admin/business/chat/mapper/xml/ChatMapper.xml index 9da0f69..77e3406 100644 --- a/src/main/java/com/dd/admin/business/chat/mapper/xml/ChatMapper.xml +++ b/src/main/java/com/dd/admin/business/chat/mapper/xml/ChatMapper.xml @@ -113,58 +113,52 @@ diff --git a/src/main/java/com/dd/admin/business/operationLog/domain/OperationLogVo.java b/src/main/java/com/dd/admin/business/operationLog/domain/OperationLogVo.java index d6d4546..f457847 100644 --- a/src/main/java/com/dd/admin/business/operationLog/domain/OperationLogVo.java +++ b/src/main/java/com/dd/admin/business/operationLog/domain/OperationLogVo.java @@ -27,6 +27,10 @@ public class OperationLogVo { @ApiModelProperty(value = "日志id") private String operId; + @ApiModelProperty(value = "操作ip") + @TableField("OPER_IP_ADDRESS") + private String operIpAddress; + @ApiModelProperty(value = "请求模块") private String operModule; diff --git a/src/main/java/com/dd/admin/business/webSocket/handler/GetReadCoutMessageHandler.java b/src/main/java/com/dd/admin/business/webSocket/handler/GetReadCoutMessageHandler.java new file mode 100644 index 0000000..c4f1010 --- /dev/null +++ b/src/main/java/com/dd/admin/business/webSocket/handler/GetReadCoutMessageHandler.java @@ -0,0 +1,26 @@ +package com.dd.admin.business.webSocket.handler; + +import com.dd.admin.business.chat.service.ChatService; +import com.dd.admin.business.webSocket.MsgHandlerInterface; +import com.dd.admin.business.webSocket.util.TioUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.tio.core.ChannelContext; + +import java.util.Map; + + +@Slf4j +@Service("8") +public class GetReadCoutMessageHandler implements MsgHandlerInterface { + @Autowired + ChatService chatService; + @Override + public Object handler(Map map, ChannelContext context) { + String authorId = String.valueOf(map.get("authorId")); + Integer unReadCount = chatService.selectUnReadCount(authorId); + TioUtil.sendChatMessageToUser(context.getGroupContext(),authorId,"8",unReadCount); + return null; + } +} diff --git a/src/main/java/com/dd/admin/business/webSocket/handler/ReadMessageHandler.java b/src/main/java/com/dd/admin/business/webSocket/handler/ReadMessageHandler.java new file mode 100644 index 0000000..916cf68 --- /dev/null +++ b/src/main/java/com/dd/admin/business/webSocket/handler/ReadMessageHandler.java @@ -0,0 +1,27 @@ +package com.dd.admin.business.webSocket.handler; + +import com.dd.admin.business.chat.service.ChatService; +import com.dd.admin.business.webSocket.MsgHandlerInterface; +import com.dd.admin.business.webSocket.util.TioUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.tio.core.ChannelContext; + +import java.util.Map; + + +@Slf4j +@Service("7") +public class ReadMessageHandler implements MsgHandlerInterface { + @Autowired + ChatService chatService; + @Override + public Object handler(Map map, ChannelContext context) { + String authorId = String.valueOf(map.get("authorId")); + String loginId = String.valueOf(map.get("loginId")); + chatService.readMessage(authorId,loginId); + return null; + } +} diff --git a/web/.env.development b/web/.env.development index 3e6d80b..a593222 100644 --- a/web/.env.development +++ b/web/.env.development @@ -3,8 +3,9 @@ ENV = 'development' # base api VUE_APP_BASE_API = 'http://127.0.0.1:8080' +VUE_APP_WEBSOCKET_API = 'ws://192.168.10.98:9326' # system name -VUE_APP_SYSTEM_NAME = 'DD ADMIN' +VUE_APP_SYSTEM_NAME = '小红书社区后台管理系统' VUE_APP_SYSTEM_LOGO = 'logo.png' diff --git a/web/.env.production b/web/.env.production index b740e3a..7a5cbce 100644 --- a/web/.env.production +++ b/web/.env.production @@ -3,8 +3,9 @@ ENV = 'production' # base api VUE_APP_BASE_API = 'http://8.146.211.120:8080' -VUE_APP_WEBSOCKET_API = 'ws://8.146.211.120:9326/' +VUE_APP_WEBSOCKET_API = 'ws://8.146.211.120:9326' # system name -VUE_APP_SYSTEM_NAME = 'DD STORE' +VUE_APP_SYSTEM_NAME = '小红书社区后台管理系统' +VUE_APP_SYSTEM_LOGO = 'logo.png' diff --git a/web/src/api/business/chat/chat.js b/web/src/api/business/chat/chat.js index 1ab7247..985ebb2 100644 --- a/web/src/api/business/chat/chat.js +++ b/web/src/api/business/chat/chat.js @@ -54,3 +54,24 @@ export function getAuthorChat(params) { params }) } + + +export function getUnReadCount() { + return request({ + url: '/admin/chat/getUnReadCount', + method: 'get', + }) +} + + + +export function readAuthorMessage(data) { + return request({ + url: '/admin/chat/readAuthorMessage', + method: 'post', + data: data, + noLoading:true + }) +} + + diff --git a/web/src/api/websocket.js b/web/src/api/websocket.js index e3489ef..24457c1 100644 --- a/web/src/api/websocket.js +++ b/web/src/api/websocket.js @@ -1,3 +1,5 @@ +import {error} from "@/utils"; + class WebSocketManager { constructor() { this.webSocketInstance = null; // WebSocket实例对象,使用更清晰的命名 @@ -32,6 +34,16 @@ class WebSocketManager { } console.log("连接WebSocket"); this.webSocketInstance = new WebSocket(url); + + const timeoutTimer = setTimeout(() => { + console.log(this.isConnected) + if (!this.isConnected) { + error("WebSocket连接超时") + } + clearTimeout(timeoutTimer) + }, 5000); + + this.webSocketInstance.onmessage = (event) => { const data = event.data; if (data === 'pong') { @@ -53,6 +65,8 @@ class WebSocketManager { console.log("WebSocket连接成功"); this.isConnected = true; this.heartbeatCheck.start(); + + this.onConnectCallback && this.onConnectCallback(); }; // 连接发生错误的回调方法 diff --git a/web/src/assets/logo.png b/web/src/assets/logo.png index 8bd7972..c828d68 100644 Binary files a/web/src/assets/logo.png and b/web/src/assets/logo.png differ diff --git a/web/src/layout/components/Navbar.vue b/web/src/layout/components/Navbar.vue index 0bb8386..1a955b8 100644 --- a/web/src/layout/components/Navbar.vue +++ b/web/src/layout/components/Navbar.vue @@ -4,9 +4,6 @@ - - -
@@ -39,14 +36,21 @@
- + + 客服消息 +
- + + +
@@ -56,13 +60,16 @@ import Breadcrumb from '@/components/Breadcrumb' import Hamburger from '@/components/Hamburger' import UpdatePassword from '@/views/common/system/UpdatePassword' import Im from "@/views/common/Im"; +import {getUnReadCount} from "@/api/business/chat/chat"; +import webSocketManager from "@/api/websocket"; +import {isNotEmpty} from "@/utils"; export default { components: { UpdatePassword, Breadcrumb, Hamburger, - Im + Im, }, computed: { ...mapGetters([ @@ -71,6 +78,37 @@ export default { 'avatar' ]) }, + data() { + return { + unReadCount:0 + } + }, + mounted() { + // 设置收到消息回调函数 + webSocketManager.onMessage((data) => { + console.log('我是Navbar的mounted') + console.log(data.handlerType) + if(data.handlerType == '6'){ + console.log(JSON.stringify(data.body)) + const {IMUI} = this.$refs.im.$refs; + try { + IMUI.appendMessage(data.body); + IMUI.messageViewToBottom() + const contactId = this.$refs.im.contact.id + if(isNotEmpty(contactId)&&contactId==data.body.toContactId){ + console.log('当前在该用户聊天框 设置为已读') + } + }catch (e) { + } + + //请求总数 + this.$refs.im.getUnReadCount() + } + if(data.handlerType == '8'){ + this.unReadCount = data.body + } + }); + }, methods: { updatePassword(){ this.$refs.updatePass.open() @@ -117,7 +155,7 @@ export default { .top-icon{ text-align: center; height: 50px; - width: 50px; + width: 150px; line-height: 50px; cursor: pointer; color: #606266; diff --git a/web/src/layout/index.vue b/web/src/layout/index.vue index 31da694..f70896b 100644 --- a/web/src/layout/index.vue +++ b/web/src/layout/index.vue @@ -8,6 +8,18 @@
+ +
+ + + + + + + + + +
@@ -94,4 +106,36 @@ export default { } + .floating-component { + position: fixed; /* 固定位置 */ + bottom: 5%; /* 下边距 */ + right: 1%; /* 右边距 */ + padding: 0; + border-radius: 50%; /* 圆角 */ + z-index: 10000; /* 设置 z-index 确保悬浮在顶层 */ + } + .but { + font-size: larger; + color: rgb(0,119,216); + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2); /* 阴影 */ + } + .butto { + border: 0; + padding: 10px; + margin: 0; + } + + .too.el-tooltip__popper.is-light { + border: none !important; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2); + padding: 5px + } + /* 修改箭头边框 这里方位是left,所以 x-placement^="left" 并且是设置 border-right-color 的颜色*/ + .too.el-tooltip__popper.is-light[x-placement^="left"] .popper__arrow { + border-left-color: #eaeaea !important; + } + .too.el-tooltip__popper[x-placement^="left"] .popper__arrow { + border-left-color: #eaeaea !important; + } + diff --git a/web/src/utils/request.js b/web/src/utils/request.js index f9c774a..47b741a 100644 --- a/web/src/utils/request.js +++ b/web/src/utils/request.js @@ -18,7 +18,10 @@ service.interceptors.request.use( // do something before request is sent if(config.method == 'post'){ - showLoading() + console.log(config) + if(!config.noLoading){ + showLoading() + } } if (store.getters.token) { @@ -79,7 +82,12 @@ service.interceptors.response.use( return Promise.reject(new Error(res.message || 'Error')) } else { if(response.config.method == 'post'){ - success('提交成功') + console.log(response) + + if(response.data.data=='noAlert'){ + return res + } + success('请求成功') } return res } diff --git a/web/src/views/business/operationLog/operationLogList.vue b/web/src/views/business/operationLog/operationLogList.vue index 8b7c587..6209ab4 100644 --- a/web/src/views/business/operationLog/operationLogList.vue +++ b/web/src/views/business/operationLog/operationLogList.vue @@ -73,6 +73,16 @@ + + + + - - - - - - - - @@ -185,25 +175,6 @@ - - - - - - -
@@ -24,8 +26,8 @@ diff --git a/web/src/views/dashboard/components/PieChart.vue b/web/src/views/dashboard/components/PieChart.vue new file mode 100644 index 0000000..c556d49 --- /dev/null +++ b/web/src/views/dashboard/components/PieChart.vue @@ -0,0 +1,79 @@ + + + diff --git a/web/src/views/dashboard/components/RaddarChart.vue b/web/src/views/dashboard/components/RaddarChart.vue new file mode 100644 index 0000000..9c10333 --- /dev/null +++ b/web/src/views/dashboard/components/RaddarChart.vue @@ -0,0 +1,116 @@ + + + diff --git a/web/src/views/dashboard/components/mixins/resize.js b/web/src/views/dashboard/components/mixins/resize.js new file mode 100644 index 0000000..234953b --- /dev/null +++ b/web/src/views/dashboard/components/mixins/resize.js @@ -0,0 +1,55 @@ +import { debounce } from '@/utils' + +export default { + data() { + return { + $_sidebarElm: null, + $_resizeHandler: null + } + }, + mounted() { + this.$_resizeHandler = debounce(() => { + if (this.chart) { + this.chart.resize() + } + }, 100) + this.$_initResizeEvent() + this.$_initSidebarResizeEvent() + }, + beforeDestroy() { + this.$_destroyResizeEvent() + this.$_destroySidebarResizeEvent() + }, + // to fixed bug when cached by keep-alive + // https://github.com/PanJiaChen/vue-element-admin/issues/2116 + activated() { + this.$_initResizeEvent() + this.$_initSidebarResizeEvent() + }, + deactivated() { + this.$_destroyResizeEvent() + this.$_destroySidebarResizeEvent() + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_initResizeEvent() { + window.addEventListener('resize', this.$_resizeHandler) + }, + $_destroyResizeEvent() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + $_sidebarResizeHandler(e) { + if (e.propertyName === 'width') { + this.$_resizeHandler() + } + }, + $_initSidebarResizeEvent() { + this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] + this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) + }, + $_destroySidebarResizeEvent() { + this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) + } + } +} diff --git a/web/src/views/dashboard/index.vue b/web/src/views/dashboard/index.vue index c39721e..739a193 100644 --- a/web/src/views/dashboard/index.vue +++ b/web/src/views/dashboard/index.vue @@ -1,23 +1,416 @@ + diff --git a/web/src/views/login/login.vue b/web/src/views/login/login.vue index 2e7e6a7..b7ac697 100644 --- a/web/src/views/login/login.vue +++ b/web/src/views/login/login.vue @@ -38,7 +38,7 @@ z-0 ">
-
小红书社区后台管理系统
+
{{title}}
    @@ -269,7 +269,8 @@ export default { loading: false, passwordType: 'password', redirect: undefined, - logoShow:false + logoShow:false, + title:'' } }, watch: { @@ -282,6 +283,7 @@ export default { }, mounted() { this.loginLogo = require('@/assets/' + process.env.VUE_APP_SYSTEM_LOGO) + this.title = process.env.VUE_APP_SYSTEM_NAME }, methods: { showPwd() {