diff --git a/pom.xml b/pom.xml index e79897b..35e07c7 100644 --- a/pom.xml +++ b/pom.xml @@ -98,6 +98,12 @@ ${cm-cloud.version} + + com.cm + cloud-common-websocket + 1.0.1-SNAPSHOT + + com.cm cloud-common-wechat diff --git a/src/main/java/com/cm/serviceusercenter/config/WebSecurityConfig.java b/src/main/java/com/cm/serviceusercenter/config/WebSecurityConfig.java index 33e434e..7ad51cb 100644 --- a/src/main/java/com/cm/serviceusercenter/config/WebSecurityConfig.java +++ b/src/main/java/com/cm/serviceusercenter/config/WebSecurityConfig.java @@ -68,7 +68,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { "/wxminiapp/**", "/route/file/**", "/api/sms/getverificationcode/*", - "/api/user/getsignintype/**") + "/api/user/getsignintype/**", + "/ws") .permitAll() .and() .authorizeRequests() diff --git a/src/main/java/com/cm/serviceusercenter/service/system/user/IUserImService.java b/src/main/java/com/cm/serviceusercenter/service/system/user/IUserImService.java index cf11712..4717f46 100644 --- a/src/main/java/com/cm/serviceusercenter/service/system/user/IUserImService.java +++ b/src/main/java/com/cm/serviceusercenter/service/system/user/IUserImService.java @@ -1,10 +1,6 @@ package com.cm.serviceusercenter.service.system.user; -import com.cm.common.exception.SearchException; -import com.cm.common.result.SuccessResultLayImData; - -import java.util.List; -import java.util.Map; +import com.cm.common.plugin.interfaces.IUserImBaseService; /** * When you feel like quitting. Think about why you started @@ -16,14 +12,5 @@ import java.util.Map; * @Date: 2021/1/15 10:55 上午 * @Version: 1.0 */ -public interface IUserImService { - - /** - * 获取部门用户信息 - * - * @param params - * @return - * @throws SearchException - */ - List listDepartmentUser(Map params) throws SearchException; +public interface IUserImService extends IUserImBaseService { } diff --git a/src/main/java/com/cm/serviceusercenter/service/system/user/impl/UserImService.java b/src/main/java/com/cm/serviceusercenter/service/system/user/impl/UserImService.java index bcc8b23..6dfd88f 100644 --- a/src/main/java/com/cm/serviceusercenter/service/system/user/impl/UserImService.java +++ b/src/main/java/com/cm/serviceusercenter/service/system/user/impl/UserImService.java @@ -1,8 +1,11 @@ package com.cm.serviceusercenter.service.system.user.impl; import com.alibaba.druid.util.StringUtils; +import com.cm.common.config.properties.ApiPathProperties; +import com.cm.common.constants.ISystemConstant; import com.cm.common.exception.SearchException; -import com.cm.common.plugin.pojo.bos.department.DepartmentResourceBO; +import com.cm.common.plugin.pojo.bos.user.UserDepartmentResourceBO; +import com.cm.common.pojo.bos.UserInfoBO; import com.cm.common.result.SuccessResultLayImData; import com.cm.serviceusercenter.pojo.dtos.DepartmentUserDTO; import com.cm.serviceusercenter.pojo.dtos.department.DepartmentSimpleDTO; @@ -13,10 +16,7 @@ import com.cm.serviceusercenter.service.system.user.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * When you feel like quitting. Think about why you started @@ -35,6 +35,47 @@ public class UserImService extends BaseService implements IUserImService { private IDepartmentService departmentService; @Autowired private IUserService userService; + @Autowired + private ApiPathProperties apiPathProperties; + + @Override + public SuccessResultLayImData getInitData() { + try { + // 个人信息 + UserInfoBO currentUser = securityComponent.getCurrentUser(); + SuccessResultLayImData.User mine = new SuccessResultLayImData.User(); + mine.setId(currentUser.getUserId()); + mine.setUsername(org.apache.commons.lang3.StringUtils.equals(currentUser.getUserUsername(), ISystemConstant.ADMIN) ? "系统管理员" : currentUser.getUserName()); + if (org.apache.commons.lang3.StringUtils.isBlank(currentUser.getUserAvatar())) { + mine.setAvatar(DEFAULT_USER_AVATAR); + } else { + mine.setAvatar(apiPathProperties.getUserCenter() + "/route/file/downloadfile/true/" + currentUser.getUserAvatar()); + } + mine.setStatus("online"); + mine.setSign("暂无签名"); + + Map params = getHashMap(2); + // 用户所在部门集合 + Set userFriendSet = new HashSet<>(); + + // 朋友 + List friends = listDepartmentUser(params); + setFriends(currentUser, mine, friends, userFriendSet); + + // 用户组 + List groups = new ArrayList<>(); + setGroups(currentUser, friends, groups, userFriendSet); + + SuccessResultLayImData.InitData initData = new SuccessResultLayImData.InitData(); + initData.setMine(mine); + initData.setFriend(friends); + initData.setGroup(groups); + return new SuccessResultLayImData<>(0, "", initData); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + return new SuccessResultLayImData<>(400, e.getMessage(), null); + } + } @Override public List listDepartmentUser(Map params) throws SearchException { @@ -79,4 +120,139 @@ public class UserImService extends BaseService implements IUserImService { } return friends; } + + @Override + public SuccessResultLayImData getMemberData(String departmentId) { + try { + List departmentIds = new ArrayList<>(); + departmentIds.add(departmentId); + List userDepartmentResourceBOS = userService.listUserDepartmentResourceByDepartmentIds(departmentIds); + SuccessResultLayImData.MemberData memberData = new SuccessResultLayImData.MemberData(); + List list = new ArrayList<>(); + memberData.setList(list); + for (UserDepartmentResourceBO userDepartmentResourceBO : userDepartmentResourceBOS) { + SuccessResultLayImData.User user = new SuccessResultLayImData.User(); + user.setId(userDepartmentResourceBO.getUserId()); + user.setUsername(userDepartmentResourceBO.getUserName()); + user.setSign("暂无签名"); + if (org.apache.commons.lang3.StringUtils.isBlank(userDepartmentResourceBO.getUserAvatar())) { + user.setAvatar(DEFAULT_USER_AVATAR); + } else { + user.setAvatar(apiPathProperties.getUserCenter() + "/route/file/downloadfile/true/" + userDepartmentResourceBO.getUserAvatar()); + } + list.add(user); + } + // 添加管理员 + SuccessResultLayImData.User adminUser = getAdminUser(); + adminUser.setAvatar(DEFAULT_USER_AVATAR); + list.add(adminUser); + return new SuccessResultLayImData<>(0, "", memberData); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + return new SuccessResultLayImData<>(400, e.getMessage(), null); + } + } + + @Override + public List listUserDepartmentResource(List departmentIds) { + List userDepartmentResourceBOS = userService.listUserDepartmentResourceByDepartmentIds(departmentIds); + // 添加系统管理员 + UserDepartmentResourceBO userDepartmentResourceBO = new UserDepartmentResourceBO(); + userDepartmentResourceBO.setUserId("1"); + userDepartmentResourceBO.setUserUsername(ISystemConstant.ADMIN); + userDepartmentResourceBO.setUserName("系统管理员"); + userDepartmentResourceBO.setUserAvatar(DEFAULT_USER_AVATAR); + userDepartmentResourceBOS.add(userDepartmentResourceBO); + return userDepartmentResourceBOS; + } + + /** + * 设置朋友列表 + * + * @param currentUser + * @param mine + * @param friends + */ + private void setFriends(UserInfoBO currentUser, SuccessResultLayImData.User mine, List friends, Set userFriendSet) { + // 非管理员添加系统管理员 + if (!org.apache.commons.lang3.StringUtils.equals(ISystemConstant.ADMIN, currentUser.getUserUsername())) { + // 加载管理员 + SuccessResultLayImData.Friend adminFriend = new SuccessResultLayImData.Friend(); + adminFriend.setId("admin"); + adminFriend.setGroupname("管理员"); + List users = new ArrayList<>(); + users.add(getAdminUser()); + adminFriend.setList(users); + friends.add(adminFriend); + } + for (int i = 0; i < friends.size(); i++) { + SuccessResultLayImData.Friend friend = friends.get(i); + for (int j = 0; j < friend.getList().size(); j++) { + SuccessResultLayImData.User user = friend.getList().get(j); + // 删除自己 + if (org.apache.commons.lang3.StringUtils.equals(user.getId(), mine.getId())) { + userFriendSet.add(friend); + friend.getList().remove(j); + j--; + continue; + } + if (org.apache.commons.lang3.StringUtils.isBlank(user.getAvatar())) { + user.setAvatar(DEFAULT_USER_AVATAR); + } else { + user.setAvatar(apiPathProperties.getUserCenter() + "/route/file/downloadfile/true/" + user.getAvatar()); + } + // 默认离线 + user.setStatus("offline"); + user.setSign("暂无签名"); + } + // 删除用户为空的朋友组 + if (friend.getList().isEmpty()) { + friends.remove(i); + i--; + } + } + } + + /** + * 系统管理员 + * + * @return + */ + private SuccessResultLayImData.User getAdminUser() { + SuccessResultLayImData.User user = new SuccessResultLayImData.User(); + user.setId("1"); + user.setStatus("offline"); + user.setUsername("系统管理员"); + return user; + } + + /** + * 设置组 + * + * @param currentUser + * @param friends + * @param groups + * @param userFriendIdSet + */ + private void setGroups(UserInfoBO currentUser, List friends, List groups, Set userFriendSet) { + // 管理员添加所有组 + if (org.apache.commons.lang3.StringUtils.equals(ISystemConstant.ADMIN, currentUser.getUserUsername())) { + for (SuccessResultLayImData.Friend friend : friends) { + SuccessResultLayImData.Group group = new SuccessResultLayImData.Group(); + group.setId(friend.getId()); + group.setGroupname(friend.getGroupname()); + group.setAvatar(DEFAULT_GROUP_AVATAR); + groups.add(group); + } + return; + } + // 非管理员添加自己所在部门 + for (SuccessResultLayImData.Friend friend : userFriendSet) { + SuccessResultLayImData.Group group = new SuccessResultLayImData.Group(); + group.setId(friend.getId()); + group.setGroupname(friend.getGroupname()); + group.setAvatar(DEFAULT_GROUP_AVATAR); + groups.add(group); + } + } } diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 9e99dbd..7b81f47 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -1,6 +1,7 @@ server: port: 7001 url: http://127.0.0.1:7001/usercenter + ws: ws://127.0.0.1:7001/usercenter/ws title: 统一用户管理平台 login-page-name: 统一用户管理平台 servlet: diff --git a/src/main/resources/static/assets/layuiadmin/modules/im-socket.js b/src/main/resources/static/assets/layuiadmin/modules/im-socket.js new file mode 100644 index 0000000..119d1f9 --- /dev/null +++ b/src/main/resources/static/assets/layuiadmin/modules/im-socket.js @@ -0,0 +1,336 @@ +layui.define(['layim'], function(exports) { + var $ = layui.$; + var layim = layui.layim; + + var Socket = { + Message: function() { + this.id = null; + this.type = null; + this.isSystem = false; + this.from = null; + this.to = null; + this.body = null; + this.timestamp = null; + }, + Register: function() { + this.appId = null; + this.userId = null; + }, + TypeEnum: { + REGISTER: 100, + MESSAGE: 101, + GROUP_MESSAGE: 102, + SYSTEM_MESSAGE: 103, + SYSTEM_GROUP_MESSAGE: 104, + SYSTEM_TARGET_MESSAGE: 105, + NOTICE: 106, + NOTICE_GROUP_MESSAGE: 107, + NOTICE_TARGET_MESSAGE: 108, + SEARCH_ONLINE_USER: 600, + SEARCH_ONLINE_USER_FRIEND: 601, + SEND_STATUS: 1100, + SEND_STATUS_ONLINE: 1101, + SEND_STATUS_OFFLINE: 1102 + }, + getUUID: function() { + var s = []; + var hexDigits = "0123456789abcdef"; + for (var i = 0; i < 36; i++) { + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); + } + s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010 + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 + s[8] = s[13] = s[18] = s[23] = "-"; + var uuid = s.join(""); + return uuid; + }, + initSocket: function(ws, layIM, options) { + var appId = Socket.getUUID(); + var userId = options.mine.id; + + function getFriend(userId) { + for(var i = 0, item; item = options.friend[i++];) { + for(var j = 0, jItem; jItem = item.list[j++];) { + if(jItem.id === userId) { + return jItem; + } + } + } + return null; + } + + // 全部在线人员 + function listOnlineUser() { + var message = new Socket.Message(); + message.id = Socket.getUUID(); + message.type = Socket.TypeEnum.SEARCH_ONLINE_USER; + message.timestamp = new Date().getTime(); + message.from = userId; + message.to = userId; + // 消息体 + message.body = userId; + socket.send(JSON.stringify(message)); + } + + // 注册socket服务 + var socket = new WebSocket(ws); + socket.onclose = function() { + top.dialog.msg('消息服务器连接断开,5s后重试'); + setTimeout(function() { + Socket.initSocket(options); + }, 5000); + } + socket.onerror = function(event) { + console.error('消息服务器连接异常,请刷新页面重试', event); + } + socket.onopen = function() { + // 消息体 + var message = new Socket.Message(); + message.id = Socket.getUUID(); + message.type = Socket.TypeEnum.REGISTER; + message.timestamp = new Date().getTime(); + // 注册体 + var register = new Socket.Register(); + register.appId = appId; + register.userId = userId; + message.body = JSON.stringify(register); + // 注册设备 + socket.send(JSON.stringify(message)); + }; + + socket.onmessage = function(res) { + var data = JSON.parse(res.data); + if(data.type === 100) { + // 连接状态 + var body = JSON.parse(data.body); + if(body.code == 200) { + console.log('消息服务器连接成功'); + setTimeout(function() { + listOnlineUser(); + }, 1000); + return; + } + } else if(data.type === 101 || data.type === 102) { + // 消息 + var user; + if(data.from === options.mine.id) { + user = options.mine; + } else { + user = getFriend(data.from); + console.log(user); + if(user == null) { + user = {}; + user.id = 'otherUser'+ new Date().getTime(); + user.username = '未知用户'; + user.avatar = 'assets/images/random-avatar2.jpg'; + } + } + if(data.type === 101) { + layIM.getMessage({ + username: user.username, + avatar: user.avatar, + id: user.id, + content: data.body, + fromid: data.from, + type: 'friend', + mine: data.from === options.mine.id ? true : false, + timestamp: data.timestamp + }); + } else if(data.type === 102) { + layIM.getMessage({ + username: user.username, + avatar: user.avatar, + id: data.to, + content: data.body, + fromid: data.from, + type: 'group', + mine: false, + timestamp: data.timestamp + }); + } + } else if(data.type === 1100) { + // 发送状态 + var body = JSON.parse(data.body); + if(body.code === 200) { + console.log('发送成功'); + return; + } else if(body.code === 300) { + var user = getFriend(data.to); + if(user != null) { + layIM.getMessage({ + system: true, + id: user.id, + type: 'friend', + content: body.msg + }); + } else { + top.dialog.msg(body.msg); + } + return; + } + } else if(data.type === 1101) { + var onlineUserIdArray = JSON.parse(data.body); + for(var i = 0, item; item = onlineUserIdArray[i++];) { + var user = getFriend(item); + if(user == null) { + return; + } + user.status = 'online'; + layim.setFriendStatus(item, 'online'); + } + } else if(data.type === 1102) { + var onlineUserIdArray = JSON.parse(data.body); + for(var i = 0, item; item = onlineUserIdArray[i++];) { + var user = getFriend(item); + if(user == null) { + return; + } + user.status = 'offline'; + layim.setFriendStatus(item, 'offline'); + } + } + }; + layIM.on('sendMessage', function(res) { + console.log(res); + + var mine = res.mine; + // 消息体 + var message = new Socket.Message(); + message.id = Socket.getUUID(); + if(res.to.type === 'friend') { + message.type = Socket.TypeEnum.MESSAGE; + } else if(res.to.type === 'group') { + message.type = Socket.TypeEnum.GROUP_MESSAGE; + } + message.timestamp = new Date().getTime(); + message.from = mine.id; + message.to = res.to.id; + // 消息体,查找消息体中的url + var content = mine.content; + // 是否图片 + var imageMatchArray = mine.content.match(/img\[.*\]/g); + // 是否文件 + var fileMatchArray = mine.content.match(/file\(.*\)\[.*\]/g); + // 是否视频 + var videoMatchArray = mine.content.match(/video\[.*\]/g); + if(imageMatchArray == null && fileMatchArray == null && videoMatchArray == null) { + var urlArray = mine.content.match(/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)(:?\w+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/g); + if(urlArray != null && urlArray.length > 0) { + for(var i = 0, item; item = urlArray[i++];) { + content = content.replace(item, 'a('+ item +')['+ item +']'); + } + } + } + message.body = content; + if(socket.readyState === 1) { + socket.send(JSON.stringify(message)); + } else { + top.dialog.msg('发送异常,请刷新页面重试'); + } + }); + } + } + + // 即使消息 + layim.config({ + init: { + url: 'api/user/im/getinitdata', + type: 'get', + data: {} + }, + members: { + url: 'api/user/im/getmemberdata', + type: 'get', + data: {} + }, + min: false, + isfriend: true, + isgroup: true, + copyright: true, + isAudio: false, + isVideo: false, + tool: [{ + alias: 'image', + title: '发送图片', + icon: '' + }, { + alias: 'file', + title: '发送文件', + icon: '' + }, { + alias: 'video', + title: '发送视频', + icon: '' + } + ] + }); + + layim.on('ready', function(options) { + Socket.initSocket($('#wsUrl').val(), layim, options); + }); + layim.on('chatChange', function(res) { + var data = res.data; + if(data.type === 'friend') { + layim.setChatStatus(data.sign); + } + }); + layim.on('tool(image)', function(insert, send, obj) { + top.dialog.file({ + type: 'image', + title: '发送图片', + width: '400px', + height: '420px', + maxFileCount: '1', + onClose: function() { + var uploadFileArray = top.dialog.dialogData.uploadFileArray; + if(typeof(uploadFileArray) != 'undefined' && uploadFileArray.length > 0) { + for(var i = 0, item; item = uploadFileArray[i++];) { + var fullUrl = $('#serverUrl').val() +'/route/file/downloadfile/true/'+ item.data; + insert('img['+ fullUrl +']'); + send(); + } + } + } + }); + }); + layim.on('tool(file)', function(insert, send, obj) { + top.dialog.file({ + type: 'file', + title: '发送文件', + width: '400px', + height: '420px', + maxFileCount: '1', + onClose: function() { + var uploadFileArray = top.dialog.dialogData.uploadFileArray; + if(typeof(uploadFileArray) != 'undefined' && uploadFileArray.length > 0) { + for(var i = 0, item; item = uploadFileArray[i++];) { + var fullUrl = $('#serverUrl').val() +'/route/file/downloadfile/false/'+ item.data; + insert('file('+ fullUrl +')[ ]'); + send(); + } + } + } + }); + }); + layim.on('tool(video)', function(insert, send, obj) { + top.dialog.file({ + type: 'video', + title: '发送视频', + width: '400px', + height: '420px', + maxFileCount: '1', + onClose: function() { + var uploadFileArray = top.dialog.dialogData.uploadFileArray; + if(typeof(uploadFileArray) != 'undefined' && uploadFileArray.length > 0) { + for(var i = 0, item; item = uploadFileArray[i++];) { + var fullUrl = $('#serverUrl').val() +'/route/file/downloadfile/true/'+ item.data; + insert('video['+ fullUrl +']'); + send(); + } + } + } + }); + }); + + exports('im-socket', {}); +}) \ No newline at end of file diff --git a/src/main/resources/templates/main.html b/src/main/resources/templates/main.html index c669ac3..b5f6bc6 100644 --- a/src/main/resources/templates/main.html +++ b/src/main/resources/templates/main.html @@ -152,6 +152,8 @@
+ + @@ -160,7 +162,7 @@ base: 'assets/layuiadmin/' //静态资源所在路径 }).extend({ index: 'lib/index' //主入口模块 - }).use(['index', 'restajax', 'datamessage', 'dialog'], function() { + }).use(['index', 'restajax', 'datamessage', 'dialog', 'im-socket'], function() { var $ = layui.$; var layer = layui.layer;