From 736c5376e096bed7e9be40a33683531333deb8d7 Mon Sep 17 00:00:00 2001 From: rowger Date: Thu, 14 Sep 2023 14:59:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E8=A1=A8=E5=92=8C=E9=9F=B3=E8=A7=86=E9=A2=91demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .gitignore.bak | 7 + hs-im-server/.gitignore | 4 + hs-im-server/README.md | 0 hs-im-server/doc/im-core-send.sql | 283 +++++++ hs-im-server/doc/im-core-study.sql | 222 ++++++ hs-im-server/im-common/pom.xml | 52 ++ .../java/com/lld/im/common/BaseErrorCode.java | 31 + .../java/com/lld/im/common/ClientType.java | 34 + .../java/com/lld/im/common/ResponseVO.java | 66 ++ .../im/common/enums/AllowFriendTypeEnum.java | 27 + .../ApproverFriendRequestStatusEnum.java | 22 + .../common/enums/CheckFriendShipTypeEnum.java | 22 + .../com/lld/im/common/enums/DelFlagEnum.java | 22 + .../im/common/enums/FriendShipErrorCode.java | 57 ++ .../im/common/enums/FriendShipStatusEnum.java | 31 + .../lld/im/common/enums/GroupErrorCode.java | 66 ++ .../im/common/enums/GroupMemberRoleEnum.java | 50 ++ .../im/common/enums/GroupMuteTypeEnum.java | 43 + .../lld/im/common/enums/GroupStatusEnum.java | 42 + .../lld/im/common/enums/GroupTypeEnum.java | 42 + .../lld/im/common/enums/UserErrorCode.java | 30 + .../common/enums/UserForbiddenFlagEnum.java | 22 + .../im/common/enums/UserSilentFlagEnum.java | 22 + .../exception/ApplicationException.java | 44 ++ .../exception/ApplicationExceptionEnum.java | 8 + .../com/lld/im/common/model/ClientInfo.java | 25 + .../com/lld/im/common/model/RequestBase.java | 19 + .../lld/im/common/model/UserClientDto.java | 21 + .../com/lld/im/common/model/UserSession.java | 37 + hs-im-server/im-service/im-service.iml | 0 hs-im-server/im-service/pom.xml | 101 +++ .../java/com/lld/im/service/Application.java | 20 + .../com/lld/im/service/config/BeanConfig.java | 13 + .../com/lld/im/service/config/WebConfig.java | 26 + .../exception/GlobalExceptionHandler.java | 110 +++ .../controller/ImFriendShipController.java | 86 ++ .../ImFriendShipGroupController.java | 56 ++ .../ImFriendShipRequestController.java | 42 + .../friendship/dao/ImFriendShipEntity.java | 46 ++ .../dao/ImFriendShipGroupEntity.java | 32 + .../dao/ImFriendShipGroupMemberEntity.java | 17 + .../dao/ImFriendShipRequestEntity.java | 49 ++ .../dao/mapper/ImFriendShipGroupMapper.java | 9 + .../mapper/ImFriendShipGroupMemberMapper.java | 9 + .../dao/mapper/ImFriendShipMapper.java | 91 +++ .../dao/mapper/ImFriendShipRequestMapper.java | 9 + .../friendship/model/req/AddFriendReq.java | 19 + .../model/req/AddFriendShipBlackReq.java | 16 + .../req/AddFriendShipGroupMemberReq.java | 24 + .../model/req/AddFriendShipGroupReq.java | 21 + .../model/req/ApproverFriendRequestReq.java | 14 + .../model/req/CheckFriendShipReq.java | 27 + .../friendship/model/req/DeleteBlackReq.java | 19 + .../friendship/model/req/DeleteFriendReq.java | 18 + .../req/DeleteFriendShipGroupMemberReq.java | 27 + .../model/req/DeleteFriendShipGroupReq.java | 23 + .../friendship/model/req/FriendDto.java | 19 + .../model/req/GetAllFriendShipReq.java | 14 + .../model/req/GetFriendShipRequestReq.java | 19 + .../friendship/model/req/GetRelationReq.java | 16 + .../model/req/ImporFriendShipReq.java | 33 + .../model/req/ReadFriendShipRequestReq.java | 14 + .../friendship/model/req/UpdateFriendReq.java | 18 + .../model/resp/CheckFriendShipResp.java | 30 + .../model/resp/ImportFriendShipResp.java | 14 + .../friendship/service/ImFriendService.java | 40 + .../ImFriendShipGroupMemberService.java | 20 + .../service/ImFriendShipGroupService.java | 21 + .../service/ImFriendShipRequestService.java | 18 + .../service/impl/ImFriendServiceImpl.java | 439 +++++++++++ .../ImFriendShipGroupMemberServiceImpl.java | 122 +++ .../impl/ImFriendShipGroupServiceImpl.java | 119 +++ .../impl/ImFriendShipRequestServiceImpl.java | 140 ++++ .../group/controller/ImGroupController.java | 80 ++ .../controller/ImGroupMemberController.java | 59 ++ .../im/service/group/dao/ImGroupEntity.java | 54 ++ .../group/dao/ImGroupMemberEntity.java | 44 ++ .../dao/ImGroupMessageHistoryEntity.java | 32 + .../group/dao/mapper/ImGroupMapper.java | 13 + .../group/dao/mapper/ImGroupMemberMapper.java | 65 ++ .../mapper/ImGroupMessageHistoryMapper.java | 11 + .../group/model/req/AddGroupMemberReq.java | 24 + .../group/model/req/CreateGroupReq.java | 41 + .../group/model/req/DestroyGroupReq.java | 18 + .../service/group/model/req/GetGroupReq.java | 15 + .../group/model/req/GetJoinedGroupReq.java | 29 + .../group/model/req/GetRoleInGroupReq.java | 18 + .../group/model/req/GroupMemberDto.java | 28 + .../group/model/req/ImportGroupMemberReq.java | 22 + .../group/model/req/ImportGroupReq.java | 42 + .../service/group/model/req/MuteGroupReq.java | 22 + .../group/model/req/RemoveGroupMemberReq.java | 20 + .../group/model/req/SendGroupMessageReq.java | 35 + .../group/model/req/SpeaMemberReq.java | 25 + .../group/model/req/TransferGroupReq.java | 17 + .../group/model/req/UpdateGroupMemberReq.java | 27 + .../group/model/req/UpdateGroupReq.java | 34 + .../group/model/resp/AddMemberResp.java | 19 + .../group/model/resp/GetGroupResp.java | 48 ++ .../group/model/resp/GetJoinedGroupResp.java | 19 + .../group/model/resp/GetRoleInGroupResp.java | 20 + .../group/service/ImGroupMemberService.java | 43 + .../service/group/service/ImGroupService.java | 32 + .../impl/ImGroupMemberServiceImpl.java | 537 +++++++++++++ .../service/impl/ImGroupServiceImpl.java | 393 ++++++++++ .../user/controller/ImUserController.java | 35 + .../user/controller/ImUserDataController.java | 47 ++ .../im/service/user/dao/ImUserDataEntity.java | 60 ++ .../user/dao/mapper/ImUserDataMapper.java | 9 + .../model/UserStatusChangeNotifyContent.java | 22 + .../service/user/model/req/DeleteUserReq.java | 15 + .../user/model/req/GetUserInfoReq.java | 15 + .../user/model/req/GetUserSequenceReq.java | 16 + .../service/user/model/req/ImportUserReq.java | 16 + .../user/model/req/ModifyUserInfoReq.java | 46 ++ .../lld/im/service/user/model/req/UserId.java | 15 + .../user/model/resp/GetUserInfoResp.java | 20 + .../user/model/resp/ImportUserResp.java | 18 + .../user/model/resp/UserOnlineStatusResp.java | 22 + .../service/user/service/ImUserService.java | 26 + .../user/service/impl/ImUserviceImpl.java | 173 ++++ .../src/main/resources/application.yml | 38 + .../src/main/resources/logback-spring.xml | 88 +++ hs-im-server/pom.xml | 216 +++++ media-server-demo-node/.gitignore | 40 + media-server-demo-node/.jshintrc | 4 + media-server-demo-node/Dockerfile | 16 + media-server-demo-node/LICENSE | 21 + media-server-demo-node/README.md | 36 + media-server-demo-node/build.sh | 6 + media-server-demo-node/entrypoint.sh | 14 + media-server-demo-node/index.js | 128 +++ media-server-demo-node/lib/broadcast.js | 147 ++++ media-server-demo-node/lib/datachannels.js | 74 ++ media-server-demo-node/lib/playback.js | 134 ++++ media-server-demo-node/lib/playback_1.js | 134 ++++ media-server-demo-node/lib/recording.js | 121 +++ media-server-demo-node/lib/simulcast.js | 141 ++++ media-server-demo-node/lib/svc.js | 153 ++++ media-server-demo-node/package.json | 24 + .../recordings/your_recordings_will_be_here | 0 .../www/broadcast/index.html | 200 +++++ .../www/broadcast/js/broadcast.js | 121 +++ .../www/datachannels/index.html | 16 + .../www/datachannels/js/datachannels.js | 50 ++ .../www/js/dialog-polyfill.js | 738 ++++++++++++++++++ media-server-demo-node/www/js/gauge.min.js | 1 + .../www/js/transaction-manager.js | 536 +++++++++++++ .../www/playback/index.html | 193 +++++ .../www/playback/js/playback.js | 134 ++++ media-server-demo-node/www/rec/index.html | 197 +++++ media-server-demo-node/www/rec/js/rec.js | 159 ++++ .../www/simulcast/index.html | 286 +++++++ .../www/simulcast/js/simulcast.js | 427 ++++++++++ media-server-demo-node/www/svc/index.html | 297 +++++++ media-server-demo-node/www/svc/js/svc.js | 340 ++++++++ 157 files changed, 11044 insertions(+) create mode 100644 .gitignore.bak create mode 100644 hs-im-server/.gitignore create mode 100644 hs-im-server/README.md create mode 100644 hs-im-server/doc/im-core-send.sql create mode 100644 hs-im-server/doc/im-core-study.sql create mode 100644 hs-im-server/im-common/pom.xml create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/BaseErrorCode.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/ClientType.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/ResponseVO.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/AllowFriendTypeEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/ApproverFriendRequestStatusEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/CheckFriendShipTypeEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/DelFlagEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/FriendShipErrorCode.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/FriendShipStatusEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupErrorCode.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupMemberRoleEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupMuteTypeEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupStatusEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupTypeEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserErrorCode.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserForbiddenFlagEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserSilentFlagEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/exception/ApplicationException.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/exception/ApplicationExceptionEnum.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/model/ClientInfo.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/model/RequestBase.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/model/UserClientDto.java create mode 100644 hs-im-server/im-common/src/main/java/com/lld/im/common/model/UserSession.java create mode 100644 hs-im-server/im-service/im-service.iml create mode 100644 hs-im-server/im-service/pom.xml create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/Application.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/config/BeanConfig.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/config/WebConfig.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/exception/GlobalExceptionHandler.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipController.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipGroupController.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipRequestController.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipEntity.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipGroupEntity.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipGroupMemberEntity.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipRequestEntity.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipGroupMapper.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipGroupMemberMapper.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipMapper.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipRequestMapper.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipBlackReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipGroupMemberReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ApproverFriendRequestReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/CheckFriendShipReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteBlackReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendShipGroupMemberReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendShipGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/FriendDto.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetAllFriendShipReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetFriendShipRequestReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetRelationReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ImporFriendShipReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ReadFriendShipRequestReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/UpdateFriendReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/resp/CheckFriendShipResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/resp/ImportFriendShipResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendService.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipGroupMemberService.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipGroupService.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipRequestService.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendServiceImpl.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipGroupMemberServiceImpl.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipGroupServiceImpl.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipRequestServiceImpl.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/controller/ImGroupController.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/controller/ImGroupMemberController.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupEntity.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupMemberEntity.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupMessageHistoryEntity.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMapper.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMemberMapper.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMessageHistoryMapper.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/AddGroupMemberReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/CreateGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/DestroyGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetJoinedGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetRoleInGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GroupMemberDto.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/ImportGroupMemberReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/ImportGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/MuteGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/RemoveGroupMemberReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/SendGroupMessageReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/SpeaMemberReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/TransferGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/UpdateGroupMemberReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/UpdateGroupReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/AddMemberResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetGroupResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetJoinedGroupResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetRoleInGroupResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/ImGroupMemberService.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/ImGroupService.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/impl/ImGroupMemberServiceImpl.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/impl/ImGroupServiceImpl.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/controller/ImUserController.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/controller/ImUserDataController.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/dao/ImUserDataEntity.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/dao/mapper/ImUserDataMapper.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/UserStatusChangeNotifyContent.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/DeleteUserReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/GetUserInfoReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/GetUserSequenceReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/ImportUserReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/ModifyUserInfoReq.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/UserId.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/GetUserInfoResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/ImportUserResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/UserOnlineStatusResp.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/service/ImUserService.java create mode 100644 hs-im-server/im-service/src/main/java/com/lld/im/service/user/service/impl/ImUserviceImpl.java create mode 100644 hs-im-server/im-service/src/main/resources/application.yml create mode 100644 hs-im-server/im-service/src/main/resources/logback-spring.xml create mode 100644 hs-im-server/pom.xml create mode 100644 media-server-demo-node/.gitignore create mode 100644 media-server-demo-node/.jshintrc create mode 100644 media-server-demo-node/Dockerfile create mode 100644 media-server-demo-node/LICENSE create mode 100644 media-server-demo-node/README.md create mode 100644 media-server-demo-node/build.sh create mode 100644 media-server-demo-node/entrypoint.sh create mode 100644 media-server-demo-node/index.js create mode 100644 media-server-demo-node/lib/broadcast.js create mode 100644 media-server-demo-node/lib/datachannels.js create mode 100644 media-server-demo-node/lib/playback.js create mode 100644 media-server-demo-node/lib/playback_1.js create mode 100644 media-server-demo-node/lib/recording.js create mode 100644 media-server-demo-node/lib/simulcast.js create mode 100644 media-server-demo-node/lib/svc.js create mode 100644 media-server-demo-node/package.json create mode 100644 media-server-demo-node/recordings/your_recordings_will_be_here create mode 100644 media-server-demo-node/www/broadcast/index.html create mode 100644 media-server-demo-node/www/broadcast/js/broadcast.js create mode 100644 media-server-demo-node/www/datachannels/index.html create mode 100644 media-server-demo-node/www/datachannels/js/datachannels.js create mode 100644 media-server-demo-node/www/js/dialog-polyfill.js create mode 100644 media-server-demo-node/www/js/gauge.min.js create mode 100644 media-server-demo-node/www/js/transaction-manager.js create mode 100644 media-server-demo-node/www/playback/index.html create mode 100644 media-server-demo-node/www/playback/js/playback.js create mode 100644 media-server-demo-node/www/rec/index.html create mode 100644 media-server-demo-node/www/rec/js/rec.js create mode 100644 media-server-demo-node/www/simulcast/index.html create mode 100644 media-server-demo-node/www/simulcast/js/simulcast.js create mode 100644 media-server-demo-node/www/svc/index.html create mode 100644 media-server-demo-node/www/svc/js/svc.js diff --git a/.gitignore b/.gitignore index 485f9f0..030d602 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ **/im-common/target **/im-message-store/target **/im-tcp/target + +**/node_modules/** \ No newline at end of file diff --git a/.gitignore.bak b/.gitignore.bak new file mode 100644 index 0000000..485f9f0 --- /dev/null +++ b/.gitignore.bak @@ -0,0 +1,7 @@ +**/.idea +/logs +**/im-service/target +**/im-codec/target +**/im-common/target +**/im-message-store/target +**/im-tcp/target diff --git a/hs-im-server/.gitignore b/hs-im-server/.gitignore new file mode 100644 index 0000000..5fa1743 --- /dev/null +++ b/hs-im-server/.gitignore @@ -0,0 +1,4 @@ +/.idea +/logs +/im-service/target +/im-common/target diff --git a/hs-im-server/README.md b/hs-im-server/README.md new file mode 100644 index 0000000..e69de29 diff --git a/hs-im-server/doc/im-core-send.sql b/hs-im-server/doc/im-core-send.sql new file mode 100644 index 0000000..ade37a3 --- /dev/null +++ b/hs-im-server/doc/im-core-send.sql @@ -0,0 +1,283 @@ +/* + Navicat Premium Data Transfer + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 50740 + Source Host : localhost:3306 + Source Schema : im-core + + Target Server Type : MySQL + Target Server Version : 50740 + File Encoding : 65001 + + Date: 30/01/2023 18:41:03 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for app_user +-- ---------------------------- +DROP TABLE IF EXISTS `app_user`; +CREATE TABLE `app_user` ( + `user_id` varchar(20) NOT NULL, + `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `update_time` bigint(20) NULL DEFAULT NULL, + PRIMARY KEY (`user_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of app_user +-- ---------------------------- +INSERT INTO `app_user` VALUES ('lld', 'lld', 'lld', '13266520732', NULL, NULL); + +-- ---------------------------- +-- Table structure for im_conversation_set +-- ---------------------------- +DROP TABLE IF EXISTS `im_conversation_set`; +CREATE TABLE `im_conversation_set` ( + `conversation_id` varchar(255) NOT NULL, + `conversation_type` int(10) NULL DEFAULT NULL COMMENT '0 单聊 1群聊 2机器人 3公众号', + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `to_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `is_mute` int(10) NULL DEFAULT NULL COMMENT '是否免打扰 1免打扰', + `is_top` int(10) NULL DEFAULT NULL COMMENT '是否置顶 1置顶', + `sequence` bigint(20) NULL DEFAULT NULL COMMENT 'sequence', + `readed_sequence` bigint(20) NULL DEFAULT NULL, + `app_id` int(10) NOT NULL, + PRIMARY KEY (`app_id`, `conversation_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of im_conversation_set +-- ---------------------------- +INSERT INTO `im_conversation_set` VALUES ('0_lld2_lld', 0, 'lld2', 'lld', 0, 0, 23, 120201, 10000); +INSERT INTO `im_conversation_set` VALUES ('0_lld_lld2', 0, 'lld', 'lld2', 1, 0, 22, 120200, 10000); +INSERT INTO `im_conversation_set` VALUES ('0_lld_lld3', 0, 'lld', 'lld3', 0, 0, 21, 1, 10000); + +-- ---------------------------- +-- Table structure for im_friendship +-- ---------------------------- +DROP TABLE IF EXISTS `im_friendship`; +CREATE TABLE `im_friendship` ( + `app_id` int(20) NOT NULL, + `from_id` varchar(50) NOT NULL, + `to_id` varchar(50) NOT NULL, + `remark` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', + `status` int(10) NULL DEFAULT NULL COMMENT '状态 1正常 2删除', + `black` int(10) NULL DEFAULT NULL COMMENT '1正常 2拉黑', + `create_time` bigint(20) NULL DEFAULT NULL, + `friend_sequence` bigint(20) NULL DEFAULT NULL, + `black_sequence` bigint(20) NULL DEFAULT NULL, + `add_source` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '来源', + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '来源', + PRIMARY KEY (`app_id`, `from_id`, `to_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of im_friendship +-- ---------------------------- +INSERT INTO `im_friendship` VALUES (10000, 'lld', 'lld2', 'lld小号', 1, NULL, 1673919586042, NULL, NULL, 'group', NULL); +INSERT INTO `im_friendship` VALUES (10000, 'lld2', 'lld', 'lld小号', 2, NULL, 1673919586046, NULL, NULL, 'group', NULL); + +-- ---------------------------- +-- Table structure for im_friendship_group +-- ---------------------------- +DROP TABLE IF EXISTS `im_friendship_group`; +CREATE TABLE `im_friendship_group` ( + `app_id` int(20) NOT NULL, + `from_id` varchar(50) NOT NULL, + `group_id` int(50) NOT NULL AUTO_INCREMENT, + `group_name` varchar(50) NOT NULL, + `sequence` bigint(20) NULL DEFAULT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `update_time` bigint(20) NULL DEFAULT NULL, + `del_flag` int(10) NULL DEFAULT NULL, + PRIMARY KEY (`group_id`) USING BTREE, + UNIQUE INDEX `UNIQUE`(`app_id`, `from_id`, `group_name`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_friendship_group_member +-- ---------------------------- +DROP TABLE IF EXISTS `im_friendship_group_member`; +CREATE TABLE `im_friendship_group_member` ( + `group_id` bigint(20) NOT NULL, + `to_id` varchar(50) NOT NULL, + PRIMARY KEY (`group_id`,`to_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_friendship_request +-- ---------------------------- +DROP TABLE IF EXISTS `im_friendship_request`; +CREATE TABLE `im_friendship_request` ( + `id` int(20) NOT NULL AUTO_INCREMENT, + `app_id` int(20) NULL DEFAULT NULL COMMENT 'app_id', + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'from_id', + `to_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'to_id', + `remark` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', + `read_status` int(10) NULL DEFAULT NULL COMMENT '是否已读 1已读', + `add_source` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '好友来源', + `add_wording` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '好友验证信息', + `approve_status` int(10) NULL DEFAULT NULL COMMENT '审批状态 1同意 2拒绝', + `create_time` bigint(20) NULL DEFAULT NULL, + `update_time` bigint(20) NULL DEFAULT NULL, + `sequence` bigint(20) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of im_friendship_request +-- ---------------------------- +INSERT INTO `im_friendship_request` VALUES (3, 10000, 'lld', 'lld2', 'lld小号', 0, 'group', NULL, 1, 1671612496849, 1673921920088, 4); + +-- ---------------------------- +-- Table structure for im_group +-- ---------------------------- +DROP TABLE IF EXISTS `im_group`; +CREATE TABLE `im_group` ( + `app_id` int(20) NOT NULL, + `group_id` varchar(50) NOT NULL, + `owner_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '群主\r\n', + `group_type` int(10) NULL DEFAULT NULL COMMENT '群类型 1私有群(类似微信) 2公开群(类似qq)', + `group_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `mute` int(10) NULL DEFAULT NULL COMMENT '是否全员禁言,0 不禁言;1 全员禁言', + `apply_join_type` int(10) NULL DEFAULT NULL COMMENT '// 申请加群选项包括如下几种:\r\n// 0 表示禁止任何人申请加入\r\n// 1 表示需要群主或管理员审批\r\n// 2 表示允许无需审批自由加入群组', + `photo` varchar(300) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `max_member_count` int(20) NULL DEFAULT NULL, + `introduction` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '群简介', + `notification` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '群公告', + `status` int(5) NULL DEFAULT NULL COMMENT '群状态 0正常 1解散', + `sequence` bigint(20) NULL DEFAULT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '来源', + `update_time` bigint(20) NULL DEFAULT NULL, + PRIMARY KEY (`app_id`, `group_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of im_group +-- ---------------------------- +INSERT INTO `im_group` VALUES (10000, '123123', 'lld', 1, 'lld测试群3', 0, NULL, '', NULL, '', '', 1, 7, 1669686480634, NULL, 1671615867897); +INSERT INTO `im_group` VALUES (10000, 'test1', 'lld', 1, 'lld测试群', 0, NULL, '', NULL, '', '', 1, 1, 1671625608444, NULL, NULL); +INSERT INTO `im_group` VALUES (10000, 'test2', 'lld', 1, '123456', 0, NULL, '', NULL, '', '', 1, 3, 1671625622489, NULL, 1671625653981); + +-- ---------------------------- +-- Table structure for im_group_member +-- ---------------------------- +DROP TABLE IF EXISTS `im_group_member`; +CREATE TABLE `im_group_member` ( + `group_member_id` bigint(20) NOT NULL AUTO_INCREMENT, + `group_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'group_id', + `app_id` int(10) NULL DEFAULT NULL, + `member_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '成员id\r\n', + `role` int(10) NULL DEFAULT NULL COMMENT '群成员类型,0 普通成员, 1 管理员, 2 群主, 3 禁言,4 已经移除的成员', + `speak_date` bigint(100) NULL DEFAULT NULL, + `mute` int(10) NULL DEFAULT NULL COMMENT '是否全员禁言,0 不禁言;1 全员禁言', + `alias` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '群昵称', + `join_time` bigint(20) NULL DEFAULT NULL COMMENT '加入时间', + `leave_time` bigint(20) NULL DEFAULT NULL COMMENT '离开时间', + `join_type` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '加入类型', + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`group_member_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of im_group_member +-- ---------------------------- +INSERT INTO `im_group_member` VALUES (5, '123123', 10000, 'lld', 2, NULL, NULL, NULL, 1669686484744, NULL, NULL, NULL); +INSERT INTO `im_group_member` VALUES (6, '123123', 10000, 'lld2', 1, NULL, NULL, '', 1669686485958, NULL, 'joinType', NULL); +INSERT INTO `im_group_member` VALUES (7, 'test1', 10000, 'lld', 2, NULL, NULL, NULL, 1671615885587, NULL, NULL, NULL); +INSERT INTO `im_group_member` VALUES (8, 'test1', 10000, 'lld2', 1, NULL, NULL, '', 1671615885595, NULL, 'joinType', NULL); +INSERT INTO `im_group_member` VALUES (9, 'test2', 10000, 'lld', 0, NULL, NULL, NULL, 1671616883069, NULL, NULL, NULL); +INSERT INTO `im_group_member` VALUES (10, 'test2', 10000, 'lld2', 2, NULL, NULL, '', 1671616883077, NULL, 'joinType', NULL); + +-- ---------------------------- +-- Table structure for im_group_message_history +-- ---------------------------- +DROP TABLE IF EXISTS `im_group_message_history`; +CREATE TABLE `im_group_message_history` ( + `app_id` int(20) NOT NULL, + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'from_id', + `group_id` varchar(50) NOT NULL, + `message_key` bigint(50) NOT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `sequence` bigint(20) NULL DEFAULT NULL, + `message_random` int(20) NULL DEFAULT NULL, + `message_time` bigint(20) NULL DEFAULT NULL COMMENT '来源', + PRIMARY KEY (`app_id`, `group_id`, `message_key`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_message_body +-- ---------------------------- +DROP TABLE IF EXISTS `im_message_body`; +CREATE TABLE `im_message_body` ( + `app_id` int(10) NOT NULL, + `message_key` bigint(50) NOT NULL, + `message_body` varchar(5000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `security_key` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `message_time` bigint(20) NULL DEFAULT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `del_flag` int(10) NULL DEFAULT NULL, + PRIMARY KEY (`message_key`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_message_history +-- ---------------------------- +DROP TABLE IF EXISTS `im_message_history`; +CREATE TABLE `im_message_history` ( + `app_id` int(20) NOT NULL, + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'from_id', + `to_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'to_id\r\n', + `owner_id` varchar(50) NOT NULL, + `message_key` bigint(50) NOT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `sequence` bigint(20) NULL DEFAULT NULL, + `message_random` int(20) NULL DEFAULT NULL, + `message_time` bigint(20) NULL DEFAULT NULL COMMENT '来源', + PRIMARY KEY (`app_id`, `owner_id`, `message_key`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_user_data +-- ---------------------------- +DROP TABLE IF EXISTS `im_user_data`; +CREATE TABLE `im_user_data` ( + `user_id` varchar(50) NOT NULL, + `app_id` int(11) NOT NULL, + `nick_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称', + `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `photo` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `user_sex` int(10) NULL DEFAULT NULL, + `birth_day` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生日', + `location` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址', + `self_signature` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '个性签名', + `friend_allow_type` int(10) NOT NULL DEFAULT 1 COMMENT '加好友验证类型(Friend_AllowType) 1无需验证 2需要验证', + `forbidden_flag` int(10) NOT NULL DEFAULT 0 COMMENT '禁用标识 1禁用', + `disable_add_friend` int(10) NOT NULL DEFAULT 0 COMMENT '管理员禁止用户添加加好友:0 未禁用 1 已禁用', + `silent_flag` int(10) NOT NULL DEFAULT 0 COMMENT '禁言标识 1禁言', + `user_type` int(10) NOT NULL DEFAULT 1 COMMENT '用户类型 1普通用户 2客服 3机器人', + `del_flag` int(20) NOT NULL DEFAULT 0, + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`app_id`, `user_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of im_user_data +-- ---------------------------- +INSERT INTO `im_user_data` VALUES ('lld', 10000, 'lldlld', '123', NULL, NULL, NULL, NULL, NULL, 1, 0, 0, 0, 1, 0, NULL); +INSERT INTO `im_user_data` VALUES ('lld2', 10000, 'lld2', NULL, NULL, NULL, NULL, NULL, NULL, 2, 0, 0, 0, 1, 0, NULL); +INSERT INTO `im_user_data` VALUES ('lld3', 10000, 'lld3', NULL, NULL, NULL, NULL, NULL, NULL, 1, 0, 0, 0, 1, 0, NULL); +INSERT INTO `im_user_data` VALUES ('lld6', 10000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, 0, 0, 0, 1, 1, NULL); +INSERT INTO `im_user_data` VALUES ('lld7', 10000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, 0, 0, 0, 1, 1, NULL); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/hs-im-server/doc/im-core-study.sql b/hs-im-server/doc/im-core-study.sql new file mode 100644 index 0000000..3705c8b --- /dev/null +++ b/hs-im-server/doc/im-core-study.sql @@ -0,0 +1,222 @@ +/* + Navicat Premium Data Transfer + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 50740 + Source Host : localhost:3306 + Source Schema : im-core + + Target Server Type : MySQL + Target Server Version : 50740 + File Encoding : 65001 + + Date: 17/01/2023 16:52:36 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for app_user +-- ---------------------------- +DROP TABLE IF EXISTS `app_user`; +CREATE TABLE `app_user` ( + `user_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `update_time` bigint(20) NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_conversation_set +-- ---------------------------- +DROP TABLE IF EXISTS `im_conversation_set`; +CREATE TABLE `im_conversation_set` ( + `conversation_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `conversation_type` int(10) NULL DEFAULT NULL COMMENT '0 单聊 1群聊 2机器人 3公众号', + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `to_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `is_mute` int(10) NULL DEFAULT NULL COMMENT '是否免打扰 1免打扰', + `is_top` int(10) NULL DEFAULT NULL COMMENT '是否置顶 1置顶', + `sequence` bigint(20) NULL DEFAULT NULL COMMENT 'sequence', + `readed_sequence` bigint(20) NULL DEFAULT NULL, + `app_id` int(10) NOT NULL +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_friendship +-- ---------------------------- +DROP TABLE IF EXISTS `im_friendship`; +CREATE TABLE `im_friendship` ( + `app_id` int(20) NOT NULL COMMENT 'app_id', + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'from_id', + `to_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'to_id', + `remark` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', + `status` int(10) NULL DEFAULT NULL COMMENT '状态 1正常 2删除', + `black` int(10) NULL DEFAULT NULL COMMENT '1正常 2拉黑', + `create_time` bigint(20) NULL DEFAULT NULL, + `friend_sequence` bigint(20) NULL DEFAULT NULL, + `black_sequence` bigint(20) NULL DEFAULT NULL, + `add_source` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '来源', + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '来源' +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_friendship_group +-- ---------------------------- +DROP TABLE IF EXISTS `im_friendship_group`; +CREATE TABLE `im_friendship_group` ( + `app_id` int(20) NULL DEFAULT NULL COMMENT 'app_id', + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'from_id', + `group_id` int(50) NOT NULL AUTO_INCREMENT, + `group_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `sequence` bigint(20) NULL DEFAULT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `update_time` bigint(20) NULL DEFAULT NULL, + `del_flag` int(10) NULL DEFAULT NULL +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_friendship_group_member +-- ---------------------------- +DROP TABLE IF EXISTS `im_friendship_group_member`; +CREATE TABLE `im_friendship_group_member` ( + `group_id` bigint(20) NOT NULL, + `to_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_friendship_request +-- ---------------------------- +DROP TABLE IF EXISTS `im_friendship_request`; +CREATE TABLE `im_friendship_request` ( + `id` int(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `app_id` int(20) NULL DEFAULT NULL COMMENT 'app_id', + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'from_id', + `to_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'to_id', + `remark` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', + `read_status` int(10) NULL DEFAULT NULL COMMENT '是否已读 1已读', + `add_source` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '好友来源', + `add_wording` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '好友验证信息', + `approve_status` int(10) NULL DEFAULT NULL COMMENT '审批状态 1同意 2拒绝', + `create_time` bigint(20) NULL DEFAULT NULL, + `update_time` bigint(20) NULL DEFAULT NULL, + `sequence` bigint(20) NULL DEFAULT NULL +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_group +-- ---------------------------- +DROP TABLE IF EXISTS `im_group`; +CREATE TABLE `im_group` ( + `app_id` int(20) NOT NULL COMMENT 'app_id', + `group_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'group_id', + `owner_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '群主\r\n', + `group_type` int(10) NULL DEFAULT NULL COMMENT '群类型 1私有群(类似微信) 2公开群(类似qq)', + `group_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `mute` int(10) NULL DEFAULT NULL COMMENT '是否全员禁言,0 不禁言;1 全员禁言', + `apply_join_type` int(10) NULL DEFAULT NULL COMMENT '// 申请加群选项包括如下几种:\r\n// 0 表示禁止任何人申请加入\r\n// 1 表示需要群主或管理员审批\r\n// 2 表示允许无需审批自由加入群组', + `photo` varchar(300) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `max_member_count` int(20) NULL DEFAULT NULL, + `introduction` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '群简介', + `notification` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '群公告', + `status` int(5) NULL DEFAULT NULL COMMENT '群状态 0正常 1解散', + `sequence` bigint(20) NULL DEFAULT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '来源', + `update_time` bigint(20) NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_group_member +-- ---------------------------- +DROP TABLE IF EXISTS `im_group_member`; +CREATE TABLE `im_group_member` ( + `group_member_id` bigint(20) NOT NULL AUTO_INCREMENT, + `group_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'group_id', + `app_id` int(10) NULL DEFAULT NULL, + `member_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '成员id\r\n', + `role` int(10) NULL DEFAULT NULL COMMENT '群成员类型,0 普通成员, 1 管理员, 2 群主, 3 禁言,4 已经移除的成员', + `speak_date` bigint(100) NULL DEFAULT NULL, + `mute` int(10) NULL DEFAULT NULL COMMENT '是否全员禁言,0 不禁言;1 全员禁言', + `alias` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '群昵称', + `join_time` bigint(20) NULL DEFAULT NULL COMMENT '加入时间', + `leave_time` bigint(20) NULL DEFAULT NULL COMMENT '离开时间', + `join_type` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '加入类型', + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL +) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_group_message_history +-- ---------------------------- +DROP TABLE IF EXISTS `im_group_message_history`; +CREATE TABLE `im_group_message_history` ( + `app_id` int(20) NOT NULL COMMENT 'app_id', + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'from_id', + `group_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'group_id', + `message_key` bigint(50) NOT NULL COMMENT 'messageBodyId', + `create_time` bigint(20) NULL DEFAULT NULL, + `sequence` bigint(20) NULL DEFAULT NULL, + `message_random` int(20) NULL DEFAULT NULL, + `message_time` bigint(20) NULL DEFAULT NULL COMMENT +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_message_body +-- ---------------------------- +DROP TABLE IF EXISTS `im_message_body`; +CREATE TABLE `im_message_body` ( + `app_id` int(10) NOT NULL, + `message_key` bigint(50) NOT NULL, + `message_body` varchar(5000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `security_key` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `message_time` bigint(20) NULL DEFAULT NULL, + `create_time` bigint(20) NULL DEFAULT NULL, + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `del_flag` int(10) NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + + +-- ---------------------------- +-- Table structure for im_message_history +-- ---------------------------- +DROP TABLE IF EXISTS `im_message_history`; +CREATE TABLE `im_message_history` ( + `app_id` int(20) NOT NULL COMMENT 'app_id', + `from_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'from_id', + `to_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'to_id\r\n', + `owner_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'owner_id\r\n', + `message_key` bigint(50) NOT NULL COMMENT 'messageBodyId', + `create_time` bigint(20) NULL DEFAULT NULL, + `sequence` bigint(20) NULL DEFAULT NULL, + `message_random` int(20) NULL DEFAULT NULL, + `message_time` bigint(20) NULL DEFAULT NULL COMMENT '来源' +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for im_user_data +-- ---------------------------- +DROP TABLE IF EXISTS `im_user_data`; +CREATE TABLE `im_user_data` ( + `user_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `app_id` int(11) NOT NULL, + `nick_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称', + `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `photo` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `user_sex` int(10) NULL DEFAULT NULL, + `birth_day` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生日', + `location` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址', + `self_signature` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '个性签名', + `friend_allow_type` int(10) NOT NULL DEFAULT 1 COMMENT '加好友验证类型(Friend_AllowType) 1无需验证 2需要验证', + `forbidden_flag` int(10) NOT NULL DEFAULT 0 COMMENT '禁用标识 1禁用', + `disable_add_friend` int(10) NOT NULL DEFAULT 0 COMMENT '管理员禁止用户添加加好友:0 未禁用 1 已禁用', + `silent_flag` int(10) NOT NULL DEFAULT 0 COMMENT '禁言标识 1禁言', + `user_type` int(10) NOT NULL DEFAULT 1 COMMENT '用户类型 1普通用户 2客服 3机器人', + `del_flag` int(20) NOT NULL DEFAULT 0, + `extra` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/hs-im-server/im-common/pom.xml b/hs-im-server/im-common/pom.xml new file mode 100644 index 0000000..f13bea3 --- /dev/null +++ b/hs-im-server/im-common/pom.xml @@ -0,0 +1,52 @@ + + + + im-system + com.lld + 1.0.0-SNAPSHOT + + 4.0.0 + + common + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + + org.apache.httpcomponents + httpclient + + + + + org.apache.commons + commons-lang3 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + \ No newline at end of file diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/BaseErrorCode.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/BaseErrorCode.java new file mode 100644 index 0000000..d0b3e1e --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/BaseErrorCode.java @@ -0,0 +1,31 @@ +package com.lld.im.common; + + +import com.lld.im.common.exception.ApplicationExceptionEnum; + + +public enum BaseErrorCode implements ApplicationExceptionEnum { + + SUCCESS(200,"success"), + SYSTEM_ERROR(90000,"服务器内部错误,请联系管理员"), + PARAMETER_ERROR(90001,"参数校验错误"), + + + ; + + private int code; + private String error; + + BaseErrorCode(int code, String error){ + this.code = code; + this.error = error; + } + public int getCode() { + return this.code; + } + + public String getError() { + return this.error; + } + +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/ClientType.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/ClientType.java new file mode 100644 index 0000000..244194b --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/ClientType.java @@ -0,0 +1,34 @@ +package com.lld.im.common; + +/** + * @author: Chackylee + * @description: + **/ +public enum ClientType { + + WEBAPI(0,"webApi"), + WEB(1,"web"), + IOS(2,"ios"), + ANDROID(3,"android"), + WINDOWS(4,"windows"), + MAC(5,"mac"), + ; + + private int code; + private String error; + + ClientType(int code, String error){ + this.code = code; + this.error = error; + } + public int getCode() { + return this.code; + } + + public String getError() { + return this.error; + } + + + +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/ResponseVO.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/ResponseVO.java new file mode 100644 index 0000000..7917aba --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/ResponseVO.java @@ -0,0 +1,66 @@ +package com.lld.im.common; + +import com.lld.im.common.exception.ApplicationExceptionEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ResponseVO { + + private int code; + + private String msg; + + private T data; + + public static ResponseVO successResponse(Object data) { + return new ResponseVO(200, "success", data); + } + + public static ResponseVO successResponse() { + return new ResponseVO(200, "success"); + } + + public static ResponseVO errorResponse() { + return new ResponseVO(500, "系统内部异常"); + } + + public static ResponseVO errorResponse(int code, String msg) { + return new ResponseVO(code, msg); + } + + public static ResponseVO errorResponse(ApplicationExceptionEnum enums) { + return new ResponseVO(enums.getCode(), enums.getError()); + } + + public boolean isOk(){ + return this.code == 200; + } + + + public ResponseVO(int code, String msg) { + this.code = code; + this.msg = msg; +// this.data = null; + } + + public ResponseVO success(){ + this.code = 200; + this.msg = "success"; + return this; + } + + public ResponseVO success(T data){ + this.code = 200; + this.msg = "success"; + this.data = data; + return this; + } + +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/AllowFriendTypeEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/AllowFriendTypeEnum.java new file mode 100644 index 0000000..17093c0 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/AllowFriendTypeEnum.java @@ -0,0 +1,27 @@ +package com.lld.im.common.enums; + +public enum AllowFriendTypeEnum { + + /** + * 验证 + */ + NEED(2), + + /** + * 不需要验证 + */ + NOT_NEED(1), + + ; + + + private int code; + + AllowFriendTypeEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/ApproverFriendRequestStatusEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/ApproverFriendRequestStatusEnum.java new file mode 100644 index 0000000..d4998d8 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/ApproverFriendRequestStatusEnum.java @@ -0,0 +1,22 @@ +package com.lld.im.common.enums; + +public enum ApproverFriendRequestStatusEnum { + + /** + * 1 同意;2 拒绝。 + */ + AGREE(1), + + REJECT(2), + ; + + private int code; + + ApproverFriendRequestStatusEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/CheckFriendShipTypeEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/CheckFriendShipTypeEnum.java new file mode 100644 index 0000000..8c69d6c --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/CheckFriendShipTypeEnum.java @@ -0,0 +1,22 @@ +package com.lld.im.common.enums; + +public enum CheckFriendShipTypeEnum { + + /** + * 1 单方校验;2双方校验。 + */ + SINGLE(1), + + BOTH(2), + ; + + private int type; + + CheckFriendShipTypeEnum(int type){ + this.type=type; + } + + public int getType() { + return type; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/DelFlagEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/DelFlagEnum.java new file mode 100644 index 0000000..2ba3b9e --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/DelFlagEnum.java @@ -0,0 +1,22 @@ +package com.lld.im.common.enums; + +public enum DelFlagEnum { + + /** + * 0 正常;1 删除。 + */ + NORMAL(0), + + DELETE(1), + ; + + private int code; + + DelFlagEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/FriendShipErrorCode.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/FriendShipErrorCode.java new file mode 100644 index 0000000..3c0e25c --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/FriendShipErrorCode.java @@ -0,0 +1,57 @@ +package com.lld.im.common.enums; + +import com.lld.im.common.exception.ApplicationExceptionEnum; + +public enum FriendShipErrorCode implements ApplicationExceptionEnum { + + + IMPORT_SIZE_BEYOND(30000,"导入數量超出上限"), + + ADD_FRIEND_ERROR(30001,"添加好友失败"), + + TO_IS_YOUR_FRIEND(30002,"对方已经是你的好友"), + + TO_IS_NOT_YOUR_FRIEND(30003,"对方不是你的好友"), + + FRIEND_IS_DELETED(30004,"好友已被删除"), + + FRIEND_IS_BLACK(30006,"好友已被拉黑"), + + TARGET_IS_BLACK_YOU(30007,"对方把你拉黑"), + + REPEATSHIP_IS_NOT_EXIST(30008,"关系链记录不存在"), + + ADD_BLACK_ERROR(30009,"添加黑名單失败"), + + FRIEND_IS_NOT_YOUR_BLACK(30010,"好友已經不在你的黑名單内"), + + NOT_APPROVER_OTHER_MAN_REQUEST(30011,"无法审批其他人的好友请求"), + + FRIEND_REQUEST_IS_NOT_EXIST(30012,"好友申请不存在"), + + FRIEND_SHIP_GROUP_CREATE_ERROR(30014,"好友分组创建失败"), + + FRIEND_SHIP_GROUP_IS_EXIST(30015,"好友分组已存在"), + + FRIEND_SHIP_GROUP_IS_NOT_EXIST(30016,"好友分组不存在"), + + + + ; + + private int code; + private String error; + + FriendShipErrorCode(int code, String error){ + this.code = code; + this.error = error; + } + public int getCode() { + return this.code; + } + + public String getError() { + return this.error; + } + +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/FriendShipStatusEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/FriendShipStatusEnum.java new file mode 100644 index 0000000..88f77d9 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/FriendShipStatusEnum.java @@ -0,0 +1,31 @@ +package com.lld.im.common.enums; + +public enum FriendShipStatusEnum { + + /** + * 0未添加 1正常 2删除 + */ + FRIEND_STATUS_NO_FRIEND(0), + + FRIEND_STATUS_NORMAL(1), + + FRIEND_STATUS_DELETE(2), + + /** + * 0未添加 1正常 2删除 + */ + BLACK_STATUS_NORMAL(1), + + BLACK_STATUS_BLACKED(2), + ; + + private int code; + + FriendShipStatusEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupErrorCode.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupErrorCode.java new file mode 100644 index 0000000..aa2a2fe --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupErrorCode.java @@ -0,0 +1,66 @@ +package com.lld.im.common.enums; + +import com.lld.im.common.exception.ApplicationExceptionEnum; + +/** + * @author: Chackylee + * @description: + **/ +public enum GroupErrorCode implements ApplicationExceptionEnum { + + GROUP_IS_NOT_EXIST(40000,"群不存在"), + + GROUP_IS_EXIST(40001,"群已存在"), + + GROUP_IS_HAVE_OWNER(40002,"群已存在群主"), + + USER_IS_JOINED_GROUP(40003,"该用户已经进入该群"), + + USER_JOIN_GROUP_ERROR(40004,"群成员添加失败"), + + GROUP_MEMBER_IS_BEYOND(40005,"群成员已达到上限"), + + MEMBER_IS_NOT_JOINED_GROUP(40006,"该用户不在群内"), + + THIS_OPERATE_NEED_MANAGER_ROLE(40007,"该操作只允许群主/管理员操作"), + + THIS_OPERATE_NEED_APPMANAGER_ROLE(40008,"该操作只允许APP管理员操作"), + + THIS_OPERATE_NEED_OWNER_ROLE(40009,"该操作只允许群主操作"), + + GROUP_OWNER_IS_NOT_REMOVE(40010,"群主无法移除"), + + UPDATE_GROUP_BASE_INFO_ERROR(40011,"更新群信息失败"), + + THIS_GROUP_IS_MUTE(40012,"该群禁止发言"), + + IMPORT_GROUP_ERROR(40013,"导入群组失败"), + + THIS_OPERATE_NEED_ONESELF(40014,"该操作只允许自己操作"), + + PRIVATE_GROUP_CAN_NOT_DESTORY(40015,"私有群不允许解散"), + + PUBLIC_GROUP_MUST_HAVE_OWNER(40016,"公开群必须指定群主"), + + GROUP_MEMBER_IS_SPEAK(40017,"群成员被禁言"), + + GROUP_IS_DESTROY(40018,"群组已解散"), + + ; + + private int code; + private String error; + + GroupErrorCode(int code, String error){ + this.code = code; + this.error = error; + } + public int getCode() { + return this.code; + } + + public String getError() { + return this.error; + } + +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupMemberRoleEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupMemberRoleEnum.java new file mode 100644 index 0000000..3326b34 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupMemberRoleEnum.java @@ -0,0 +1,50 @@ +package com.lld.im.common.enums; + +public enum GroupMemberRoleEnum { + + /** + * 普通成员 + */ + ORDINARY(0), + + /** + * 管理员 + */ + MAMAGER(1), + + /** + * 群主 + */ + OWNER(2), + + /** + * 离开 + */ + LEAVE(3); + ; + + + private int code; + + /** + * 不能用 默认的 enumType b= enumType.values()[i]; 因为本枚举是类形式封装 + * @param ordinal + * @return + */ + public static GroupMemberRoleEnum getItem(int ordinal) { + for (int i = 0; i < GroupMemberRoleEnum.values().length; i++) { + if (GroupMemberRoleEnum.values()[i].getCode() == ordinal) { + return GroupMemberRoleEnum.values()[i]; + } + } + return null; + } + + GroupMemberRoleEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupMuteTypeEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupMuteTypeEnum.java new file mode 100644 index 0000000..d518113 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupMuteTypeEnum.java @@ -0,0 +1,43 @@ +package com.lld.im.common.enums; + +public enum GroupMuteTypeEnum { + + /** + * 是否全员禁言,0 不禁言;1 全员禁言。 + */ + NOT_MUTE(0), + + + MUTE(1), + + ; + + /** + * 不能用 默认的 enumType b= enumType.values()[i]; 因为本枚举是类形式封装 + * @param ordinal + * @return + */ + public static GroupMuteTypeEnum getEnum(Integer ordinal) { + + if(ordinal == null){ + return null; + } + + for (int i = 0; i < GroupMuteTypeEnum.values().length; i++) { + if (GroupMuteTypeEnum.values()[i].getCode() == ordinal) { + return GroupMuteTypeEnum.values()[i]; + } + } + return null; + } + + private int code; + + GroupMuteTypeEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupStatusEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupStatusEnum.java new file mode 100644 index 0000000..338868e --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupStatusEnum.java @@ -0,0 +1,42 @@ +package com.lld.im.common.enums; + +public enum GroupStatusEnum { + + /** + * 1正常 2解散 其他待定比如封禁... + */ + NORMAL(1), + + DESTROY(2), + + ; + + /** + * 不能用 默认的 enumType b= enumType.values()[i]; 因为本枚举是类形式封装 + * @param ordinal + * @return + */ + public static GroupStatusEnum getEnum(Integer ordinal) { + + if(ordinal == null){ + return null; + } + + for (int i = 0; i < GroupStatusEnum.values().length; i++) { + if (GroupStatusEnum.values()[i].getCode() == ordinal) { + return GroupStatusEnum.values()[i]; + } + } + return null; + } + + private int code; + + GroupStatusEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupTypeEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupTypeEnum.java new file mode 100644 index 0000000..be7a908 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/GroupTypeEnum.java @@ -0,0 +1,42 @@ +package com.lld.im.common.enums; + +public enum GroupTypeEnum { + + /** + * 群类型 1私有群(类似微信) 2公开群(类似qq) + */ + PRIVATE(1), + + PUBLIC(2), + + ; + + /** + * 不能用 默认的 enumType b= enumType.values()[i]; 因为本枚举是类形式封装 + * @param ordinal + * @return + */ + public static GroupTypeEnum getEnum(Integer ordinal) { + + if(ordinal == null){ + return null; + } + + for (int i = 0; i < GroupTypeEnum.values().length; i++) { + if (GroupTypeEnum.values()[i].getCode() == ordinal) { + return GroupTypeEnum.values()[i]; + } + } + return null; + } + + private int code; + + GroupTypeEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserErrorCode.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserErrorCode.java new file mode 100644 index 0000000..76c2cd0 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserErrorCode.java @@ -0,0 +1,30 @@ +package com.lld.im.common.enums; + +import com.lld.im.common.exception.ApplicationExceptionEnum; + +public enum UserErrorCode implements ApplicationExceptionEnum { + + + IMPORT_SIZE_BEYOND(20000,"导入數量超出上限"), + USER_IS_NOT_EXIST(20001,"用户不存在"), + SERVER_GET_USER_ERROR(20002,"服务获取用户失败"), + MODIFY_USER_ERROR(20003,"更新用户失败"), + SERVER_NOT_AVAILABLE(71000, "没有可用的服务"), + ; + + private int code; + private String error; + + UserErrorCode(int code, String error){ + this.code = code; + this.error = error; + } + public int getCode() { + return this.code; + } + + public String getError() { + return this.error; + } + +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserForbiddenFlagEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserForbiddenFlagEnum.java new file mode 100644 index 0000000..bc0fffc --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserForbiddenFlagEnum.java @@ -0,0 +1,22 @@ +package com.lld.im.common.enums; + +public enum UserForbiddenFlagEnum { + + /** + * 0 正常;1 禁用。 + */ + NORMAL(0), + + FORBIBBEN(1), + ; + + private int code; + + UserForbiddenFlagEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserSilentFlagEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserSilentFlagEnum.java new file mode 100644 index 0000000..3e5ccb3 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/enums/UserSilentFlagEnum.java @@ -0,0 +1,22 @@ +package com.lld.im.common.enums; + +public enum UserSilentFlagEnum { + + /** + * 0 正常;1 禁言。 + */ + NORMAL(0), + + MUTE(1), + ; + + private int code; + + UserSilentFlagEnum(int code){ + this.code=code; + } + + public int getCode() { + return code; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/exception/ApplicationException.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/exception/ApplicationException.java new file mode 100644 index 0000000..12bf555 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/exception/ApplicationException.java @@ -0,0 +1,44 @@ +package com.lld.im.common.exception; + +/** + * @author: Chackylee + * @description: + **/ +public class ApplicationException extends RuntimeException { + + private int code; + + private String error; + + + public ApplicationException(int code, String message) { + super(message); + this.code = code; + this.error = message; + } + + public ApplicationException(ApplicationExceptionEnum exceptionEnum) { + super(exceptionEnum.getError()); + this.code = exceptionEnum.getCode(); + this.error = exceptionEnum.getError(); + } + + public int getCode() { + return code; + } + + public String getError() { + return error; + } + + + /** + * avoid the expensive and useless stack trace for api exceptions + * @see Throwable#fillInStackTrace() + */ + @Override + public Throwable fillInStackTrace() { + return this; + } + +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/exception/ApplicationExceptionEnum.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/exception/ApplicationExceptionEnum.java new file mode 100644 index 0000000..af1df84 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/exception/ApplicationExceptionEnum.java @@ -0,0 +1,8 @@ +package com.lld.im.common.exception; + +public interface ApplicationExceptionEnum { + + int getCode(); + + String getError(); +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/model/ClientInfo.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/model/ClientInfo.java new file mode 100644 index 0000000..0116c79 --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/model/ClientInfo.java @@ -0,0 +1,25 @@ +package com.lld.im.common.model; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: Chackylee + * @description: + **/ +@Data +@NoArgsConstructor +public class ClientInfo { + + private Integer appId; + + private Integer clientType; + + private String imei; + + public ClientInfo(Integer appId, Integer clientType, String imei) { + this.appId = appId; + this.clientType = clientType; + this.imei = imei; + } +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/model/RequestBase.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/model/RequestBase.java new file mode 100644 index 0000000..12ff3fc --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/model/RequestBase.java @@ -0,0 +1,19 @@ +package com.lld.im.common.model; + +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class RequestBase { + private Integer appId; + + private String operater; + + private Integer clientType; + + private String imei; +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/model/UserClientDto.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/model/UserClientDto.java new file mode 100644 index 0000000..1dcc36d --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/model/UserClientDto.java @@ -0,0 +1,21 @@ +package com.lld.im.common.model; + +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class UserClientDto { + + private Integer appId; + + private Integer clientType; + + private String userId; + + private String imei; + +} diff --git a/hs-im-server/im-common/src/main/java/com/lld/im/common/model/UserSession.java b/hs-im-server/im-common/src/main/java/com/lld/im/common/model/UserSession.java new file mode 100644 index 0000000..907c5aa --- /dev/null +++ b/hs-im-server/im-common/src/main/java/com/lld/im/common/model/UserSession.java @@ -0,0 +1,37 @@ +package com.lld.im.common.model; + +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class UserSession { + + private String userId; + + /** + * 应用ID + */ + private Integer appId; + + /** + * 端的标识 + */ + private Integer clientType; + + //sdk 版本号 + private Integer version; + + //连接状态 1=在线 2=离线 + private Integer connectState; + + private Integer brokerId; + + private String brokerHost; + + private String imei; + +} diff --git a/hs-im-server/im-service/im-service.iml b/hs-im-server/im-service/im-service.iml new file mode 100644 index 0000000..e69de29 diff --git a/hs-im-server/im-service/pom.xml b/hs-im-server/im-service/pom.xml new file mode 100644 index 0000000..637de92 --- /dev/null +++ b/hs-im-server/im-service/pom.xml @@ -0,0 +1,101 @@ + + + + im-system + com.lld + 1.0.0-SNAPSHOT + + 4.0.0 + + lld-im-service project + + 1.8 + + + service + + + + + + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + + + + + cn.hutool + hutool-all + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + + mysql + mysql-connector-java + runtime + + + + + com.baomidou + mybatis-plus-boot-starter + + + com.github.jeffreyning + mybatisplus-plus + 1.5.1-RELEASE + + + + + com.lld + common + 1.0.0-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/Application.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/Application.java new file mode 100644 index 0000000..272d021 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/Application.java @@ -0,0 +1,20 @@ +package com.lld.im.service; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.lld.im.service.*.dao.mapper") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + +} + + + + diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/config/BeanConfig.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/config/BeanConfig.java new file mode 100644 index 0000000..c07d827 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/config/BeanConfig.java @@ -0,0 +1,13 @@ +package com.lld.im.service.config; + +import org.springframework.context.annotation.Configuration; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Configuration +public class BeanConfig { + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/config/WebConfig.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/config/WebConfig.java new file mode 100644 index 0000000..b6bd571 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/config/WebConfig.java @@ -0,0 +1,26 @@ +package com.lld.im.service.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Configuration +public class WebConfig implements WebMvcConfigurer { + + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") + .allowCredentials(true) + .maxAge(3600) + .allowedHeaders("*"); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/exception/GlobalExceptionHandler.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..e5a5657 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/exception/GlobalExceptionHandler.java @@ -0,0 +1,110 @@ +package com.lld.im.service.exception; + +import com.lld.im.common.BaseErrorCode; +import com.lld.im.common.ResponseVO; +import com.lld.im.common.exception.ApplicationException; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.util.Set; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + + @ExceptionHandler(value=Exception.class) + @ResponseBody + public ResponseVO unknowException(Exception e){ + e.printStackTrace(); + ResponseVO resultBean =new ResponseVO(); + resultBean.setCode(BaseErrorCode.SYSTEM_ERROR.getCode()); + resultBean.setMsg(BaseErrorCode.SYSTEM_ERROR.getError()); + /** + * 未知异常的话,这里写逻辑,发邮件,发短信都可以、、 + */ + return resultBean; + } + + + /** + * Validator 参数校验异常处理 + * + * @param ex + * @return + */ + @ExceptionHandler(value = ConstraintViolationException.class) + @ResponseBody + public Object handleMethodArgumentNotValidException(ConstraintViolationException ex) { + + Set> constraintViolations = ex.getConstraintViolations(); + ResponseVO resultBean =new ResponseVO(); + resultBean.setCode(BaseErrorCode.PARAMETER_ERROR.getCode()); + for (ConstraintViolation constraintViolation : constraintViolations) { + PathImpl pathImpl = (PathImpl) constraintViolation.getPropertyPath(); + // 读取参数字段,constraintViolation.getMessage() 读取验证注解中的message值 + String paramName = pathImpl.getLeafNode().getName(); + String message = "参数{".concat(paramName).concat("}").concat(constraintViolation.getMessage()); + resultBean.setMsg(message); + + return resultBean; + } + resultBean.setMsg(BaseErrorCode.PARAMETER_ERROR.getError() + ex.getMessage()); + return resultBean; + } + + @ExceptionHandler(ApplicationException.class) + @ResponseBody + public Object applicationExceptionHandler(ApplicationException e) { + // 使用公共的结果类封装返回结果, 这里我指定状态码为 + ResponseVO resultBean =new ResponseVO(); + resultBean.setCode(e.getCode()); + resultBean.setMsg(e.getError()); + return resultBean; + } + + /** + * Validator 参数校验异常处理 + * + * @param ex + * @return + */ + @ExceptionHandler(value = BindException.class) + @ResponseBody + public Object handleException2(BindException ex) { + FieldError err = ex.getFieldError(); + String message = "参数{".concat(err.getField()).concat("}").concat(err.getDefaultMessage()); + ResponseVO resultBean =new ResponseVO(); + resultBean.setCode(BaseErrorCode.PARAMETER_ERROR.getCode()); + resultBean.setMsg(message); + return resultBean; + + + } + + //json格式 + @ExceptionHandler(value = MethodArgumentNotValidException.class) + @ResponseBody + public Object handleException1(MethodArgumentNotValidException ex) { + StringBuilder errorMsg = new StringBuilder(); + BindingResult re = ex.getBindingResult(); + for (ObjectError error : re.getAllErrors()) { + errorMsg.append(error.getDefaultMessage()).append(","); + } + errorMsg.delete(errorMsg.length() - 1, errorMsg.length()); + + ResponseVO resultBean =new ResponseVO(); + resultBean.setCode(BaseErrorCode.PARAMETER_ERROR.getCode()); + resultBean.setMsg(BaseErrorCode.PARAMETER_ERROR.getError() + " : " + errorMsg.toString()); + return resultBean; + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipController.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipController.java new file mode 100644 index 0000000..80524c4 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipController.java @@ -0,0 +1,86 @@ +package com.lld.im.service.friendship.controller; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.friendship.model.req.*; +import com.lld.im.service.friendship.service.ImFriendService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("v1/friendship") +public class ImFriendShipController { + + @Autowired + ImFriendService imFriendShipService; + + @RequestMapping("/importFriendShip") + public ResponseVO importFriendShip(@RequestBody @Validated ImporFriendShipReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.importFriendShip(req); + } + + @RequestMapping("/addFriend") + public ResponseVO addFriend(@RequestBody @Validated AddFriendReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.addFriend(req); + } + + @RequestMapping("/updateFriend") + public ResponseVO updateFriend(@RequestBody @Validated UpdateFriendReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.updateFriend(req); + } + + @RequestMapping("/deleteFriend") + public ResponseVO deleteFriend(@RequestBody @Validated DeleteFriendReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.deleteFriend(req); + } + + @RequestMapping("/deleteAllFriend") + public ResponseVO deleteAllFriend(@RequestBody @Validated DeleteFriendReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.deleteAllFriend(req); + } + + @RequestMapping("/getAllFriendShip") + public ResponseVO getAllFriendShip(@RequestBody @Validated GetAllFriendShipReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.getAllFriendShip(req); + } + + @RequestMapping("/getRelation") + public ResponseVO getRelation(@RequestBody @Validated GetRelationReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.getRelation(req); + } + + @RequestMapping("/checkFriend") + public ResponseVO checkFriend(@RequestBody @Validated CheckFriendShipReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.checkFriendship(req); + } + + @RequestMapping("/addBlack") + public ResponseVO addBlack(@RequestBody @Validated AddFriendShipBlackReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.addBlack(req); + } + + @RequestMapping("/deleteBlack") + public ResponseVO deleteBlack(@RequestBody @Validated DeleteBlackReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.deleteBlack(req); + } + + @RequestMapping("/checkBlck") + public ResponseVO checkBlck(@RequestBody @Validated CheckFriendShipReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipService.checkBlck(req); + } + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipGroupController.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipGroupController.java new file mode 100644 index 0000000..9a3ced9 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipGroupController.java @@ -0,0 +1,56 @@ +package com.lld.im.service.friendship.controller; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.friendship.model.req.AddFriendShipGroupMemberReq; +import com.lld.im.service.friendship.model.req.AddFriendShipGroupReq; +import com.lld.im.service.friendship.model.req.DeleteFriendShipGroupMemberReq; +import com.lld.im.service.friendship.model.req.DeleteFriendShipGroupReq; +import com.lld.im.service.friendship.service.ImFriendShipGroupMemberService; +import com.lld.im.service.friendship.service.ImFriendShipGroupService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: Chackylee + * @description: + **/ +@RestController +@RequestMapping("v1/friendship/group") +public class ImFriendShipGroupController { + + @Autowired + ImFriendShipGroupService imFriendShipGroupService; + + @Autowired + ImFriendShipGroupMemberService imFriendShipGroupMemberService; + + + @RequestMapping("/add") + public ResponseVO add(@RequestBody @Validated AddFriendShipGroupReq req, Integer appId) { + req.setAppId(appId); + return imFriendShipGroupService.addGroup(req); + } + + @RequestMapping("/del") + public ResponseVO del(@RequestBody @Validated DeleteFriendShipGroupReq req, Integer appId) { + req.setAppId(appId); + return imFriendShipGroupService.deleteGroup(req); + } + + @RequestMapping("/member/add") + public ResponseVO memberAdd(@RequestBody @Validated AddFriendShipGroupMemberReq req, Integer appId) { + req.setAppId(appId); + return imFriendShipGroupMemberService.addGroupMember(req); + } + + @RequestMapping("/member/del") + public ResponseVO memberdel(@RequestBody @Validated DeleteFriendShipGroupMemberReq req, Integer appId) { + req.setAppId(appId); + return imFriendShipGroupMemberService.delGroupMember(req); + } + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipRequestController.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipRequestController.java new file mode 100644 index 0000000..57ad0b8 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/controller/ImFriendShipRequestController.java @@ -0,0 +1,42 @@ +package com.lld.im.service.friendship.controller; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.friendship.model.req.ApproverFriendRequestReq; +import com.lld.im.service.friendship.model.req.GetFriendShipRequestReq; +import com.lld.im.service.friendship.model.req.ReadFriendShipRequestReq; +import com.lld.im.service.friendship.service.ImFriendShipRequestService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("v1/friendshipRequest") +public class ImFriendShipRequestController { + + @Autowired + ImFriendShipRequestService imFriendShipRequestService; + + @RequestMapping("/approveFriendRequest") + public ResponseVO approveFriendRequest(@RequestBody @Validated + ApproverFriendRequestReq req, Integer appId, String identifier){ + req.setAppId(appId); + req.setOperater(identifier); + return imFriendShipRequestService.approverFriendRequest(req); + } + @RequestMapping("/getFriendRequest") + public ResponseVO getFriendRequest(@RequestBody @Validated GetFriendShipRequestReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipRequestService.getFriendRequest(req.getFromId(),req.getAppId()); + } + + @RequestMapping("/readFriendShipRequestReq") + public ResponseVO readFriendShipRequestReq(@RequestBody @Validated ReadFriendShipRequestReq req, Integer appId){ + req.setAppId(appId); + return imFriendShipRequestService.readFriendShipRequestReq(req); + } + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipEntity.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipEntity.java new file mode 100644 index 0000000..bc98d52 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipEntity.java @@ -0,0 +1,46 @@ +package com.lld.im.service.friendship.dao; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.github.jeffreyning.mybatisplus.anno.AutoMap; +import lombok.Data; + +/** + * @author: Chackylee + * @description: + **/ + +@Data +@TableName("im_friendship") +@AutoMap +public class ImFriendShipEntity { + + @TableField(value = "app_id") + private Integer appId; + + @TableField(value = "from_id") + private String fromId; + + @TableField(value = "to_id") + private String toId; + /** 备注*/ + private String remark; + /** 状态 1正常 2删除*/ + private Integer status; + /** 状态 1正常 2拉黑*/ + private Integer black; +// @TableField(updateStrategy = FieldStrategy.IGNORED) + private Long createTime; + /** 好友关系序列号*/ +// @TableField(updateStrategy = FieldStrategy.IGNORED) + private Long friendSequence; + + /** 黑名单关系序列号*/ + private Long blackSequence; + /** 好友来源*/ +// @TableField(updateStrategy = FieldStrategy.IGNORED) + private String addSource; + + private String extra; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipGroupEntity.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipGroupEntity.java new file mode 100644 index 0000000..2319f3f --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipGroupEntity.java @@ -0,0 +1,32 @@ +package com.lld.im.service.friendship.dao; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +@Data +@TableName("im_friendship_group") +public class ImFriendShipGroupEntity { + + @TableId(value = "group_id",type = IdType.AUTO) + private Long groupId; + + private String fromId; + + private Integer appId; + + private String groupName; + /** 备注*/ + private Long createTime; + + /** 备注*/ + private Long updateTime; + + /** 序列号*/ + private Long sequence; + + private int delFlag; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipGroupMemberEntity.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipGroupMemberEntity.java new file mode 100644 index 0000000..c8cf26b --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipGroupMemberEntity.java @@ -0,0 +1,17 @@ +package com.lld.im.service.friendship.dao; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + + +@Data +@TableName("im_friendship_group_member") +public class ImFriendShipGroupMemberEntity { + + @TableId(value = "group_id") + private Long groupId; + + private String toId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipRequestEntity.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipRequestEntity.java new file mode 100644 index 0000000..c2de949 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/ImFriendShipRequestEntity.java @@ -0,0 +1,49 @@ +package com.lld.im.service.friendship.dao; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + + +@Data +@TableName("im_friendship_request") +public class ImFriendShipRequestEntity { + + @TableId(type = IdType.AUTO) + private Long id; + + private Integer appId; + + private String fromId; + + private String toId; + /** 备注*/ +// @TableField(updateStrategy = FieldStrategy.IGNORED) + private String remark; + + //是否已读 1已读 + private Integer readStatus; + + /** 好友来源*/ +// @TableField(updateStrategy = FieldStrategy.IGNORED) + private String addSource; + +// @TableField(updateStrategy = FieldStrategy.IGNORED) + private String addWording; + + //审批状态 1同意 2拒绝 + private Integer approveStatus; + +// @TableField(updateStrategy = FieldStrategy.IGNORED) + private Long createTime; + + private Long updateTime; + + /** 序列号*/ + private Long sequence; + + + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipGroupMapper.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipGroupMapper.java new file mode 100644 index 0000000..b621a23 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipGroupMapper.java @@ -0,0 +1,9 @@ +package com.lld.im.service.friendship.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.lld.im.service.friendship.dao.ImFriendShipGroupEntity; + +public interface ImFriendShipGroupMapper extends BaseMapper { + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipGroupMemberMapper.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipGroupMemberMapper.java new file mode 100644 index 0000000..b477445 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipGroupMemberMapper.java @@ -0,0 +1,9 @@ +package com.lld.im.service.friendship.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.lld.im.service.friendship.dao.ImFriendShipGroupMemberEntity; + +public interface ImFriendShipGroupMemberMapper extends BaseMapper { + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipMapper.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipMapper.java new file mode 100644 index 0000000..87e0430 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipMapper.java @@ -0,0 +1,91 @@ +package com.lld.im.service.friendship.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.lld.im.service.friendship.dao.ImFriendShipEntity; +import com.lld.im.service.friendship.model.req.CheckFriendShipReq; +import com.lld.im.service.friendship.model.resp.CheckFriendShipResp; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +@Mapper +public interface ImFriendShipMapper extends BaseMapper { + + + + + @Select("") + public List checkFriendShip(CheckFriendShipReq req); + + + @Select("" + ) + List checkFriendShipBoth(CheckFriendShipReq toId); + + + + + + + @Select("" + ) + List checkFriendShipBlack(CheckFriendShipReq req); + + @Select("" + ) + List checkFriendShipBlackBoth(CheckFriendShipReq toId); + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipRequestMapper.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipRequestMapper.java new file mode 100644 index 0000000..c153dae --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/dao/mapper/ImFriendShipRequestMapper.java @@ -0,0 +1,9 @@ +package com.lld.im.service.friendship.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.lld.im.service.friendship.dao.ImFriendShipRequestEntity; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ImFriendShipRequestMapper extends BaseMapper { +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendReq.java new file mode 100644 index 0000000..cd31d0a --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendReq.java @@ -0,0 +1,19 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + + +@Data +public class AddFriendReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + private String fromId; + + @NotNull(message = "toItem不能为空") + private FriendDto toItem; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipBlackReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipBlackReq.java new file mode 100644 index 0000000..69f5017 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipBlackReq.java @@ -0,0 +1,16 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + + +@Data +public class AddFriendShipBlackReq extends RequestBase { + + @NotBlank(message = "用户id不能为空") + private String fromId; + + private String toId; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipGroupMemberReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipGroupMemberReq.java new file mode 100644 index 0000000..4f02bf7 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipGroupMemberReq.java @@ -0,0 +1,24 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + + +@Data +public class AddFriendShipGroupMemberReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + private String fromId; + + @NotBlank(message = "分组名称不能为空") + private String groupName; + + @NotEmpty(message = "请选择用户") + private List toIds; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipGroupReq.java new file mode 100644 index 0000000..ed3ed09 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/AddFriendShipGroupReq.java @@ -0,0 +1,21 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.util.List; + + +@Data +public class AddFriendShipGroupReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + public String fromId; + + @NotBlank(message = "分组名称不能为空") + private String groupName; + + private List toIds; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ApproverFriendRequestReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ApproverFriendRequestReq.java new file mode 100644 index 0000000..22d1833 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ApproverFriendRequestReq.java @@ -0,0 +1,14 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + + +@Data +public class ApproverFriendRequestReq extends RequestBase { + + private Long id; + + //1同意 2拒绝 + private Integer status; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/CheckFriendShipReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/CheckFriendShipReq.java new file mode 100644 index 0000000..721d8b2 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/CheckFriendShipReq.java @@ -0,0 +1,27 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class CheckFriendShipReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + private String fromId; + + @NotEmpty(message = "toIds不能为空") + private List toIds; + + @NotNull(message = "checkType不能为空") + private Integer checkType; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteBlackReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteBlackReq.java new file mode 100644 index 0000000..0ae5dd6 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteBlackReq.java @@ -0,0 +1,19 @@ +package com.lld.im.service.friendship.model.req; + + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + + +@Data +public class DeleteBlackReq extends RequestBase { + + @NotBlank(message = "用户id不能为空") + private String fromId; + + @NotBlank(message = "好友id不能为空") + private String toId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendReq.java new file mode 100644 index 0000000..3915d90 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendReq.java @@ -0,0 +1,18 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + + +@Data +public class DeleteFriendReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + private String fromId; + + @NotBlank(message = "toId不能为空") + private String toId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendShipGroupMemberReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendShipGroupMemberReq.java new file mode 100644 index 0000000..af98c18 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendShipGroupMemberReq.java @@ -0,0 +1,27 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class DeleteFriendShipGroupMemberReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + private String fromId; + + @NotBlank(message = "分组名称不能为空") + private String groupName; + + @NotEmpty(message = "请选择用户") + private List toIds; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendShipGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendShipGroupReq.java new file mode 100644 index 0000000..7d23925 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/DeleteFriendShipGroupReq.java @@ -0,0 +1,23 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @author: Chackylee + * @description: 删除分组,同时删除分组下的成员 + **/ +@Data +public class DeleteFriendShipGroupReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + private String fromId; + + @NotEmpty(message = "分组名称不能为空") + private List groupName; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/FriendDto.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/FriendDto.java new file mode 100644 index 0000000..2a69fdf --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/FriendDto.java @@ -0,0 +1,19 @@ +package com.lld.im.service.friendship.model.req; + +import lombok.Data; + + +@Data +public class FriendDto { + + private String toId; + + private String remark; + + private String addSource; + + private String extra; + + private String addWording; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetAllFriendShipReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetAllFriendShipReq.java new file mode 100644 index 0000000..e997236 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetAllFriendShipReq.java @@ -0,0 +1,14 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + + +@Data +public class GetAllFriendShipReq extends RequestBase { + + @NotBlank(message = "用户id不能为空") + private String fromId; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetFriendShipRequestReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetFriendShipRequestReq.java new file mode 100644 index 0000000..a277767 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetFriendShipRequestReq.java @@ -0,0 +1,19 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class GetFriendShipRequestReq extends RequestBase { + + @NotBlank(message = "用户id不能为空") + private String fromId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetRelationReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetRelationReq.java new file mode 100644 index 0000000..eb97c78 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/GetRelationReq.java @@ -0,0 +1,16 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + + +@Data +public class GetRelationReq extends RequestBase { + @NotBlank(message = "fromId不能为空") + private String fromId; + + @NotBlank(message = "toId不能为空") + private String toId; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ImporFriendShipReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ImporFriendShipReq.java new file mode 100644 index 0000000..8e6a6cd --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ImporFriendShipReq.java @@ -0,0 +1,33 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.enums.FriendShipStatusEnum; +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.util.List; + + +@Data +public class ImporFriendShipReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + private String fromId; + + private List friendItem; + + @Data + public static class ImportFriendDto{ + + private String toId; + + private String remark; + + private String addSource; + + private Integer status = FriendShipStatusEnum.FRIEND_STATUS_NO_FRIEND.getCode(); + + private Integer black = FriendShipStatusEnum.BLACK_STATUS_NORMAL.getCode(); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ReadFriendShipRequestReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ReadFriendShipRequestReq.java new file mode 100644 index 0000000..afbe591 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/ReadFriendShipRequestReq.java @@ -0,0 +1,14 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + + +@Data +public class ReadFriendShipRequestReq extends RequestBase { + + @NotBlank(message = "用户id不能为空") + private String fromId; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/UpdateFriendReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/UpdateFriendReq.java new file mode 100644 index 0000000..3250543 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/req/UpdateFriendReq.java @@ -0,0 +1,18 @@ +package com.lld.im.service.friendship.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + + +@Data +public class UpdateFriendReq extends RequestBase { + + @NotBlank(message = "fromId不能为空") + private String fromId; + + @NotNull(message = "toItem不能为空") + private FriendDto toItem; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/resp/CheckFriendShipResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/resp/CheckFriendShipResp.java new file mode 100644 index 0000000..c8951e2 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/resp/CheckFriendShipResp.java @@ -0,0 +1,30 @@ +package com.lld.im.service.friendship.model.resp; + +import lombok.Data; + + +@Data +public class CheckFriendShipResp { + + private String fromId; + + private String toId; + + + //校验状态,根据双向校验和单向校验有不同的status + //单向校验:1 from添加了to,不确定to是否添加了from CheckResult_single_Type_AWithB; + // 0 from没有添加to,也不确定to有没有添加from CheckResult_single_Type_NoRelation + //双向校验 1 from添加了to,to也添加了from CheckResult_Type_BothWay + // 2 from添加了t0,to没有添加from CheckResult_Both_Type_AWithB + // 3 from没有添加to,to添加了from, CheckResult_Both_Type_BWithA + // 4 双方都没有添加 CheckResult_Both_Type_NoRelation + + //单向校验黑名单:1 from没有拉黑to,不确定to是否拉黑了from CheckResult_singe_Type_AWithB; + // 0 from拉黑to,不确定to是佛拉黑from CheckResult_singe_Type_NoRelation + //双向校验黑名单 1 from没有拉黑to,to也没有拉黑from CheckResult_Type_BothWay + // 2 from没有拉黑to,to拉黑from CheckResult_Both_Type_AWithB + // 3 from拉黑了to,to没有拉黑from CheckResult_Both_Type_BWithA + // 4 双方都拉黑 CheckResult_Both_Type_NoRelation + private Integer status; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/resp/ImportFriendShipResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/resp/ImportFriendShipResp.java new file mode 100644 index 0000000..240016b --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/model/resp/ImportFriendShipResp.java @@ -0,0 +1,14 @@ +package com.lld.im.service.friendship.model.resp; + +import lombok.Data; + +import java.util.List; + + +@Data +public class ImportFriendShipResp { + + private List successId; + + private List errorId; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendService.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendService.java new file mode 100644 index 0000000..885cfd5 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendService.java @@ -0,0 +1,40 @@ +package com.lld.im.service.friendship.service; + +import com.lld.im.common.ResponseVO; +import com.lld.im.common.model.RequestBase; +import com.lld.im.service.friendship.model.req.*; + +import java.util.List; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +public interface ImFriendService { + + public ResponseVO importFriendShip(ImporFriendShipReq req); + + public ResponseVO addFriend(AddFriendReq req); + + public ResponseVO updateFriend(UpdateFriendReq req); + + public ResponseVO deleteFriend(DeleteFriendReq req); + + public ResponseVO deleteAllFriend(DeleteFriendReq req); + + public ResponseVO getAllFriendShip(GetAllFriendShipReq req); + + public ResponseVO getRelation(GetRelationReq req); + + public ResponseVO doAddFriend(RequestBase requestBase,String fromId, FriendDto dto, Integer appId); + + public ResponseVO checkFriendship(CheckFriendShipReq req); + + public ResponseVO addBlack(AddFriendShipBlackReq req); + + public ResponseVO deleteBlack(DeleteBlackReq req); + + public ResponseVO checkBlck(CheckFriendShipReq req); + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipGroupMemberService.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipGroupMemberService.java new file mode 100644 index 0000000..2783dbe --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipGroupMemberService.java @@ -0,0 +1,20 @@ +package com.lld.im.service.friendship.service; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.friendship.model.req.AddFriendShipGroupMemberReq; +import com.lld.im.service.friendship.model.req.DeleteFriendShipGroupMemberReq; + +/** + * @author: Chackylee + * @description: + **/ +public interface ImFriendShipGroupMemberService { + + public ResponseVO addGroupMember(AddFriendShipGroupMemberReq req); + + public ResponseVO delGroupMember(DeleteFriendShipGroupMemberReq req); + + public int doAddGroupMember(Long groupId, String toId); + + public int clearGroupMember(Long groupId); +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipGroupService.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipGroupService.java new file mode 100644 index 0000000..2a1c268 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipGroupService.java @@ -0,0 +1,21 @@ +package com.lld.im.service.friendship.service; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.friendship.dao.ImFriendShipGroupEntity; +import com.lld.im.service.friendship.model.req.AddFriendShipGroupReq; +import com.lld.im.service.friendship.model.req.DeleteFriendShipGroupReq; + +/** + * @author: Chackylee + * @description: + **/ +public interface ImFriendShipGroupService { + + public ResponseVO addGroup(AddFriendShipGroupReq req); + + public ResponseVO deleteGroup(DeleteFriendShipGroupReq req); + + public ResponseVO getGroup(String fromId, String groupName, Integer appId); + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipRequestService.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipRequestService.java new file mode 100644 index 0000000..8f98a2b --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/ImFriendShipRequestService.java @@ -0,0 +1,18 @@ +package com.lld.im.service.friendship.service; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.friendship.model.req.ApproverFriendRequestReq; +import com.lld.im.service.friendship.model.req.FriendDto; +import com.lld.im.service.friendship.model.req.ReadFriendShipRequestReq; + + +public interface ImFriendShipRequestService { + + public ResponseVO addFienshipRequest(String fromId, FriendDto dto, Integer appId); + + public ResponseVO approverFriendRequest(ApproverFriendRequestReq req); + + public ResponseVO readFriendShipRequestReq(ReadFriendShipRequestReq req); + + public ResponseVO getFriendRequest(String fromId, Integer appId); +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendServiceImpl.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendServiceImpl.java new file mode 100644 index 0000000..88c4142 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendServiceImpl.java @@ -0,0 +1,439 @@ +package com.lld.im.service.friendship.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.lld.im.common.ResponseVO; +import com.lld.im.common.enums.AllowFriendTypeEnum; +import com.lld.im.common.enums.CheckFriendShipTypeEnum; +import com.lld.im.common.enums.FriendShipErrorCode; +import com.lld.im.common.enums.FriendShipStatusEnum; +import com.lld.im.common.exception.ApplicationException; +import com.lld.im.common.model.RequestBase; +import com.lld.im.service.friendship.dao.ImFriendShipEntity; +import com.lld.im.service.friendship.dao.mapper.ImFriendShipMapper; +import com.lld.im.service.friendship.model.req.*; +import com.lld.im.service.friendship.model.resp.CheckFriendShipResp; +import com.lld.im.service.friendship.model.resp.ImportFriendShipResp; +import com.lld.im.service.friendship.service.ImFriendService; +import com.lld.im.service.friendship.service.ImFriendShipRequestService; +import com.lld.im.service.user.dao.ImUserDataEntity; +import com.lld.im.service.user.service.ImUserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Service +public class ImFriendServiceImpl implements ImFriendService { + + @Autowired + ImFriendShipMapper imFriendShipMapper; + + @Autowired + ImUserService imUserService; + + @Autowired + ImFriendService imFriendService; + + @Autowired + ImFriendShipRequestService imFriendShipRequestService; + + @Override + public ResponseVO importFriendShip(ImporFriendShipReq req) { + + if(req.getFriendItem().size() > 100){ + return ResponseVO.errorResponse(FriendShipErrorCode.IMPORT_SIZE_BEYOND); + } + ImportFriendShipResp resp = new ImportFriendShipResp(); + List successId = new ArrayList<>(); + List errorId = new ArrayList<>(); + + for (ImporFriendShipReq.ImportFriendDto dto: + req.getFriendItem()) { + ImFriendShipEntity entity = new ImFriendShipEntity(); + BeanUtils.copyProperties(dto,entity); + entity.setAppId(req.getAppId()); + entity.setFromId(req.getFromId()); + try { + int insert = imFriendShipMapper.insert(entity); + if(insert == 1){ + successId.add(dto.getToId()); + }else{ + errorId.add(dto.getToId()); + } + }catch (Exception e){ + e.printStackTrace(); + errorId.add(dto.getToId()); + } + + } + + resp.setErrorId(errorId); + resp.setSuccessId(successId); + + return ResponseVO.successResponse(resp); + } + + @Override + public ResponseVO addFriend(AddFriendReq req) { + + ResponseVO fromInfo = imUserService.getSingleUserInfo(req.getFromId(), req.getAppId()); + if(!fromInfo.isOk()){ + return fromInfo; + } + + ResponseVO toInfo = imUserService.getSingleUserInfo(req.getToItem().getToId(), req.getAppId()); + if(!toInfo.isOk()){ + return toInfo; + } + + ImUserDataEntity data = toInfo.getData(); + + if(data.getFriendAllowType() != null && data.getFriendAllowType() == AllowFriendTypeEnum.NOT_NEED.getCode()){ + return this.doAddFriend(req,req.getFromId(), req.getToItem(), req.getAppId()); + }else{ + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id",req.getAppId()); + query.eq("from_id",req.getFromId()); + query.eq("to_id",req.getToItem().getToId()); + ImFriendShipEntity fromItem = imFriendShipMapper.selectOne(query); + if(fromItem == null || fromItem.getStatus() + != FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()){ + //插入一条好友申请的数据 + ResponseVO responseVO = imFriendShipRequestService.addFienshipRequest(req.getFromId(), req.getToItem(), req.getAppId()); + if(!responseVO.isOk()){ + return responseVO; + } + }else{ + return ResponseVO.errorResponse(FriendShipErrorCode.TO_IS_YOUR_FRIEND); + } + + } + + return ResponseVO.successResponse(); + } + + @Override + public ResponseVO updateFriend(UpdateFriendReq req) { + + ResponseVO fromInfo = imUserService.getSingleUserInfo(req.getFromId(), req.getAppId()); + if(!fromInfo.isOk()){ + return fromInfo; + } + + ResponseVO toInfo = imUserService.getSingleUserInfo(req.getToItem().getToId(), req.getAppId()); + if(!toInfo.isOk()){ + return toInfo; + } + + ResponseVO responseVO = this.doUpdate(req.getFromId(), req.getToItem(), req.getAppId()); + return responseVO; + } + + @Transactional + public ResponseVO doUpdate(String fromId, FriendDto dto,Integer appId){ + + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.lambda().set(ImFriendShipEntity::getAddSource,dto.getAddSource()) + .set(ImFriendShipEntity::getExtra,dto.getExtra()) + .set(ImFriendShipEntity::getRemark,dto.getRemark()) + .eq(ImFriendShipEntity::getAppId,appId) + .eq(ImFriendShipEntity::getToId,dto.getToId()) + .eq(ImFriendShipEntity::getFromId,fromId); + + int update = imFriendShipMapper.update(null, updateWrapper); + if(update == 1){ + return ResponseVO.successResponse(); + } + + return ResponseVO.errorResponse(); + } + + @Override + @Transactional + public ResponseVO doAddFriend(RequestBase requestBase,String fromId, FriendDto dto, Integer appId){ + + //A-B + //Friend表插入A 和 B 两条记录 + //查询是否有记录存在,如果存在则判断状态,如果是已添加,则提示已添加,如果是未添加,则修改状态 + + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id",appId); + query.eq("from_id",fromId); + query.eq("to_id",dto.getToId()); + ImFriendShipEntity fromItem = imFriendShipMapper.selectOne(query); + if(fromItem == null){ + //走添加逻辑。 + fromItem = new ImFriendShipEntity(); + fromItem.setAppId(appId); + fromItem.setFromId(fromId); +// entity.setToId(to); + BeanUtils.copyProperties(dto,fromItem); + fromItem.setStatus(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()); + fromItem.setCreateTime(System.currentTimeMillis()); + int insert = imFriendShipMapper.insert(fromItem); + if(insert != 1){ + return ResponseVO.errorResponse(FriendShipErrorCode.ADD_FRIEND_ERROR); + } + } else{ + //如果存在则判断状态,如果是已添加,则提示已添加,如果是未添加,则修改状态 + if(fromItem.getStatus() == FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()){ + return ResponseVO.errorResponse(FriendShipErrorCode.TO_IS_YOUR_FRIEND); + } else{ + ImFriendShipEntity update = new ImFriendShipEntity(); + + if(StringUtils.isNotBlank(dto.getAddSource())){ + update.setAddSource(dto.getAddSource()); + } + + if(StringUtils.isNotBlank(dto.getRemark())){ + update.setRemark(dto.getRemark()); + } + + if(StringUtils.isNotBlank(dto.getExtra())){ + update.setExtra(dto.getExtra()); + } + update.setStatus(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()); + + int result = imFriendShipMapper.update(update, query); + if(result != 1){ + return ResponseVO.errorResponse(FriendShipErrorCode.ADD_FRIEND_ERROR); + } + } + + } + + QueryWrapper toQuery = new QueryWrapper<>(); + toQuery.eq("app_id",appId); + toQuery.eq("from_id",dto.getToId()); + toQuery.eq("to_id",fromId); + ImFriendShipEntity toItem = imFriendShipMapper.selectOne(toQuery); + if(toItem == null){ + toItem = new ImFriendShipEntity(); + toItem.setAppId(appId); + toItem.setFromId(dto.getToId()); + BeanUtils.copyProperties(dto,toItem); + toItem.setToId(fromId); + toItem.setStatus(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()); + toItem.setCreateTime(System.currentTimeMillis()); +// toItem.setBlack(FriendShipStatusEnum.BLACK_STATUS_NORMAL.getCode()); + int insert = imFriendShipMapper.insert(toItem); + }else{ + if(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode() != + toItem.getStatus()){ + ImFriendShipEntity update = new ImFriendShipEntity(); + update.setStatus(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()); + imFriendShipMapper.update(update,toQuery); + } + } + return ResponseVO.successResponse(); + } + + + @Override + public ResponseVO deleteFriend(DeleteFriendReq req) { + + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id",req.getAppId()); + query.eq("from_id",req.getFromId()); + query.eq("to_id",req.getToId()); + ImFriendShipEntity fromItem = imFriendShipMapper.selectOne(query); + if(fromItem == null){ + return ResponseVO.errorResponse(FriendShipErrorCode.TO_IS_NOT_YOUR_FRIEND); + }else{ + if(fromItem.getStatus() != null && fromItem.getStatus() == FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()){ + ImFriendShipEntity update = new ImFriendShipEntity(); + update.setStatus(FriendShipStatusEnum.FRIEND_STATUS_DELETE.getCode()); + imFriendShipMapper.update(update,query); + + }else{ + return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_IS_DELETED); + } + } + return ResponseVO.successResponse(); + } + + @Override + public ResponseVO deleteAllFriend(DeleteFriendReq req) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id",req.getAppId()); + query.eq("from_id",req.getFromId()); + query.eq("status",FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()); + + ImFriendShipEntity update = new ImFriendShipEntity(); + update.setStatus(FriendShipStatusEnum.FRIEND_STATUS_DELETE.getCode()); + imFriendShipMapper.update(update,query); + return ResponseVO.successResponse(); + } + + @Override + public ResponseVO getAllFriendShip(GetAllFriendShipReq req) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id",req.getAppId()); + query.eq("from_id",req.getFromId()); + return ResponseVO.successResponse(imFriendShipMapper.selectList(query)); + } + + @Override + public ResponseVO getRelation(GetRelationReq req) { + + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id",req.getAppId()); + query.eq("from_id",req.getFromId()); + query.eq("to_id",req.getToId()); + + ImFriendShipEntity entity = imFriendShipMapper.selectOne(query); + if(entity == null){ + return ResponseVO.errorResponse(FriendShipErrorCode.REPEATSHIP_IS_NOT_EXIST); + } + return ResponseVO.successResponse(entity); + } + + @Override + public ResponseVO checkBlck(CheckFriendShipReq req) { + + Map toIdMap + = req.getToIds().stream().collect(Collectors + .toMap(Function.identity(), s -> 0)); + List result = new ArrayList<>(); + if (req.getCheckType() == CheckFriendShipTypeEnum.SINGLE.getType()) { + result = imFriendShipMapper.checkFriendShipBlack(req); + } else { + result = imFriendShipMapper.checkFriendShipBlackBoth(req); + } + + Map collect = result.stream() + .collect(Collectors + .toMap(CheckFriendShipResp::getToId, + CheckFriendShipResp::getStatus)); + for (String toId: + toIdMap.keySet()) { + if(!collect.containsKey(toId)){ + CheckFriendShipResp checkFriendShipResp = new CheckFriendShipResp(); + checkFriendShipResp.setToId(toId); + checkFriendShipResp.setFromId(req.getFromId()); + checkFriendShipResp.setStatus(toIdMap.get(toId)); + result.add(checkFriendShipResp); + } + } + + return ResponseVO.successResponse(result); + } + + + @Override + public ResponseVO addBlack(AddFriendShipBlackReq req) { + + ResponseVO fromInfo = imUserService.getSingleUserInfo(req.getFromId(), req.getAppId()); + if(!fromInfo.isOk()){ + return fromInfo; + } + + ResponseVO toInfo = imUserService.getSingleUserInfo(req.getToId(), req.getAppId()); + if(!toInfo.isOk()){ + return toInfo; + } + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id",req.getAppId()); + query.eq("from_id",req.getFromId()); + query.eq("to_id",req.getToId()); + + ImFriendShipEntity fromItem = imFriendShipMapper.selectOne(query); + if(fromItem == null){ + //走添加逻辑。 + fromItem = new ImFriendShipEntity(); + fromItem.setFromId(req.getFromId()); + fromItem.setToId(req.getToId()); + fromItem.setAppId(req.getAppId()); + fromItem.setBlack(FriendShipStatusEnum.BLACK_STATUS_BLACKED.getCode()); + fromItem.setCreateTime(System.currentTimeMillis()); + int insert = imFriendShipMapper.insert(fromItem); + if(insert != 1){ + return ResponseVO.errorResponse(FriendShipErrorCode.ADD_FRIEND_ERROR); + } + + } else{ + //如果存在则判断状态,如果是拉黑,则提示已拉黑,如果是未拉黑,则修改状态 + if(fromItem.getBlack() != null && fromItem.getBlack() == FriendShipStatusEnum.BLACK_STATUS_BLACKED.getCode()){ + return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_IS_BLACK); + } else { + ImFriendShipEntity update = new ImFriendShipEntity(); + update.setBlack(FriendShipStatusEnum.BLACK_STATUS_BLACKED.getCode()); + int result = imFriendShipMapper.update(update, query); + if(result != 1){ + return ResponseVO.errorResponse(FriendShipErrorCode.ADD_BLACK_ERROR); + } + } + } + + return ResponseVO.successResponse(); + } + + @Override + public ResponseVO deleteBlack(DeleteBlackReq req) { + QueryWrapper queryFrom = new QueryWrapper<>() + .eq("from_id", req.getFromId()) + .eq("app_id", req.getAppId()) + .eq("to_id", req.getToId()); + ImFriendShipEntity fromItem = imFriendShipMapper.selectOne(queryFrom); + if (fromItem.getBlack() != null && fromItem.getBlack() == FriendShipStatusEnum.BLACK_STATUS_NORMAL.getCode()) { + throw new ApplicationException(FriendShipErrorCode.FRIEND_IS_NOT_YOUR_BLACK); + } + + ImFriendShipEntity update = new ImFriendShipEntity(); + update.setBlack(FriendShipStatusEnum.BLACK_STATUS_NORMAL.getCode()); + int update1 = imFriendShipMapper.update(update, queryFrom); + if(update1 == 1){ + return ResponseVO.successResponse(); + } + return ResponseVO.errorResponse(); + } + + @Override + public ResponseVO checkFriendship(CheckFriendShipReq req) { + + Map result + = req.getToIds().stream() + .collect(Collectors.toMap(Function.identity(), s -> 0)); + + List resp = new ArrayList<>(); + + if(req.getCheckType() == CheckFriendShipTypeEnum.SINGLE.getType()){ + resp =imFriendShipMapper.checkFriendShip(req); + }else { + resp =imFriendShipMapper.checkFriendShipBoth(req); + } + + Map collect = resp.stream() + .collect(Collectors.toMap(CheckFriendShipResp::getToId + , CheckFriendShipResp::getStatus)); + + for (String toId : result.keySet()){ + if(!collect.containsKey(toId)){ + CheckFriendShipResp checkFriendShipResp = new CheckFriendShipResp(); + checkFriendShipResp.setFromId(req.getFromId()); + checkFriendShipResp.setToId(toId); + checkFriendShipResp.setStatus(result.get(toId)); + resp.add(checkFriendShipResp); + } + } + + return ResponseVO.successResponse(resp); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipGroupMemberServiceImpl.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipGroupMemberServiceImpl.java new file mode 100644 index 0000000..0431113 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipGroupMemberServiceImpl.java @@ -0,0 +1,122 @@ +package com.lld.im.service.friendship.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.lld.im.common.ResponseVO; +import com.lld.im.service.friendship.dao.ImFriendShipGroupEntity; +import com.lld.im.service.friendship.dao.ImFriendShipGroupMemberEntity; +import com.lld.im.service.friendship.dao.mapper.ImFriendShipGroupMemberMapper; +import com.lld.im.service.friendship.model.req.AddFriendShipGroupMemberReq; +import com.lld.im.service.friendship.model.req.DeleteFriendShipGroupMemberReq; +import com.lld.im.service.friendship.service.ImFriendShipGroupMemberService; +import com.lld.im.service.friendship.service.ImFriendShipGroupService; +import com.lld.im.service.user.dao.ImUserDataEntity; +import com.lld.im.service.user.service.ImUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author: Chackylee + * @description: + **/ +@Service +public class ImFriendShipGroupMemberServiceImpl + implements ImFriendShipGroupMemberService { + + @Autowired + ImFriendShipGroupMemberMapper imFriendShipGroupMemberMapper; + + @Autowired + ImFriendShipGroupService imFriendShipGroupService; + + @Autowired + ImUserService imUserService; + + @Autowired + ImFriendShipGroupMemberService thisService; + + @Override + @Transactional + public ResponseVO addGroupMember(AddFriendShipGroupMemberReq req) { + + ResponseVO group = imFriendShipGroupService + .getGroup(req.getFromId(),req.getGroupName(),req.getAppId()); + if(!group.isOk()){ + return group; + } + + List successId = new ArrayList<>(); + for (String toId : req.getToIds()) { + ResponseVO singleUserInfo = imUserService.getSingleUserInfo(toId, req.getAppId()); + if(singleUserInfo.isOk()){ + int i = thisService.doAddGroupMember(group.getData().getGroupId(), toId); + if(i == 1){ + successId.add(toId); + } + } + } + + return ResponseVO.successResponse(successId); + } + + @Override + public ResponseVO delGroupMember(DeleteFriendShipGroupMemberReq req) { + ResponseVO group = imFriendShipGroupService + .getGroup(req.getFromId(),req.getGroupName(),req.getAppId()); + if(!group.isOk()){ + return group; + } + + ArrayList list = new ArrayList(); + for (String toId : req.getToIds()) { + ResponseVO singleUserInfo = imUserService.getSingleUserInfo(toId, req.getAppId()); + if(singleUserInfo.isOk()){ + int i = deleteGroupMember(group.getData().getGroupId(), toId); + if(i == 1){ + list.add(toId); + } + } + } + return ResponseVO.successResponse(list); + } + + @Override + public int doAddGroupMember(Long groupId, String toId) { + ImFriendShipGroupMemberEntity imFriendShipGroupMemberEntity = new ImFriendShipGroupMemberEntity(); + imFriendShipGroupMemberEntity.setGroupId(groupId); + imFriendShipGroupMemberEntity.setToId(toId); + try { + int insert = imFriendShipGroupMemberMapper.insert(imFriendShipGroupMemberEntity); + return insert; + }catch (Exception e){ + e.printStackTrace(); + return 0; + } + } + + public int deleteGroupMember(Long groupId, String toId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("group_id",groupId); + queryWrapper.eq("to_id",toId); + + try { + int delete = imFriendShipGroupMemberMapper.delete(queryWrapper); +// int insert = imFriendShipGroupMemberMapper.insert(imFriendShipGroupMemberEntity); + return delete; + }catch (Exception e){ + e.printStackTrace(); + return 0; + } + } + + @Override + public int clearGroupMember(Long groupId) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("group_id",groupId); + int delete = imFriendShipGroupMemberMapper.delete(query); + return delete; + } +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipGroupServiceImpl.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipGroupServiceImpl.java new file mode 100644 index 0000000..350e226 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipGroupServiceImpl.java @@ -0,0 +1,119 @@ +package com.lld.im.service.friendship.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.lld.im.common.ResponseVO; +import com.lld.im.common.enums.DelFlagEnum; +import com.lld.im.common.enums.FriendShipErrorCode; +import com.lld.im.service.friendship.dao.ImFriendShipGroupEntity; +import com.lld.im.service.friendship.dao.mapper.ImFriendShipGroupMapper; +import com.lld.im.service.friendship.model.req.AddFriendShipGroupMemberReq; +import com.lld.im.service.friendship.model.req.AddFriendShipGroupReq; +import com.lld.im.service.friendship.model.req.DeleteFriendShipGroupReq; +import com.lld.im.service.friendship.service.ImFriendShipGroupMemberService; +import com.lld.im.service.friendship.service.ImFriendShipGroupService; +import com.lld.im.service.user.service.ImUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class ImFriendShipGroupServiceImpl implements ImFriendShipGroupService { + + @Autowired + ImFriendShipGroupMapper imFriendShipGroupMapper; + + @Autowired + ImFriendShipGroupMemberService imFriendShipGroupMemberService; + + @Autowired + ImUserService imUserService; + + @Override + @Transactional + public ResponseVO addGroup(AddFriendShipGroupReq req) { + + QueryWrapper query = new QueryWrapper<>(); + query.eq("group_name", req.getGroupName()); + query.eq("app_id", req.getAppId()); + query.eq("from_id", req.getFromId()); + query.eq("del_flag", DelFlagEnum.NORMAL.getCode()); + + ImFriendShipGroupEntity entity = imFriendShipGroupMapper.selectOne(query); + + if (entity != null) { + return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_SHIP_GROUP_IS_EXIST); + } + + //写入db + ImFriendShipGroupEntity insert = new ImFriendShipGroupEntity(); + insert.setAppId(req.getAppId()); + insert.setCreateTime(System.currentTimeMillis()); + insert.setDelFlag(DelFlagEnum.NORMAL.getCode()); + insert.setGroupName(req.getGroupName()); + insert.setFromId(req.getFromId()); + try { + int insert1 = imFriendShipGroupMapper.insert(insert); + + if (insert1 != 1) { + return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_SHIP_GROUP_CREATE_ERROR); + } + + if (insert1 == 1 && CollectionUtil.isNotEmpty(req.getToIds())) { + AddFriendShipGroupMemberReq addFriendShipGroupMemberReq = new AddFriendShipGroupMemberReq(); + addFriendShipGroupMemberReq.setFromId(req.getFromId()); + addFriendShipGroupMemberReq.setGroupName(req.getGroupName()); + addFriendShipGroupMemberReq.setToIds(req.getToIds()); + addFriendShipGroupMemberReq.setAppId(req.getAppId()); + imFriendShipGroupMemberService.addGroupMember(addFriendShipGroupMemberReq); + return ResponseVO.successResponse(); + } + } catch (DuplicateKeyException e) { + e.getStackTrace(); + return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_SHIP_GROUP_IS_EXIST); + } + return ResponseVO.successResponse(); + } + + @Override + @Transactional + public ResponseVO deleteGroup(DeleteFriendShipGroupReq req) { + + for (String groupName : req.getGroupName()) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("group_name", groupName); + query.eq("app_id", req.getAppId()); + query.eq("from_id", req.getFromId()); + query.eq("del_flag", DelFlagEnum.NORMAL.getCode()); + + ImFriendShipGroupEntity entity = imFriendShipGroupMapper.selectOne(query); + + if (entity != null) { + ImFriendShipGroupEntity update = new ImFriendShipGroupEntity(); + update.setGroupId(entity.getGroupId()); + update.setDelFlag(DelFlagEnum.DELETE.getCode()); + imFriendShipGroupMapper.updateById(update); + imFriendShipGroupMemberService.clearGroupMember(entity.getGroupId()); + + } + } + return ResponseVO.successResponse(); + } + + @Override + public ResponseVO getGroup(String fromId, String groupName, Integer appId) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("group_name", groupName); + query.eq("app_id", appId); + query.eq("from_id", fromId); + query.eq("del_flag", DelFlagEnum.NORMAL.getCode()); + + ImFriendShipGroupEntity entity = imFriendShipGroupMapper.selectOne(query); + if (entity == null) { + return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_SHIP_GROUP_IS_NOT_EXIST); + } + return ResponseVO.successResponse(entity); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipRequestServiceImpl.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipRequestServiceImpl.java new file mode 100644 index 0000000..d806731 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/friendship/service/impl/ImFriendShipRequestServiceImpl.java @@ -0,0 +1,140 @@ +package com.lld.im.service.friendship.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.lld.im.common.ResponseVO; +import com.lld.im.common.enums.ApproverFriendRequestStatusEnum; +import com.lld.im.common.enums.FriendShipErrorCode; +import com.lld.im.common.exception.ApplicationException; +import com.lld.im.service.friendship.dao.ImFriendShipRequestEntity; +import com.lld.im.service.friendship.dao.mapper.ImFriendShipRequestMapper; +import com.lld.im.service.friendship.model.req.ApproverFriendRequestReq; +import com.lld.im.service.friendship.model.req.FriendDto; +import com.lld.im.service.friendship.model.req.ReadFriendShipRequestReq; +import com.lld.im.service.friendship.service.ImFriendService; +import com.lld.im.service.friendship.service.ImFriendShipRequestService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +@Service +public class ImFriendShipRequestServiceImpl implements ImFriendShipRequestService { + + @Autowired + ImFriendShipRequestMapper imFriendShipRequestMapper; + + @Autowired + ImFriendService imFriendShipService; + + @Override + public ResponseVO getFriendRequest(String fromId, Integer appId) { + + QueryWrapper query = new QueryWrapper(); + query.eq("app_id", appId); + query.eq("to_id", fromId); + + List requestList = imFriendShipRequestMapper.selectList(query); + + return ResponseVO.successResponse(requestList); + } + + + //A + B + @Override + public ResponseVO addFienshipRequest(String fromId, FriendDto dto, Integer appId) { + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("app_id",appId); + queryWrapper.eq("from_id",fromId); + queryWrapper.eq("to_id",dto.getToId()); + ImFriendShipRequestEntity request = imFriendShipRequestMapper.selectOne(queryWrapper); + + if(request == null){ + request = new ImFriendShipRequestEntity(); + request.setAddSource(dto.getAddSource()); + request.setAddWording(dto.getAddWording()); + request.setAppId(appId); + request.setFromId(fromId); + request.setToId(dto.getToId()); + request.setReadStatus(0); + request.setApproveStatus(0); + request.setRemark(dto.getRemark()); + request.setCreateTime(System.currentTimeMillis()); + imFriendShipRequestMapper.insert(request); + + }else { + //修改记录内容 和更新时间 + if(StringUtils.isNotBlank(dto.getAddSource())){ + request.setAddWording(dto.getAddWording()); + } + if(StringUtils.isNotBlank(dto.getRemark())){ + request.setRemark(dto.getRemark()); + } + if(StringUtils.isNotBlank(dto.getAddWording())){ + request.setAddWording(dto.getAddWording()); + } + request.setApproveStatus(0); + request.setReadStatus(0); + imFriendShipRequestMapper.updateById(request); + } + + return ResponseVO.successResponse(); + } + + @Override + @Transactional + public ResponseVO approverFriendRequest(ApproverFriendRequestReq req) { + + ImFriendShipRequestEntity imFriendShipRequestEntity = imFriendShipRequestMapper.selectById(req.getId()); + if(imFriendShipRequestEntity == null){ + throw new ApplicationException(FriendShipErrorCode.FRIEND_REQUEST_IS_NOT_EXIST); + } + + if(!req.getOperater().equals(imFriendShipRequestEntity.getToId())){ + //只能审批发给自己的好友请求 + throw new ApplicationException(FriendShipErrorCode.NOT_APPROVER_OTHER_MAN_REQUEST); + } + + ImFriendShipRequestEntity update = new ImFriendShipRequestEntity(); + update.setApproveStatus(req.getStatus()); + update.setUpdateTime(System.currentTimeMillis()); + update.setId(req.getId()); + imFriendShipRequestMapper.updateById(update); + + if(ApproverFriendRequestStatusEnum.AGREE.getCode() == req.getStatus()){ + //同意 ===> 去执行添加好友逻辑 + FriendDto dto = new FriendDto(); + dto.setAddSource(imFriendShipRequestEntity.getAddSource()); + dto.setAddWording(imFriendShipRequestEntity.getAddWording()); + dto.setRemark(imFriendShipRequestEntity.getRemark()); + dto.setToId(imFriendShipRequestEntity.getToId()); + ResponseVO responseVO = imFriendShipService.doAddFriend(req,imFriendShipRequestEntity.getFromId(), dto,req.getAppId()); +// if(!responseVO.isOk()){ +//// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); +// return responseVO; +// } + if(!responseVO.isOk() && responseVO.getCode() != FriendShipErrorCode.TO_IS_YOUR_FRIEND.getCode()){ + return responseVO; + } + } + + return ResponseVO.successResponse(); + } + + @Override + public ResponseVO readFriendShipRequestReq(ReadFriendShipRequestReq req) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id", req.getAppId()); + query.eq("to_id", req.getFromId()); + + ImFriendShipRequestEntity update = new ImFriendShipRequestEntity(); + update.setReadStatus(1); + imFriendShipRequestMapper.update(update, query); + + return ResponseVO.successResponse(); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/controller/ImGroupController.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/controller/ImGroupController.java new file mode 100644 index 0000000..f1bd328 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/controller/ImGroupController.java @@ -0,0 +1,80 @@ +package com.lld.im.service.group.controller; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.group.model.req.*; +import com.lld.im.service.group.service.ImGroupService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@RestController +@RequestMapping("v1/group") +public class ImGroupController { + + @Autowired + ImGroupService groupService; + + @RequestMapping("/importGroup") + public ResponseVO importGroup(@RequestBody @Validated ImportGroupReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupService.importGroup(req); + } + + @RequestMapping("/createGroup") + public ResponseVO createGroup(@RequestBody @Validated CreateGroupReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupService.createGroup(req); + } + + @RequestMapping("/getGroupInfo") + public ResponseVO getGroupInfo(@RequestBody @Validated GetGroupReq req, Integer appId) { + req.setAppId(appId); + return groupService.getGroup(req); + } + + @RequestMapping("/update") + public ResponseVO update(@RequestBody @Validated UpdateGroupReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupService.updateBaseGroupInfo(req); + } + + @RequestMapping("/getJoinedGroup") + public ResponseVO getJoinedGroup(@RequestBody @Validated GetJoinedGroupReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupService.getJoinedGroup(req); + } + + + @RequestMapping("/destroyGroup") + public ResponseVO destroyGroup(@RequestBody @Validated DestroyGroupReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupService.destroyGroup(req); + } + + @RequestMapping("/transferGroup") + public ResponseVO transferGroup(@RequestBody @Validated TransferGroupReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupService.transferGroup(req); + } + + @RequestMapping("/forbidSendMessage") + public ResponseVO forbidSendMessage(@RequestBody @Validated MuteGroupReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupService.muteGroup(req); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/controller/ImGroupMemberController.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/controller/ImGroupMemberController.java new file mode 100644 index 0000000..4d9ce84 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/controller/ImGroupMemberController.java @@ -0,0 +1,59 @@ +package com.lld.im.service.group.controller; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.group.model.req.*; +import com.lld.im.service.group.service.ImGroupMemberService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@RestController +@RequestMapping("v1/group/member") +public class ImGroupMemberController { + + @Autowired + ImGroupMemberService groupMemberService; + + @RequestMapping("/importGroupMember") + public ResponseVO importGroupMember(@RequestBody @Validated ImportGroupMemberReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupMemberService.importGroupMember(req); + } + + @RequestMapping("/add") + public ResponseVO addMember(@RequestBody @Validated AddGroupMemberReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupMemberService.addMember(req); + } + + @RequestMapping("/remove") + public ResponseVO removeMember(@RequestBody @Validated RemoveGroupMemberReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupMemberService.removeMember(req); + } + + @RequestMapping("/update") + public ResponseVO updateGroupMember(@RequestBody @Validated UpdateGroupMemberReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupMemberService.updateGroupMember(req); + } + + @RequestMapping("/speak") + public ResponseVO speak(@RequestBody @Validated SpeaMemberReq req, Integer appId, String identifier) { + req.setAppId(appId); + req.setOperater(identifier); + return groupMemberService.speak(req); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupEntity.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupEntity.java new file mode 100644 index 0000000..a9c2b73 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupEntity.java @@ -0,0 +1,54 @@ +package com.lld.im.service.group.dao; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +@TableName("im_group") +public class ImGroupEntity { + + @TableId(value = "group_id") + private String groupId; + + private Integer appId; + + //群主id + private String ownerId; + + //群类型 1私有群(类似微信) 2公开群(类似qq) + private Integer groupType; + + private String groupName; + + private Integer mute;// 是否全员禁言,0 不禁言;1 全员禁言。 + +// 申请加群选项包括如下几种: +// 0 表示禁止任何人申请加入 +// 1 表示需要群主或管理员审批 +// 2 表示允许无需审批自由加入群组 + private Integer applyJoinType; + + private String introduction;//群简介 + + private String notification;//群公告 + + private String photo;//群头像 + + private Integer maxMemberCount;//群成员上限 + + private Integer status;//群状态 0正常 1解散 + + private Long sequence; + + private Long createTime; + + private Long updateTime; + + private String extra; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupMemberEntity.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupMemberEntity.java new file mode 100644 index 0000000..8d0ae0b --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupMemberEntity.java @@ -0,0 +1,44 @@ +package com.lld.im.service.group.dao; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +@TableName("im_group_member") +public class ImGroupMemberEntity { + + @TableId(type = IdType.AUTO) + private Long groupMemberId; + + private Integer appId; + + private String groupId; + + //成员id + private String memberId; + + //群成员类型,0 普通成员, 1 管理员, 2 群主, 3 禁言,4 已经移除的成员 + private Integer role; + + private Long speakDate; + + //群昵称 + private String alias; + + //加入时间 + private Long joinTime; + + //离开时间 + private Long leaveTime; + + private String joinType; + + private String extra; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupMessageHistoryEntity.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupMessageHistoryEntity.java new file mode 100644 index 0000000..cd95cf4 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/ImGroupMessageHistoryEntity.java @@ -0,0 +1,32 @@ +package com.lld.im.service.group.dao; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @author: Chackylee + * @description: + **/ +@Data +@TableName("im_group_message_history") +public class ImGroupMessageHistoryEntity { + + private Integer appId; + + private String fromId; + + private String groupId; + + /** messageBodyId*/ + private Long messageKey; + /** 序列号*/ + private Long sequence; + + private String messageRandom; + + private Long messageTime; + + private Long createTime; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMapper.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMapper.java new file mode 100644 index 0000000..c361fcc --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMapper.java @@ -0,0 +1,13 @@ +package com.lld.im.service.group.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.lld.im.service.group.dao.ImGroupEntity; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.Collection; + +@Repository +public interface ImGroupMapper extends BaseMapper { + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMemberMapper.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMemberMapper.java new file mode 100644 index 0000000..e7a9311 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMemberMapper.java @@ -0,0 +1,65 @@ +package com.lld.im.service.group.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.lld.im.service.group.dao.ImGroupMemberEntity; +import com.lld.im.service.group.model.req.GroupMemberDto; +import org.apache.ibatis.annotations.Result; +import org.apache.ibatis.annotations.Results; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ImGroupMemberMapper extends BaseMapper { + + @Select("select group_id from im_group_member where app_id = #{appId} AND member_id = #{memberId} ") + public List getJoinedGroupId(Integer appId, String memberId); + + + + @Results({ + @Result(column = "member_id", property = "memberId"), +// @Result(column = "speak_flag", property = "speakFlag"), + @Result(column = "speak_date", property = "speakDate"), + @Result(column = "role", property = "role"), + @Result(column = "alias", property = "alias"), + @Result(column = "join_time", property = "joinTime"), + @Result(column = "join_type", property = "joinType") + }) + @Select("select " + + " member_id, " + +// " speak_flag, " + + " speak_date, " + + " role, " + + " alias, " + + " join_time ," + + " join_type " + + " from im_group_member where app_id = #{appId} AND group_id = #{groupId} ") + public List getGroupMember(Integer appId, String groupId); + + @Select("select " + + " member_id " + + " from im_group_member where app_id = #{appId} AND group_id = #{groupId} and role != 3") + public List getGroupMemberId(Integer appId, String groupId); + + + @Results({ + @Result(column = "member_id", property = "memberId"), +// @Result(column = "speak_flag", property = "speakFlag"), + @Result(column = "role", property = "role") +// @Result(column = "alias", property = "alias"), +// @Result(column = "join_time", property = "joinTime"), +// @Result(column = "join_type", property = "joinType") + }) + @Select("select " + + " member_id, " + +// " speak_flag, " + + " role " + +// " alias, " + +// " join_time ," + +// " join_type " + + " from im_group_member where app_id = #{appId} AND group_id = #{groupId} and role in (1,2) ") + public List getGroupManager(String groupId, Integer appId); + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMessageHistoryMapper.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMessageHistoryMapper.java new file mode 100644 index 0000000..4962788 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/dao/mapper/ImGroupMessageHistoryMapper.java @@ -0,0 +1,11 @@ +package com.lld.im.service.group.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.lld.im.service.group.dao.ImGroupMessageHistoryEntity; +import org.springframework.stereotype.Repository; + +@Repository +public interface ImGroupMessageHistoryMapper extends BaseMapper { + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/AddGroupMemberReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/AddGroupMemberReq.java new file mode 100644 index 0000000..6f8ce3b --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/AddGroupMemberReq.java @@ -0,0 +1,24 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class AddGroupMemberReq extends RequestBase { + + @NotBlank(message = "群id不能为空") + private String groupId; + + @NotEmpty(message = "群成员不能为空") + private List members; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/CreateGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/CreateGroupReq.java new file mode 100644 index 0000000..ba4f30b --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/CreateGroupReq.java @@ -0,0 +1,41 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import java.util.List; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class CreateGroupReq extends RequestBase { + + private String groupId; + //群主id + private String ownerId; + + //群类型 1私有群(类似微信) 2公开群(类似qq) + private Integer groupType; + + private String groupName; + + private Integer mute;// 是否全员禁言,0 不禁言;1 全员禁言。 + + private Integer applyJoinType;//加入群权限,0 所有人可以加入;1 群成员可以拉人;2 群管理员或群组可以拉人。 + + private String introduction;//群简介 + + private String notification;//群公告 + + private String photo;//群头像 + + private Integer MaxMemberCount; + + private List member; + + private String extra; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/DestroyGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/DestroyGroupReq.java new file mode 100644 index 0000000..15bffb8 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/DestroyGroupReq.java @@ -0,0 +1,18 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class DestroyGroupReq extends RequestBase { + + @NotNull(message = "群id不能为空") + private String groupId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetGroupReq.java new file mode 100644 index 0000000..6c1c007 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetGroupReq.java @@ -0,0 +1,15 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class GetGroupReq extends RequestBase { + + private String groupId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetJoinedGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetJoinedGroupReq.java new file mode 100644 index 0000000..8038d60 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetJoinedGroupReq.java @@ -0,0 +1,29 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class GetJoinedGroupReq extends RequestBase { + + @NotBlank(message = "用户id不能为空") + private String memberId; + + //群类型 + private List groupType; + + //单次拉取的群组数量,如果不填代表所有群组 + private Integer limit; + + //第几页 + private Integer offset; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetRoleInGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetRoleInGroupReq.java new file mode 100644 index 0000000..bb3f242 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GetRoleInGroupReq.java @@ -0,0 +1,18 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import java.util.List; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class GetRoleInGroupReq extends RequestBase { + + private String groupId; + + private List memberId; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GroupMemberDto.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GroupMemberDto.java new file mode 100644 index 0000000..f6fb2c4 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/GroupMemberDto.java @@ -0,0 +1,28 @@ +package com.lld.im.service.group.model.req; + +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ + +@Data +public class GroupMemberDto { + + private String memberId; + + private String alias; + + private Integer role;//群成员类型,0 普通成员, 1 管理员, 2 群主, 3 已经移除的成员,当修改群成员信息时,只能取值0/1,其他值由其他接口实现,暂不支持3 + +// private Integer speakFlag; + + private Long speakDate; + + private String joinType; + + private Long joinTime; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/ImportGroupMemberReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/ImportGroupMemberReq.java new file mode 100644 index 0000000..0a61e0f --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/ImportGroupMemberReq.java @@ -0,0 +1,22 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class ImportGroupMemberReq extends RequestBase { + + @NotBlank(message = "群id不能为空") + private String groupId; + + private List members; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/ImportGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/ImportGroupReq.java new file mode 100644 index 0000000..422dabb --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/ImportGroupReq.java @@ -0,0 +1,42 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class ImportGroupReq extends RequestBase { + + private String groupId; + //群主id + private String ownerId; + + //群类型 1私有群(类似微信) 2公开群(类似qq) + private Integer groupType; + + @NotBlank(message = "群名称不能为空") + private String groupName; + + private Integer mute;// 是否全员禁言,0 不禁言;1 全员禁言。 + + private Integer applyJoinType;//加入群权限,0 所有人可以加入;1 群成员可以拉人;2 群管理员或群组可以拉人。 + + private String introduction;//群简介 + + private String notification;//群公告 + + private String photo;//群头像 + + private Integer MaxMemberCount; + + private Long createTime; + + private String extra; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/MuteGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/MuteGroupReq.java new file mode 100644 index 0000000..680e432 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/MuteGroupReq.java @@ -0,0 +1,22 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class MuteGroupReq extends RequestBase { + + @NotBlank(message = "groupId不能为空") + private String groupId; + + @NotNull(message = "mute不能为空") + private Integer mute; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/RemoveGroupMemberReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/RemoveGroupMemberReq.java new file mode 100644 index 0000000..32db73e --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/RemoveGroupMemberReq.java @@ -0,0 +1,20 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class RemoveGroupMemberReq extends RequestBase { + + @NotBlank(message = "群id不能为空") + private String groupId; + + private String memberId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/SendGroupMessageReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/SendGroupMessageReq.java new file mode 100644 index 0000000..f9e6781 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/SendGroupMessageReq.java @@ -0,0 +1,35 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class SendGroupMessageReq extends RequestBase { + + //客户端传的messageId + private String messageId; + + private String fromId; + + private String groupId; + + private int messageRandom; + + private long messageTime; + + private String messageBody; + /** + * 这个字段缺省或者为 0 表示需要计数,为 1 表示本条消息不需要计数,即右上角图标数字不增加 + */ + private int badgeMode; + + private Long messageLifeTime; + + private Integer appId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/SpeaMemberReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/SpeaMemberReq.java new file mode 100644 index 0000000..64cbae7 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/SpeaMemberReq.java @@ -0,0 +1,25 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class SpeaMemberReq extends RequestBase { + + @NotBlank(message = "群id不能为空") + private String groupId; + + @NotBlank(message = "memberId不能为空") + private String memberId; + + //禁言时间,单位毫秒 + @NotNull(message = "禁言时间不能为空") + private Long speakDate; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/TransferGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/TransferGroupReq.java new file mode 100644 index 0000000..f53567f --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/TransferGroupReq.java @@ -0,0 +1,17 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotNull; + + +@Data +public class TransferGroupReq extends RequestBase { + + @NotNull(message = "群id不能为空") + private String groupId; + + private String ownerId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/UpdateGroupMemberReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/UpdateGroupMemberReq.java new file mode 100644 index 0000000..e44d4ba --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/UpdateGroupMemberReq.java @@ -0,0 +1,27 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class UpdateGroupMemberReq extends RequestBase { + + @NotBlank(message = "群id不能为空") + private String groupId; + + @NotBlank(message = "memberId不能为空") + private String memberId; + + private String alias; + + private Integer role; + + private String extra; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/UpdateGroupReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/UpdateGroupReq.java new file mode 100644 index 0000000..d70a9a7 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/req/UpdateGroupReq.java @@ -0,0 +1,34 @@ +package com.lld.im.service.group.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class UpdateGroupReq extends RequestBase { + + @NotBlank(message = "群id不能为空") + private String groupId; + + private String groupName; + + private Integer mute;// 是否全员禁言,0 不禁言;1 全员禁言。 + + private Integer applyJoinType;//加入群权限,0 所有人可以加入;1 群成员可以拉人;2 群管理员或群组可以拉人。 + + private String introduction;//群简介 + + private String notification;//群公告 + + private String photo;//群头像 + + private Integer maxMemberCount;//群成员上限 + + private String extra; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/AddMemberResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/AddMemberResp.java new file mode 100644 index 0000000..8614833 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/AddMemberResp.java @@ -0,0 +1,19 @@ +package com.lld.im.service.group.model.resp; + +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class AddMemberResp { + + private String memberId; + + // 加人结果:0 为成功;1 为失败;2 为已经是群成员 + private Integer result; + + private String resultMessage; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetGroupResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetGroupResp.java new file mode 100644 index 0000000..3cd10bb --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetGroupResp.java @@ -0,0 +1,48 @@ +package com.lld.im.service.group.model.resp; + +import com.lld.im.service.group.model.req.GroupMemberDto; +import lombok.Data; + +import java.util.List; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class GetGroupResp { + + private String groupId; + + private Integer appId; + + //群主id + private String ownerId; + + //群类型 1私有群(类似微信) 2公开群(类似qq) + private Integer groupType; + + private String groupName; + + private Integer mute;// 是否全员禁言,0 不禁言;1 全员禁言。 + + // 申请加群选项包括如下几种: +// 0 表示禁止任何人申请加入 +// 1 表示需要群主或管理员审批 +// 2 表示允许无需审批自由加入群组 + private Integer applyJoinType; + + private Integer privateChat; //是否禁止私聊,0 允许群成员发起私聊;1 不允许群成员发起私聊。 + + private String introduction;//群简介 + + private String notification;//群公告 + + private String photo;//群头像 + + private Integer maxMemberCount;//群成员上限 + + private Integer status;//群状态 0正常 1解散 + + private List memberList; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetJoinedGroupResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetJoinedGroupResp.java new file mode 100644 index 0000000..a156a49 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetJoinedGroupResp.java @@ -0,0 +1,19 @@ +package com.lld.im.service.group.model.resp; + +import com.lld.im.service.group.dao.ImGroupEntity; +import lombok.Data; + +import java.util.List; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class GetJoinedGroupResp { + + private Integer totalCount; + + private List groupList; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetRoleInGroupResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetRoleInGroupResp.java new file mode 100644 index 0000000..e4cf87d --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/model/resp/GetRoleInGroupResp.java @@ -0,0 +1,20 @@ +package com.lld.im.service.group.model.resp; + +import lombok.Data; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class GetRoleInGroupResp { + + private Long groupMemberId; + + private String memberId; + + private Integer role; + + private Long speakDate; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/ImGroupMemberService.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/ImGroupMemberService.java new file mode 100644 index 0000000..6154733 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/ImGroupMemberService.java @@ -0,0 +1,43 @@ +package com.lld.im.service.group.service; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.group.model.req.*; +import com.lld.im.service.group.model.resp.GetRoleInGroupResp; + +import java.util.Collection; +import java.util.List; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +public interface ImGroupMemberService { + + public ResponseVO importGroupMember(ImportGroupMemberReq req); + + public ResponseVO addMember(AddGroupMemberReq req); + + public ResponseVO removeMember(RemoveGroupMemberReq req); + + public ResponseVO addGroupMember(String groupId, Integer appId, GroupMemberDto dto); + + public ResponseVO removeGroupMember(String groupId, Integer appId, String memberId); + + public ResponseVO getRoleInGroupOne(String groupId, String memberId, Integer appId); + + public ResponseVO> getMemberJoinedGroup(GetJoinedGroupReq req); + + public ResponseVO> getGroupMember(String groupId, Integer appId); + + public List getGroupMemberId(String groupId, Integer appId); + + public List getGroupManager(String groupId, Integer appId); + + public ResponseVO updateGroupMember(UpdateGroupMemberReq req); + + public ResponseVO transferGroupMember(String owner, String groupId, Integer appId); + + public ResponseVO speak(SpeaMemberReq req); + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/ImGroupService.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/ImGroupService.java new file mode 100644 index 0000000..eeb140c --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/ImGroupService.java @@ -0,0 +1,32 @@ +package com.lld.im.service.group.service; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.group.dao.ImGroupEntity; +import com.lld.im.service.group.model.req.*; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +public interface ImGroupService { + + public ResponseVO importGroup(ImportGroupReq req); + + public ResponseVO createGroup(CreateGroupReq req); + + public ResponseVO updateBaseGroupInfo(UpdateGroupReq req); + + public ResponseVO getJoinedGroup(GetJoinedGroupReq req); + + public ResponseVO destroyGroup(DestroyGroupReq req); + + public ResponseVO transferGroup(TransferGroupReq req); + + public ResponseVO getGroup(String groupId, Integer appId); + + public ResponseVO getGroup(GetGroupReq req); + + public ResponseVO muteGroup(MuteGroupReq req); + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/impl/ImGroupMemberServiceImpl.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/impl/ImGroupMemberServiceImpl.java new file mode 100644 index 0000000..df91cd8 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/impl/ImGroupMemberServiceImpl.java @@ -0,0 +1,537 @@ +package com.lld.im.service.group.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.lld.im.common.ResponseVO; +import com.lld.im.common.enums.GroupErrorCode; +import com.lld.im.common.enums.GroupMemberRoleEnum; +import com.lld.im.common.enums.GroupStatusEnum; +import com.lld.im.common.enums.GroupTypeEnum; +import com.lld.im.common.exception.ApplicationException; +import com.lld.im.service.group.dao.ImGroupEntity; +import com.lld.im.service.group.dao.ImGroupMemberEntity; +import com.lld.im.service.group.dao.mapper.ImGroupMemberMapper; +import com.lld.im.service.group.model.req.*; +import com.lld.im.service.group.model.resp.AddMemberResp; +import com.lld.im.service.group.model.resp.GetRoleInGroupResp; +import com.lld.im.service.group.service.ImGroupMemberService; +import com.lld.im.service.group.service.ImGroupService; +import com.lld.im.service.user.dao.ImUserDataEntity; +import com.lld.im.service.user.service.ImUserService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Service +@Slf4j +public class ImGroupMemberServiceImpl implements ImGroupMemberService { + + @Autowired + ImGroupMemberMapper imGroupMemberMapper; + + @Autowired + ImGroupService groupService; + + @Autowired + ImGroupMemberService groupMemberService; + + @Autowired + ImUserService imUserService; + + @Override + public ResponseVO importGroupMember(ImportGroupMemberReq req) { + + List resp = new ArrayList<>(); + + ResponseVO groupResp = groupService.getGroup(req.getGroupId(), req.getAppId()); + if (!groupResp.isOk()) { + return groupResp; + } + + for (GroupMemberDto memberId : + req.getMembers()) { + ResponseVO responseVO = null; + try { + responseVO = groupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), memberId); + } catch (Exception e) { + e.printStackTrace(); + responseVO = ResponseVO.errorResponse(); + } + AddMemberResp addMemberResp = new AddMemberResp(); + addMemberResp.setMemberId(memberId.getMemberId()); + if (responseVO.isOk()) { + addMemberResp.setResult(0); + } else if (responseVO.getCode() == GroupErrorCode.USER_IS_JOINED_GROUP.getCode()) { + addMemberResp.setResult(2); + } else { + addMemberResp.setResult(1); + } + resp.add(addMemberResp); + } + + return ResponseVO.successResponse(resp); + } + + /** + * @param + * @return com.lld.im.common.ResponseVO + * @description: 添加群成员,内部调用 + * @author lld + */ + @Override + @Transactional + public ResponseVO addGroupMember(String groupId, Integer appId, GroupMemberDto dto) { + + ResponseVO singleUserInfo = imUserService.getSingleUserInfo(dto.getMemberId(), appId); + if (!singleUserInfo.isOk()) { + return singleUserInfo; + } + + if (dto.getRole() != null && GroupMemberRoleEnum.OWNER.getCode() == dto.getRole()) { + QueryWrapper queryOwner = new QueryWrapper<>(); + queryOwner.eq("group_id", groupId); + queryOwner.eq("app_id", appId); + queryOwner.eq("role", GroupMemberRoleEnum.OWNER.getCode()); + Integer ownerNum = imGroupMemberMapper.selectCount(queryOwner); + if (ownerNum > 0) { + return ResponseVO.errorResponse(GroupErrorCode.GROUP_IS_HAVE_OWNER); + } + } + + QueryWrapper query = new QueryWrapper<>(); + query.eq("group_id", groupId); + query.eq("app_id", appId); + query.eq("member_id", dto.getMemberId()); + ImGroupMemberEntity memberDto = imGroupMemberMapper.selectOne(query); + + long now = System.currentTimeMillis(); + if (memberDto == null) { + //初次加群 + memberDto = new ImGroupMemberEntity(); + BeanUtils.copyProperties(dto, memberDto); + memberDto.setGroupId(groupId); + memberDto.setAppId(appId); + memberDto.setJoinTime(now); + int insert = imGroupMemberMapper.insert(memberDto); + if (insert == 1) { + return ResponseVO.successResponse(); + } + return ResponseVO.errorResponse(GroupErrorCode.USER_JOIN_GROUP_ERROR); + } else if (GroupMemberRoleEnum.LEAVE.getCode() == memberDto.getRole()) { + //重新进群 + memberDto = new ImGroupMemberEntity(); + BeanUtils.copyProperties(dto, memberDto); + memberDto.setJoinTime(now); + int update = imGroupMemberMapper.update(memberDto, query); + if (update == 1) { + return ResponseVO.successResponse(); + } + return ResponseVO.errorResponse(GroupErrorCode.USER_JOIN_GROUP_ERROR); + } + + return ResponseVO.errorResponse(GroupErrorCode.USER_IS_JOINED_GROUP); + + } + + /** + * @param + * @return com.lld.im.common.ResponseVO + * @description: 删除群成员,内部调用 + * @author lld + */ + @Override + public ResponseVO removeGroupMember(String groupId, Integer appId, String memberId) { + + ResponseVO singleUserInfo = imUserService.getSingleUserInfo(memberId, appId); + if (!singleUserInfo.isOk()) { + return singleUserInfo; + } + + ResponseVO roleInGroupOne = getRoleInGroupOne(groupId, memberId, appId); + if (!roleInGroupOne.isOk()) { + return roleInGroupOne; + } + + GetRoleInGroupResp data = roleInGroupOne.getData(); + ImGroupMemberEntity imGroupMemberEntity = new ImGroupMemberEntity(); + imGroupMemberEntity.setRole(GroupMemberRoleEnum.LEAVE.getCode()); + imGroupMemberEntity.setLeaveTime(System.currentTimeMillis()); + imGroupMemberEntity.setGroupMemberId(data.getGroupMemberId()); + imGroupMemberMapper.updateById(imGroupMemberEntity); + return ResponseVO.successResponse(); + } + + /** + * @param [groupId, memberId, appId] + * @return com.lld.im.common.ResponseVO + * @description 查询用户在群内的角色 + * @author chackylee + */ + @Override + public ResponseVO getRoleInGroupOne(String groupId, String memberId, Integer appId) { + + GetRoleInGroupResp resp = new GetRoleInGroupResp(); + + QueryWrapper queryOwner = new QueryWrapper<>(); + queryOwner.eq("group_id", groupId); + queryOwner.eq("app_id", appId); + queryOwner.eq("member_id", memberId); + + ImGroupMemberEntity imGroupMemberEntity = imGroupMemberMapper.selectOne(queryOwner); + if (imGroupMemberEntity == null || imGroupMemberEntity.getRole() == GroupMemberRoleEnum.LEAVE.getCode()) { + return ResponseVO.errorResponse(GroupErrorCode.MEMBER_IS_NOT_JOINED_GROUP); + } + + resp.setSpeakDate(imGroupMemberEntity.getSpeakDate()); + resp.setGroupMemberId(imGroupMemberEntity.getGroupMemberId()); + resp.setMemberId(imGroupMemberEntity.getMemberId()); + resp.setRole(imGroupMemberEntity.getRole()); + return ResponseVO.successResponse(resp); + } + + @Override + public ResponseVO> getMemberJoinedGroup(GetJoinedGroupReq req) { + + if (req.getLimit() != null) { + Page objectPage = new Page<>(req.getOffset(), req.getLimit()); + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id", req.getAppId()); + query.eq("member_id", req.getMemberId()); + IPage imGroupMemberEntityPage = imGroupMemberMapper.selectPage(objectPage, query); + + Set groupId = new HashSet<>(); + List records = imGroupMemberEntityPage.getRecords(); + records.forEach(e -> { + groupId.add(e.getGroupId()); + }); + + return ResponseVO.successResponse(groupId); + } else { + return ResponseVO.successResponse(imGroupMemberMapper.getJoinedGroupId(req.getAppId(), req.getMemberId())); + } + } + + /** + * @param + * @return com.lld.im.common.ResponseVO + * @description: 添加群成员,拉人入群的逻辑,直接进入群聊。如果是后台管理员,则直接拉入群, + * 否则只有私有群可以调用本接口,并且群成员也可以拉人入群.只有私有群可以调用本接口 + * @author lld + */ + @Override + public ResponseVO addMember(AddGroupMemberReq req) { + + List resp = new ArrayList<>(); + + boolean isAdmin = false; + ResponseVO groupResp = groupService.getGroup(req.getGroupId(), req.getAppId()); + if (!groupResp.isOk()) { + return groupResp; + } + + List memberDtos = req.getMembers(); + + ImGroupEntity group = groupResp.getData(); + + /** + * 私有群(private) 类似普通微信群,创建后仅支持已在群内的好友邀请加群,且无需被邀请方同意或群主审批 + * 公开群(Public) 类似 QQ 群,创建后群主可以指定群管理员,需要群主或管理员审批通过才能入群 + * 群类型 1私有群(类似微信) 2公开群(类似qq) + */ + if (!isAdmin && GroupTypeEnum.PUBLIC.getCode() == group.getGroupType()) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_APPMANAGER_ROLE); + } + + List successId = new ArrayList<>(); + for (GroupMemberDto memberId : + memberDtos) { + ResponseVO responseVO = null; + try { + responseVO = groupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), memberId); + } catch (Exception e) { + e.printStackTrace(); + responseVO = ResponseVO.errorResponse(); + } + AddMemberResp addMemberResp = new AddMemberResp(); + addMemberResp.setMemberId(memberId.getMemberId()); + if (responseVO.isOk()) { + successId.add(memberId.getMemberId()); + addMemberResp.setResult(0); + } else if (responseVO.getCode() == GroupErrorCode.USER_IS_JOINED_GROUP.getCode()) { + addMemberResp.setResult(2); + addMemberResp.setResultMessage(responseVO.getMsg()); + } else { + addMemberResp.setResult(1); + addMemberResp.setResultMessage(responseVO.getMsg()); + } + resp.add(addMemberResp); + } + + return ResponseVO.successResponse(resp); + } + + @Override + public ResponseVO removeMember(RemoveGroupMemberReq req) { + + boolean isAdmin = false; + ResponseVO groupResp = groupService.getGroup(req.getGroupId(), req.getAppId()); + if (!groupResp.isOk()) { + return groupResp; + } + + ImGroupEntity group = groupResp.getData(); + + if (!isAdmin) { + if (GroupTypeEnum.PUBLIC.getCode() == group.getGroupType()) { + + //获取操作人的权限 是管理员or群主or群成员 + ResponseVO role = getRoleInGroupOne(req.getGroupId(), req.getOperater(), req.getAppId()); + if (!role.isOk()) { + return role; + } + + GetRoleInGroupResp data = role.getData(); + Integer roleInfo = data.getRole(); + + boolean isOwner = roleInfo == GroupMemberRoleEnum.OWNER.getCode(); + boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode(); + + if (!isOwner && !isManager) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE); + } + + //私有群必须是群主才能踢人 + if (!isOwner && GroupTypeEnum.PRIVATE.getCode() == group.getGroupType()) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE); + } + + //公开群管理员和群主可踢人,但管理员只能踢普通群成员 + if (GroupTypeEnum.PUBLIC.getCode() == group.getGroupType()) { +// throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE); + //获取被踢人的权限 + ResponseVO roleInGroupOne = this.getRoleInGroupOne(req.getGroupId(), req.getMemberId(), req.getAppId()); + if (!roleInGroupOne.isOk()) { + return roleInGroupOne; + } + GetRoleInGroupResp memberRole = roleInGroupOne.getData(); + if (memberRole.getRole() == GroupMemberRoleEnum.OWNER.getCode()) { + throw new ApplicationException(GroupErrorCode.GROUP_OWNER_IS_NOT_REMOVE); + } + //是管理员并且被踢人不是群成员,无法操作 + if (isManager && memberRole.getRole() != GroupMemberRoleEnum.ORDINARY.getCode()) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE); + } + } + } + } + ResponseVO responseVO = groupMemberService.removeGroupMember(req.getGroupId(), req.getAppId(), req.getMemberId()); + return responseVO; + } + + @Override + public ResponseVO> getGroupMember(String groupId, Integer appId) { + List groupMember = imGroupMemberMapper.getGroupMember(appId, groupId); + return ResponseVO.successResponse(groupMember); + } + + @Override + public List getGroupMemberId(String groupId, Integer appId) { + return imGroupMemberMapper.getGroupMemberId(appId, groupId); + } + + @Override + public List getGroupManager(String groupId, Integer appId) { + return imGroupMemberMapper.getGroupManager(groupId, appId); + } + + @Override + public ResponseVO updateGroupMember(UpdateGroupMemberReq req) { + + boolean isadmin = false; + + ResponseVO group = groupService.getGroup(req.getGroupId(), req.getAppId()); + if (!group.isOk()) { + return group; + } + + ImGroupEntity groupData = group.getData(); + if (groupData.getStatus() == GroupStatusEnum.DESTROY.getCode()) { + throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY); + } + + //是否是自己修改自己的资料 + boolean isMeOperate = req.getOperater().equals(req.getMemberId()); + + if (!isadmin) { + //昵称只能自己修改 权限只能群主或管理员修改 + if (StringUtils.isBlank(req.getAlias()) && !isMeOperate) { + return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_ONESELF); + } + + //如果要修改权限相关的则走下面的逻辑 + if (req.getRole() != null) { + //私有群不能设置管理员 + if (groupData.getGroupType() == GroupTypeEnum.PRIVATE.getCode() && + req.getRole() != null && (req.getRole() == GroupMemberRoleEnum.MAMAGER.getCode() || + req.getRole() == GroupMemberRoleEnum.OWNER.getCode())) { + return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_APPMANAGER_ROLE); + } + + //获取被操作人的是否在群内 + ResponseVO roleInGroupOne = this.getRoleInGroupOne(req.getGroupId(), req.getMemberId(), req.getAppId()); + if (!roleInGroupOne.isOk()) { + return roleInGroupOne; + } + + //获取操作人权限 + ResponseVO operateRoleInGroupOne = this.getRoleInGroupOne(req.getGroupId(), req.getOperater(), req.getAppId()); + if (!operateRoleInGroupOne.isOk()) { + return operateRoleInGroupOne; + } + + GetRoleInGroupResp data = operateRoleInGroupOne.getData(); + Integer roleInfo = data.getRole(); + boolean isOwner = roleInfo == GroupMemberRoleEnum.OWNER.getCode(); + boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode(); + + //不是管理员不能修改权限 + if (req.getRole() != null && !isOwner && !isManager) { + return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE); + } + + //管理员只有群主能够设置 + if (req.getRole() != null && req.getRole() == GroupMemberRoleEnum.MAMAGER.getCode() && !isOwner) { + return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE); + } + + } + } + + ImGroupMemberEntity update = new ImGroupMemberEntity(); + + if (StringUtils.isNotBlank(req.getAlias())) { + update.setAlias(req.getAlias()); + } + + //不能直接修改为群主 + if(req.getRole() != null && req.getRole() != GroupMemberRoleEnum.OWNER.getCode()){ + update.setRole(req.getRole()); + } + + UpdateWrapper objectUpdateWrapper = new UpdateWrapper<>(); + objectUpdateWrapper.eq("app_id", req.getAppId()); + objectUpdateWrapper.eq("member_id", req.getMemberId()); + objectUpdateWrapper.eq("group_id", req.getGroupId()); + imGroupMemberMapper.update(update, objectUpdateWrapper); + + return ResponseVO.successResponse(); + } + + @Override + @Transactional + public ResponseVO transferGroupMember(String owner, String groupId, Integer appId) { + + //更新旧群主 + ImGroupMemberEntity imGroupMemberEntity = new ImGroupMemberEntity(); + imGroupMemberEntity.setRole(GroupMemberRoleEnum.ORDINARY.getCode()); + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("app_id", appId); + updateWrapper.eq("group_id", groupId); + updateWrapper.eq("role", GroupMemberRoleEnum.OWNER.getCode()); + imGroupMemberMapper.update(imGroupMemberEntity, updateWrapper); + + //更新新群主 + ImGroupMemberEntity newOwner = new ImGroupMemberEntity(); + newOwner.setRole(GroupMemberRoleEnum.OWNER.getCode()); + UpdateWrapper ownerWrapper = new UpdateWrapper<>(); + ownerWrapper.eq("app_id", appId); + ownerWrapper.eq("group_id", groupId); + ownerWrapper.eq("member_id", owner); + imGroupMemberMapper.update(newOwner, ownerWrapper); + + return ResponseVO.successResponse(); + } + + @Override + public ResponseVO speak(SpeaMemberReq req) { + + ResponseVO groupResp = groupService.getGroup(req.getGroupId(), req.getAppId()); + if (!groupResp.isOk()) { + return groupResp; + } + + boolean isadmin = false; + boolean isOwner = false; + boolean isManager = false; + GetRoleInGroupResp memberRole = null; + + if (!isadmin) { + + //获取操作人的权限 是管理员or群主or群成员 + ResponseVO role = getRoleInGroupOne(req.getGroupId(), req.getOperater(), req.getAppId()); + if (!role.isOk()) { + return role; + } + + GetRoleInGroupResp data = role.getData(); + Integer roleInfo = data.getRole(); + + isOwner = roleInfo == GroupMemberRoleEnum.OWNER.getCode(); + isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode(); + + if (!isOwner && !isManager) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE); + } + + //获取被操作的权限 + ResponseVO roleInGroupOne = this.getRoleInGroupOne(req.getGroupId(), req.getMemberId(), req.getAppId()); + if (!roleInGroupOne.isOk()) { + return roleInGroupOne; + } + memberRole = roleInGroupOne.getData(); + //被操作人是群主只能app管理员操作 + if (memberRole.getRole() == GroupMemberRoleEnum.OWNER.getCode()) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_APPMANAGER_ROLE); + } + + //是管理员并且被操作人不是群成员,无法操作 + if (isManager && memberRole.getRole() != GroupMemberRoleEnum.ORDINARY.getCode()) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE); + } + } + + ImGroupMemberEntity imGroupMemberEntity = new ImGroupMemberEntity(); + if (memberRole == null) { + //获取被操作的权限 + ResponseVO roleInGroupOne = this.getRoleInGroupOne(req.getGroupId(), req.getMemberId(), req.getAppId()); + if (!roleInGroupOne.isOk()) { + return roleInGroupOne; + } + memberRole = roleInGroupOne.getData(); + } + + imGroupMemberEntity.setGroupMemberId(memberRole.getGroupMemberId()); + if (req.getSpeakDate() > 0) { + imGroupMemberEntity.setSpeakDate(System.currentTimeMillis() + req.getSpeakDate()); + } else { + imGroupMemberEntity.setSpeakDate(req.getSpeakDate()); + } + + int i = imGroupMemberMapper.updateById(imGroupMemberEntity); + return ResponseVO.successResponse(); + } + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/impl/ImGroupServiceImpl.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/impl/ImGroupServiceImpl.java new file mode 100644 index 0000000..d8760e9 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/group/service/impl/ImGroupServiceImpl.java @@ -0,0 +1,393 @@ +package com.lld.im.service.group.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.lld.im.common.ResponseVO; +import com.lld.im.common.enums.GroupErrorCode; +import com.lld.im.common.enums.GroupMemberRoleEnum; +import com.lld.im.common.enums.GroupStatusEnum; +import com.lld.im.common.enums.GroupTypeEnum; +import com.lld.im.common.exception.ApplicationException; +import com.lld.im.common.model.ClientInfo; +import com.lld.im.service.group.dao.ImGroupEntity; +import com.lld.im.service.group.dao.mapper.ImGroupMapper; +import com.lld.im.service.group.model.req.*; +import com.lld.im.service.group.model.resp.GetGroupResp; +import com.lld.im.service.group.model.resp.GetJoinedGroupResp; +import com.lld.im.service.group.model.resp.GetRoleInGroupResp; +import com.lld.im.service.group.service.ImGroupMemberService; +import com.lld.im.service.group.service.ImGroupService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Service +public class ImGroupServiceImpl implements ImGroupService { + + @Autowired + ImGroupMapper imGroupDataMapper; + + @Autowired + ImGroupMemberService groupMemberService; + + + @Override + public ResponseVO importGroup(ImportGroupReq req) { + + //1.判断群id是否存在 + QueryWrapper query = new QueryWrapper<>(); + + if (StringUtils.isEmpty(req.getGroupId())) { + req.setGroupId(UUID.randomUUID().toString().replace("-", "")); + } else { + query.eq("group_id", req.getGroupId()); + query.eq("app_id", req.getAppId()); + Integer integer = imGroupDataMapper.selectCount(query); + if (integer > 0) { + throw new ApplicationException(GroupErrorCode.GROUP_IS_EXIST); + } + } + + ImGroupEntity imGroupEntity = new ImGroupEntity(); + + if (req.getGroupType() == GroupTypeEnum.PUBLIC.getCode() && StringUtils.isBlank(req.getOwnerId())) { + throw new ApplicationException(GroupErrorCode.PUBLIC_GROUP_MUST_HAVE_OWNER); + } + + if (req.getCreateTime() == null) { + imGroupEntity.setCreateTime(System.currentTimeMillis()); + } + imGroupEntity.setStatus(GroupStatusEnum.NORMAL.getCode()); + BeanUtils.copyProperties(req, imGroupEntity); + int insert = imGroupDataMapper.insert(imGroupEntity); + + if (insert != 1) { + throw new ApplicationException(GroupErrorCode.IMPORT_GROUP_ERROR); + } + + return ResponseVO.successResponse(); + } + + @Override + @Transactional + public ResponseVO createGroup(CreateGroupReq req) { + + boolean isAdmin = false; + + if (!isAdmin) { + req.setOwnerId(req.getOperater()); + } + + //1.判断群id是否存在 + QueryWrapper query = new QueryWrapper<>(); + + if (StringUtils.isEmpty(req.getGroupId())) { + req.setGroupId(UUID.randomUUID().toString().replace("-", "")); + } else { + query.eq("group_id", req.getGroupId()); + query.eq("app_id", req.getAppId()); + Integer integer = imGroupDataMapper.selectCount(query); + if (integer > 0) { + throw new ApplicationException(GroupErrorCode.GROUP_IS_EXIST); + } + } + + if (req.getGroupType() == GroupTypeEnum.PUBLIC.getCode() && StringUtils.isBlank(req.getOwnerId())) { + throw new ApplicationException(GroupErrorCode.PUBLIC_GROUP_MUST_HAVE_OWNER); + } + + ImGroupEntity imGroupEntity = new ImGroupEntity(); + imGroupEntity.setCreateTime(System.currentTimeMillis()); + imGroupEntity.setStatus(GroupStatusEnum.NORMAL.getCode()); + BeanUtils.copyProperties(req, imGroupEntity); + int insert = imGroupDataMapper.insert(imGroupEntity); + + GroupMemberDto groupMemberDto = new GroupMemberDto(); + groupMemberDto.setMemberId(req.getOwnerId()); + groupMemberDto.setRole(GroupMemberRoleEnum.OWNER.getCode()); + groupMemberDto.setJoinTime(System.currentTimeMillis()); + groupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), groupMemberDto); + + //插入群成员 + for (GroupMemberDto dto : req.getMember()) { + groupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), dto); + } + + return ResponseVO.successResponse(); + } + + /** + * @param [req] + * @return com.lld.im.common.ResponseVO + * @description 修改群基础信息,如果是后台管理员调用,则不检查权限,如果不是则检查权限,如果是私有群(微信群)任何人都可以修改资料,公开群只有管理员可以修改 + * 如果是群主或者管理员可以修改其他信息。 + * @author chackylee + */ + @Override + @Transactional + public ResponseVO updateBaseGroupInfo(UpdateGroupReq req) { + + //1.判断群id是否存在 + QueryWrapper query = new QueryWrapper<>(); + query.eq("group_id", req.getGroupId()); + query.eq("app_id", req.getAppId()); + ImGroupEntity imGroupEntity = imGroupDataMapper.selectOne(query); + if (imGroupEntity == null) { + throw new ApplicationException(GroupErrorCode.GROUP_IS_EXIST); + } + + if(imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()){ + throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY); + } + + boolean isAdmin = false; + + if (!isAdmin) { + //不是后台调用需要检查权限 + ResponseVO role = groupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOperater(), req.getAppId()); + + if (!role.isOk()) { + return role; + } + + GetRoleInGroupResp data = role.getData(); + Integer roleInfo = data.getRole(); + + boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode() || roleInfo == GroupMemberRoleEnum.OWNER.getCode(); + + //公开群只能群主修改资料 + if (!isManager && GroupTypeEnum.PUBLIC.getCode() == imGroupEntity.getGroupType()) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE); + } + + } + + ImGroupEntity update = new ImGroupEntity(); + BeanUtils.copyProperties(req, update); + update.setUpdateTime(System.currentTimeMillis()); + int row = imGroupDataMapper.update(update, query); + if (row != 1) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE); + } + + return ResponseVO.successResponse(); + } + + /** + * @param [req] + * @return com.lld.im.common.ResponseVO + * @description 获取用户加入的群组 + * @author chackylee + */ + @Override + public ResponseVO getJoinedGroup(GetJoinedGroupReq req) { + + ResponseVO> memberJoinedGroup = groupMemberService.getMemberJoinedGroup(req); + if (memberJoinedGroup.isOk()) { + + GetJoinedGroupResp resp = new GetJoinedGroupResp(); + + if (CollectionUtils.isEmpty(memberJoinedGroup.getData())) { + resp.setTotalCount(0); + resp.setGroupList(new ArrayList<>()); + return ResponseVO.successResponse(resp); + } + + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id", req.getAppId()); + query.in("group_id", memberJoinedGroup.getData()); + + if (CollectionUtils.isNotEmpty(req.getGroupType())) { + query.in("group_type", req.getGroupType()); + } + + List groupList = imGroupDataMapper.selectList(query); + resp.setGroupList(groupList); + if (req.getLimit() == null) { + resp.setTotalCount(groupList.size()); + } else { + resp.setTotalCount(imGroupDataMapper.selectCount(query)); + } + return ResponseVO.successResponse(resp); + } else { + return memberJoinedGroup; + } + } + + + /** + * @param [req] + * @return com.lld.im.common.ResponseVO + * @description 解散群组,只支持后台管理员和群主解散 + * @author chackylee + */ + @Override + @Transactional + public ResponseVO destroyGroup(DestroyGroupReq req) { + + boolean isAdmin = false; + + QueryWrapper objectQueryWrapper = new QueryWrapper<>(); + objectQueryWrapper.eq("group_id", req.getGroupId()); + objectQueryWrapper.eq("app_id", req.getAppId()); + ImGroupEntity imGroupEntity = imGroupDataMapper.selectOne(objectQueryWrapper); + if (imGroupEntity == null) { + throw new ApplicationException(GroupErrorCode.PRIVATE_GROUP_CAN_NOT_DESTORY); + } + + if(imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()){ + throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY); + } + + if (!isAdmin) { + if (imGroupEntity.getGroupType() == GroupTypeEnum.PUBLIC.getCode()) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE); + } + + if (imGroupEntity.getGroupType() == GroupTypeEnum.PUBLIC.getCode() && + !imGroupEntity.getOwnerId().equals(req.getOperater())) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE); + } + } + + ImGroupEntity update = new ImGroupEntity(); + + update.setStatus(GroupStatusEnum.DESTROY.getCode()); + int update1 = imGroupDataMapper.update(update, objectQueryWrapper); + if (update1 != 1) { + throw new ApplicationException(GroupErrorCode.UPDATE_GROUP_BASE_INFO_ERROR); + } + return ResponseVO.successResponse(); + } + + @Override + @Transactional + public ResponseVO transferGroup(TransferGroupReq req) { + + ResponseVO roleInGroupOne = groupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOperater(), req.getAppId()); + if (!roleInGroupOne.isOk()) { + return roleInGroupOne; + } + + if (roleInGroupOne.getData().getRole() != GroupMemberRoleEnum.OWNER.getCode()) { + return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE); + } + + ResponseVO newOwnerRole = groupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOwnerId(), req.getAppId()); + if (!newOwnerRole.isOk()) { + return newOwnerRole; + } + + QueryWrapper objectQueryWrapper = new QueryWrapper<>(); + objectQueryWrapper.eq("group_id", req.getGroupId()); + objectQueryWrapper.eq("app_id", req.getAppId()); + ImGroupEntity imGroupEntity = imGroupDataMapper.selectOne(objectQueryWrapper); + if(imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()){ + throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY); + } + + ImGroupEntity updateGroup = new ImGroupEntity(); + updateGroup.setOwnerId(req.getOwnerId()); + UpdateWrapper updateGroupWrapper = new UpdateWrapper<>(); + updateGroupWrapper.eq("app_id", req.getAppId()); + updateGroupWrapper.eq("group_id", req.getGroupId()); + imGroupDataMapper.update(updateGroup, updateGroupWrapper); + groupMemberService.transferGroupMember(req.getOwnerId(), req.getGroupId(), req.getAppId()); + + return ResponseVO.successResponse(); + } + + @Override + public ResponseVO getGroup(String groupId, Integer appId) { + + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id", appId); + query.eq("group_id", groupId); + ImGroupEntity imGroupEntity = imGroupDataMapper.selectOne(query); + + if (imGroupEntity == null) { + return ResponseVO.errorResponse(GroupErrorCode.GROUP_IS_NOT_EXIST); + } + return ResponseVO.successResponse(imGroupEntity); + } + + @Override + public ResponseVO getGroup(GetGroupReq req) { + + ResponseVO group = this.getGroup(req.getGroupId(), req.getAppId()); + + if(!group.isOk()){ + return group; + } + + GetGroupResp getGroupResp = new GetGroupResp(); + BeanUtils.copyProperties(group.getData(), getGroupResp); + try { + ResponseVO> groupMember = groupMemberService.getGroupMember(req.getGroupId(), req.getAppId()); + if (groupMember.isOk()) { + getGroupResp.setMemberList(groupMember.getData()); + } + } catch (Exception e) { + e.printStackTrace(); + } + return ResponseVO.successResponse(getGroupResp); + } + + @Override + public ResponseVO muteGroup(MuteGroupReq req) { + + ResponseVO groupResp = getGroup(req.getGroupId(), req.getAppId()); + if (!groupResp.isOk()) { + return groupResp; + } + + if(groupResp.getData().getStatus() == GroupStatusEnum.DESTROY.getCode()){ + throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY); + } + + boolean isadmin = false; + + if (!isadmin) { + //不是后台调用需要检查权限 + ResponseVO role = groupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOperater(), req.getAppId()); + + if (!role.isOk()) { + return role; + } + + GetRoleInGroupResp data = role.getData(); + Integer roleInfo = data.getRole(); + + boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode() || roleInfo == GroupMemberRoleEnum.OWNER.getCode(); + + //公开群只能群主修改资料 + if (!isManager) { + throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE); + } + } + + ImGroupEntity update = new ImGroupEntity(); + update.setMute(req.getMute()); + + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.eq("group_id",req.getGroupId()); + wrapper.eq("app_id",req.getAppId()); + imGroupDataMapper.update(update,wrapper); + + return ResponseVO.successResponse(); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/controller/ImUserController.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/controller/ImUserController.java new file mode 100644 index 0000000..b91428f --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/controller/ImUserController.java @@ -0,0 +1,35 @@ +package com.lld.im.service.user.controller; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.user.model.req.*; +import com.lld.im.service.user.service.ImUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@RestController +@RequestMapping("v1/user") +public class ImUserController { + @Autowired + ImUserService imUserService; + + @RequestMapping("importUser") + public ResponseVO importUser(@RequestBody ImportUserReq req, Integer appId) { + return imUserService.importUser(req); + } + + @RequestMapping("/deleteUser") + public ResponseVO deleteUser(@RequestBody @Validated DeleteUserReq req, Integer appId) { + req.setAppId(appId); + return imUserService.deleteUser(req); + } + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/controller/ImUserDataController.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/controller/ImUserDataController.java new file mode 100644 index 0000000..1999b0f --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/controller/ImUserDataController.java @@ -0,0 +1,47 @@ +package com.lld.im.service.user.controller; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.user.model.req.GetUserInfoReq; +import com.lld.im.service.user.model.req.ModifyUserInfoReq; +import com.lld.im.service.user.model.req.UserId; +import com.lld.im.service.user.service.ImUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@RestController +@RequestMapping("v1/user/data") +public class ImUserDataController { + + private static Logger logger = LoggerFactory.getLogger(ImUserDataController.class); + + @Autowired + ImUserService imUserService; + + @RequestMapping("/getUserInfo") + public ResponseVO getUserInfo(@RequestBody GetUserInfoReq req, Integer appId){//@Validated + req.setAppId(appId); + return imUserService.getUserInfo(req); + } + + @RequestMapping("/getSingleUserInfo") + public ResponseVO getSingleUserInfo(@RequestBody @Validated UserId req, Integer appId){ + req.setAppId(appId); + return imUserService.getSingleUserInfo(req.getUserId(),req.getAppId()); + } + + @RequestMapping("/modifyUserInfo") + public ResponseVO modifyUserInfo(@RequestBody @Validated ModifyUserInfoReq req, Integer appId){ + req.setAppId(appId); + return imUserService.modifyUserInfo(req); + } +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/dao/ImUserDataEntity.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/dao/ImUserDataEntity.java new file mode 100644 index 0000000..e9afb2b --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/dao/ImUserDataEntity.java @@ -0,0 +1,60 @@ +package com.lld.im.service.user.dao; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @author: Chackylee + * @description: 数据库用户数据实体类 + **/ + +@Data +@TableName("im_user_data") +public class ImUserDataEntity { + + // 用户id + private String userId; + + // 用户名称 + private String nickName; + + //位置 + private String location; + + //生日 + private String birthDay; + + private String password; + + // 头像 + private String photo; + + // 性别 + private Integer userSex; + + // 个性签名 + private String selfSignature; + + // 加好友验证类型(Friend_AllowType) 1需要验证 + private Integer friendAllowType; + + // 管理员禁止用户添加加好友:0 未禁用 1 已禁用 + private Integer disableAddFriend; + + // 禁用标识(0 未禁用 1 已禁用) + private Integer forbiddenFlag; + + // 禁言标识 + private Integer silentFlag; + /** + * 用户类型 1普通用户 2客服 3机器人 + */ + private Integer userType; + + private Integer appId; + + private Integer delFlag; + + private String extra; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/dao/mapper/ImUserDataMapper.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/dao/mapper/ImUserDataMapper.java new file mode 100644 index 0000000..b21552b --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/dao/mapper/ImUserDataMapper.java @@ -0,0 +1,9 @@ +package com.lld.im.service.user.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.lld.im.service.user.dao.ImUserDataEntity; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ImUserDataMapper extends BaseMapper { +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/UserStatusChangeNotifyContent.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/UserStatusChangeNotifyContent.java new file mode 100644 index 0000000..40e23b4 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/UserStatusChangeNotifyContent.java @@ -0,0 +1,22 @@ +package com.lld.im.service.user.model; + +import com.lld.im.common.model.ClientInfo; +import lombok.Data; + +/** + * @description: status区分是上线还是下线 + * @author: lld + * @version: 1.0 + */ +@Data +public class UserStatusChangeNotifyContent extends ClientInfo { + + + private String userId; + + //服务端状态 1上线 2离线 + private Integer status; + + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/DeleteUserReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/DeleteUserReq.java new file mode 100644 index 0000000..82f3169 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/DeleteUserReq.java @@ -0,0 +1,15 @@ +package com.lld.im.service.user.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.List; + + +@Data +public class DeleteUserReq extends RequestBase { + + @NotEmpty(message = "用户id不能为空") + private List userId; +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/GetUserInfoReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/GetUserInfoReq.java new file mode 100644 index 0000000..0c1c9c4 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/GetUserInfoReq.java @@ -0,0 +1,15 @@ +package com.lld.im.service.user.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import java.util.List; + + +@Data +public class GetUserInfoReq extends RequestBase { + + private List userIds; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/GetUserSequenceReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/GetUserSequenceReq.java new file mode 100644 index 0000000..2984d9c --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/GetUserSequenceReq.java @@ -0,0 +1,16 @@ +package com.lld.im.service.user.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class GetUserSequenceReq extends RequestBase { + + private String userId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/ImportUserReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/ImportUserReq.java new file mode 100644 index 0000000..3ae365c --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/ImportUserReq.java @@ -0,0 +1,16 @@ +package com.lld.im.service.user.model.req; + +import com.lld.im.common.model.RequestBase; +import com.lld.im.service.user.dao.ImUserDataEntity; +import lombok.Data; + +import java.util.List; + + +@Data +public class ImportUserReq extends RequestBase { + + private List userData; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/ModifyUserInfoReq.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/ModifyUserInfoReq.java new file mode 100644 index 0000000..3132523 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/ModifyUserInfoReq.java @@ -0,0 +1,46 @@ +package com.lld.im.service.user.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class ModifyUserInfoReq extends RequestBase { + + // 用户id + @NotEmpty(message = "用户id不能为空") + private String userId; + + // 用户名称 + private String nickName; + + //位置 + private String location; + + //生日 + private String birthDay; + + private String password; + + // 头像 + private String photo; + + // 性别 + private String userSex; + + // 个性签名 + private String selfSignature; + + // 加好友验证类型(Friend_AllowType) 1需要验证 + private Integer friendAllowType; + + private String extra; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/UserId.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/UserId.java new file mode 100644 index 0000000..3856261 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/req/UserId.java @@ -0,0 +1,15 @@ +package com.lld.im.service.user.model.req; + +import com.lld.im.common.model.RequestBase; +import lombok.Data; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class UserId extends RequestBase { + + private String userId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/GetUserInfoResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/GetUserInfoResp.java new file mode 100644 index 0000000..742618c --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/GetUserInfoResp.java @@ -0,0 +1,20 @@ +package com.lld.im.service.user.model.resp; + +import com.lld.im.service.user.dao.ImUserDataEntity; +import lombok.Data; + +import java.util.List; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class GetUserInfoResp { + + private List userDataItem; + + private List failUser; + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/ImportUserResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/ImportUserResp.java new file mode 100644 index 0000000..2440a67 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/ImportUserResp.java @@ -0,0 +1,18 @@ +package com.lld.im.service.user.model.resp; + +import lombok.Data; + +import java.util.List; + +/** + * @author: Chackylee + * @description: + **/ +@Data +public class ImportUserResp { + + private List successId; + + private List errorId; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/UserOnlineStatusResp.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/UserOnlineStatusResp.java new file mode 100644 index 0000000..60f48a0 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/model/resp/UserOnlineStatusResp.java @@ -0,0 +1,22 @@ +package com.lld.im.service.user.model.resp; + +import com.lld.im.common.model.UserSession; +import lombok.Data; + +import java.util.List; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Data +public class UserOnlineStatusResp { + + private List session; + + private String customText; + + private Integer customStatus; + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/service/ImUserService.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/service/ImUserService.java new file mode 100644 index 0000000..81b61f9 --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/service/ImUserService.java @@ -0,0 +1,26 @@ +package com.lld.im.service.user.service; + +import com.lld.im.common.ResponseVO; +import com.lld.im.service.user.dao.ImUserDataEntity; +import com.lld.im.service.user.model.req.*; +import com.lld.im.service.user.model.resp.GetUserInfoResp; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +public interface ImUserService { + + public ResponseVO importUser(ImportUserReq req); + + public ResponseVO getUserInfo(GetUserInfoReq req); + + public ResponseVO getSingleUserInfo(String userId , Integer appId); + + public ResponseVO deleteUser(DeleteUserReq req); + + public ResponseVO modifyUserInfo(ModifyUserInfoReq req); + + +} diff --git a/hs-im-server/im-service/src/main/java/com/lld/im/service/user/service/impl/ImUserviceImpl.java b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/service/impl/ImUserviceImpl.java new file mode 100644 index 0000000..ef590bd --- /dev/null +++ b/hs-im-server/im-service/src/main/java/com/lld/im/service/user/service/impl/ImUserviceImpl.java @@ -0,0 +1,173 @@ +package com.lld.im.service.user.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.lld.im.common.ResponseVO; +import com.lld.im.common.enums.DelFlagEnum; +import com.lld.im.common.enums.UserErrorCode; +import com.lld.im.common.exception.ApplicationException; +import com.lld.im.service.group.service.ImGroupService; +import com.lld.im.service.user.dao.ImUserDataEntity; +import com.lld.im.service.user.dao.mapper.ImUserDataMapper; +import com.lld.im.service.user.model.req.*; +import com.lld.im.service.user.model.resp.GetUserInfoResp; +import com.lld.im.service.user.model.resp.ImportUserResp; +import com.lld.im.service.user.service.ImUserService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @description: + * @author: lld + * @version: 1.0 + */ +@Service +public class ImUserviceImpl implements ImUserService { + + @Autowired + ImUserDataMapper imUserDataMapper; + + + @Autowired + ImGroupService imGroupService; + + @Override + public ResponseVO importUser(ImportUserReq req) { + + if(req.getUserData().size() > 100){ + return ResponseVO.errorResponse(UserErrorCode.IMPORT_SIZE_BEYOND); + } + + ImportUserResp resp = new ImportUserResp(); + List successId = new ArrayList<>(); + List errorId = new ArrayList<>(); + + for (ImUserDataEntity data: + req.getUserData()) { + try { + data.setAppId(req.getAppId()); + int insert = imUserDataMapper.insert(data); + if(insert == 1){ + successId.add(data.getUserId()); + } + }catch (Exception e){ + e.printStackTrace(); + errorId.add(data.getUserId()); + } + } + + resp.setErrorId(errorId); + resp.setSuccessId(successId); + return ResponseVO.successResponse(resp); + } + + @Override + public ResponseVO getUserInfo(GetUserInfoReq req) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("app_id",req.getAppId()); + queryWrapper.in("user_id",req.getUserIds()); + queryWrapper.eq("del_flag", DelFlagEnum.NORMAL.getCode()); + + List userDataEntities = imUserDataMapper.selectList(queryWrapper); + HashMap map = new HashMap<>(); + + for (ImUserDataEntity data: + userDataEntities) { + map.put(data.getUserId(),data); + } + + List failUser = new ArrayList<>(); + for (String uid: + req.getUserIds()) { + if(!map.containsKey(uid)){ + failUser.add(uid); + } + } + + GetUserInfoResp resp = new GetUserInfoResp(); + resp.setUserDataItem(userDataEntities); + resp.setFailUser(failUser); + return ResponseVO.successResponse(resp); + } + + @Override + public ResponseVO getSingleUserInfo(String userId, Integer appId) { + QueryWrapper objectQueryWrapper = new QueryWrapper<>(); + objectQueryWrapper.eq("app_id",appId); + objectQueryWrapper.eq("user_id",userId); + objectQueryWrapper.eq("del_flag", DelFlagEnum.NORMAL.getCode()); + + ImUserDataEntity ImUserDataEntity = imUserDataMapper.selectOne(objectQueryWrapper); + if(ImUserDataEntity == null){ + return ResponseVO.errorResponse(UserErrorCode.USER_IS_NOT_EXIST); + } + + return ResponseVO.successResponse(ImUserDataEntity); + } + + @Override + public ResponseVO deleteUser(DeleteUserReq req) { + ImUserDataEntity entity = new ImUserDataEntity(); + entity.setDelFlag(DelFlagEnum.DELETE.getCode()); + + List errorId = new ArrayList(); + List successId = new ArrayList(); + + for (String userId: + req.getUserId()) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("app_id",req.getAppId()); + wrapper.eq("user_id",userId); + wrapper.eq("del_flag",DelFlagEnum.NORMAL.getCode()); + int update = 0; + + try { + update = imUserDataMapper.update(entity, wrapper); + if(update > 0){ + successId.add(userId); + }else{ + errorId.add(userId); + } + }catch (Exception e){ + errorId.add(userId); + } + } + + ImportUserResp resp = new ImportUserResp(); + resp.setSuccessId(successId); + resp.setErrorId(errorId); + return ResponseVO.successResponse(resp); + } + + @Override + @Transactional + public ResponseVO modifyUserInfo(ModifyUserInfoReq req) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("app_id",req.getAppId()); + query.eq("user_id",req.getUserId()); + query.eq("del_flag",DelFlagEnum.NORMAL.getCode()); + ImUserDataEntity user = imUserDataMapper.selectOne(query); + if(user == null){ + throw new ApplicationException(UserErrorCode.USER_IS_NOT_EXIST); + } + + ImUserDataEntity update = new ImUserDataEntity(); + BeanUtils.copyProperties(req,update); + + update.setAppId(null); + update.setUserId(null); + int update1 = imUserDataMapper.update(update, query); + if(update1 == 1){ + return ResponseVO.successResponse(); + } + throw new ApplicationException(UserErrorCode.MODIFY_USER_ERROR); + } + +} diff --git a/hs-im-server/im-service/src/main/resources/application.yml b/hs-im-server/im-service/src/main/resources/application.yml new file mode 100644 index 0000000..f3230cb --- /dev/null +++ b/hs-im-server/im-service/src/main/resources/application.yml @@ -0,0 +1,38 @@ +spring: + profiles: + active: dev + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + password: beAs0LHX2GyTxMw4 + url: jdbc:mysql://192.168.2.201:3306/im-core?serverTimezone=UTC&useSSL=false&characterEncoding=UTF8 + username: root + + application: + name: im-core + +# logger 配置 +logging: + config: classpath:logback-spring.xml + +server: + port: 8000 + +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + mapper-locations: classpath*:mapper/*.xml + global-config: + db-config: + update-strategy: NOT_EMPTY + +#mybatis: +# configuration: +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +httpclient: + maxTotal: 100 + defaultMaxPerRoute: 50 + connectTimeout: 2000 + connectionRequestTimeout: 2000 + socketTimeout: 5000 + staleConnectionCheckEnabled: true diff --git a/hs-im-server/im-service/src/main/resources/logback-spring.xml b/hs-im-server/im-service/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..b9cd630 --- /dev/null +++ b/hs-im-server/im-service/src/main/resources/logback-spring.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DEBUG + + + ${CONSOLE_LOG_PATTERN} + utf8 + + + + + + + + + + + + + true + + + ${logFile}.%d{yyyy-MM-dd}.log + + + + + %d{yyyy-MM-dd HH:mm:ss} -%msg%n + + + + + + + + + + + + true + + + ${logFile}.%d{yyyy-MM-dd}.log + + + + + %d{yyyy-MM-dd HH:mm:ss} -%msg%n + + + + + + + + + + \ No newline at end of file diff --git a/hs-im-server/pom.xml b/hs-im-server/pom.xml new file mode 100644 index 0000000..136f53c --- /dev/null +++ b/hs-im-server/pom.xml @@ -0,0 +1,216 @@ + + 4.0.0 + + com.lld + im-system + pom + 1.0.0-SNAPSHOT + lld-im + l-im project + + + org.springframework.boot + spring-boot-starter-parent + 2.3.2.RELEASE + + + + + im-service + im-common + + + + 1.8 + 3.4.2 + 5.0.6 + 3.15.6 + 1.27 + 1.0.10 + 0.1 + 4.5.9 + 5.6.0 + 3.12.0 + 1.2.51 + 2.3.2.RELEASE + 4.1.35.Final + 8.18.0 + + + + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-access + + + + + com.alibaba + fastjson + + + + + org.projectlombok + lombok + + + + + + + + + + com.dyuproject.protostuff + protostuff-api + ${protostuff-api} + + + com.dyuproject.protostuff + protostuff-core + ${protostuff-api} + + + com.dyuproject.protostuff + protostuff-runtime + ${protostuff-api} + + + + + + org.apache.commons + commons-lang3 + ${apache.commons.version} + + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + org.springframework.boot + spring-boot-starter-data-redis + ${springboot.version} + + + + + org.springframework.boot + spring-boot-starter-amqp + ${springboot.version} + + + com.rabbitmq + amqp-client + ${rabbit.client.version} + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + + + + + + + org.hibernate + hibernate-validator + ${hibernate-validator.version} + + + + + org.redisson + redisson + ${redisson.version} + + + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + + + cn.hutool + hutool-all + ${hutool.version} + + + + org.apache.httpcomponents + httpclient + ${apache.httpclient.version} + + + + + com.github.sgroschupf + zkclient + ${zkClient.version} + + + + + io.netty + netty-all + ${netty.version} + + + + + com.netflix.feign + feign-core + ${feign.version} + + + com.netflix.feign + feign-jackson + ${feign.version} + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + + \ No newline at end of file diff --git a/media-server-demo-node/.gitignore b/media-server-demo-node/.gitignore new file mode 100644 index 0000000..718cb3e --- /dev/null +++ b/media-server-demo-node/.gitignore @@ -0,0 +1,40 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history +/nbproject/private/ +/recordings/*.mp4 +/recordings/*.pcap diff --git a/media-server-demo-node/.jshintrc b/media-server-demo-node/.jshintrc new file mode 100644 index 0000000..6c4d862 --- /dev/null +++ b/media-server-demo-node/.jshintrc @@ -0,0 +1,4 @@ +{ + "esversion":6, + "esnext": true +} diff --git a/media-server-demo-node/Dockerfile b/media-server-demo-node/Dockerfile new file mode 100644 index 0000000..79f7752 --- /dev/null +++ b/media-server-demo-node/Dockerfile @@ -0,0 +1,16 @@ +# Use the current Long Term Support (LTS) version of Node.js +FROM node:16 + +# Copy the signalling server source code from the build context +COPY ./ /opt/media-server + +# Install the dependencies for the signalling server +WORKDIR /opt/media-server +RUN npm install --registry=https://registry.npmmirror.com + +EXPOSE 28000 + + + +# Set the signalling server as the container's entrypoint +CMD ["sh","/opt/media-server/entrypoint.sh"] diff --git a/media-server-demo-node/LICENSE b/media-server-demo-node/LICENSE new file mode 100644 index 0000000..7b33eea --- /dev/null +++ b/media-server-demo-node/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Sergio Garcia Murillo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/media-server-demo-node/README.md b/media-server-demo-node/README.md new file mode 100644 index 0000000..63894d8 --- /dev/null +++ b/media-server-demo-node/README.md @@ -0,0 +1,36 @@ +# media-server-demo-node +Demo application for the Medooze Media Server for Node.js + +## Intallation +``` +npm install +``` + +## Run +You need to run the demo passing as argument the public IP address of the media server that will be included in the SDP. This IP address is the one facing your clients. +``` +node index.js +``` + +The demo will open an HTPPS/WSS server at port 8000. + +## Demos +### SVC Layer selection + +To run this demo just open `https://ip:8000/svc/` on a Chrome browser and follow instructions. + +### Recording + +To run this demo just open `https://ip:8000/rec/` with Chrome or Firefox. + +### Broadcasting + +To run this demo just open `https://ip:8000/broadcast/` with Chrome or Firefox and follow instructions. + +### Simulcat + +To run this demo just open `https://ip:8000/simulcast/` with Chrome or Firefox and follow instructions. + + + + diff --git a/media-server-demo-node/build.sh b/media-server-demo-node/build.sh new file mode 100644 index 0000000..05a32ac --- /dev/null +++ b/media-server-demo-node/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +docker stop ue +docker rm ue +docker build -t ue:node . +docker run -it -d -p 8081:8081 -p 8888:8888 -p 8889:8889 --name ue ue:node \ No newline at end of file diff --git a/media-server-demo-node/entrypoint.sh b/media-server-demo-node/entrypoint.sh new file mode 100644 index 0000000..ab8ba37 --- /dev/null +++ b/media-server-demo-node/entrypoint.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# entrypoint.sh + +# read the file that sets variables +/usr/local/bin/node /opt/media-server/index.js 43.139.191.204 +# /usr/local/bin/node /opt/SignallingWebServer/cirrus.js --peerConnectionOptions="{ \""iceServers\"": [{\""urls\"": [\""stun:stun4.l.google.com:19302\""]}]}" + +# run the main container command +exec "$@" + + + + + diff --git a/media-server-demo-node/index.js b/media-server-demo-node/index.js new file mode 100644 index 0000000..dfe16df --- /dev/null +++ b/media-server-demo-node/index.js @@ -0,0 +1,128 @@ +const https = require ('https'); +const url = require ('url'); +const fs = require ('fs'); +const path = require ('path'); +const WebSocketServer = require ('websocket').server; + +//Get the Medooze Media Server interface +const MediaServer = require("medooze-media-server"); + +//Check +if (process.argv.length!=3) + throw new Error("Missing IP address\nUsage: node index.js "); +//Get ip +const ip = process.argv[2]; + +//Create UDP server endpoint +const endpoint = MediaServer.createEndpoint(ip); + +const base = 'www'; + +const options = { + key: fs.readFileSync ('server.key'), + cert: fs.readFileSync ('server.cert') +}; + +//Enable debug +MediaServer.enableDebug(false); +MediaServer.enableUltraDebug(false); + +//Restrict port range +MediaServer.setPortRange(10000,20000); + +// maps file extention to MIME typere +const map = { + '.ico': 'image/x-icon', + '.html': 'text/html', + '.js': 'text/javascript', + '.json': 'application/json', + '.css': 'text/css', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.wav': 'audio/wav', + '.mp3': 'audio/mpeg', + '.svg': 'image/svg+xml', + '.pdf': 'application/pdf', + '.doc': 'application/msword' +}; + +//Create HTTP server +const server = https.createServer (options, (req, res) => { + // parse URL + const parsedUrl = url.parse (req.url); + // extract URL path + let pathname = base + parsedUrl.pathname; + // based on the URL path, extract the file extention. e.g. .js, .doc, ... + const ext = path.parse (pathname).ext; + + //DO static file handling + fs.exists (pathname, (exist) => { + if (!exist) + { + // if the file is not found, return 404 + res.statusCode = 404; + res.end (`File ${pathname} not found!`); + return; + } + + // if is a directory search for index file matching the extention + if (fs.statSync (pathname).isDirectory ()) + pathname += '/index.html'; + + // read file from file system + fs.readFile (pathname, (err, data) => { + if (err) + { + //Error + res.statusCode = 500; + res.end (`Error getting the file: ${err}.`); + } else { + // if the file is found, set Content-type and send data + res.setHeader ('Content-type', map[ext] || 'text/html'); + res.end (data); + } + }); + }); +}).listen (28000); + +const wsServer = new WebSocketServer ({ + httpServer: server, + autoAcceptConnections: false +}); + +// Load the demo handlers +const handlers = { + "svc" : require("./lib/svc.js"), + "rec" : require("./lib/recording.js"), + "broadcast" : require("./lib/broadcast.js"), + "simulcast" : require("./lib/simulcast.js"), + "playback" : require("./lib/playback.js"), + "datachannels" : require("./lib/datachannels.js"), +}; + +wsServer.on ('request', (request) => { + //Get protocol for demo + var protocol = request.requestedProtocols[0]; + + console.log("-Got request for: " + protocol); + //If nor found + if (!handlers.hasOwnProperty (protocol)) + //Reject connection + return request.reject(); + + //Process it + handlers[protocol](request,protocol,endpoint); +}); + + +//Try to clean up on exit +const onExit = (e) => { + if (e) console.error(e); + MediaServer.terminate(); + process.exit(); +}; + +process.on("uncaughtException" , onExit); +process.on("SIGINT" , onExit); +process.on("SIGTERM" , onExit); +process.on("SIGQUIT" , onExit); diff --git a/media-server-demo-node/lib/broadcast.js b/media-server-demo-node/lib/broadcast.js new file mode 100644 index 0000000..2d16126 --- /dev/null +++ b/media-server-demo-node/lib/broadcast.js @@ -0,0 +1,147 @@ +//Get the Medooze Media Server interface +const MediaServer = require("medooze-media-server"); + +//Get Semantic SDP objects +const SemanticSDP = require("semantic-sdp"); +const SDPInfo = SemanticSDP.SDPInfo; +const MediaInfo = SemanticSDP.MediaInfo; +const CandidateInfo = SemanticSDP.CandidateInfo; +const DTLSInfo = SemanticSDP.DTLSInfo; +const ICEInfo = SemanticSDP.ICEInfo; +const StreamInfo = SemanticSDP.StreamInfo; +const TrackInfo = SemanticSDP.TrackInfo; +const Direction = SemanticSDP.Direction; +const CodecInfo = SemanticSDP.CodecInfo; + + +//Create new streamer +const streamer = MediaServer.createStreamer(); + +//Create new video session codecs +const video = new MediaInfo("video","video"); + +//Add h264 codec +video.addCodec(new CodecInfo("h264",96)); + +//Create session for video +const session = streamer.createSession(video,{ + local : { + port: 5004 + } +}); + +module.exports = function(request,protocol,endpoint) +{ + const connection = request.accept(protocol); + + connection.on('message', (frame) => + { + //Get cmd + var msg = JSON.parse(frame.utf8Data); + + + //Get cmd + if (msg.cmd==="OFFER") + { + + //Process the sdp + var offer = SDPInfo.process(msg.offer); + + //Create an DTLS ICE transport in that enpoint + const transport = endpoint.createTransport({ + dtls : offer.getDTLS(), + ice : offer.getICE() + }); + + //Set RTP remote properties + transport.setRemoteProperties({ + audio : offer.getMedia("audio"), + video : offer.getMedia("video") + }); + + //Get local DTLS and ICE info + const dtls = transport.getLocalDTLSInfo(); + const ice = transport.getLocalICEInfo(); + + //Get local candidates + const candidates = endpoint.getLocalCandidates(); + + //Create local SDP info + let answer = new SDPInfo(); + + //Add ice and dtls info + answer.setDTLS(dtls); + answer.setICE(ice); + //For each local candidate + for (let i=0;i { + //Stop + transport.stop(); + }); + } + }); +}; \ No newline at end of file diff --git a/media-server-demo-node/lib/datachannels.js b/media-server-demo-node/lib/datachannels.js new file mode 100644 index 0000000..1b4d4cf --- /dev/null +++ b/media-server-demo-node/lib/datachannels.js @@ -0,0 +1,74 @@ +//Get the Medooze Media Server interface +const MediaServer = require("medooze-media-server"); + +//Get Semantic SDP objects +const SemanticSDP = require("semantic-sdp"); +const SDPInfo = SemanticSDP.SDPInfo; +const MediaInfo = SemanticSDP.MediaInfo; +const CandidateInfo = SemanticSDP.CandidateInfo; +const DTLSInfo = SemanticSDP.DTLSInfo; +const ICEInfo = SemanticSDP.ICEInfo; +const StreamInfo = SemanticSDP.StreamInfo; +const TrackInfo = SemanticSDP.TrackInfo; +const Direction = SemanticSDP.Direction; +const CodecInfo = SemanticSDP.CodecInfo; + +const Capabilities = { + data : { + + } +}; + +module.exports = function(request,protocol,endpoint) +{ + const connection = request.accept(protocol); + + connection.on('message', (frame) => + { + //Get cmd + var msg = JSON.parse(frame.utf8Data); + + //Get cmd + if (msg.cmd==="OFFER") + { + //Process the sdp + var offer = SDPInfo.process(msg.offer); + + //Create an DTLS ICE transport in that enpoint + const transport = endpoint.createTransport(offer); + + //transport.dump("/tmp/svc.pcap"); + + //Set RTP remote properties + transport.setRemoteProperties({ + audio : offer.getMedia("audio"), + video : offer.getMedia("video") + }); + + //Create local SDP info + const answer = offer.answer({ + dtls : transport.getLocalDTLSInfo(), + ice : transport.getLocalICEInfo(), + candidates : endpoint.getLocalCandidates(), + capabilities : Capabilities + }); + + //Set RTP local properties + transport.setLocalProperties({ + video : answer.getMedia("video") + }); + + //Get offered stream info + //Send response + connection.sendUTF(JSON.stringify({ + answer : answer.toString() + })); + + //Close on disconnect + connection.on("close",() => { + //Stop + transport.stop(); + }); + } + }); +}; diff --git a/media-server-demo-node/lib/playback.js b/media-server-demo-node/lib/playback.js new file mode 100644 index 0000000..98bdad3 --- /dev/null +++ b/media-server-demo-node/lib/playback.js @@ -0,0 +1,134 @@ +//Get the Medooze Media Server interface +const MediaServer = require("medooze-media-server"); +const FileSystem = require("fs"); +const Path = require("path"); +//Get Semantic SDP objects +const SemanticSDP = require("semantic-sdp"); +const SDPInfo = SemanticSDP.SDPInfo; +const MediaInfo = SemanticSDP.MediaInfo; +const CandidateInfo = SemanticSDP.CandidateInfo; +const DTLSInfo = SemanticSDP.DTLSInfo; +const ICEInfo = SemanticSDP.ICEInfo; +const StreamInfo = SemanticSDP.StreamInfo; +const TrackInfo = SemanticSDP.TrackInfo; +const Direction = SemanticSDP.Direction; +const CodecInfo = SemanticSDP.CodecInfo; + +const Capabilities = { + audio : { + codecs : ["opus"], + extensions : [ "urn:ietf:params:rtp-hdrext:ssrc-audio-level", "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"] + }, + video : { + codecs : ["vp9","vp8","h264;packetization-mode=1"], + rtx : true, + rtcpfbs : [ + { "id": "transport-cc"}, + { "id": "ccm", "params": ["fir"]}, + { "id": "nack"}, + { "id": "nack", "params": ["pli"]} + ], + extensions : [ "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"] + } +}; + +module.exports = function(request,protocol,endpoint) +{ + const connection = request.accept(protocol); + let player; + + connection.on('message', (frame) => + { + //Get cmd + var msg = JSON.parse(frame.utf8Data); + + //Get cmd + if (msg.cmd==="OFFER") + { + let mp4; + + //Get all files in recording dir + const files = FileSystem.readdirSync('recordings'); + for(let i in files) + { + if (Path.extname(files[i])===".mp4") + { + //got ir + mp4 = files[i]; + break; + } + } + + //Check + if (!mp4) + { + console.error("no mp4 found"); + return connection.close(); + } + + //Create player + player = MediaServer.createPlayer(Path.join("recordings",mp4)); + + //Process the sdp + var offer = SDPInfo.process(msg.offer); + + //Create an DTLS ICE transport in that enpoint + const transport = endpoint.createTransport(offer); + + //Set RTP remote properties + transport.setRemoteProperties(offer); + + transport.dump("recordings/play-"+ Date.now()+".pcap",{ + outgoing : true, + rtcp : true + }); + + //Set RTP remote properties + transport.setRemoteProperties(offer); + + //Create local SDP info + const answer = offer.answer({ + dtls : transport.getLocalDTLSInfo(), + ice : transport.getLocalICEInfo(), + candidates : endpoint.getLocalCandidates(), + capabilities : Capabilities + }); + + //Set RTP local properties + transport.setLocalProperties(answer); + + //Create new local stream with only video + const outgoingStream = transport.createOutgoingStream({ + audio: true, + video: true + }); + + //Copy incoming data from the broadcast stream to the local one + outgoingStream.attachTo(player); + + //Get local stream info + const info = outgoingStream.getStreamInfo(); + + //Add local stream info it to the answer + answer.addStream(info); + + //Send response + connection.sendUTF(JSON.stringify({ + answer : answer.toString() + })); + + + //Close on disconnect + connection.on("close",() => { + //Stop + transport.stop(); + //Stop playback too + player.stop(); + }); + } else if (msg.cmd==="PLAY") { + player.play({ + repeat : true + }); + } + }); +}; diff --git a/media-server-demo-node/lib/playback_1.js b/media-server-demo-node/lib/playback_1.js new file mode 100644 index 0000000..3c60113 --- /dev/null +++ b/media-server-demo-node/lib/playback_1.js @@ -0,0 +1,134 @@ +//Get the Medooze Media Server interface +const MediaServer = require("medooze-media-server"); +const FileSystem = require("fs"); +const Path = require("path"); +//Get Semantic SDP objects +const SemanticSDP = require("semantic-sdp"); +const SDPInfo = SemanticSDP.SDPInfo; +const MediaInfo = SemanticSDP.MediaInfo; +const CandidateInfo = SemanticSDP.CandidateInfo; +const DTLSInfo = SemanticSDP.DTLSInfo; +const ICEInfo = SemanticSDP.ICEInfo; +const StreamInfo = SemanticSDP.StreamInfo; +const TrackInfo = SemanticSDP.TrackInfo; +const Direction = SemanticSDP.Direction; +const CodecInfo = SemanticSDP.CodecInfo; + +const Capabilities = { + audio : { + codecs : ["opus"], + extensions : [ "urn:ietf:params:rtp-hdrext:ssrc-audio-level", "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"] + }, + video : { + codecs : ["vp9","vp8","h264;packetization-mode=1"], + rtx : true, + rtcpfbs : [ + { "id": "transport-cc"}, + { "id": "ccm", "params": ["fir"]}, + { "id": "nack"}, + { "id": "nack", "params": ["pli"]} + ], + extensions : [ "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"] + } +}; + +module.exports = function(request,protocol,endpoint) +{ + const connection = request.accept(protocol); + let player; + + connection.on('message', (frame) => + { + //Get cmd + var msg = JSON.parse(frame.utf8Data); + + //Get cmd + if (msg.cmd==="OFFER") + { + let mp4; + + //Get all files in recording dir + const files = FileSystem.readdirSync('recordings'); + for(let i in files) + { + if (Path.extname(files[i])===".mp4") + { + //got ir + mp4 = files[i]; + break; + } + } + + //Check + if (!mp4) + { + console.error("no mp4 found"); + return connection.close(); + } + + //Create player + player = MediaServer.createPlayer(Path.join("recordings",mp4)); + + //Process the sdp + var offer = SDPInfo.process(msg.offer); + + //Create an DTLS ICE transport in that enpoint + const transport = endpoint.createTransport(offer); + + //Set RTP remote properties + transport.setRemoteProperties(offer); + + transport.dump("recordings/play-"+ Date.now()+".pcap",{ + outgoing : true, + rtcp : true + }); + + //Set RTP remote properties + transport.setRemoteProperties(offer); + + //Create local SDP info + const answer = offer.answer({ + dtls : transport.getLocalDTLSInfo(), + ice : transport.getLocalICEInfo(), + candidates : endpoint.getLocalCandidates(), + capabilities : Capabilities + }); + + //Set RTP local properties + transport.setLocalProperties(answer); + + //Create new local stream with only video + const outgoingStream = transport.createOutgoingStream({ + audio: true, + video: true + }); + + //Copy incoming data from the broadcast stream to the local one + outgoingStream.attachTo(player); + + //Get local stream info + const info = outgoingStream.getStreamInfo(); + + //Add local stream info it to the answer + answer.addStream(info); + + //Send response + connection.sendUTF(JSON.stringify({ + answer : answer.toString() + })); + + + //Close on disconnect + connection.on("close",() => { + //Stop + transport.stop(); + //Stop playback too + player.stop(); + }); + } else if (msg.cmd==="PLAY") { + player.play({ + repeat : true + }); + } + }); +}; \ No newline at end of file diff --git a/media-server-demo-node/lib/recording.js b/media-server-demo-node/lib/recording.js new file mode 100644 index 0000000..311faa7 --- /dev/null +++ b/media-server-demo-node/lib/recording.js @@ -0,0 +1,121 @@ +//Get the Medooze Media Server interface +const MediaServer = require("medooze-media-server"); + +//Get Semantic SDP objects +const SemanticSDP = require("semantic-sdp"); +const SDPInfo = SemanticSDP.SDPInfo; +const MediaInfo = SemanticSDP.MediaInfo; +const CandidateInfo = SemanticSDP.CandidateInfo; +const DTLSInfo = SemanticSDP.DTLSInfo; +const ICEInfo = SemanticSDP.ICEInfo; +const StreamInfo = SemanticSDP.StreamInfo; +const TrackInfo = SemanticSDP.TrackInfo; +const Direction = SemanticSDP.Direction; +const CodecInfo = SemanticSDP.CodecInfo; + +const Capabilities = { + audio : { + codecs : ["opus"], + extensions : [ "urn:ietf:params:rtp-hdrext:ssrc-audio-level"] + }, + video : { + codecs : ["h264;packetization-mode=1"], + rtx : true, + rtcpfbs : [ + { "id": "transport-cc"}, + { "id": "ccm", "params": ["fir"]}, + { "id": "nack"}, + { "id": "nack", "params": ["pli"]} + ], + extensions : [ "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"] + } +}; + +module.exports = function(request,protocol,endpoint) +{ + const connection = request.accept(protocol); + + connection.on('message', (frame) => + { + //Get cmd + var msg = JSON.parse(frame.utf8Data); + + //Get cmd + if (msg.cmd==="OFFER") + { + console.log(msg.offer); + //Process the sdp + var offer = SDPInfo.process(msg.offer); + + //Create recoreder + const recorder = MediaServer.createRecorder ("recordings/"+ Date.now() +".mp4",{ + // refresh : 15000, + // timeShift : 60000, + disableHints : true, + }); + + //Create an DTLS ICE transport in that enpoint + const transport = endpoint.createTransport(offer); + transport.setBandwidthProbing(true); + transport.setMaxProbingBitrate(1024000); + transport.on("targetbitrate", bitrate=>console.log("targetbitrate " + bitrate)); + transport.on("dtlsstate", state=>console.log("dtlsstate:"+state)); + + transport.dump("recordings/rec-"+ Date.now()+".pcap"); + + //Set RTP remote properties + transport.setRemoteProperties(offer); + + //Create local SDP info + const answer = offer.answer({ + dtls : transport.getLocalDTLSInfo(), + ice : transport.getLocalICEInfo(), + candidates : endpoint.getLocalCandidates(), + capabilities : Capabilities + }); + + //Set RTP local properties + transport.setLocalProperties(answer); + + //For each stream offered + for (let offered of offer.getStreams().values()) + { + //Create the remote stream into the transport + const incomingStream = transport.createIncomingStream(offered); + + //Create new local stream with only audio + const outgoingStream = transport.createOutgoingStream({ + audio: false, + video: true + }); + + //Get local stream info + const info = outgoingStream.getStreamInfo(); + + //Copy incoming data from the remote stream to the local one + outgoingStream.attachTo(incomingStream); + + //Add local stream info it to the answer + answer.addStream(info); + + //Record it + recorder.record(incomingStream); + } + + //setTimeout(()=>recorder.flush(),60000); + + //Send response + connection.sendUTF(JSON.stringify({ + answer : answer.toString() + })); + + //Close on disconnect + connection.on("close",() => { + console.log("close"); + //Stop + recorder.stop(); + transport && transport.stop(); + }); + } + }); +}; diff --git a/media-server-demo-node/lib/simulcast.js b/media-server-demo-node/lib/simulcast.js new file mode 100644 index 0000000..321e101 --- /dev/null +++ b/media-server-demo-node/lib/simulcast.js @@ -0,0 +1,141 @@ +//Get the Medooze Media Server interface +const MediaServer = require("medooze-media-server"); + +//Get Semantic SDP objects +const SemanticSDP = require("semantic-sdp"); +const SDPInfo = SemanticSDP.SDPInfo; +const MediaInfo = SemanticSDP.MediaInfo; +const CandidateInfo = SemanticSDP.CandidateInfo; +const DTLSInfo = SemanticSDP.DTLSInfo; +const ICEInfo = SemanticSDP.ICEInfo; +const StreamInfo = SemanticSDP.StreamInfo; +const TrackInfo = SemanticSDP.TrackInfo; +const Direction = SemanticSDP.Direction; +const CodecInfo = SemanticSDP.CodecInfo; + + +const Capabilities = { + video : { + codecs : ["h264;packetization-mode=1"], + rtx : true, + rtcpfbs : [ + { "id": "goog-remb"}, + { "id": "transport-cc"}, + { "id": "ccm", "params": ["fir"]}, + { "id": "nack"}, + { "id": "nack", "params": ["pli"]} + ], + extensions : [ + "urn:3gpp:video-orientation", + "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01", + "urn:ietf:params:rtp-hdrext:sdes:mid", + "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", + "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id", + "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time" + ], + simulcast : true + } +}; + +module.exports = function(request,protocol,endpoint) +{ + const connection = request.accept(protocol); + + connection.on('message', (frame) => + { + //Get cmd + var msg = JSON.parse(frame.utf8Data); + + + //Get cmd + if (msg.cmd==="OFFER") + { + + //Process the sdp + var offer = SDPInfo.process(msg.offer); + + //Create an DTLS ICE transport in that enpoint + const transport = endpoint.createTransport(offer); + + //Set RTP remote properties + transport.setRemoteProperties(offer); + + //Enable bandwidth probing + transport.setBandwidthProbing(true); + transport.setMaxProbingBitrate(300*1000); + + //Create local SDP info + const answer = offer.answer({ + dtls : transport.getLocalDTLSInfo(), + ice : transport.getLocalICEInfo(), + candidates : endpoint.getLocalCandidates(), + capabilities : Capabilities + }); + + //Set RTP local properties + transport.setLocalProperties({ + video : answer.getMedia("video") + }); + + //Get timestamp + const ts = Date.now(); + + //Dump contents + transport.dump("recordings/simulcast-"+ts+".pcap"); + + //Create recoreder + //const recorder = MediaServer.createRecorder ("recordings/simulcast"+ts +".mp4"); + + //For each stream offered + for (let offered of offer.getStreams().values()) + { + + //Create the remote stream into the transport + const incomingStream = transport.createIncomingStream(offered); + + //Create new local stream + const outgoingStream = transport.createOutgoingStream({ + audio: false, + video: true + }); + + //Get local stream info + const info = outgoingStream.getStreamInfo(); + + //Copy incoming data from the remote stream to the local one + connection.transporder = outgoingStream.attachTo(incomingStream)[0]; + + //Add local stream info it to the answer + answer.addStream(info); + + setInterval(()=>{ + //console.dir(incomingStream.getStats(),{depth:null}); + //console.log(outgoingStream.getStats()); + },1000); + //Record it + //recorder.record(incomingStream); + } + + //Send response + connection.sendUTF(JSON.stringify({ + answer : answer.toString().replace("h264","H264") + })); + + console.log("OFFER"); + console.log(msg.offer); + console.log("ANSWER"); + console.log(answer.toString().replace("h264","H264")); + //Close on disconnect + connection.on("close",() => { + //Stop transport an recorded + transport.stop(); + //recorder.stop(); + }); + } else { + connection.transporder.selectEncoding(msg.rid); + //Select layer + connection.transporder.selectLayer(parseInt(msg.spatialLayerId),parseInt(msg.temporalLayerId)); + } + }); + +}; diff --git a/media-server-demo-node/lib/svc.js b/media-server-demo-node/lib/svc.js new file mode 100644 index 0000000..17f9cb3 --- /dev/null +++ b/media-server-demo-node/lib/svc.js @@ -0,0 +1,153 @@ +//Get the Medooze Media Server interface +const MediaServer = require("medooze-media-server"); + +//Get Semantic SDP objects +const SemanticSDP = require("semantic-sdp"); +const SDPInfo = SemanticSDP.SDPInfo; +const MediaInfo = SemanticSDP.MediaInfo; +const CandidateInfo = SemanticSDP.CandidateInfo; +const DTLSInfo = SemanticSDP.DTLSInfo; +const ICEInfo = SemanticSDP.ICEInfo; +const StreamInfo = SemanticSDP.StreamInfo; +const TrackInfo = SemanticSDP.TrackInfo; +const Direction = SemanticSDP.Direction; +const CodecInfo = SemanticSDP.CodecInfo; + +const Capabilities = { + video : { + codecs : ["vp9"], + rtx : true, + rtcpfbs : [ + { "id": "transport-cc"}, + { "id": "ccm", "params": ["fir"]}, + { "id": "nack"}, + { "id": "nack", "params": ["pli"]} + ], + extensions : [ "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"] + } +}; + +module.exports = function(request,protocol,endpoint) +{ + const connection = request.accept(protocol); + + connection.on('message', (frame) => + { + //Get cmd + var msg = JSON.parse(frame.utf8Data); + + //Get cmd + if (msg.cmd==="OFFER") + { + const streams = {}; + + //Process the sdp + var offer = SDPInfo.process(msg.offer); + + //Create an DTLS ICE transport in that enpoint + const transport = endpoint.createTransport(offer); + + //Enable probing + transport.setBandwidthProbing(true); + transport.setMaxProbingBitrate(512000); + + //DUMP + //transport.dump("recordings/svc-"+ts+".pcap",{incoming:true,rtcp:true,rtpHeadersOnly:true,bwe:true}); + + //Set RTP remote properties + transport.setRemoteProperties({ + audio : offer.getMedia("audio"), + video : offer.getMedia("video") + }); + + //Create local SDP info + const answer = offer.answer({ + dtls : transport.getLocalDTLSInfo(), + ice : transport.getLocalICEInfo(), + candidates : endpoint.getLocalCandidates(), + capabilities : Capabilities + }); + + //Set RTP local properties + transport.setLocalProperties({ + video : answer.getMedia("video") + }); + + //Get offered stream info + const offered = offer.getFirstStream(); + + //Create the remote stream into the transports + const incomingStream = transport.createIncomingStream(offered); + + //Create new local stream + const outgoingStream = transport.createOutgoingStream({ + audio: false, + video: true + }); + + //Get local stream info + const info = outgoingStream.getStreamInfo(); + + //Copy incoming data from the remote stream to the local one + const transponder = connection.transporder = outgoingStream.attachTo(incomingStream)[0]; + + //Start on low + transponder.selectLayer(0,0); + + //Listen for bwe events + transport.on("targetbitrate", bitrate=>{ + //Get previous layer ids + const sid = transponder.getSelectedSpatialLayerId(); + const tid = transponder.getSelectedTemporalLayerId(); + //Select stream layer from bitrate + const rate = transponder.setTargetBitrate(bitrate); + //Get next layer + const next = rate.layers[rate.layerIndex-1]; + //Probing + let probing = false; + //If the jump is lower + if (next) + { + //Set probing bitrate + probing = next.bitrate-rate; + //Set it on transport + transport.setMaxProbingBitrate(next.bitrate-rate); + //Enable + transport.setBandwidthProbing(true); + } else + //Disable + transport.setBandwidthProbing(false); + + //Log + console.log("targetbitrate :" + bitrate + " probing:" + probing +" sid:" + transponder.getSelectedSpatialLayerId() + " tid:" +transponder.getSelectedTemporalLayerId()); + //If changed + if (sid!=transponder.getSelectedSpatialLayerId() || tid!=transponder.getSelectedTemporalLayerId()) + //Send response + connection.sendUTF(JSON.stringify({ + sid : transponder.getSelectedSpatialLayerId(), + tid : transponder.getSelectedTemporalLayerId() + })); + }); + + //Add local stream info it to the answer + answer.addStream(info); + + //Add to streams + streams[incomingStream.getId()] = incomingStream; + + //Send response + connection.sendUTF(JSON.stringify({ + answer : answer.toString() + })); + + //Close on disconnect + connection.on("close",() => { + //Stop + transport.stop(); + }); + } else { + //Select layer + connection.transporder.selectLayer(parseInt(msg.spatialLayerId),parseInt(msg.temporalLayerId)); + } + }); +}; diff --git a/media-server-demo-node/package.json b/media-server-demo-node/package.json new file mode 100644 index 0000000..e195d00 --- /dev/null +++ b/media-server-demo-node/package.json @@ -0,0 +1,24 @@ +{ + "name": "medooze-media-server-demo", + "version": "1.1.0", + "description": "Demo application for the Medooze Media Server for Node.js", + "main": "index.js", + "scripts": { + "install": "openssl req -nodes -new -x509 -keyout server.key -out server.cert -subj \"/CN=localhost\"" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/murillo128/media-server-demo-node.git" + }, + "author": "Sergio Garcia Murillo", + "license": "MIT", + "bugs": { + "url": "https://github.com/murillo128/media-server-demo-node/issues" + }, + "homepage": "https://github.com/murillo128/media-server-demo-node#readme", + "dependencies": { + "medooze-media-server": "^0", + "semantic-sdp": "^3", + "websocket": "^1.0.26" + } +} diff --git a/media-server-demo-node/recordings/your_recordings_will_be_here b/media-server-demo-node/recordings/your_recordings_will_be_here new file mode 100644 index 0000000..e69de29 diff --git a/media-server-demo-node/www/broadcast/index.html b/media-server-demo-node/www/broadcast/index.html new file mode 100644 index 0000000..68e8b42 --- /dev/null +++ b/media-server-demo-node/www/broadcast/index.html @@ -0,0 +1,200 @@ + + + + + + + + + + + + + +
+
REMOTE + +
+ +

Ready to test H264 broadcasting?

+
+

+ This demo allows you to test the streaming capabilities of the Medooze Media Server. +

+

+ You will need to open an mp4 file with VLC and start streaming it with: +

+					
+:sout=#duplicate{dst=rtp{dst=your_ip_address,port=5004,sap,name=sergio},dst=display}
+:no-sout-audio 
+:sout-vide 
+:sout-keep
+				
+			
+

+
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/media-server-demo-node/www/broadcast/js/broadcast.js b/media-server-demo-node/www/broadcast/js/broadcast.js new file mode 100644 index 0000000..a27aa12 --- /dev/null +++ b/media-server-demo-node/www/broadcast/js/broadcast.js @@ -0,0 +1,121 @@ +const url = "wss://"+window.location.hostname+":"+window.location.port; + +const roomId = (new Date()).getTime() + "-" + Math.random(); + + +function addVideoForStream(stream,muted) +{ + //Create new video element + const video = document.querySelector (muted ? "#local" : "#remote"); + //Set same id + video.id = stream.id; + //Set src stream + video.src = URL.createObjectURL(stream); + //Set other properties + video.autoplay = true; + video.muted = muted; +} +function removeVideoForStream(stream) +{ + //Get video + var video = document.getElementById(stream.id); + //Remove it when done + video.addEventListener('webkitTransitionEnd',function(){ + //Delete it + video.parentElement.removeChild(video); + }); + //Disable it first + video.className = "disabled"; +} + +var sdp; +var pc; + +function connect() +{ + + if (window.RTCPeerConnection) + pc = new RTCPeerConnection({ + bundlePolicy: "max-bundle", + rtcpMuxPolicy : "require" + }); + else + pc = new webkitRTCPeerConnection(null); + + var ws = new WebSocket(url,"broadcast"); + + pc.onaddstream = function(event) { + var prev = 0; + console.debug("onAddStream",event); + //Play it + addVideoForStream(event.stream); + + + }; + + pc.onremovestream = function(event) { + console.debug("onRemoveStream",event); + //Play it + removeVideoForStream(event.stream); + }; + + ws.onopen = function(){ + console.log("opened"); + + //Create new offer + pc.createOffer({ + offerToReceiveVideo: true + }) + .then(function(offer){ + console.debug("createOffer sucess",offer); + //We have sdp + sdp = offer.sdp; + //Set it + pc.setLocalDescription(offer); + console.log(sdp); + //Create room + ws.send(JSON.stringify({ + cmd : "OFFER", + offer : sdp + })); + }) + .catch(function(error){ + console.error("Error",error); + }); + }; + + ws.onmessage = function(event){ + console.log(event); + + //Get protocol message + const msg = JSON.parse(event.data); + + console.log(msg.answer); + pc.setRemoteDescription(new RTCSessionDescription({ + type:'answer', + sdp: msg.answer + }), function () { + console.log("JOINED"); + }, function (err) { + console.error("Error joining",err); + } + ); + }; +} + +var dialog = document.querySelector('dialog'); +if (dialog.showModal) +{ + dialog.showModal(); + dialog.querySelector('.ready').addEventListener('click', function() { + dialog.close(); + connect(); + }); +} else { + connect(); +} + + + + + diff --git a/media-server-demo-node/www/datachannels/index.html b/media-server-demo-node/www/datachannels/index.html new file mode 100644 index 0000000..d778ef8 --- /dev/null +++ b/media-server-demo-node/www/datachannels/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/media-server-demo-node/www/datachannels/js/datachannels.js b/media-server-demo-node/www/datachannels/js/datachannels.js new file mode 100644 index 0000000..acfdd41 --- /dev/null +++ b/media-server-demo-node/www/datachannels/js/datachannels.js @@ -0,0 +1,50 @@ +const url = "wss://"+window.location.hostname+":"+window.location.port; + + + +var sdp; +var pc; + +function connect() +{ + + //Create PC + pc = new RTCPeerConnection(); + const dc = pc.createDataChannel("aaaaaaaaaaaaaaaa"); + + var ws = new WebSocket(url,"datachannels"); + + ws.onopen = async function() { + //Create new offer + const offer = await pc.createOffer(); + //We have sdp + sdp = offer.sdp; + console.log("offer",sdp); + //Set it + await pc.setLocalDescription(offer); + //Create room + ws.send(JSON.stringify({ + cmd : "OFFER", + offer : sdp + })); + }; + + ws.onmessage = function(event){ + //Get protocol message + const msg = JSON.parse(event.data); + const answer = msg.answer.replace("m=application 9 UDP/TLS/RTP/SAVPF","m=application 9 DTLS/SCTP 5000") + "a=sctpmap:5000 webrtc-datachannel 1024\r\n" + console.log("answer",answer); + pc.setRemoteDescription(new RTCSessionDescription({ + type:'answer', + sdp: answer + }), function () { + console.log("JOINED"); + }, function (err) { + console.error("Error joining",err); + } + ); + }; +} + +connect(); + diff --git a/media-server-demo-node/www/js/dialog-polyfill.js b/media-server-demo-node/www/js/dialog-polyfill.js new file mode 100644 index 0000000..be4b43d --- /dev/null +++ b/media-server-demo-node/www/js/dialog-polyfill.js @@ -0,0 +1,738 @@ +(function() { + + // nb. This is for IE10 and lower _only_. + var supportCustomEvent = window.CustomEvent; + if (!supportCustomEvent || typeof supportCustomEvent === 'object') { + supportCustomEvent = function CustomEvent(event, x) { + x = x || {}; + var ev = document.createEvent('CustomEvent'); + ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null); + return ev; + }; + supportCustomEvent.prototype = window.Event.prototype; + } + + /** + * @param {Element} el to check for stacking context + * @return {boolean} whether this el or its parents creates a stacking context + */ + function createsStackingContext(el) { + while (el && el !== document.body) { + var s = window.getComputedStyle(el); + var invalid = function(k, ok) { + return !(s[k] === undefined || s[k] === ok); + } + if (s.opacity < 1 || + invalid('zIndex', 'auto') || + invalid('transform', 'none') || + invalid('mixBlendMode', 'normal') || + invalid('filter', 'none') || + invalid('perspective', 'none') || + s['isolation'] === 'isolate' || + s.position === 'fixed' || + s.webkitOverflowScrolling === 'touch') { + return true; + } + el = el.parentElement; + } + return false; + } + + /** + * Finds the nearest from the passed element. + * + * @param {Element} el to search from + * @return {HTMLDialogElement} dialog found + */ + function findNearestDialog(el) { + while (el) { + if (el.localName === 'dialog') { + return /** @type {HTMLDialogElement} */ (el); + } + el = el.parentElement; + } + return null; + } + + /** + * Blur the specified element, as long as it's not the HTML body element. + * This works around an IE9/10 bug - blurring the body causes Windows to + * blur the whole application. + * + * @param {Element} el to blur + */ + function safeBlur(el) { + if (el && el.blur && el !== document.body) { + el.blur(); + } + } + + /** + * @param {!NodeList} nodeList to search + * @param {Node} node to find + * @return {boolean} whether node is inside nodeList + */ + function inNodeList(nodeList, node) { + for (var i = 0; i < nodeList.length; ++i) { + if (nodeList[i] === node) { + return true; + } + } + return false; + } + + /** + * @param {HTMLFormElement} el to check + * @return {boolean} whether this form has method="dialog" + */ + function isFormMethodDialog(el) { + if (!el || !el.hasAttribute('method')) { + return false; + } + return el.getAttribute('method').toLowerCase() === 'dialog'; + } + + /** + * @param {!HTMLDialogElement} dialog to upgrade + * @constructor + */ + function dialogPolyfillInfo(dialog) { + this.dialog_ = dialog; + this.replacedStyleTop_ = false; + this.openAsModal_ = false; + + // Set a11y role. Browsers that support dialog implicitly know this already. + if (!dialog.hasAttribute('role')) { + dialog.setAttribute('role', 'dialog'); + } + + dialog.show = this.show.bind(this); + dialog.showModal = this.showModal.bind(this); + dialog.close = this.close.bind(this); + + if (!('returnValue' in dialog)) { + dialog.returnValue = ''; + } + + if ('MutationObserver' in window) { + var mo = new MutationObserver(this.maybeHideModal.bind(this)); + mo.observe(dialog, {attributes: true, attributeFilter: ['open']}); + } else { + // IE10 and below support. Note that DOMNodeRemoved etc fire _before_ removal. They also + // seem to fire even if the element was removed as part of a parent removal. Use the removed + // events to force downgrade (useful if removed/immediately added). + var removed = false; + var cb = function() { + removed ? this.downgradeModal() : this.maybeHideModal(); + removed = false; + }.bind(this); + var timeout; + var delayModel = function(ev) { + if (ev.target !== dialog) { return; } // not for a child element + var cand = 'DOMNodeRemoved'; + removed |= (ev.type.substr(0, cand.length) === cand); + window.clearTimeout(timeout); + timeout = window.setTimeout(cb, 0); + }; + ['DOMAttrModified', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument'].forEach(function(name) { + dialog.addEventListener(name, delayModel); + }); + } + // Note that the DOM is observed inside DialogManager while any dialog + // is being displayed as a modal, to catch modal removal from the DOM. + + Object.defineProperty(dialog, 'open', { + set: this.setOpen.bind(this), + get: dialog.hasAttribute.bind(dialog, 'open') + }); + + this.backdrop_ = document.createElement('div'); + this.backdrop_.className = 'backdrop'; + this.backdrop_.addEventListener('click', this.backdropClick_.bind(this)); + } + + dialogPolyfillInfo.prototype = { + + get dialog() { + return this.dialog_; + }, + + /** + * Maybe remove this dialog from the modal top layer. This is called when + * a modal dialog may no longer be tenable, e.g., when the dialog is no + * longer open or is no longer part of the DOM. + */ + maybeHideModal: function() { + if (this.dialog_.hasAttribute('open') && document.body.contains(this.dialog_)) { return; } + this.downgradeModal(); + }, + + /** + * Remove this dialog from the modal top layer, leaving it as a non-modal. + */ + downgradeModal: function() { + if (!this.openAsModal_) { return; } + this.openAsModal_ = false; + this.dialog_.style.zIndex = ''; + + // This won't match the native exactly because if the user set top on a centered + // polyfill dialog, that top gets thrown away when the dialog is closed. Not sure it's + // possible to polyfill this perfectly. + if (this.replacedStyleTop_) { + this.dialog_.style.top = ''; + this.replacedStyleTop_ = false; + } + + // Clear the backdrop and remove from the manager. + this.backdrop_.parentNode && this.backdrop_.parentNode.removeChild(this.backdrop_); + dialogPolyfill.dm.removeDialog(this); + }, + + /** + * @param {boolean} value whether to open or close this dialog + */ + setOpen: function(value) { + if (value) { + this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', ''); + } else { + this.dialog_.removeAttribute('open'); + this.maybeHideModal(); // nb. redundant with MutationObserver + } + }, + + /** + * Handles clicks on the fake .backdrop element, redirecting them as if + * they were on the dialog itself. + * + * @param {!Event} e to redirect + */ + backdropClick_: function(e) { + if (!this.dialog_.hasAttribute('tabindex')) { + // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be + // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this + // would not be needed - clicks would move the implicit cursor there. + var fake = document.createElement('div'); + this.dialog_.insertBefore(fake, this.dialog_.firstChild); + fake.tabIndex = -1; + fake.focus(); + this.dialog_.removeChild(fake); + } else { + this.dialog_.focus(); + } + + var redirectedEvent = document.createEvent('MouseEvents'); + redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window, + e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, + e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget); + this.dialog_.dispatchEvent(redirectedEvent); + e.stopPropagation(); + }, + + /** + * Focuses on the first focusable element within the dialog. This will always blur the current + * focus, even if nothing within the dialog is found. + */ + focus_: function() { + // Find element with `autofocus` attribute, or fall back to the first form/tabindex control. + var target = this.dialog_.querySelector('[autofocus]:not([disabled])'); + if (!target && this.dialog_.tabIndex >= 0) { + target = this.dialog_; + } + if (!target) { + // Note that this is 'any focusable area'. This list is probably not exhaustive, but the + // alternative involves stepping through and trying to focus everything. + var opts = ['button', 'input', 'keygen', 'select', 'textarea']; + var query = opts.map(function(el) { + return el + ':not([disabled])'; + }); + // TODO(samthor): tabindex values that are not numeric are not focusable. + query.push('[tabindex]:not([disabled]):not([tabindex=""])'); // tabindex != "", not disabled + target = this.dialog_.querySelector(query.join(', ')); + } + safeBlur(document.activeElement); + target && target.focus(); + }, + + /** + * Sets the zIndex for the backdrop and dialog. + * + * @param {number} dialogZ + * @param {number} backdropZ + */ + updateZIndex: function(dialogZ, backdropZ) { + if (dialogZ < backdropZ) { + throw new Error('dialogZ should never be < backdropZ'); + } + this.dialog_.style.zIndex = dialogZ; + this.backdrop_.style.zIndex = backdropZ; + }, + + /** + * Shows the dialog. If the dialog is already open, this does nothing. + */ + show: function() { + if (!this.dialog_.open) { + this.setOpen(true); + this.focus_(); + } + }, + + /** + * Show this dialog modally. + */ + showModal: function() { + if (this.dialog_.hasAttribute('open')) { + throw new Error('Failed to execute \'showModal\' on dialog: The element is already open, and therefore cannot be opened modally.'); + } + if (!document.body.contains(this.dialog_)) { + throw new Error('Failed to execute \'showModal\' on dialog: The element is not in a Document.'); + } + if (!dialogPolyfill.dm.pushDialog(this)) { + throw new Error('Failed to execute \'showModal\' on dialog: There are too many open modal dialogs.'); + } + + if (createsStackingContext(this.dialog_.parentElement)) { + console.warn('A dialog is being shown inside a stacking context. ' + + 'This may cause it to be unusable. For more information, see this link: ' + + 'https://github.com/GoogleChrome/dialog-polyfill/#stacking-context'); + } + + this.setOpen(true); + this.openAsModal_ = true; + + // Optionally center vertically, relative to the current viewport. + if (dialogPolyfill.needsCentering(this.dialog_)) { + dialogPolyfill.reposition(this.dialog_); + this.replacedStyleTop_ = true; + } else { + this.replacedStyleTop_ = false; + } + + // Insert backdrop. + this.dialog_.parentNode.insertBefore(this.backdrop_, this.dialog_.nextSibling); + + // Focus on whatever inside the dialog. + this.focus_(); + }, + + /** + * Closes this HTMLDialogElement. This is optional vs clearing the open + * attribute, however this fires a 'close' event. + * + * @param {string=} opt_returnValue to use as the returnValue + */ + close: function(opt_returnValue) { + if (!this.dialog_.hasAttribute('open')) { + throw new Error('Failed to execute \'close\' on dialog: The element does not have an \'open\' attribute, and therefore cannot be closed.'); + } + this.setOpen(false); + + // Leave returnValue untouched in case it was set directly on the element + if (opt_returnValue !== undefined) { + this.dialog_.returnValue = opt_returnValue; + } + + // Triggering "close" event for any attached listeners on the . + var closeEvent = new supportCustomEvent('close', { + bubbles: false, + cancelable: false + }); + this.dialog_.dispatchEvent(closeEvent); + } + + }; + + var dialogPolyfill = {}; + + dialogPolyfill.reposition = function(element) { + var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; + var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2; + element.style.top = Math.max(scrollTop, topValue) + 'px'; + }; + + dialogPolyfill.isInlinePositionSetByStylesheet = function(element) { + for (var i = 0; i < document.styleSheets.length; ++i) { + var styleSheet = document.styleSheets[i]; + var cssRules = null; + // Some browsers throw on cssRules. + try { + cssRules = styleSheet.cssRules; + } catch (e) {} + if (!cssRules) { continue; } + for (var j = 0; j < cssRules.length; ++j) { + var rule = cssRules[j]; + var selectedNodes = null; + // Ignore errors on invalid selector texts. + try { + selectedNodes = document.querySelectorAll(rule.selectorText); + } catch(e) {} + if (!selectedNodes || !inNodeList(selectedNodes, element)) { + continue; + } + var cssTop = rule.style.getPropertyValue('top'); + var cssBottom = rule.style.getPropertyValue('bottom'); + if ((cssTop && cssTop !== 'auto') || (cssBottom && cssBottom !== 'auto')) { + return true; + } + } + } + return false; + }; + + dialogPolyfill.needsCentering = function(dialog) { + var computedStyle = window.getComputedStyle(dialog); + if (computedStyle.position !== 'absolute') { + return false; + } + + // We must determine whether the top/bottom specified value is non-auto. In + // WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but + // Firefox returns the used value. So we do this crazy thing instead: check + // the inline style and then go through CSS rules. + if ((dialog.style.top !== 'auto' && dialog.style.top !== '') || + (dialog.style.bottom !== 'auto' && dialog.style.bottom !== '')) { + return false; + } + return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog); + }; + + /** + * @param {!Element} element to force upgrade + */ + dialogPolyfill.forceRegisterDialog = function(element) { + if (window.HTMLDialogElement || element.showModal) { + console.warn('This browser already supports , the polyfill ' + + 'may not work correctly', element); + } + if (element.localName !== 'dialog') { + throw new Error('Failed to register dialog: The element is not a dialog.'); + } + new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ (element)); + }; + + /** + * @param {!Element} element to upgrade, if necessary + */ + dialogPolyfill.registerDialog = function(element) { + if (!element.showModal) { + dialogPolyfill.forceRegisterDialog(element); + } + }; + + /** + * @constructor + */ + dialogPolyfill.DialogManager = function() { + /** @type {!Array} */ + this.pendingDialogStack = []; + + var checkDOM = this.checkDOM_.bind(this); + + // The overlay is used to simulate how a modal dialog blocks the document. + // The blocking dialog is positioned on top of the overlay, and the rest of + // the dialogs on the pending dialog stack are positioned below it. In the + // actual implementation, the modal dialog stacking is controlled by the + // top layer, where z-index has no effect. + this.overlay = document.createElement('div'); + this.overlay.className = '_dialog_overlay'; + this.overlay.addEventListener('click', function(e) { + this.forwardTab_ = undefined; + e.stopPropagation(); + checkDOM([]); // sanity-check DOM + }.bind(this)); + + this.handleKey_ = this.handleKey_.bind(this); + this.handleFocus_ = this.handleFocus_.bind(this); + + this.zIndexLow_ = 100000; + this.zIndexHigh_ = 100000 + 150; + + this.forwardTab_ = undefined; + + if ('MutationObserver' in window) { + this.mo_ = new MutationObserver(function(records) { + var removed = []; + records.forEach(function(rec) { + for (var i = 0, c; c = rec.removedNodes[i]; ++i) { + if (!(c instanceof Element)) { + continue; + } else if (c.localName === 'dialog') { + removed.push(c); + } + removed = removed.concat(c.querySelectorAll('dialog')); + } + }); + removed.length && checkDOM(removed); + }); + } + }; + + /** + * Called on the first modal dialog being shown. Adds the overlay and related + * handlers. + */ + dialogPolyfill.DialogManager.prototype.blockDocument = function() { + document.documentElement.addEventListener('focus', this.handleFocus_, true); + document.addEventListener('keydown', this.handleKey_); + this.mo_ && this.mo_.observe(document, {childList: true, subtree: true}); + }; + + /** + * Called on the first modal dialog being removed, i.e., when no more modal + * dialogs are visible. + */ + dialogPolyfill.DialogManager.prototype.unblockDocument = function() { + document.documentElement.removeEventListener('focus', this.handleFocus_, true); + document.removeEventListener('keydown', this.handleKey_); + this.mo_ && this.mo_.disconnect(); + }; + + /** + * Updates the stacking of all known dialogs. + */ + dialogPolyfill.DialogManager.prototype.updateStacking = function() { + var zIndex = this.zIndexHigh_; + + for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) { + dpi.updateZIndex(--zIndex, --zIndex); + if (i === 0) { + this.overlay.style.zIndex = --zIndex; + } + } + + // Make the overlay a sibling of the dialog itself. + var last = this.pendingDialogStack[0]; + if (last) { + var p = last.dialog.parentNode || document.body; + p.appendChild(this.overlay); + } else if (this.overlay.parentNode) { + this.overlay.parentNode.removeChild(this.overlay); + } + }; + + /** + * @param {Element} candidate to check if contained or is the top-most modal dialog + * @return {boolean} whether candidate is contained in top dialog + */ + dialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function(candidate) { + while (candidate = findNearestDialog(candidate)) { + for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) { + if (dpi.dialog === candidate) { + return i === 0; // only valid if top-most + } + } + candidate = candidate.parentElement; + } + return false; + }; + + dialogPolyfill.DialogManager.prototype.handleFocus_ = function(event) { + if (this.containedByTopDialog_(event.target)) { return; } + + event.preventDefault(); + event.stopPropagation(); + safeBlur(/** @type {Element} */ (event.target)); + + if (this.forwardTab_ === undefined) { return; } // move focus only from a tab key + + var dpi = this.pendingDialogStack[0]; + var dialog = dpi.dialog; + var position = dialog.compareDocumentPosition(event.target); + if (position & Node.DOCUMENT_POSITION_PRECEDING) { + if (this.forwardTab_) { // forward + dpi.focus_(); + } else { // backwards + document.documentElement.focus(); + } + } else { + // TODO: Focus after the dialog, is ignored. + } + + return false; + }; + + dialogPolyfill.DialogManager.prototype.handleKey_ = function(event) { + this.forwardTab_ = undefined; + if (event.keyCode === 27) { + event.preventDefault(); + event.stopPropagation(); + var cancelEvent = new supportCustomEvent('cancel', { + bubbles: false, + cancelable: true + }); + var dpi = this.pendingDialogStack[0]; + if (dpi && dpi.dialog.dispatchEvent(cancelEvent)) { + dpi.dialog.close(); + } + } else if (event.keyCode === 9) { + this.forwardTab_ = !event.shiftKey; + } + }; + + /** + * Finds and downgrades any known modal dialogs that are no longer displayed. Dialogs that are + * removed and immediately readded don't stay modal, they become normal. + * + * @param {!Array} removed that have definitely been removed + */ + dialogPolyfill.DialogManager.prototype.checkDOM_ = function(removed) { + // This operates on a clone because it may cause it to change. Each change also calls + // updateStacking, which only actually needs to happen once. But who removes many modal dialogs + // at a time?! + var clone = this.pendingDialogStack.slice(); + clone.forEach(function(dpi) { + if (removed.indexOf(dpi.dialog) !== -1) { + dpi.downgradeModal(); + } else { + dpi.maybeHideModal(); + } + }); + }; + + /** + * @param {!dialogPolyfillInfo} dpi + * @return {boolean} whether the dialog was allowed + */ + dialogPolyfill.DialogManager.prototype.pushDialog = function(dpi) { + var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1; + if (this.pendingDialogStack.length >= allowed) { + return false; + } + if (this.pendingDialogStack.unshift(dpi) === 1) { + this.blockDocument(); + } + this.updateStacking(); + return true; + }; + + /** + * @param {!dialogPolyfillInfo} dpi + */ + dialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) { + var index = this.pendingDialogStack.indexOf(dpi); + if (index === -1) { return; } + + this.pendingDialogStack.splice(index, 1); + if (this.pendingDialogStack.length === 0) { + this.unblockDocument(); + } + this.updateStacking(); + }; + + dialogPolyfill.dm = new dialogPolyfill.DialogManager(); + dialogPolyfill.formSubmitter = null; + dialogPolyfill.useValue = null; + + /** + * Installs global handlers, such as click listers and native method overrides. These are needed + * even if a no dialog is registered, as they deal with
. + */ + if (window.HTMLDialogElement === undefined) { + + /** + * If HTMLFormElement translates method="DIALOG" into 'get', then replace the descriptor with + * one that returns the correct value. + */ + var testForm = document.createElement('form'); + testForm.setAttribute('method', 'dialog'); + if (testForm.method !== 'dialog') { + var methodDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'method'); + if (methodDescriptor) { + // nb. Some older iOS and older PhantomJS fail to return the descriptor. Don't do anything + // and don't bother to update the element. + var realGet = methodDescriptor.get; + methodDescriptor.get = function() { + if (isFormMethodDialog(this)) { + return 'dialog'; + } + return realGet.call(this); + }; + var realSet = methodDescriptor.set; + methodDescriptor.set = function(v) { + if (typeof v === 'string' && v.toLowerCase() === 'dialog') { + return this.setAttribute('method', v); + } + return realSet.call(this, v); + }; + Object.defineProperty(HTMLFormElement.prototype, 'method', methodDescriptor); + } + } + + /** + * Global 'click' handler, to capture the or +
+ + + + + + + \ No newline at end of file diff --git a/media-server-demo-node/www/playback/js/playback.js b/media-server-demo-node/www/playback/js/playback.js new file mode 100644 index 0000000..b824c8e --- /dev/null +++ b/media-server-demo-node/www/playback/js/playback.js @@ -0,0 +1,134 @@ +const url = "wss://"+window.location.hostname+":"+window.location.port; + +const roomId = (new Date()).getTime() + "-" + Math.random(); + + +function addVideoForStream(stream,muted) +{ + //Create new video element + const video = document.querySelector (muted ? "#local" : "#remote"); + //Set same id + video.id = stream.id; + //Set src stream + video.srcObject = stream; + //Set other properties + video.autoplay = true; + video.muted = muted; +} +function removeVideoForStream(stream) +{ + //Get video + var video = document.getElementById(stream.id); + //Remove it when done + video.addEventListener('webkitTransitionEnd',function(){ + //Delete it + video.parentElement.removeChild(video); + }); + //Disable it first + video.className = "disabled"; +} + +var sdp; +var pc; + +function connect() +{ + + if (window.RTCPeerConnection) + pc = new RTCPeerConnection({ + bundlePolicy: "max-bundle", + rtcpMuxPolicy : "require" + }); + else + pc = new webkitRTCPeerConnection(null); + + var ws = new WebSocket(url,"playback"); + + pc.onaddstream = function(event) { + var prev = 0; + console.debug("onAddStream",event); + //Play it + addVideoForStream(event.stream); + + + }; + + pc.onremovestream = function(event) { + console.debug("onRemoveStream",event); + //Play it + removeVideoForStream(event.stream); + }; + + ws.onopen = function(){ + console.log("opened"); + + //Create new offer + pc.createOffer({ + offerToReceiveAudio: true, + offerToReceiveVideo: true + }) + .then(function(offer){ + console.debug("createOffer sucess",offer); + //We have sdp + sdp = offer.sdp; + //Set it + pc.setLocalDescription(offer); + console.log(sdp); + //Create room + ws.send(JSON.stringify({ + cmd : "OFFER", + offer : sdp + })); + }) + .catch(function(error){ + console.error("Error",error); + }); + }; + + ws.onmessage = function(event){ + console.log(event); + + //Get protocol message + const msg = JSON.parse(event.data); + + console.log(msg.answer); + pc.setRemoteDescription(new RTCSessionDescription({ + type:'answer', + sdp: msg.answer + }), function () { + console.log("JOINED"); + + }, function (err) { + console.error("Error joining",err); + } + ); + pc.addEventListener("connectionstatechange",(event)=>{ + if (pc.connectionState=="connected") + { + console.log("CONNECTED"); + //Start playing + ws.send(JSON.stringify({ + cmd : "PLAY" + })); + } + }); + + }; +} + +var dialog = document.querySelector('dialog'); +if (dialog.showModal) +{ + dialog.showModal(); + dialog.querySelector('.ready').addEventListener('click', function() { + dialog.close(); + connect(); + }); +} else { + connect(); +} + + + + + diff --git a/media-server-demo-node/www/rec/index.html b/media-server-demo-node/www/rec/index.html new file mode 100644 index 0000000..2e6565f --- /dev/null +++ b/media-server-demo-node/www/rec/index.html @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + +
+
LOCAL + +
+
REMOTE + +
+ +

Ready to test MP4 recording?

+
+

+ This demo allows you to test the MP4 Recording of the Medooze Media Server. +

+

+ The call will be recorded on an mp4 file located at the /tmp folder of the server you are running the Medooze Media Server. +

+
+
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/media-server-demo-node/www/rec/js/rec.js b/media-server-demo-node/www/rec/js/rec.js new file mode 100644 index 0000000..5d23e96 --- /dev/null +++ b/media-server-demo-node/www/rec/js/rec.js @@ -0,0 +1,159 @@ +const url = "wss://"+window.location.hostname+":"+window.location.port; + +let videoResolution = true; +//Get our url +const href = new URL (window.location.href); +if (href.searchParams.has ("video")) + switch (href.searchParams.get ("video").toLowerCase ()) + { + case "1080p": + videoResolution = { + width: {min: 1920, max: 1920}, + height: {min: 1080, max: 1080}, + }; + break; + case "720p": + videoResolution = { + width: {min: 1280, max: 1280}, + height: {min: 720, max: 720}, + }; + break; + case "576p": + videoResolution = { + width: {min: 720, max: 720}, + height: {min: 576, max: 576}, + }; + break; + case "480p": + videoResolution = { + width: {min: 640, max: 640}, + height: {min: 480, max: 480}, + }; + break; + case "no": + videoResolution = false; + break; + } +const roomId = (new Date()).getTime() + "-" + Math.random(); + +var texts = document.querySelectorAll('.gaugeChartLabel'); + +function addVideoForStream(stream,muted) +{ + //Create new video element + const video = document.querySelector (muted ? "#local" : "#remote"); + //Set src stream + video.srcObject = stream; + //Set other properties + video.autoplay = true; + video.muted = muted; +} +function removeVideoForStream(stream) +{ + //Get video + var video = document.getElementById(stream.id); + //Remove it when done + video.addEventListener('webkitTransitionEnd',function(){ + //Delete it + video.parentElement.removeChild(video); + }); + //Disable it first + video.className = "disabled"; +} + +var sdp; +var pc; + +function connect() +{ + pc = new RTCPeerConnection(null); + + var ws = new WebSocket(url,"rec"); + + pc.onaddstream = function(event) { + var prev = 0; + console.debug("onAddStream",event); + //Play it + addVideoForStream(event.stream); + + + }; + + pc.onremovestream = function(event) { + console.debug("onRemoveStream",event); + //Play it + removeVideoForStream(event.stream); + }; + + ws.onopen = function(){ + console.log("opened"); + + navigator.mediaDevices.getUserMedia({ + audio: true, + video: videoResolution + }) + .then(function(stream){ + var prev = 0; + console.debug("getUserMedia sucess",stream); + //Play it + addVideoForStream(stream,true); + window.s = stream; + //Add stream to peer connection + pc.addStream(stream); + //Create new offer + return pc.createOffer(stream); + }) + .then(function(offer){ + console.debug("createOffer sucess",offer); + //We have sdp + sdp = offer.sdp; + //Set it + pc.setLocalDescription(offer); + console.log(sdp); + //Create room + ws.send(JSON.stringify({ + cmd : "OFFER", + offer : sdp + })); + }) + .catch(function(error){ + console.error("Error",error); + alert(error); + }); + }; + + ws.onmessage = function(event){ + console.log(event); + + //Get protocol message + const msg = JSON.parse(event.data); + + console.log(msg.answer); + pc.setRemoteDescription(new RTCSessionDescription({ + type:'answer', + sdp: msg.answer + }), function () { + console.log("JOINED"); + }, function (err) { + console.error("Error joining",err); + } + ); + }; +} + +var dialog = document.querySelector('dialog'); +if (dialog.showModal) +{ + dialog.showModal(); + dialog.querySelector('.ready').addEventListener('click', function() { + dialog.close(); + connect(); + }); +} else { + connect(); +} + + + + + diff --git a/media-server-demo-node/www/simulcast/index.html b/media-server-demo-node/www/simulcast/index.html new file mode 100644 index 0000000..1014fda --- /dev/null +++ b/media-server-demo-node/www/simulcast/index.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + +
+
LOCAL + +
+
REMOTE + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FPS/4FPS/2FPS
High
Medium
Low
+
+ + - - + Sent Width + 0 + 640 +
+ + +
+ + - - + Sent Height + 0 + 480 +
+ + +
+ + - - + Sent FPS + 0 + 30 +
+ + +
+ + - - + Sent kbps + 0 + 1024 +
+ + +
+ + - - + Recv Width + 0 + 640 +
+
+ + - - + Recv Heigth + 0 + 480 +
+
+ + - - + Recv FPS + 0 + 30 +
+
+ + - - + Recv kbps + 0 + 1024 +
+
+ +

Ready to test VP8 Simulcast layer selection?

+
+

+ This demo allows you to test the P8 Simulcast layer selection. +

+

+ This uses standard WebRTC rid/encodings api, so it only works on firefox currently +

+
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/media-server-demo-node/www/simulcast/js/simulcast.js b/media-server-demo-node/www/simulcast/js/simulcast.js new file mode 100644 index 0000000..535cde4 --- /dev/null +++ b/media-server-demo-node/www/simulcast/js/simulcast.js @@ -0,0 +1,427 @@ +const url = "wss://"+window.location.hostname+":"+window.location.port; + +let videoResolution = true; +//Get our url +const href = new URL (window.location.href); +if (href.searchParams.has ("video")) + switch (href.searchParams.get ("video").toLowerCase ()) + { + case "1080p": + videoResolution = { + width: {min: 1920, max: 1920}, + height: {min: 1080, max: 1080}, + }; + break; + case "720p": + videoResolution = { + width: {min: 1280, max: 1280}, + height: {min: 720, max: 720}, + }; + break; + case "576p": + videoResolution = { + width: {min: 720, max: 720}, + height: {min: 576, max: 576}, + }; + break; + case "480p": + videoResolution = { + width: {min: 640, max: 640}, + height: {min: 480, max: 480}, + }; + break; + case "no": + videoResolution = false; + break; + } +var opts = { + lines: 12, // The number of lines to draw + angle: 0.15, // The length of each line + lineWidth: 0.44, // 0.44 The line thickness + pointer: { + length: 0.8, // 0.9 The radius of the inner circle + strokeWidth: 0.035, // The rotation offset + color: '#A0A0A0' // Fill color + }, + limitMax: true, + colorStart: '#28c1d1', // Colors + colorStop: '#28c1d1', // just experiment with them + strokeColor: '#F0F0F0', // to see which ones work best for you + generateGradient: false, + gradientType: 0 +}; +var targets = document.querySelectorAll('.gaugeChart'); // your canvas element +var gauges = []; +for (var i=0;i pc.addTrack(track, stream)); + + //Check API "compatibility" + if (pc.getSenders()[0].setParameters) + { + try { + //Enable simulcast + pc.getSenders()[0].setParameters({ + encodings: [ + { rid: "a"}, + { rid: "b" , scaleDownResolutionBy: 2.0 }, + { rid: "c" , scaleDownResolutionBy: 4.0 } + ] + }); + } catch(e) { + } + } + //Create new offer + return pc.createOffer(); + }) + .then(function(offer){ + console.debug("createOffer sucess",offer); + + //Get offer + let sdp = offer.sdp; + + //Check simulcast 04 format + if (sdp.indexOf(": send rid")) + { + //Convert from simulcast_03 to simulcast + sdp = sdp.replace(": send rid=",":send "); + //We need to modify answer too + simulcast_03 = true; + } + + //If offer doesn't have simulcast + if (sdp.indexOf("simulcast")==-1) + try { + //OK, chrome way + const reg1 = RegExp("m=video.*\?a=ssrc:(\\d*) cname:(.+?)\\r\\n","s"); + const reg2 = RegExp("m=video.*\?a=ssrc:(\\d*) mslabel:(.+?)\\r\\n","s"); + const reg3 = RegExp("m=video.*\?a=ssrc:(\\d*) msid:(.+?)\\r\\n","s"); + const reg4 = RegExp("m=video.*\?a=ssrc:(\\d*) label:(.+?)\\r\\n","s"); + //Get ssrc and cname + let res = reg1.exec(sdp); + const ssrc = res[1]; + const cname = res[2]; + //Get other params + const mslabel = reg2.exec(sdp)[2]; + const msid = reg3.exec(sdp)[2]; + const label = reg4.exec(sdp)[2]; + //Add simulcasts ssrcs + const num = 2; + const ssrcs = [ssrc]; + + for (let i=0;i + + + + + + + + + + + + +
+
LOCAL + +
+
REMOTE + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FPS/4FPS/2FPS
320x240
640x480
1280x960
+
+ + - - + Sent Width + 0 + +
+ + +
+ + - - + Sent Height + 0 + +
+ + +
+ + - - + Sent FPS + 0 + 30 +
+ + +
+ + - - + Sent kbps + 0 + 2048 +
+ + +
+ + - - + Recv Width + 0 + +
+
+ + - - + Recv Heigth + 0 + +
+
+ + - - + Recv FPS + 0 + 30 +
+
+ + - - + Recv kbps + 0 + 2048 +
+
+ +

Ready to test VP9 SVC layer selection?

+
+

+ This demo allows you to test the VP9 SVC layer selection which will be a key functionality of the Medooze SFU. +

+

+ When SVC is enabled on latest Chrome version, it will send 3 different Temporal Layers and 2 Spatial layers. Chrome will send a single media stream to our SFU containing all SVC layers, and you will be able to select which layers the SFU send backs to you. +

+

+ By swithcing between the different layers you will be able to retrieve different sizes and fps from a single encoding, lowering the bandwith requirements without requiring extra processing power. +

+

+ Important: This demo only works only works on Google Canary and you must enable VP9 SVC by running it with the following command line +

+
+				
+ chrome.exe --force-fieldtrials=WebRTC-SupportVP9SVC/EnabledByFlag_2SL3TL	
+				
+			
+
+
+ +
+
+ + + + + diff --git a/media-server-demo-node/www/svc/js/svc.js b/media-server-demo-node/www/svc/js/svc.js new file mode 100644 index 0000000..443fed7 --- /dev/null +++ b/media-server-demo-node/www/svc/js/svc.js @@ -0,0 +1,340 @@ +const url = "wss://"+window.location.hostname+":"+window.location.port; + +let videoResolution = true; +//Get our url +const href = new URL (window.location.href); +if (href.searchParams.has ("video")) + switch (href.searchParams.get ("video").toLowerCase ()) + { + case "1080p": + videoResolution = { + width: {min: 1920, max: 1920}, + height: {min: 1080, max: 1080}, + }; + break; + case "720p": + videoResolution = { + width: {min: 1280, max: 1280}, + height: {min: 720, max: 720}, + }; + break; + case "576p": + videoResolution = { + width: {min: 720, max: 720}, + height: {min: 576, max: 576}, + }; + break; + case "480p": + videoResolution = { + width: {min: 640, max: 640}, + height: {min: 480, max: 480}, + }; + break; + case "no": + videoResolution = false; + break; + } +var opts = { + lines: 12, // The number of lines to draw + angle: 0.15, // The length of each line + lineWidth: 0.44, // 0.44 The line thickness + pointer: { + length: 0.8, // 0.9 The radius of the inner circle + strokeWidth: 0.035, // The rotation offset + color: '#A0A0A0' // Fill color + }, + limitMax: true, + colorStart: '#28c1d1', // Colors + colorStop: '#28c1d1', // just experiment with them + strokeColor: '#F0F0F0', // to see which ones work best for you + generateGradient: false, + gradientType: 0 +}; +var targets = document.querySelectorAll('.gaugeChart'); // your canvas element +var gauges = []; +for (var i=0;i