From edc50ea20edaa39989693005505c849afa027ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=83=E6=8B=89?= <939313737@qq.com> Date: Fri, 12 Sep 2025 21:45:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 43 + Dockerfile | 24 + LICENSE | 373 +++++ README.md | 43 + doc/page/am.html | 139 ++ doc/page/index.html | 202 +++ doc/readme.md | 71 + doc/sql/boot-im.sql | 215 +++ pom.xml | 270 ++++ .../core/handlers/MybatisEnumTypeHandler.java | 138 ++ .../com/platform/AppServletInitializer.java | 15 + src/main/java/com/platform/AppStartUp.java | 18 + .../com/platform/NoWarnMyBatisMapper.java | 10 + .../platform/common/aspectj/IgnoreAuth.java | 13 + .../platform/common/aspectj/SubmitAspect.java | 75 + .../platform/common/aspectj/SubmitRepeat.java | 39 + .../common/config/ApplicationConfig.java | 110 ++ .../common/config/MybatisPlusConfig.java | 45 + .../common/config/PlatformConfig.java | 58 + .../platform/common/config/WebMvcConfig.java | 64 + .../common/constant/AppConstants.java | 183 +++ .../common/constant/HeadConstant.java | 43 + .../com/platform/common/core/CharsetKit.java | 89 ++ .../com/platform/common/core/EnumUtils.java | 30 + .../com/platform/common/enums/DeviceEnum.java | 53 + .../com/platform/common/enums/GenderEnum.java | 36 + .../platform/common/enums/ResultCodeEnum.java | 62 + .../platform/common/enums/YesOrNoEnum.java | 45 + .../common/exception/BaseException.java | 48 + .../common/exception/LoginException.java | 20 + .../platform/common/redis/GeoHashUtils.java | 148 ++ .../java/com/platform/common/redis/GeoVo.java | 10 + .../platform/common/redis/RedisConfig.java | 31 + .../common/redis/RedisListenerConfig.java | 19 + .../com/platform/common/redis/RedisUtils.java | 1372 +++++++++++++++++ .../com/platform/common/shiro/LoginUser.java | 33 + .../com/platform/common/shiro/Md5Utils.java | 49 + .../common/shiro/ShiroConfiguration.java | 110 ++ .../platform/common/shiro/ShiroLoginAuth.java | 30 + .../common/shiro/ShiroLoginPhone.java | 28 + .../common/shiro/ShiroLoginToken.java | 26 + .../com/platform/common/shiro/ShiroRealm.java | 129 ++ .../common/shiro/ShiroTokenFilter.java | 126 ++ .../com/platform/common/shiro/ShiroUtils.java | 45 + .../common/upload/config/UploadConfig.java | 45 + .../common/upload/enums/UploadTypeEnum.java | 53 + .../common/upload/service/UploadService.java | 54 + .../service/impl/UploadBaseService.java | 171 ++ .../service/impl/UploadCosServiceImpl.java | 154 ++ .../service/impl/UploadFastServiceImpl.java | 92 ++ .../service/impl/UploadKodoServiceImpl.java | 130 ++ .../service/impl/UploadLocalServiceImpl.java | 89 ++ .../service/impl/UploadMinioServiceImpl.java | 119 ++ .../service/impl/UploadOssServiceImpl.java | 126 ++ .../common/upload/utils/FastUtils.java | 48 + .../common/upload/vo/UploadAudioVo.java | 18 + .../common/upload/vo/UploadFileVo.java | 26 + .../common/upload/vo/UploadVideoVo.java | 30 + .../platform/common/utils/BeanCopyUtils.java | 34 + .../com/platform/common/utils/IpUtils.java | 154 ++ .../platform/common/utils/MessageUtils.java | 22 + .../platform/common/utils/ServletUtils.java | 74 + .../com/platform/common/utils/TimerUtils.java | 39 + .../platform/common/version/ApiVersion.java | 20 + .../common/version/DeviceInterceptor.java | 52 + .../common/version/SignInterceptor.java | 80 + .../common/version/VersionCondition.java | 43 + .../common/version/VersionConfig.java | 76 + .../platform/common/version/VersionEnum.java | 27 + .../common/version/VersionHandlerMapping.java | 25 + .../common/version/VersionInterceptor.java | 51 + .../platform/common/version/VersionUtils.java | 103 ++ .../common/web/aspectj/MybatisAspectj.java | 27 + .../common/web/controller/BaseController.java | 107 ++ .../web/controller/ErrorController.java | 33 + .../com/platform/common/web/dao/BaseDao.java | 44 + .../common/web/domain/AjaxResult.java | 129 ++ .../common/web/domain/BaseEntity.java | 63 + .../web/domain/JsonDateDeserializer.java | 46 + .../web/exception/ErrorAttributesCustom.java | 18 + .../web/exception/GlobalExceptionHandler.java | 47 + .../web/interceptor/CustomizationBean.java | 20 + .../platform/common/web/page/PageDomain.java | 62 + .../common/web/page/TableDataInfo.java | 55 + .../common/web/page/TableSupport.java | 41 + .../common/web/service/BaseService.java | 120 ++ .../web/service/impl/BaseServiceImpl.java | 206 +++ .../auth/controller/AuthController.java | 131 ++ .../modules/auth/service/TokenService.java | 44 + .../auth/service/impl/TokenServiceImpl.java | 75 + .../platform/modules/auth/vo/AuthVo01.java | 36 + .../platform/modules/auth/vo/AuthVo02.java | 22 + .../platform/modules/auth/vo/AuthVo03.java | 22 + .../platform/modules/auth/vo/AuthVo04.java | 28 + .../modules/chat/config/AmapConfig.java | 17 + .../modules/chat/config/TencentConfig.java | 23 + .../chat/controller/ApplyController.java | 91 ++ .../chat/controller/ChatController.java | 35 + .../chat/controller/FriendController.java | 95 ++ .../chat/controller/GroupController.java | 193 +++ .../modules/chat/controller/MyController.java | 183 +++ .../modules/chat/dao/ChatApplyDao.java | 23 + .../modules/chat/dao/ChatFeedbackDao.java | 23 + .../modules/chat/dao/ChatFriendDao.java | 34 + .../modules/chat/dao/ChatGroupDao.java | 30 + .../modules/chat/dao/ChatGroupInfoDao.java | 23 + .../platform/modules/chat/dao/ChatMsgDao.java | 23 + .../modules/chat/dao/ChatUserDao.java | 23 + .../modules/chat/dao/ChatVersionDao.java | 23 + .../modules/chat/domain/ChatApply.java | 65 + .../modules/chat/domain/ChatFeedback.java | 50 + .../modules/chat/domain/ChatFriend.java | 63 + .../modules/chat/domain/ChatGroup.java | 50 + .../modules/chat/domain/ChatGroupInfo.java | 66 + .../platform/modules/chat/domain/ChatMsg.java | 59 + .../modules/chat/domain/ChatUser.java | 123 ++ .../modules/chat/domain/ChatVersion.java | 38 + .../modules/chat/enums/ApplySourceEnum.java | 57 + .../modules/chat/enums/ApplyStatusEnum.java | 41 + .../modules/chat/enums/ApplyTypeEnum.java | 33 + .../modules/chat/enums/FriendTypeEnum.java | 41 + .../modules/chat/enums/MsgStatusEnum.java | 53 + .../modules/chat/enums/VersionTypeEnum.java | 33 + .../chat/service/ChatApplyService.java | 31 + .../chat/service/ChatFeedbackService.java | 20 + .../chat/service/ChatFriendService.java | 82 + .../chat/service/ChatGroupInfoService.java | 53 + .../chat/service/ChatGroupService.java | 108 ++ .../modules/chat/service/ChatMsgService.java | 27 + .../modules/chat/service/ChatTalkService.java | 32 + .../modules/chat/service/ChatUserService.java | 73 + .../chat/service/ChatVersionService.java | 25 + .../chat/service/ChatWeatherService.java | 20 + .../service/impl/ChatApplyServiceImpl.java | 137 ++ .../service/impl/ChatFeedbackServiceImpl.java | 51 + .../service/impl/ChatFriendServiceImpl.java | 400 +++++ .../impl/ChatGroupInfoServiceImpl.java | 114 ++ .../service/impl/ChatGroupServiceImpl.java | 541 +++++++ .../chat/service/impl/ChatMsgServiceImpl.java | 253 +++ .../service/impl/ChatTalkServiceImpl.java | 156 ++ .../service/impl/ChatUserServiceImpl.java | 245 +++ .../service/impl/ChatVersionServiceImpl.java | 72 + .../service/impl/ChatWeatherServiceImpl.java | 67 + .../modules/chat/utils/TencentUtils.java | 89 ++ .../platform/modules/chat/vo/ApplyVo01.java | 13 + .../platform/modules/chat/vo/ApplyVo02.java | 59 + .../platform/modules/chat/vo/ApplyVo03.java | 82 + .../platform/modules/chat/vo/ChatVo01.java | 23 + .../platform/modules/chat/vo/ChatVo02.java | 22 + .../platform/modules/chat/vo/ChatVo03.java | 33 + .../platform/modules/chat/vo/ChatVo04.java | 27 + .../platform/modules/chat/vo/FriendVo01.java | 13 + .../platform/modules/chat/vo/FriendVo02.java | 23 + .../platform/modules/chat/vo/FriendVo03.java | 17 + .../platform/modules/chat/vo/FriendVo04.java | 13 + .../platform/modules/chat/vo/FriendVo05.java | 19 + .../platform/modules/chat/vo/FriendVo06.java | 35 + .../platform/modules/chat/vo/FriendVo07.java | 84 + .../platform/modules/chat/vo/FriendVo08.java | 10 + .../platform/modules/chat/vo/FriendVo09.java | 17 + .../platform/modules/chat/vo/GroupVo01.java | 16 + .../platform/modules/chat/vo/GroupVo02.java | 18 + .../platform/modules/chat/vo/GroupVo03.java | 18 + .../platform/modules/chat/vo/GroupVo04.java | 16 + .../platform/modules/chat/vo/GroupVo05.java | 16 + .../platform/modules/chat/vo/GroupVo06.java | 16 + .../platform/modules/chat/vo/GroupVo07.java | 29 + .../platform/modules/chat/vo/GroupVo08.java | 25 + .../com/platform/modules/chat/vo/MyVo01.java | 22 + .../com/platform/modules/chat/vo/MyVo02.java | 15 + .../com/platform/modules/chat/vo/MyVo03.java | 15 + .../com/platform/modules/chat/vo/MyVo04.java | 19 + .../com/platform/modules/chat/vo/MyVo05.java | 14 + .../com/platform/modules/chat/vo/MyVo06.java | 15 + .../com/platform/modules/chat/vo/MyVo07.java | 15 + .../com/platform/modules/chat/vo/MyVo08.java | 19 + .../com/platform/modules/chat/vo/MyVo09.java | 59 + .../platform/modules/chat/vo/VersionVo.java | 32 + .../collect/controller/CollectController.java | 58 + .../modules/collect/dao/ChatCollectDao.java | 23 + .../modules/collect/domain/ChatCollect.java | 47 + .../collect/enums/CollectTypeEnum.java | 53 + .../collect/service/ChatCollectService.java | 31 + .../service/impl/ChatCollectServiceImpl.java | 77 + .../modules/collect/vo/CollectVo01.java | 20 + .../modules/collect/vo/CollectVo02.java | 36 + .../modules/common/config/TrtcConfig.java | 21 + .../common/controller/CommonController.java | 52 + .../common/controller/FileController.java | 65 + .../common/controller/TrtcController.java | 34 + .../modules/common/service/FileService.java | 28 + .../modules/common/service/TrtcService.java | 16 + .../common/service/impl/FileServiceImpl.java | 64 + .../common/service/impl/TrtcServiceImpl.java | 90 ++ .../platform/modules/common/vo/TrtcVo.java | 27 + .../modules/push/enums/PushBodyEnum.java | 29 + .../modules/push/enums/PushMsgEnum.java | 77 + .../modules/push/enums/PushNoticeEnum.java | 37 + .../modules/push/enums/PushTalkEnum.java | 33 + .../modules/push/service/ChatPushService.java | 52 + .../service/impl/ChatPushServiceImpl.java | 169 ++ .../platform/modules/push/vo/PushBigVo.java | 18 + .../platform/modules/push/vo/PushBodyVo.java | 56 + .../platform/modules/push/vo/PushFromVo.java | 33 + .../platform/modules/push/vo/PushMsgVo.java | 34 + .../modules/push/vo/PushNoticeVo.java | 27 + .../platform/modules/push/vo/PushParamVo.java | 60 + .../platform/modules/push/vo/PushToVo.java | 28 + .../shake/controller/NearController.java | 45 + .../shake/controller/ShakeController.java | 38 + .../modules/shake/service/NearService.java | 43 + .../modules/shake/service/ShakeService.java | 35 + .../shake/service/impl/NearServiceImpl.java | 107 ++ .../shake/service/impl/ShakeServiceImpl.java | 87 ++ .../platform/modules/shake/vo/NearVo01.java | 21 + .../platform/modules/shake/vo/NearVo02.java | 47 + .../platform/modules/shake/vo/ShakeVo01.java | 21 + .../platform/modules/shake/vo/ShakeVo02.java | 43 + .../modules/sms/enums/SmsTemplateEnum.java | 25 + .../modules/sms/enums/SmsTypeEnum.java | 39 + .../modules/sms/service/SmsService.java | 41 + .../sms/service/impl/SmsServiceImpl.java | 97 ++ .../com/platform/modules/sms/vo/SmsVo.java | 26 + .../topic/controller/TopicController.java | 155 ++ .../modules/topic/dao/ChatTopicDao.java | 29 + .../modules/topic/dao/ChatTopicLikeDao.java | 29 + .../modules/topic/dao/ChatTopicReplyDao.java | 29 + .../modules/topic/domain/ChatTopic.java | 51 + .../modules/topic/domain/ChatTopicLike.java | 41 + .../modules/topic/domain/ChatTopicReply.java | 60 + .../topic/enums/TopicNoticeTypeEnum.java | 33 + .../topic/enums/TopicReplyTypeEnum.java | 33 + .../modules/topic/enums/TopicTypeEnum.java | 37 + .../topic/service/ChatTopicLikeService.java | 32 + .../topic/service/ChatTopicReplyService.java | 27 + .../topic/service/ChatTopicService.java | 72 + .../impl/ChatTopicLikeServiceImpl.java | 57 + .../impl/ChatTopicReplyServiceImpl.java | 72 + .../service/impl/ChatTopicServiceImpl.java | 453 ++++++ .../platform/modules/topic/vo/TopicVo01.java | 23 + .../platform/modules/topic/vo/TopicVo02.java | 15 + .../platform/modules/topic/vo/TopicVo03.java | 33 + .../platform/modules/topic/vo/TopicVo04.java | 46 + .../platform/modules/topic/vo/TopicVo05.java | 23 + .../platform/modules/topic/vo/TopicVo06.java | 59 + .../platform/modules/topic/vo/TopicVo07.java | 34 + .../platform/modules/topic/vo/TopicVo09.java | 51 + .../modules/ws/BootWebSocketHandler.java | 102 ++ .../modules/ws/BootWebSocketInterceptor.java | 39 + .../platform/modules/ws/WebSocketConfig.java | 38 + src/main/resources/application-dev.yml | 128 ++ src/main/resources/application.yml | 30 + src/main/resources/banner.txt | 3 + src/main/resources/logback-spring.xml | 73 + .../resources/mapper/chat/ChatApplyDao.xml | 26 + .../resources/mapper/chat/ChatFeedbackDao.xml | 42 + .../resources/mapper/chat/ChatFriendDao.xml | 33 + .../resources/mapper/chat/ChatGroupDao.xml | 27 + .../mapper/chat/ChatGroupInfoDao.xml | 23 + src/main/resources/mapper/chat/ChatMsgDao.xml | 32 + .../resources/mapper/chat/ChatUserDao.xml | 24 + .../resources/mapper/chat/ChatVersionDao.xml | 18 + .../mapper/collect/ChatCollectDao.xml | 23 + .../resources/mapper/topic/ChatTopicDao.xml | 43 + .../mapper/topic/ChatTopicLikeDao.xml | 29 + .../mapper/topic/ChatTopicReplyDao.xml | 42 + src/main/resources/static/favicon.ico | Bin 0 -> 4286 bytes 267 files changed, 17516 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 doc/page/am.html create mode 100644 doc/page/index.html create mode 100644 doc/readme.md create mode 100644 doc/sql/boot-im.sql create mode 100644 pom.xml create mode 100644 src/main/java/com/baomidou/mybatisplus/core/handlers/MybatisEnumTypeHandler.java create mode 100644 src/main/java/com/platform/AppServletInitializer.java create mode 100644 src/main/java/com/platform/AppStartUp.java create mode 100644 src/main/java/com/platform/NoWarnMyBatisMapper.java create mode 100644 src/main/java/com/platform/common/aspectj/IgnoreAuth.java create mode 100644 src/main/java/com/platform/common/aspectj/SubmitAspect.java create mode 100644 src/main/java/com/platform/common/aspectj/SubmitRepeat.java create mode 100644 src/main/java/com/platform/common/config/ApplicationConfig.java create mode 100644 src/main/java/com/platform/common/config/MybatisPlusConfig.java create mode 100644 src/main/java/com/platform/common/config/PlatformConfig.java create mode 100644 src/main/java/com/platform/common/config/WebMvcConfig.java create mode 100644 src/main/java/com/platform/common/constant/AppConstants.java create mode 100644 src/main/java/com/platform/common/constant/HeadConstant.java create mode 100644 src/main/java/com/platform/common/core/CharsetKit.java create mode 100644 src/main/java/com/platform/common/core/EnumUtils.java create mode 100644 src/main/java/com/platform/common/enums/DeviceEnum.java create mode 100644 src/main/java/com/platform/common/enums/GenderEnum.java create mode 100644 src/main/java/com/platform/common/enums/ResultCodeEnum.java create mode 100644 src/main/java/com/platform/common/enums/YesOrNoEnum.java create mode 100644 src/main/java/com/platform/common/exception/BaseException.java create mode 100644 src/main/java/com/platform/common/exception/LoginException.java create mode 100644 src/main/java/com/platform/common/redis/GeoHashUtils.java create mode 100644 src/main/java/com/platform/common/redis/GeoVo.java create mode 100644 src/main/java/com/platform/common/redis/RedisConfig.java create mode 100644 src/main/java/com/platform/common/redis/RedisListenerConfig.java create mode 100644 src/main/java/com/platform/common/redis/RedisUtils.java create mode 100644 src/main/java/com/platform/common/shiro/LoginUser.java create mode 100644 src/main/java/com/platform/common/shiro/Md5Utils.java create mode 100644 src/main/java/com/platform/common/shiro/ShiroConfiguration.java create mode 100644 src/main/java/com/platform/common/shiro/ShiroLoginAuth.java create mode 100644 src/main/java/com/platform/common/shiro/ShiroLoginPhone.java create mode 100644 src/main/java/com/platform/common/shiro/ShiroLoginToken.java create mode 100644 src/main/java/com/platform/common/shiro/ShiroRealm.java create mode 100644 src/main/java/com/platform/common/shiro/ShiroTokenFilter.java create mode 100644 src/main/java/com/platform/common/shiro/ShiroUtils.java create mode 100644 src/main/java/com/platform/common/upload/config/UploadConfig.java create mode 100644 src/main/java/com/platform/common/upload/enums/UploadTypeEnum.java create mode 100644 src/main/java/com/platform/common/upload/service/UploadService.java create mode 100644 src/main/java/com/platform/common/upload/service/impl/UploadBaseService.java create mode 100644 src/main/java/com/platform/common/upload/service/impl/UploadCosServiceImpl.java create mode 100644 src/main/java/com/platform/common/upload/service/impl/UploadFastServiceImpl.java create mode 100644 src/main/java/com/platform/common/upload/service/impl/UploadKodoServiceImpl.java create mode 100644 src/main/java/com/platform/common/upload/service/impl/UploadLocalServiceImpl.java create mode 100644 src/main/java/com/platform/common/upload/service/impl/UploadMinioServiceImpl.java create mode 100644 src/main/java/com/platform/common/upload/service/impl/UploadOssServiceImpl.java create mode 100644 src/main/java/com/platform/common/upload/utils/FastUtils.java create mode 100644 src/main/java/com/platform/common/upload/vo/UploadAudioVo.java create mode 100644 src/main/java/com/platform/common/upload/vo/UploadFileVo.java create mode 100644 src/main/java/com/platform/common/upload/vo/UploadVideoVo.java create mode 100644 src/main/java/com/platform/common/utils/BeanCopyUtils.java create mode 100644 src/main/java/com/platform/common/utils/IpUtils.java create mode 100644 src/main/java/com/platform/common/utils/MessageUtils.java create mode 100644 src/main/java/com/platform/common/utils/ServletUtils.java create mode 100644 src/main/java/com/platform/common/utils/TimerUtils.java create mode 100644 src/main/java/com/platform/common/version/ApiVersion.java create mode 100644 src/main/java/com/platform/common/version/DeviceInterceptor.java create mode 100644 src/main/java/com/platform/common/version/SignInterceptor.java create mode 100644 src/main/java/com/platform/common/version/VersionCondition.java create mode 100644 src/main/java/com/platform/common/version/VersionConfig.java create mode 100644 src/main/java/com/platform/common/version/VersionEnum.java create mode 100644 src/main/java/com/platform/common/version/VersionHandlerMapping.java create mode 100644 src/main/java/com/platform/common/version/VersionInterceptor.java create mode 100644 src/main/java/com/platform/common/version/VersionUtils.java create mode 100644 src/main/java/com/platform/common/web/aspectj/MybatisAspectj.java create mode 100644 src/main/java/com/platform/common/web/controller/BaseController.java create mode 100644 src/main/java/com/platform/common/web/controller/ErrorController.java create mode 100644 src/main/java/com/platform/common/web/dao/BaseDao.java create mode 100644 src/main/java/com/platform/common/web/domain/AjaxResult.java create mode 100644 src/main/java/com/platform/common/web/domain/BaseEntity.java create mode 100644 src/main/java/com/platform/common/web/domain/JsonDateDeserializer.java create mode 100644 src/main/java/com/platform/common/web/exception/ErrorAttributesCustom.java create mode 100644 src/main/java/com/platform/common/web/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/platform/common/web/interceptor/CustomizationBean.java create mode 100644 src/main/java/com/platform/common/web/page/PageDomain.java create mode 100644 src/main/java/com/platform/common/web/page/TableDataInfo.java create mode 100644 src/main/java/com/platform/common/web/page/TableSupport.java create mode 100644 src/main/java/com/platform/common/web/service/BaseService.java create mode 100644 src/main/java/com/platform/common/web/service/impl/BaseServiceImpl.java create mode 100644 src/main/java/com/platform/modules/auth/controller/AuthController.java create mode 100644 src/main/java/com/platform/modules/auth/service/TokenService.java create mode 100644 src/main/java/com/platform/modules/auth/service/impl/TokenServiceImpl.java create mode 100644 src/main/java/com/platform/modules/auth/vo/AuthVo01.java create mode 100644 src/main/java/com/platform/modules/auth/vo/AuthVo02.java create mode 100644 src/main/java/com/platform/modules/auth/vo/AuthVo03.java create mode 100644 src/main/java/com/platform/modules/auth/vo/AuthVo04.java create mode 100644 src/main/java/com/platform/modules/chat/config/AmapConfig.java create mode 100644 src/main/java/com/platform/modules/chat/config/TencentConfig.java create mode 100644 src/main/java/com/platform/modules/chat/controller/ApplyController.java create mode 100644 src/main/java/com/platform/modules/chat/controller/ChatController.java create mode 100644 src/main/java/com/platform/modules/chat/controller/FriendController.java create mode 100644 src/main/java/com/platform/modules/chat/controller/GroupController.java create mode 100644 src/main/java/com/platform/modules/chat/controller/MyController.java create mode 100644 src/main/java/com/platform/modules/chat/dao/ChatApplyDao.java create mode 100644 src/main/java/com/platform/modules/chat/dao/ChatFeedbackDao.java create mode 100644 src/main/java/com/platform/modules/chat/dao/ChatFriendDao.java create mode 100644 src/main/java/com/platform/modules/chat/dao/ChatGroupDao.java create mode 100644 src/main/java/com/platform/modules/chat/dao/ChatGroupInfoDao.java create mode 100644 src/main/java/com/platform/modules/chat/dao/ChatMsgDao.java create mode 100644 src/main/java/com/platform/modules/chat/dao/ChatUserDao.java create mode 100644 src/main/java/com/platform/modules/chat/dao/ChatVersionDao.java create mode 100644 src/main/java/com/platform/modules/chat/domain/ChatApply.java create mode 100644 src/main/java/com/platform/modules/chat/domain/ChatFeedback.java create mode 100644 src/main/java/com/platform/modules/chat/domain/ChatFriend.java create mode 100644 src/main/java/com/platform/modules/chat/domain/ChatGroup.java create mode 100644 src/main/java/com/platform/modules/chat/domain/ChatGroupInfo.java create mode 100644 src/main/java/com/platform/modules/chat/domain/ChatMsg.java create mode 100644 src/main/java/com/platform/modules/chat/domain/ChatUser.java create mode 100644 src/main/java/com/platform/modules/chat/domain/ChatVersion.java create mode 100644 src/main/java/com/platform/modules/chat/enums/ApplySourceEnum.java create mode 100644 src/main/java/com/platform/modules/chat/enums/ApplyStatusEnum.java create mode 100644 src/main/java/com/platform/modules/chat/enums/ApplyTypeEnum.java create mode 100644 src/main/java/com/platform/modules/chat/enums/FriendTypeEnum.java create mode 100644 src/main/java/com/platform/modules/chat/enums/MsgStatusEnum.java create mode 100644 src/main/java/com/platform/modules/chat/enums/VersionTypeEnum.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatApplyService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatFeedbackService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatFriendService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatGroupInfoService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatGroupService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatMsgService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatTalkService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatUserService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatVersionService.java create mode 100644 src/main/java/com/platform/modules/chat/service/ChatWeatherService.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatApplyServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatFeedbackServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatFriendServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatGroupInfoServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatGroupServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatMsgServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatTalkServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatUserServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatVersionServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/service/impl/ChatWeatherServiceImpl.java create mode 100644 src/main/java/com/platform/modules/chat/utils/TencentUtils.java create mode 100644 src/main/java/com/platform/modules/chat/vo/ApplyVo01.java create mode 100644 src/main/java/com/platform/modules/chat/vo/ApplyVo02.java create mode 100644 src/main/java/com/platform/modules/chat/vo/ApplyVo03.java create mode 100644 src/main/java/com/platform/modules/chat/vo/ChatVo01.java create mode 100644 src/main/java/com/platform/modules/chat/vo/ChatVo02.java create mode 100644 src/main/java/com/platform/modules/chat/vo/ChatVo03.java create mode 100644 src/main/java/com/platform/modules/chat/vo/ChatVo04.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo01.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo02.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo03.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo04.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo05.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo06.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo07.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo08.java create mode 100644 src/main/java/com/platform/modules/chat/vo/FriendVo09.java create mode 100644 src/main/java/com/platform/modules/chat/vo/GroupVo01.java create mode 100644 src/main/java/com/platform/modules/chat/vo/GroupVo02.java create mode 100644 src/main/java/com/platform/modules/chat/vo/GroupVo03.java create mode 100644 src/main/java/com/platform/modules/chat/vo/GroupVo04.java create mode 100644 src/main/java/com/platform/modules/chat/vo/GroupVo05.java create mode 100644 src/main/java/com/platform/modules/chat/vo/GroupVo06.java create mode 100644 src/main/java/com/platform/modules/chat/vo/GroupVo07.java create mode 100644 src/main/java/com/platform/modules/chat/vo/GroupVo08.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo01.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo02.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo03.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo04.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo05.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo06.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo07.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo08.java create mode 100644 src/main/java/com/platform/modules/chat/vo/MyVo09.java create mode 100644 src/main/java/com/platform/modules/chat/vo/VersionVo.java create mode 100644 src/main/java/com/platform/modules/collect/controller/CollectController.java create mode 100644 src/main/java/com/platform/modules/collect/dao/ChatCollectDao.java create mode 100644 src/main/java/com/platform/modules/collect/domain/ChatCollect.java create mode 100644 src/main/java/com/platform/modules/collect/enums/CollectTypeEnum.java create mode 100644 src/main/java/com/platform/modules/collect/service/ChatCollectService.java create mode 100644 src/main/java/com/platform/modules/collect/service/impl/ChatCollectServiceImpl.java create mode 100644 src/main/java/com/platform/modules/collect/vo/CollectVo01.java create mode 100644 src/main/java/com/platform/modules/collect/vo/CollectVo02.java create mode 100644 src/main/java/com/platform/modules/common/config/TrtcConfig.java create mode 100644 src/main/java/com/platform/modules/common/controller/CommonController.java create mode 100644 src/main/java/com/platform/modules/common/controller/FileController.java create mode 100644 src/main/java/com/platform/modules/common/controller/TrtcController.java create mode 100644 src/main/java/com/platform/modules/common/service/FileService.java create mode 100644 src/main/java/com/platform/modules/common/service/TrtcService.java create mode 100644 src/main/java/com/platform/modules/common/service/impl/FileServiceImpl.java create mode 100644 src/main/java/com/platform/modules/common/service/impl/TrtcServiceImpl.java create mode 100644 src/main/java/com/platform/modules/common/vo/TrtcVo.java create mode 100644 src/main/java/com/platform/modules/push/enums/PushBodyEnum.java create mode 100644 src/main/java/com/platform/modules/push/enums/PushMsgEnum.java create mode 100644 src/main/java/com/platform/modules/push/enums/PushNoticeEnum.java create mode 100644 src/main/java/com/platform/modules/push/enums/PushTalkEnum.java create mode 100644 src/main/java/com/platform/modules/push/service/ChatPushService.java create mode 100644 src/main/java/com/platform/modules/push/service/impl/ChatPushServiceImpl.java create mode 100644 src/main/java/com/platform/modules/push/vo/PushBigVo.java create mode 100644 src/main/java/com/platform/modules/push/vo/PushBodyVo.java create mode 100644 src/main/java/com/platform/modules/push/vo/PushFromVo.java create mode 100644 src/main/java/com/platform/modules/push/vo/PushMsgVo.java create mode 100644 src/main/java/com/platform/modules/push/vo/PushNoticeVo.java create mode 100644 src/main/java/com/platform/modules/push/vo/PushParamVo.java create mode 100644 src/main/java/com/platform/modules/push/vo/PushToVo.java create mode 100644 src/main/java/com/platform/modules/shake/controller/NearController.java create mode 100644 src/main/java/com/platform/modules/shake/controller/ShakeController.java create mode 100644 src/main/java/com/platform/modules/shake/service/NearService.java create mode 100644 src/main/java/com/platform/modules/shake/service/ShakeService.java create mode 100644 src/main/java/com/platform/modules/shake/service/impl/NearServiceImpl.java create mode 100644 src/main/java/com/platform/modules/shake/service/impl/ShakeServiceImpl.java create mode 100644 src/main/java/com/platform/modules/shake/vo/NearVo01.java create mode 100644 src/main/java/com/platform/modules/shake/vo/NearVo02.java create mode 100644 src/main/java/com/platform/modules/shake/vo/ShakeVo01.java create mode 100644 src/main/java/com/platform/modules/shake/vo/ShakeVo02.java create mode 100644 src/main/java/com/platform/modules/sms/enums/SmsTemplateEnum.java create mode 100644 src/main/java/com/platform/modules/sms/enums/SmsTypeEnum.java create mode 100644 src/main/java/com/platform/modules/sms/service/SmsService.java create mode 100644 src/main/java/com/platform/modules/sms/service/impl/SmsServiceImpl.java create mode 100644 src/main/java/com/platform/modules/sms/vo/SmsVo.java create mode 100644 src/main/java/com/platform/modules/topic/controller/TopicController.java create mode 100644 src/main/java/com/platform/modules/topic/dao/ChatTopicDao.java create mode 100644 src/main/java/com/platform/modules/topic/dao/ChatTopicLikeDao.java create mode 100644 src/main/java/com/platform/modules/topic/dao/ChatTopicReplyDao.java create mode 100644 src/main/java/com/platform/modules/topic/domain/ChatTopic.java create mode 100644 src/main/java/com/platform/modules/topic/domain/ChatTopicLike.java create mode 100644 src/main/java/com/platform/modules/topic/domain/ChatTopicReply.java create mode 100644 src/main/java/com/platform/modules/topic/enums/TopicNoticeTypeEnum.java create mode 100644 src/main/java/com/platform/modules/topic/enums/TopicReplyTypeEnum.java create mode 100644 src/main/java/com/platform/modules/topic/enums/TopicTypeEnum.java create mode 100644 src/main/java/com/platform/modules/topic/service/ChatTopicLikeService.java create mode 100644 src/main/java/com/platform/modules/topic/service/ChatTopicReplyService.java create mode 100644 src/main/java/com/platform/modules/topic/service/ChatTopicService.java create mode 100644 src/main/java/com/platform/modules/topic/service/impl/ChatTopicLikeServiceImpl.java create mode 100644 src/main/java/com/platform/modules/topic/service/impl/ChatTopicReplyServiceImpl.java create mode 100644 src/main/java/com/platform/modules/topic/service/impl/ChatTopicServiceImpl.java create mode 100644 src/main/java/com/platform/modules/topic/vo/TopicVo01.java create mode 100644 src/main/java/com/platform/modules/topic/vo/TopicVo02.java create mode 100644 src/main/java/com/platform/modules/topic/vo/TopicVo03.java create mode 100644 src/main/java/com/platform/modules/topic/vo/TopicVo04.java create mode 100644 src/main/java/com/platform/modules/topic/vo/TopicVo05.java create mode 100644 src/main/java/com/platform/modules/topic/vo/TopicVo06.java create mode 100644 src/main/java/com/platform/modules/topic/vo/TopicVo07.java create mode 100644 src/main/java/com/platform/modules/topic/vo/TopicVo09.java create mode 100644 src/main/java/com/platform/modules/ws/BootWebSocketHandler.java create mode 100644 src/main/java/com/platform/modules/ws/BootWebSocketInterceptor.java create mode 100644 src/main/java/com/platform/modules/ws/WebSocketConfig.java create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/banner.txt create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/main/resources/mapper/chat/ChatApplyDao.xml create mode 100644 src/main/resources/mapper/chat/ChatFeedbackDao.xml create mode 100644 src/main/resources/mapper/chat/ChatFriendDao.xml create mode 100644 src/main/resources/mapper/chat/ChatGroupDao.xml create mode 100644 src/main/resources/mapper/chat/ChatGroupInfoDao.xml create mode 100644 src/main/resources/mapper/chat/ChatMsgDao.xml create mode 100644 src/main/resources/mapper/chat/ChatUserDao.xml create mode 100644 src/main/resources/mapper/chat/ChatVersionDao.xml create mode 100644 src/main/resources/mapper/collect/ChatCollectDao.xml create mode 100644 src/main/resources/mapper/topic/ChatTopicDao.xml create mode 100644 src/main/resources/mapper/topic/ChatTopicLikeDao.xml create mode 100644 src/main/resources/mapper/topic/ChatTopicReplyDao.xml create mode 100644 src/main/resources/static/favicon.ico diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd446f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup + +!*/build/*.java +!*/build/*.html +!*/build/*.xml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e24e087 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# 基础镜像使用java +FROM openjdk:8-jdk-alpine +# 作者 +MAINTAINER q3z3 admin@q3z3.com +# 最后更新时间 +ENV REFRESHED_AT 2020-06-18 +# 允许指定端口转发 +EXPOSE 8080 +# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp +VOLUME /tmp +# 将jar包添加到容器中并更名为platform-admin.jar +WORKDIR /data +RUN mkdir logs +ADD target/chat-api.jar platform-admin.jar +# 修改时区 +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +# 图片报错问题 +ENV LANG en_US.UTF-8 +RUN echo -e "https://mirrors.ustc.edu.cn/alpine/v3.3/main" > /etc/apk/repositories \ + && echo "https://mirrors.ustc.edu.cn/alpine/v3.3/community" >> /etc/apk/repositories \ + && apk add --update ttf-dejavu fontconfig && rm -rf /var/cache/apk/* +# 运行jar包 +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/data/platform-admin.jar", "--mpw.key=fe48e54270ad9d48"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d13ad6a --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# chat-uniapp微聊 +``` +基于uniapp的即时通讯app客户端,支持iOS与Android双系统打包运行 +``` + +## 项目说明 ++ 一直以来都有一个社交梦,想做一款IM应用,看了很多优秀的开源项目,但是没有合适自己的。于是利用休息时间自己写了这么一套系统。 ++ 项目第一个版本历时2个月,前端使用`uniapp`,后端使用`SpringBoot`。 ++ 手机端使用`uniapp`实现,目前仅支持`安卓端`、`iOS端`和`H5端`。 + +## 技术使用 ++ 推送:uniPush + websocket ++ 资源:阿里OSS(图片、声音、视频、文件等) ++ 音视频:TRTC ++ 地图:高德地图 ++ 后端:Hutool、MyBatis-Plus、shiro、sharding-jdbc等 ++ 前端:uniapp(Vue3) + +## 演示效果 + + + + + + +## 传送门 ++ 您的支持,就是我们`【生发的动力】`,请手动点个`star`吧。 ++ chat-flutter + + [https://github.com/lakaola/chat-flutter](https://github.com/lakaola/chat-flutter) + + [https://gitee.com/lakaola/chat-flutter](https://gitee.com/lakaola/chat-flutter) ++ chat-uniapp + + [https://github.com/lakaola/chat-uniapp](https://github.com/lakaola/chat-uniapp) + + [https://gitee.com/lakaola/chat-uniapp](https://gitee.com/lakaola/chat-uniapp) ++ chat-api + + [https://github.com/lakaola/chat-api](https://github.com/lakaola/chat-api) + + [https://gitee.com/lakaola/chat-api](https://gitee.com/lakaola/chat-api) ++ 加入QQ群:[![加入QQ群](https://img.shields.io/badge/加入QQ群-535099683-blue.svg)](https://jq.qq.com/?_wv=1027&k=PQMnFugm) 详细文档,进群获取(535099683) \ No newline at end of file diff --git a/doc/page/am.html b/doc/page/am.html new file mode 100644 index 0000000..9b8c507 --- /dev/null +++ b/doc/page/am.html @@ -0,0 +1,139 @@ + + + + + 用户协议及隐私协议 + + +
+
+
+

服务条款确认与接纳

+

《微聊》拥有相关软件的所有权和运作权,《微聊》享有对其产品的上一切活动的监督、提示、检查、纠正及处罚等权利。用户通过注册程序阅读本服务条款并点击"注册/登录"按钮完成注册/登录,即表示用户与《微聊》已达成协议,自愿接受本服务条款的所有内容。如果用户不同意服务条款的条件,则不能获得使用《微聊》服务以及注册成为《微聊》用户的权利。 +

+


+

使用规则

+

+ 1.用户注册成功后,《微聊》将给予每个用户一个用户账号及相应的密码,该用户账号和密码由用户负责保管;用户应当对以其用户账号进行的所有活动和事件负法律责任。

+

2.用户须对在《微聊》的注册信息的真实性、合法性、有效性承担全部责任,用户不得冒充他人;不得利用他人的名义发布任何信息;不得恶意使用注册帐户导致其他用户误认;否则《微聊》有权立即停止提供服务,收回其账号并由用户独自承担由此而产生的一切法律责任。

+

3.用户不得使用《微聊》服务发送或传播敏感信息和违反国家法律制度的信息,包括但不限于下列信息:
+

+

反对宪法所确定的基本原则的;

+

危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;

+

损害国家荣誉和利益的;

+

煽动民族仇恨、民族岐视,破坏民族团结的;

+

破坏国家宗教政策,宣扬邪教和封建迷信散布谣言,扰乱社会秩序,破坏社会稳定的;

+

散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教峻犯罪的;

+

侮辱或者诽谤他人,侵害他人合法权益的;

+

含有法律、行政法规禁止的其他内容的。

+

4.《微聊》有权对用户使用《微聊》的情况进行审查和监督,如用户在使用《微聊》时违反任何上述规定《微聊》或其授权的人有权要求用户改正或直接采取一切必要的措施以减轻用户不当行为造成的影响。 +

+

5.盗取他人用户账号或利用网络通讯骚扰他人,均属于非法行为。用户不得采用测试、欺骗等任何非法手段,盗取其他用户的账号和对他人进行强扰。 +

+


+

免责声明

+

1.《微聊》不能对用户在本社区回答问题的答案或评论的准确性及合理性进行保证。

+

2.若《微聊》产品已经明示其网络服务提供方式发生变更提醒用户应当注意事项,用户未按要求操作所产生的一切后果由用户自行承担。 +

+

3.用户明确同意其使用《微聊》产品网络服务所存在的风险将完全由其自己承担;因其使用《微聊》服务而产生的一切后果也由其自己承担,《微聊》对用户不承担任何责任。

+

4.《微聊》不保证网络服务一定能满足用户的要求,也不保证网络服务不会中断,对网络服务的及时性、安全性、准确性也都不作保证。 +

+

5.对于因不可抗力或《微聊》不能控制的原因造成的网络服务中断或其它缺陷,《微聊》不承担任何责任,但将尽力减少因此而给用户造成的损失和影响。

+

6.用户同意保障和维护《微聊》及其他用户的利益,用户在《微聊》发表的内容仅表明其个人的立场和观点,并不代表《微聊》的立场或观点。由于用户发表内容违法、不真实、不正当、侵犯第三方合法权益,或用户违反本协议项下的任何条款而给《微聊》或任何其他第三人造成损失,用户同意承担由此造成的损害赔偿责任。

+


+

服务条款的修改

+

《微聊》会在必要时修改服务条款,服务条款ー旦发生变动,《微聊》将会在用户进入下一步使用前的页面提示修改内容。如果您同意改动,请再一次激活"我同意"按钮。如果您不接受,请及时取消您的帐户。用户要继续使用《微聊》各项服务需要两方面的确认:

+

1.首先确认《微聊》服务条款及其变动。 +

+

2.同意接受所有的服务条款限制。

+


+

隐私政策

+

《微聊》非常重视对用户隐私权的保护,承诺不会在未获得用户许可的情况下擅自将用户的个人资料信息出租或出售给任何第三方,但以下情况除外: +

+

您同意让第三方共享资料;

+

您同意公开你的个人资料,享受为您提供的产品和服务;

+

本站需要听从法庭传票、法律命令或遵循法律;

+

本站发现您违反了本站服务条款或本站其它使用规定;

+

在本应用平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,本应用有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。 +

+


+

+

《微聊》在哪些方面用到了您的信息:

+

登陆注册功能,用户注册时输入的个人信息仅用于验证用户真实性;

+

用户发布/交易内容的功能如失物招领,后勤曝光台、建言献策、外卖预定等您在本应用发布的有关信息数据和需要为您提供交易服务所需的确认信息;

+

您有权选择接受或拒绝接受本政策。但如果您选择拒绝接受本政策,则您可能无法登录或使用依赖于本政策的网络服务或功能。

+


+

信息的存储和交换:

+

本应用收集的有关您的信息和资料将保存在本应用及(或)其关联的服务器上,这些信息和资料均存储在中国境内服务器。 +

+


+

隐私政策的更改

+

如果决定更改隐私政策,我们会在本政策中、官方网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。

+
+
+
+ + + diff --git a/doc/page/index.html b/doc/page/index.html new file mode 100644 index 0000000..b96d3d0 --- /dev/null +++ b/doc/page/index.html @@ -0,0 +1,202 @@ + + + + + 微聊 + + + + + + +
+
+
+

《微聊》

+

单人、群聊、朋友圈、摇一摇、附近的人、收藏、扫码加好友、机器人聊天、文字、图片、名片、音/视频通话 等...

+

你能想到的这里都有

+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+

微聊下载

+

考拉Team微聊,精仿微信,仅提供学习研究请勿商业使用!

+

技术栈:uniapp(vue3)、Java

+ +
扫码下载体验安卓端
+ 下载安卓端 + 苹果端敬请期待 +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/doc/readme.md b/doc/readme.md new file mode 100644 index 0000000..1c177d8 --- /dev/null +++ b/doc/readme.md @@ -0,0 +1,71 @@ +# 找不到core-upload.jar + +修改maven配置文件settings.xml + +1.0.0版本: +``` + + q3z3-boot-tools-temp + temp + q3z3-boot-tools-temp + https://q3z3-maven.pkg.coding.net/repository/boot-tools/temp/ + +``` + +1.1.0版本: +``` + + q3z3-boot-tools-maven + maven + q3z3-boot-tools-maven + https://q3z3-maven.pkg.coding.net/repository/boot-tools/maven/ + +``` + +完整版: + +``` + + + nexus-aliyun + * + Nexus aliyun + http://maven.aliyun.com/nexus/content/groups/public + + + + aliyunmaven + * + 阿里云spring插件仓库 + https://maven.aliyun.com/repository/spring-plugin + + + + aliyunmaven + * + 阿里云公共仓库 + https://maven.aliyun.com/repository/public + + + + repo2 + Mirror from Maven Repo2 + https://repo.spring.io/plugins-release/ + * + + + + q3z3-boot-tools-temp + temp + q3z3-boot-tools-temp + https://q3z3-maven.pkg.coding.net/repository/boot-tools/temp/ + + + + q3z3-boot-tools-maven + maven + q3z3-boot-tools-maven + https://q3z3-maven.pkg.coding.net/repository/boot-tools/maven/ + + +``` \ No newline at end of file diff --git a/doc/sql/boot-im.sql b/doc/sql/boot-im.sql new file mode 100644 index 0000000..10abde2 --- /dev/null +++ b/doc/sql/boot-im.sql @@ -0,0 +1,215 @@ +/* +SQLyog Ultimate v11.25 (64 bit) +MySQL - 5.7.21-log : Database - boot-im +********************************************************************* +*/ + + +/*!40101 SET NAMES utf8 */; + +/*!40101 SET SQL_MODE=''*/; + +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +CREATE DATABASE /*!32312 IF NOT EXISTS*/`boot-im` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; + +USE `boot-im`; + +/*Table structure for table `chat_apply` */ + +DROP TABLE IF EXISTS `chat_apply`; + +CREATE TABLE `chat_apply` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `from_id` bigint(20) DEFAULT NULL COMMENT '发起id', + `to_id` bigint(20) DEFAULT NULL COMMENT '接收id', + `target_id` bigint(20) DEFAULT NULL COMMENT '目标id', + `apply_type` char(1) DEFAULT NULL COMMENT '申请类型1好友2群组', + `apply_status` char(1) DEFAULT NULL COMMENT '申请状态0无1同意2拒绝3忽略', + `apply_source` char(1) DEFAULT NULL COMMENT '申请来源', + `reason` varchar(200) DEFAULT NULL COMMENT '理由', + `create_time` datetime DEFAULT NULL COMMENT '申请时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='好友申请表'; + +/*Table structure for table `chat_collect` */ + +DROP TABLE IF EXISTS `chat_collect`; + +CREATE TABLE `chat_collect` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `user_id` bigint(20) DEFAULT NULL COMMENT '用户id', + `collect_type` varchar(20) DEFAULT NULL COMMENT '收藏类型', + `content` longtext COMMENT '内容', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收藏表'; + +/*Table structure for table `chat_feedback` */ + +DROP TABLE IF EXISTS `chat_feedback`; + +CREATE TABLE `chat_feedback` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `user_id` bigint(20) DEFAULT NULL COMMENT '用户id', + `images` varchar(2000) DEFAULT NULL COMMENT '图片', + `content` longtext COMMENT '内容', + `version` varchar(20) DEFAULT '1.0.0' COMMENT '提交版本', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='建议反馈'; + +/*Table structure for table `chat_friend` */ + +DROP TABLE IF EXISTS `chat_friend`; + +CREATE TABLE `chat_friend` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `from_id` bigint(20) DEFAULT NULL COMMENT '用户id', + `to_id` bigint(20) DEFAULT NULL COMMENT '好友id', + `remark` varchar(32) DEFAULT NULL COMMENT '备注', + `black` char(1) DEFAULT 'N' COMMENT '黑名单', + `source` char(1) DEFAULT NULL COMMENT '好友来源', + `top` char(1) DEFAULT 'N' COMMENT '是否置顶', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_user` (`from_id`,`to_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='好友表'; + +/*Table structure for table `chat_group` */ + +DROP TABLE IF EXISTS `chat_group`; + +CREATE TABLE `chat_group` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `name` varchar(50) DEFAULT NULL COMMENT '群名', + `notice` varchar(200) DEFAULT NULL COMMENT '公告', + `portrait` longtext COMMENT '头像', + `master` bigint(20) DEFAULT NULL COMMENT '群主', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='群组'; + +/*Table structure for table `chat_group_info` */ + +DROP TABLE IF EXISTS `chat_group_info`; + +CREATE TABLE `chat_group_info` ( + `info_id` bigint(20) NOT NULL COMMENT '主键', + `user_id` bigint(20) DEFAULT NULL COMMENT '用户id', + `group_id` bigint(20) DEFAULT NULL COMMENT '群组id', + `top` char(1) DEFAULT 'N' COMMENT '是否置顶', + `disturb` char(1) DEFAULT 'N' COMMENT '是否免打扰', + `keep_group` char(1) DEFAULT 'N' COMMENT '是否保存群组', + `create_time` datetime DEFAULT NULL COMMENT '加入时间', + PRIMARY KEY (`info_id`), + UNIQUE KEY `idx_group` (`user_id`,`group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +/*Table structure for table `chat_msg` */ + +DROP TABLE IF EXISTS `chat_msg`; + +CREATE TABLE `chat_msg` ( + `id` bigint(20) NOT NULL COMMENT '消息主键', + `from_id` bigint(20) DEFAULT NULL COMMENT '发送人', + `to_id` bigint(20) DEFAULT NULL COMMENT '接收人', + `msg_type` varchar(20) DEFAULT NULL COMMENT '消息类型', + `talk_type` varchar(20) DEFAULT NULL COMMENT '聊天类型', + `content` longtext COMMENT '消息内容', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='聊天消息'; + +/*Table structure for table `chat_topic` */ + +DROP TABLE IF EXISTS `chat_topic`; + +CREATE TABLE `chat_topic` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `user_id` bigint(20) DEFAULT NULL COMMENT '用户id', + `topic_type` varchar(20) DEFAULT NULL COMMENT '类型', + `content` varchar(2000) DEFAULT NULL COMMENT '内容', + `location` varchar(200) DEFAULT NULL COMMENT '经纬度', + `create_time` datetime DEFAULT NULL COMMENT '时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='主题'; + +/*Table structure for table `chat_topic_like` */ + +DROP TABLE IF EXISTS `chat_topic_like`; + +CREATE TABLE `chat_topic_like` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `topic_id` bigint(20) DEFAULT NULL COMMENT '帖子id', + `user_id` bigint(20) DEFAULT NULL COMMENT '用户id', + `has_like` char(1) DEFAULT 'Y' COMMENT '是否点赞', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='帖子点赞'; + +/*Table structure for table `chat_topic_reply` */ + +DROP TABLE IF EXISTS `chat_topic_reply`; + +CREATE TABLE `chat_topic_reply` ( + `reply_id` bigint(20) NOT NULL COMMENT '主键', + `reply_type` char(1) DEFAULT NULL COMMENT '回复类型1帖子2用户', + `reply_status` char(1) DEFAULT NULL COMMENT '回复状态', + `content` longtext COMMENT '回复内容', + `topic_id` bigint(20) DEFAULT NULL COMMENT '帖子id', + `user_id` bigint(20) DEFAULT NULL COMMENT '用户id', + `target_id` bigint(20) DEFAULT NULL COMMENT '目标id', + `create_time` datetime DEFAULT NULL COMMENT '回复时间', + PRIMARY KEY (`reply_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='帖子回复表'; + +/*Table structure for table `chat_user` */ + +DROP TABLE IF EXISTS `chat_user`; + +CREATE TABLE `chat_user` ( + `user_id` bigint(20) NOT NULL COMMENT '主键', + `phone` varchar(20) DEFAULT NULL COMMENT '手机号', + `chat_no` varchar(32) DEFAULT NULL COMMENT '微聊号码', + `nick_name` varchar(32) DEFAULT NULL COMMENT '昵称', + `token` varchar(32) DEFAULT NULL COMMENT '用户token', + `gender` varchar(1) DEFAULT '1' COMMENT '性别1男0女', + `portrait` varchar(2000) DEFAULT NULL COMMENT '头像', + `intro` varchar(200) DEFAULT NULL COMMENT '介绍', + `cover` varchar(2000) DEFAULT NULL COMMENT '封面', + `provinces` varchar(20) DEFAULT NULL COMMENT '省份', + `city` varchar(20) DEFAULT NULL COMMENT '城市', + `salt` varchar(4) DEFAULT NULL COMMENT '盐', + `password` varchar(32) DEFAULT NULL COMMENT '密码', + `create_time` datetime DEFAULT NULL COMMENT '注册时间', + `version` varchar(20) DEFAULT '1.0.0' COMMENT '版本信息', + `deleted` tinyint(1) DEFAULT '0' COMMENT '注销0正常null注销', + `deleted_time` datetime DEFAULT NULL COMMENT '注销时间', + PRIMARY KEY (`user_id`), + UNIQUE KEY `idx_phone` (`phone`,`deleted`), + UNIQUE KEY `idx_no` (`chat_no`,`deleted_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; + +/*Table structure for table `chat_version` */ + +DROP TABLE IF EXISTS `chat_version`; + +CREATE TABLE `chat_version` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `version` varchar(20) DEFAULT NULL COMMENT '版本', + `url` varchar(2000) DEFAULT NULL COMMENT '地址', + `content` longtext COMMENT '内容', + `descr` varchar(200) DEFAULT NULL COMMENT '描述', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='版本'; + +/*Data for the table `chat_version` */ + +insert into `chat_version`(`id`,`version`,`url`,`content`,`descr`) values (1,'1.0.0','http://im.q3z3.com/public/am.html','我是用户协议','用户协议'),(2,'1.0.0','http://im.q3z3.com/public/im.apk','我是安卓包','安卓升级包'),(3,'1.0.0','https://www.baidu.com/3','我是iOS包','iOS升级包'); + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..31623dd --- /dev/null +++ b/pom.xml @@ -0,0 +1,270 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + + com.platform + chat-api + 1.0.0 + jar + + chat-api + 移动端接口 + + + UTF-8 + UTF-8 + 1.8 + 1.4.1 + 3.4.3 + 5.6.7 + 1.5.2 + 4.1.1 + 3.1.473 + 4.1.31.Final + 3.11.2 + 7.8.0 + 5.6.3 + 1.27.2 + 8.2.2 + + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.springframework.boot + spring-boot-starter-undertow + + + + io.undertow + undertow-websockets-jsr + + + + + org.springframework.boot + spring-boot-starter-web + + + spring-boot-starter-tomcat + org.springframework.boot + + + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.springframework + spring-context-support + + + + + org.springframework.boot + spring-boot-starter-websocket + + + + + org.apache.shiro + shiro-spring + ${shiro.version} + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-pool2 + + + + + mysql + mysql-connector-java + runtime + + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + com.baomidou + mybatis-plus-extension + ${mybatis-plus.version} + + + + + + org.apache.shardingsphere + sharding-jdbc-spring-boot-starter + ${sharding.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.version} + + + org.mybatis + mybatis + + + org.mybatis + mybatis-spring + + + + + + + org.projectlombok + lombok + + + + + com.tencentcloudapi + tencentcloud-sdk-java + ${tencent.version} + + + + + io.netty + netty-all + ${netty.version} + + + + + commons-io + commons-io + 2.5 + + + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun-sdk-oss} + + + + + com.qiniu + qiniu-java-sdk + ${qiniu.version} + + + + + com.qcloud + cos_api + ${qcloud.cos.version} + + + org.slf4j + slf4j-log4j12 + + + + + + + com.github.tobato + fastdfs-client + ${fastdfs.version} + + + + + io.minio + minio + ${minio.version} + + + + + commons-fileupload + commons-fileupload + 1.3.3 + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + false + + + + + \ No newline at end of file diff --git a/src/main/java/com/baomidou/mybatisplus/core/handlers/MybatisEnumTypeHandler.java b/src/main/java/com/baomidou/mybatisplus/core/handlers/MybatisEnumTypeHandler.java new file mode 100644 index 0000000..d79232f --- /dev/null +++ b/src/main/java/com/baomidou/mybatisplus/core/handlers/MybatisEnumTypeHandler.java @@ -0,0 +1,138 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.baomidou.mybatisplus.core.handlers; + +import cn.hutool.core.util.EnumUtil; +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.baomidou.mybatisplus.annotation.IEnum; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils; +import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.platform.common.core.EnumUtils; +import org.apache.ibatis.reflection.DefaultReflectorFactory; +import org.apache.ibatis.reflection.MetaClass; +import org.apache.ibatis.reflection.ReflectorFactory; +import org.apache.ibatis.reflection.invoker.Invoker; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 解决mybatis-plus枚举转换问题 + */ +public class MybatisEnumTypeHandler> extends BaseTypeHandler { + private static final Map TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap(); + private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory(); + private final Class enumClassType; + private final Class propertyType; + private final Invoker getInvoker; + + public MybatisEnumTypeHandler(Class enumClassType) { + if (enumClassType == null) { + throw new IllegalArgumentException("Type argument cannot be null"); + } else { + this.enumClassType = enumClassType; + MetaClass metaClass = MetaClass.forClass(enumClassType, REFLECTOR_FACTORY); + String name = "value"; + if (!IEnum.class.isAssignableFrom(enumClassType)) { + name = (String) findEnumValueFieldName(this.enumClassType).orElseThrow(() -> { + return new IllegalArgumentException(String.format("Could not find @EnumValue in Class: %s.", this.enumClassType.getName())); + }); + } + + this.propertyType = ReflectionKit.resolvePrimitiveIfNecessary(metaClass.getGetterType(name)); + this.getInvoker = metaClass.getGetInvoker(name); + } + } + + public static Optional findEnumValueFieldName(Class clazz) { + if (clazz != null && clazz.isEnum()) { + String className = clazz.getName(); + return Optional.ofNullable(CollectionUtils.computeIfAbsent(TABLE_METHOD_OF_ENUM_TYPES, className, (key) -> { + Optional fieldOptional = findEnumValueAnnotationField(clazz); + return fieldOptional.map(Field::getName).orElse(null); + })); + } else { + return Optional.empty(); + } + } + + private static Optional findEnumValueAnnotationField(Class clazz) { + return Arrays.stream(clazz.getDeclaredFields()).filter((field) -> { + return field.isAnnotationPresent(EnumValue.class); + }).findFirst(); + } + + public static boolean isMpEnums(Class clazz) { + return clazz != null && clazz.isEnum() && (IEnum.class.isAssignableFrom(clazz) || findEnumValueFieldName(clazz).isPresent()); + } + + public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { + if (jdbcType == null) { + ps.setObject(i, this.getValue(parameter)); + } else { + ps.setObject(i, this.getValue(parameter), jdbcType.TYPE_CODE); + } + + } + + public E getNullableResult(ResultSet rs, String columnName) throws SQLException { + Object value = rs.getObject(columnName); + if (EnumUtil.isEnum(propertyType)) { + value = EnumUtils.toEnum(this.enumClassType, rs.getString(columnName)); + } + return null == value && rs.wasNull() ? null : this.valueOf(value); + } + + public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + Object value = rs.getObject(columnIndex); + if (EnumUtil.isEnum(propertyType)) { + value = EnumUtils.toEnum(this.enumClassType, rs.getString(columnIndex)); + } + return null == value && rs.wasNull() ? null : this.valueOf(value); + } + + public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + Object value = cs.getObject(columnIndex); + if (EnumUtil.isEnum(propertyType)) { + value = EnumUtils.toEnum(this.enumClassType, cs.getString(columnIndex)); + } + return null == value && cs.wasNull() ? null : this.valueOf(value); + } + + private E valueOf(Object value) { + E[] es = (E[]) this.enumClassType.getEnumConstants(); + return Arrays.stream(es).filter((e) -> { + return this.equalsValue(value, this.getValue(e)); + }).findAny().orElse(null); + } + + protected boolean equalsValue(Object sourceValue, Object targetValue) { + String sValue = StringUtils.toStringTrim(sourceValue); + String tValue = StringUtils.toStringTrim(targetValue); + return sourceValue instanceof Number && targetValue instanceof Number && (new BigDecimal(sValue)).compareTo(new BigDecimal(tValue)) == 0 ? true : Objects.equals(sValue, tValue); + } + + private Object getValue(Object object) { + try { + return this.getInvoker.invoke(object, new Object[0]); + } catch (ReflectiveOperationException var3) { + throw ExceptionUtils.mpe(var3); + } + } +} diff --git a/src/main/java/com/platform/AppServletInitializer.java b/src/main/java/com/platform/AppServletInitializer.java new file mode 100644 index 0000000..549bfba --- /dev/null +++ b/src/main/java/com/platform/AppServletInitializer.java @@ -0,0 +1,15 @@ +package com.platform; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + */ +public class AppServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(AppStartUp.class); + } +} diff --git a/src/main/java/com/platform/AppStartUp.java b/src/main/java/com/platform/AppStartUp.java new file mode 100644 index 0000000..36e8cbd --- /dev/null +++ b/src/main/java/com/platform/AppStartUp.java @@ -0,0 +1,18 @@ +package com.platform; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 启动程序 + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class AppStartUp { + + public static void main(String[] args) { + SpringApplication.run(AppStartUp.class, args); + System.out.println("(♥◠‿◠)ノ゙ 启动成功 ლ(´ڡ`ლ)゙"); + } + +} diff --git a/src/main/java/com/platform/NoWarnMyBatisMapper.java b/src/main/java/com/platform/NoWarnMyBatisMapper.java new file mode 100644 index 0000000..ffeb924 --- /dev/null +++ b/src/main/java/com/platform/NoWarnMyBatisMapper.java @@ -0,0 +1,10 @@ +package com.platform; + +import org.apache.ibatis.annotations.Mapper; + +/** + * 去除启动警告 + */ +@Mapper +public interface NoWarnMyBatisMapper { +} diff --git a/src/main/java/com/platform/common/aspectj/IgnoreAuth.java b/src/main/java/com/platform/common/aspectj/IgnoreAuth.java new file mode 100644 index 0000000..a229332 --- /dev/null +++ b/src/main/java/com/platform/common/aspectj/IgnoreAuth.java @@ -0,0 +1,13 @@ +package com.platform.common.aspectj; + +import java.lang.annotation.*; + +/** + * 忽略登录 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented //可生成文档 +public @interface IgnoreAuth { + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/aspectj/SubmitAspect.java b/src/main/java/com/platform/common/aspectj/SubmitAspect.java new file mode 100644 index 0000000..35c37e8 --- /dev/null +++ b/src/main/java/com/platform/common/aspectj/SubmitAspect.java @@ -0,0 +1,75 @@ +package com.platform.common.aspectj; + +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.json.JSONUtil; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.utils.ServletUtils; +import com.platform.common.web.domain.AjaxResult; +import lombok.SneakyThrows; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Aspect +@Configuration +public class SubmitAspect { + + private final Cache CACHES = CacheBuilder.newBuilder() + // 最大缓存 100 个 + .maximumSize(100) + // 设置缓存过期时间为S + .expireAfterWrite(2, TimeUnit.SECONDS) + .build(); + + @SneakyThrows + @Around("execution(* com.platform..*Controller.*(..)) && @annotation(submitRepeat)") + public Object around(ProceedingJoinPoint joinPoint, SubmitRepeat submitRepeat) { + String cacheKey = getCacheKey(submitRepeat); + if (!StringUtils.isEmpty(cacheKey)) { + if (CACHES.getIfPresent(cacheKey) == null) { + // 如果是第一次请求,就将key存入缓存中 + CACHES.put(cacheKey, cacheKey); + } else { + return AjaxResult.fail("请勿重复请求"); + } + } + return joinPoint.proceed(); + } + + + /** + * 加上用户的唯一标识 + */ + private String getCacheKey(SubmitRepeat submitRepeat) { + StringBuilder builder = new StringBuilder(); + HttpServletRequest request = ServletUtils.getRequest(); + String param = null; + // 如果登录获取登录参数 + if (ShiroUtils.isLogin()) { + param = ShiroUtils.getToken(); + } + // 获取param参数 + if (StringUtils.isEmpty(param)) { + Map map = ServletUtil.getParamMap(request); + param = JSONUtil.toJsonStr(map); + } + builder.append(param); + builder.append("_"); + // 获取path参数 + String path = submitRepeat.path(); + if (StringUtils.isEmpty(path)) { + path = request.getServletPath(); + } + builder.append(path); + return builder.toString(); + } + +} diff --git a/src/main/java/com/platform/common/aspectj/SubmitRepeat.java b/src/main/java/com/platform/common/aspectj/SubmitRepeat.java new file mode 100644 index 0000000..502b407 --- /dev/null +++ b/src/main/java/com/platform/common/aspectj/SubmitRepeat.java @@ -0,0 +1,39 @@ +package com.platform.common.aspectj; + +import com.platform.common.enums.YesOrNoEnum; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义一个注解,给需要防止重复提交的方法加上该注解 + *

+ * 使用:@SubmitRepeat + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface SubmitRepeat { + + /** + * 过期时间 + */ + long value() default 2L; + + /** + * 拦截uri + */ + String path() default ""; + + /** + * 拦截msg + */ + String msg() default "请勿重复请求"; + + /** + * 是否抛异常 + */ + YesOrNoEnum exception() default YesOrNoEnum.YES; + +} diff --git a/src/main/java/com/platform/common/config/ApplicationConfig.java b/src/main/java/com/platform/common/config/ApplicationConfig.java new file mode 100644 index 0000000..6f2d06c --- /dev/null +++ b/src/main/java/com/platform/common/config/ApplicationConfig.java @@ -0,0 +1,110 @@ +package com.platform.common.config; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.platform.common.enums.YesOrNoEnum; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Date; +import java.util.TimeZone; + +/** + * 程序注解配置 + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan({"com.platform.modules.**.dao"}) +// 扫描spring工具类 +@ComponentScan(basePackages = {"cn.hutool.extra.spring"}) +public class ApplicationConfig { + + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() { + return builder -> builder.timeZone(TimeZone.getDefault()); + } + + /** + * 序列化枚举值为数据库存储值 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer customizer() { + return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + } + + @Bean + public static MappingJackson2HttpMessageConverter objectMapper() { + final ObjectMapper objectMapper = new ObjectMapper(); + // 忽略未知的枚举字段 + objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL); + // 忽略多余的字段不参与序列化 + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + // null属性字段转"" + objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer() { + @Override + public void serialize(Object arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException { + arg1.writeString(""); + } + }); + SimpleModule simpleModule = new SimpleModule(); + // 格式化Long + simpleModule.addSerializer(Long.class, ToStringSerializer.instance); + // 格式化时间 + simpleModule.addSerializer(Date.class, new JsonSerializer() { + @Override + public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeString(DateUtil.format(date, DatePattern.NORM_DATETIME_FORMAT)); + } + }); + // 格式化金额 + simpleModule.addSerializer(BigDecimal.class, new JsonSerializer() { + @Override + public void serialize(BigDecimal decimal, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeString(decimal.setScale(2, BigDecimal.ROUND_HALF_DOWN).toString()); + } + }); + // 注册 module + objectMapper.registerModule(simpleModule); + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + converter.setObjectMapper(objectMapper); + return converter; + } + + @Value("${platform.cors}") + private String cors; + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + if (YesOrNoEnum.YES.getCode().equals(cors)) { + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.addAllowedOrigin("*"); + corsConfiguration.addAllowedHeader("*"); + corsConfiguration.addAllowedMethod("*"); + corsConfiguration.setAllowCredentials(true); + source.registerCorsConfiguration("/**", corsConfiguration); + } + return new CorsFilter(source); + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/config/MybatisPlusConfig.java b/src/main/java/com/platform/common/config/MybatisPlusConfig.java new file mode 100644 index 0000000..1229942 --- /dev/null +++ b/src/main/java/com/platform/common/config/MybatisPlusConfig.java @@ -0,0 +1,45 @@ +package com.platform.common.config; + +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; +import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import java.util.List; + +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 乐观锁插件 + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + // 防全表更新与删除插件 + interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); + return interceptor; + } + + /** + * 批量操作增强 + */ + @Bean + public DefaultSqlInjector mybatisSqlInjector() { + return new DefaultSqlInjector() { + @Override + public List getMethodList(Class mapperClass) { + //防止父类方法不可用 + List methods = super.getMethodList(mapperClass); + methods.add(new InsertBatchSomeColumn()); + return methods; + } + }; + } + +} diff --git a/src/main/java/com/platform/common/config/PlatformConfig.java b/src/main/java/com/platform/common/config/PlatformConfig.java new file mode 100644 index 0000000..0a8a8cb --- /dev/null +++ b/src/main/java/com/platform/common/config/PlatformConfig.java @@ -0,0 +1,58 @@ +package com.platform.common.config; + +import com.platform.common.core.EnumUtils; +import com.platform.common.enums.YesOrNoEnum; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + */ +@Component +@Configuration +@ConfigurationProperties(prefix = "platform") +public class PlatformConfig { + + /** + * 上传路径 + */ + public static String ROOT_PATH; + + /** + * 文件预览 + */ + public static String PREVIEW = "/preview/**"; + + /** + * 图标 + */ + public static String FAVICON = "/favicon.ico"; + + /** + * token超时时间(分钟) + */ + public static Integer TIMEOUT; + + /** + * 是否开启短信 + */ + public static YesOrNoEnum SMS; + + @Value("${platform.timeout}") + public void setTokenTimeout(Integer timeout) { + PlatformConfig.TIMEOUT = timeout; + } + + @Value("${platform.sms:N}") + public void setSms(String sms) { + PlatformConfig.SMS = EnumUtils.toEnum(YesOrNoEnum.class, sms, YesOrNoEnum.NO); + } + + @Value("${platform.rootPath}") + public void setRootPath(String rootPath) { + PlatformConfig.ROOT_PATH = rootPath; + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/config/WebMvcConfig.java b/src/main/java/com/platform/common/config/WebMvcConfig.java new file mode 100644 index 0000000..ae56f50 --- /dev/null +++ b/src/main/java/com/platform/common/config/WebMvcConfig.java @@ -0,0 +1,64 @@ +package com.platform.common.config; + +import cn.hutool.core.io.file.FileNameUtil; +import com.platform.common.version.DeviceInterceptor; +import com.platform.common.version.VersionHandlerMapping; +import com.platform.common.version.VersionInterceptor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 通用配置 + */ +@Configuration +public class WebMvcConfig extends WebMvcConfigurationSupport { + + @Resource + private VersionInterceptor versionInterceptor; + + @Resource + private DeviceInterceptor deviceInterceptor; + + @Override + public RequestMappingHandlerMapping createRequestMappingHandlerMapping() { + return new VersionHandlerMapping(); + } + + @Override + public void configureMessageConverters(List> converters) { + converters.add(ApplicationConfig.objectMapper()); + } + + @Value("${platform.rootPath}") + private String rootPath; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // favicon.ico + registry.addResourceHandler(PlatformConfig.FAVICON).addResourceLocations("classpath:/static/"); + // file + registry.addResourceHandler(PlatformConfig.PREVIEW).addResourceLocations("file:" + rootPath + FileNameUtil.UNIX_SEPARATOR); + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(versionInterceptor) + .addPathPatterns("/**") + .excludePathPatterns(PlatformConfig.FAVICON, PlatformConfig.PREVIEW); + registry.addInterceptor(deviceInterceptor) + .addPathPatterns("/**") + .excludePathPatterns(PlatformConfig.FAVICON, PlatformConfig.PREVIEW); + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/constant/AppConstants.java b/src/main/java/com/platform/common/constant/AppConstants.java new file mode 100644 index 0000000..8a2e7cf --- /dev/null +++ b/src/main/java/com/platform/common/constant/AppConstants.java @@ -0,0 +1,183 @@ +package com.platform.common.constant; + +/** + * 通用常量信息 + */ +public class AppConstants { + + /** + * user_id + */ + public static final String USER_ID = "user_id"; + + /** + * 默认头像 + */ + public static final String DEFAULT_PORTRAIT = "http://q3z3-im.oss-cn-beijing.aliyuncs.com/61bed1c563de173eb00e8d8c.png"; + + /** + * 注销昵称 + */ + public static final String DELETED_NICK_NAME = "已注销"; + + /** + * 用户二维码 + */ + public static final String QR_CODE_USER = "user:"; + + /** + * 群组二维码 + */ + public static final String QR_CODE_GROUP = "group:"; + + /** + * 视频参数 + */ + public static final String VIDEO_PARAM = "?x-oss-process=video/snapshot,t_1000,f_png,w_600,m_fast"; + + /** + * 新建群名称:好友创建通知 + */ + public static final String GROUP_CREATE_NAME = "{}的群聊-{}"; + + /** + * 不是你的好友 + */ + public static final String FRIEND_NOT_EXIST = "对方不是你的好友"; + + /** + * 通知:好友创建通知 + */ + public static final String NOTICE_FRIEND_CREATE = "你们已经是好友啦,现在开始聊天吧"; + + /** + * 通知:群组创建通知 + */ + public static final String NOTICE_GROUP_CREATE_MASTER = "你邀请{}加入了群聊"; + + /** + * 通知:群组创建通知 + */ + public static final String NOTICE_GROUP_CREATE_MEMBER = "{}邀请你加入了群聊"; + + /** + * 通知:群组退出通知 + */ + public static final String NOTICE_GROUP_LOGOUT = "{}退出了群聊"; + + /** + * 通知:群组转让通知 + */ + public static final String NOTICE_GROUP_TRANSFER = "你已成为新群主"; + + /** + * 通知:群组加入通知 + */ + public static final String NOTICE_GROUP_JOIN = "{}加入了群聊"; + + /** + * 通知:群组踢出通知 + */ + public static final String NOTICE_GROUP_KICKED_MASTER = "{}被移出群聊"; + + /** + * 通知:群组踢出通知 + */ + public static final String NOTICE_GROUP_KICKED_MEMBER = "你被移出群聊"; + + /** + * 通知:群组解散通知 + */ + public static final String NOTICE_GROUP_DISSOLVE = "群主解散了群聊"; + + /** + * 通知:群组改名 + */ + public static final String NOTICE_GROUP_EDIT = "{}修改群名为“{}”"; + + /** + * 通知:群组公告 + */ + public static final String NOTICE_GROUP_NOTICE = "{}修改群公告为“{}”"; + + /** + * 帖子_通知 + */ + public static final String REDIS_TOPIC_NOTICE = "topic:notice:"; + + /** + * 帖子_回复 + */ + public static final String REDIS_TOPIC_REPLY = "topic:reply:"; + + /** + * 好友_通知 + */ + public static final String REDIS_FRIEND_NOTICE = "friend:notice:"; + + /** + * redis_附近的人 + */ + public static final String REDIS_NEAR = "chat:near"; + + /** + * redis_摇一摇 + */ + public static final String REDIS_SHAKE = "chat:shake"; + + /** + * redis_摇一摇 + */ + public static final String REDIS_GEO = "chat:geo"; + + /** + * redis_消息 + */ + public static final String REDIS_MSG = "chat:msg:{}:{}"; + + /** + * redis_消息(天数) + */ + public static final Integer REDIS_MSG_TIME = 7; + + /** + * redis_天气 + */ + public static final String REDIS_MP_WEATHER = "mp:weather:{}:{}"; + + /** + * redis_天气(分钟) + */ + public static final Integer REDIS_MP_WEATHER_TIME = 30; + + /** + * redis_好友 + */ + public static final String REDIS_FRIEND = "chat:friend:{}:{}"; + + /** + * redis_好友(天) + */ + public static final Integer REDIS_FRIEND_TIME = 60; + + /** + * redis_群组 + */ + public static final String REDIS_GROUP_INFO = "chat:group:{}:{}"; + + /** + * redis_群组(天) + */ + public static final Integer REDIS_GROUP_TIME = 60; + + /** + * redis_实时音视频_签名 + */ + public static final String REDIS_TRTC_SIGN = "chat:trtc:sign:"; + + /** + * redis_实时音视频_用户前缀 + */ + public static final String REDIS_TRTC_USER = "u"; + +} diff --git a/src/main/java/com/platform/common/constant/HeadConstant.java b/src/main/java/com/platform/common/constant/HeadConstant.java new file mode 100644 index 0000000..9d3510f --- /dev/null +++ b/src/main/java/com/platform/common/constant/HeadConstant.java @@ -0,0 +1,43 @@ +package com.platform.common.constant; + +/** + * 头部常量 + */ +public class HeadConstant { + + /** + * 登录用户 redis key + */ + public static final String TOKEN_REDIS_APP = "token:app:"; + + /** + * 令牌 + */ + public static final String TOKEN_HEADER_ADMIN = "Authorization"; + + /** + * 版本号 + */ + public static final String VERSION = "version"; + + /** + * 签名 + */ + public static final String SIGN = "sign"; + + /** + * appId + */ + public static final String APP_ID = "appId"; + + /** + * 签名 + */ + public static final String TIMESTAMP = "timestamp"; + + /** + * 设备 + */ + public static final String DEVICE = "device"; + +} diff --git a/src/main/java/com/platform/common/core/CharsetKit.java b/src/main/java/com/platform/common/core/CharsetKit.java new file mode 100644 index 0000000..d2ccced --- /dev/null +++ b/src/main/java/com/platform/common/core/CharsetKit.java @@ -0,0 +1,89 @@ +package com.platform.common.core; + +import org.springframework.util.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 字符集工具类 + */ +public class CharsetKit { + /** + * ISO-8859-1 + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** + * UTF-8 + */ + public static final String UTF_8 = "UTF-8"; + /** + * GBK + */ + public static final String GBK = "GBK"; + + /** + * ISO-8859-1 + */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** + * UTF-8 + */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** + * GBK + */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + *

+ * charset 字符集,为空则返回默认字符集 + */ + public static Charset charset(String charset) { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + *

+ * source 字符串 + * srcCharset 源字符集,默认ISO-8859-1 + * destCharset 目标字符集,默认UTF-8 + * 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + *

+ * source 字符串 + * srcCharset 源字符集,默认ISO-8859-1 + * destCharset 目标字符集,默认UTF-8 + * 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) { + if (null == srcCharset) { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) { + srcCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * 系统字符集编码 + */ + public static String sysCharset() { + return Charset.defaultCharset().name(); + } + +} diff --git a/src/main/java/com/platform/common/core/EnumUtils.java b/src/main/java/com/platform/common/core/EnumUtils.java new file mode 100644 index 0000000..c772252 --- /dev/null +++ b/src/main/java/com/platform/common/core/EnumUtils.java @@ -0,0 +1,30 @@ +package com.platform.common.core; + +import cn.hutool.core.util.EnumUtil; +import org.springframework.util.StringUtils; + +import java.util.Map; + +/** + * 类型转换器 + */ +public class EnumUtils { + + public static > E toEnum(Class clazz, String code) { + return toEnum(clazz, code, null); + } + + public static > E toEnum(Class clazz, String code, E defaultValue) { + if (StringUtils.isEmpty(code)) { + return defaultValue; + } + Map enumMap = EnumUtil.getNameFieldMap(clazz, "code"); + for (String key : enumMap.keySet()) { + if (code.equals(enumMap.get(key).toString())) { + return EnumUtil.fromString(clazz, key); + } + } + return defaultValue; + } + +} diff --git a/src/main/java/com/platform/common/enums/DeviceEnum.java b/src/main/java/com/platform/common/enums/DeviceEnum.java new file mode 100644 index 0000000..3ea8f68 --- /dev/null +++ b/src/main/java/com/platform/common/enums/DeviceEnum.java @@ -0,0 +1,53 @@ +package com.platform.common.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 设备类型枚举 + */ +@Getter +public enum DeviceEnum { + + /** + * 安卓 + */ + ANDROID("android", "安卓"), + /** + * 苹果 + */ + IOS("ios", "苹果"), + /** + * 微软 + */ + WINDOWS("windows", "微软"), + /** + * MAC + */ + MAC("mac", "MAC"), + /** + * H5 + */ + H5("h5", "h5"), + /** + * PC + */ + PC("pc", "PC"), + /** + * 小程序 + */ + MINI("mini", "小程序"), + ; + + @EnumValue + @JsonValue + private final String code; + private final String info; + + DeviceEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/common/enums/GenderEnum.java b/src/main/java/com/platform/common/enums/GenderEnum.java new file mode 100644 index 0000000..655c680 --- /dev/null +++ b/src/main/java/com/platform/common/enums/GenderEnum.java @@ -0,0 +1,36 @@ +package com.platform.common.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 性别类型枚举 + */ +@Getter +public enum GenderEnum { + + /** + * 未知 + */ + UNKNOWN("0", "未知"), + /** + * 男 + */ + MALE("1", "男"), + /** + * 女 + */ + FEMALE("2", "女"); + + @EnumValue + @JsonValue + private final String code; + private final String info; + + GenderEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/common/enums/ResultCodeEnum.java b/src/main/java/com/platform/common/enums/ResultCodeEnum.java new file mode 100644 index 0000000..7a3517c --- /dev/null +++ b/src/main/java/com/platform/common/enums/ResultCodeEnum.java @@ -0,0 +1,62 @@ +package com.platform.common.enums; + +import lombok.Getter; + +/** + * 返回码枚举 + */ +@Getter +public enum ResultCodeEnum { + + /** + * 操作成功 + */ + SUCCESS(200, "操作成功"), + /** + * 未授权 + */ + UNAUTHORIZED(401, "登录已过期,请重新登录"), + /** + * 无访问权限 + */ + AUTH(403, "无访问权限"), + /** + * 客户证书已过期或无效 + */ + CERTIFICATE(40317, "客户证书已过期或无效"), + /** + * 资源/服务未找到 + */ + NOT_FOUND(404, "路径不存在,请检查路径是否正确"), + /** + * 操作失败 + */ + FAIL(500, "系统异常,请联系管理员"), + /** + * 版本号 + */ + VERSION(601, "版本过低,请升级"), + /** + * 安全验证 + */ + SECURITY(602, "安全验证"), + ; + + private final Integer code; + private final String info; + + ResultCodeEnum(Integer code, String info) { + this.code = code; + this.info = info; + } + + public static ResultCodeEnum init(Integer code) { + for (ResultCodeEnum resultCode : ResultCodeEnum.values()) { + if (resultCode.getCode().equals(code)) { + return resultCode; + } + } + return ResultCodeEnum.FAIL; + } + +} diff --git a/src/main/java/com/platform/common/enums/YesOrNoEnum.java b/src/main/java/com/platform/common/enums/YesOrNoEnum.java new file mode 100644 index 0000000..fe7fc22 --- /dev/null +++ b/src/main/java/com/platform/common/enums/YesOrNoEnum.java @@ -0,0 +1,45 @@ +package com.platform.common.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 是否类型枚举 + */ +@Getter +public enum YesOrNoEnum { + + /** + * 是 + */ + YES("Y", "是"), + /** + * 否 + */ + NO("N", "否"), + /** + * 退 + */ + REFUND("R", "退"), + ; + + @EnumValue + @JsonValue + private final String code; + private final String info; + + YesOrNoEnum(String code, String info) { + this.code = code; + this.info = info; + } + + public static boolean transform(YesOrNoEnum result) { + return YesOrNoEnum.YES.equals(result); + } + + public static YesOrNoEnum transform(boolean result) { + return result ? YesOrNoEnum.YES : YesOrNoEnum.NO; + } + +} diff --git a/src/main/java/com/platform/common/exception/BaseException.java b/src/main/java/com/platform/common/exception/BaseException.java new file mode 100644 index 0000000..63473cb --- /dev/null +++ b/src/main/java/com/platform/common/exception/BaseException.java @@ -0,0 +1,48 @@ +package com.platform.common.exception; + +import com.platform.common.enums.ResultCodeEnum; +import com.platform.common.utils.MessageUtils; +import lombok.Getter; + +/** + * 自定义异常 + */ +public class BaseException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + @Getter + private ResultCodeEnum resultCode; + + /** + * 错误消息 + */ + private String message; + + public BaseException(String message) { + this.message = message; + } + + public BaseException(ResultCodeEnum resultCode) { + this.resultCode = resultCode; + this.message = resultCode.getInfo(); + } + + public BaseException(ResultCodeEnum resultCode, String message) { + this.resultCode = resultCode; + this.message = message; + } + + @Override + public String getMessage() { + String message = this.message; + if (1 + 1 != 2) { + message = MessageUtils.message("", ""); + } + return message; + } + +} diff --git a/src/main/java/com/platform/common/exception/LoginException.java b/src/main/java/com/platform/common/exception/LoginException.java new file mode 100644 index 0000000..8d1f847 --- /dev/null +++ b/src/main/java/com/platform/common/exception/LoginException.java @@ -0,0 +1,20 @@ +package com.platform.common.exception; + +import com.platform.common.enums.ResultCodeEnum; +import lombok.Getter; +import org.apache.shiro.authc.AuthenticationException; + +/** + * 登录异常 + */ +public class LoginException extends AuthenticationException { + + @Getter + private ResultCodeEnum code; + + public LoginException(ResultCodeEnum resultCode) { + super(resultCode.getInfo()); + this.code = resultCode; + } + +} diff --git a/src/main/java/com/platform/common/redis/GeoHashUtils.java b/src/main/java/com/platform/common/redis/GeoHashUtils.java new file mode 100644 index 0000000..532d3ae --- /dev/null +++ b/src/main/java/com/platform/common/redis/GeoHashUtils.java @@ -0,0 +1,148 @@ +package com.platform.common.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.geo.*; +import org.springframework.data.redis.connection.RedisGeoCommands; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * GeoHash工具类 + */ +@Component +public class GeoHashUtils { + + @Autowired + private RedisTemplate redisTemplate; + + /*** + * 将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。 + * @param key redis的key + * @param longitude 经度 + * @param latitude 纬度 + * @param name 名称 + * @return + */ + public Long add(String key, double longitude, double latitude, String name) { +// Long addedNum = redisTemplate.opsForGeo().add("city", new Point(121.47, 31.23), "上海"); +// Long addedNum = redisTemplate.opsForGeo().add("city", new Point(113.27, 23.13), "广州"); + return redisTemplate.opsForGeo().add(key, new Point(longitude, latitude), name); + } + + /*** + * 将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。 + * @param key redis的key + * @param map 名称 - 经度 - 纬度 + * @return + */ + public Long add(String key, Map map) { + return redisTemplate.opsForGeo().add(key, map); + } + + /*** + * 将指定的地理空间移除。 + * @param key redis的key + * @param name 名称 + * @return + */ + public Long remove(String key, String... name) { + return redisTemplate.opsForGeo().remove(key, name); + } + + /*** + * 将指定的地理空间移除。 + * @param key redis的key + * @param nameList 名称集合 + * @return + */ + public Long remove(String key, List nameList) { + return redisTemplate.opsForGeo().remove(key, nameList.toArray()); + } + + /*** + * 从key里返回所有给定位置元素的位置(经度和纬度)。 + * @param key redis的key + * @param name 名称 + */ + public List get(String key, String... name) { + List points = redisTemplate.opsForGeo().position(key, name);//params: key, 地方名称... + return points; + } + + /*** + * 从key里返回所有给定位置元素的位置(经度和纬度)。 + * @param key redis的key + * @param nameList 名称的集合 + */ + public List get(String key, List nameList) { + List points = redisTemplate.opsForGeo().position(key, nameList.toArray()); + return points; + } + + /*** + * 返回两个给定位置之间的距离。 + * @param key redis的key + * @param name1 地方名称1 + * @param name2 地方名称2 + * @return + */ + public Distance dist(String key, String name1, String name2) { + Distance distance = redisTemplate.opsForGeo() + .distance(key, name1, name2, RedisGeoCommands.DistanceUnit.KILOMETERS); + return distance; + } + + /*** + * 以给定的城市为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素,并给出所有位置元素与中心的平均距离。 + * @param key redis的key + * @param name 名称 + * @param distance 距离 + * @param count 人数 + * @return + */ + public List> radius(String key, String name, Integer distance, Integer count) { + Distance distances = new Distance(distance, Metrics.KILOMETERS); + RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(count); + return redisTemplate.opsForGeo().radius(key, name, distances, args).getContent(); + } + + /*** + * 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素,并给出所有位置元素与中心的平均距离。 + * @param key redis的key + * @param longitude 经度 + * @param latitude 纬度 + * @param distance 距离 + * @param count 人数 + * @return + */ + public List> radius(String key, double longitude, double latitude, Integer distance, Integer count) { + Circle circle = new Circle(new Point(longitude, latitude), new Distance(distance, Metrics.KILOMETERS)); + RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(count); + return redisTemplate.opsForGeo().radius(key, circle, args).getContent(); + } + + /*** + * 返回一个或多个位置元素的 Geohash 表示 + * @param key redis的key + * @param name 名称的集合 + */ + public List hash(String key, String... name) { + List results = redisTemplate.opsForGeo().hash(key, name); + return results; + } + + /*** + * 返回一个或多个位置元素的 Geohash 表示 + * @param key redis的key + * @param nameList 名称的集合 + */ + public List hash(String key, List nameList) { + List results = redisTemplate.opsForGeo().hash(key, nameList.toArray()); + return results; + } + +} + diff --git a/src/main/java/com/platform/common/redis/GeoVo.java b/src/main/java/com/platform/common/redis/GeoVo.java new file mode 100644 index 0000000..233311c --- /dev/null +++ b/src/main/java/com/platform/common/redis/GeoVo.java @@ -0,0 +1,10 @@ +package com.platform.common.redis; + +import lombok.Data; + +@Data +public class GeoVo { + + private String name; + +} diff --git a/src/main/java/com/platform/common/redis/RedisConfig.java b/src/main/java/com/platform/common/redis/RedisConfig.java new file mode 100644 index 0000000..f75bdb6 --- /dev/null +++ b/src/main/java/com/platform/common/redis/RedisConfig.java @@ -0,0 +1,31 @@ +package com.platform.common.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + */ +@Configuration +@EnableCaching +public class RedisConfig { + + @Autowired + private RedisTemplate redisTemplate; + + @Bean + public RedisTemplate stringSerializerRedisTemplate() { + RedisSerializer stringSerializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(stringSerializer); + redisTemplate.setValueSerializer(stringSerializer); + redisTemplate.setHashKeySerializer(stringSerializer); + redisTemplate.setHashValueSerializer(stringSerializer); + return redisTemplate; + } + +} diff --git a/src/main/java/com/platform/common/redis/RedisListenerConfig.java b/src/main/java/com/platform/common/redis/RedisListenerConfig.java new file mode 100644 index 0000000..27b0856 --- /dev/null +++ b/src/main/java/com/platform/common/redis/RedisListenerConfig.java @@ -0,0 +1,19 @@ +package com.platform.common.redis; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; + +@Configuration +public class RedisListenerConfig { + + @Bean + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + //container.addMessageListener(new RedisExpiredListener(), new PatternTopic("__keyevent@0__:expired")); + return container; + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/redis/RedisUtils.java b/src/main/java/com/platform/common/redis/RedisUtils.java new file mode 100644 index 0000000..b367158 --- /dev/null +++ b/src/main/java/com/platform/common/redis/RedisUtils.java @@ -0,0 +1,1372 @@ +package com.platform.common.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ZSetOperations.TypedTuple; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +/** + * Redis工具类 + * + * @author WangFan + * @version 1.1 (GitHub文档: https://github.com/whvcse/RedisUtil ) + * @date 2018-02-24 下午03:09:50 + */ +@Component +public class RedisUtils { + + @Autowired + private StringRedisTemplate redisTemplate; + + /** -------------------key相关操作--------------------- */ + + /** + * 删除key + * + * @param key + */ + public void delete(String key) { + if (StringUtils.isEmpty(key)) { + return; + } + redisTemplate.delete(key); + } + + /** + * 批量删除key + * + * @param keys + */ + public void delete(Collection keys) { + if (CollectionUtils.isEmpty(keys)) { + return; + } + redisTemplate.delete(keys); + } + + /** + * 序列化key + * + * @param key + * @return + */ + public byte[] dump(String key) { + return redisTemplate.dump(key); + } + + /** + * 是否存在key + * + * @param key + * @return + */ + public Boolean hasKey(String key) { + return redisTemplate.hasKey(key); + } + + /** + * 设置过期时间 + * + * @param key + * @param timeout + * @param unit + * @return + */ + public Boolean expire(String key, long timeout, TimeUnit unit) { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 设置过期时间 + * + * @param key + * @param date + * @return + */ + public Boolean expire(String key, Date date) { + return redisTemplate.expireAt(key, date); + } + + /** + * 查找匹配的key + * + * @param pattern + * @return + */ + public Set keys(String pattern) { + return redisTemplate.keys(pattern); + } + + /** + * 将当前数据库的 key 移动到给定的数据库 db 当中 + * + * @param key + * @param dbIndex + * @return + */ + public Boolean move(String key, int dbIndex) { + return redisTemplate.move(key, dbIndex); + } + + /** + * 移除 key 的过期时间,key 将持久保持 + * + * @param key + * @return + */ + public Boolean persist(String key) { + return redisTemplate.persist(key); + } + + /** + * 返回 key 的剩余的过期时间 + * + * @param key + * @param unit + * @return + */ + public Long getExpire(String key, TimeUnit unit) { + return redisTemplate.getExpire(key, unit); + } + + /** + * 返回 key 的剩余的过期时间 + * + * @param key + * @return + */ + public Long getExpire(String key) { + return redisTemplate.getExpire(key); + } + + /** + * 从当前数据库中随机返回一个 key + * + * @return + */ + public String randomKey() { + return redisTemplate.randomKey(); + } + + /** + * 修改 key 的名称 + * + * @param oldKey + * @param newKey + */ + public void rename(String oldKey, String newKey) { + redisTemplate.rename(oldKey, newKey); + } + + /** + * 仅当 newkey 不存在时,将 oldKey 改名为 newkey + * + * @param oldKey + * @param newKey + * @return + */ + public Boolean renameIfAbsent(String oldKey, String newKey) { + return redisTemplate.renameIfAbsent(oldKey, newKey); + } + + /** + * 返回 key 所储存的值的类型 + * + * @param key + * @return + */ + public DataType type(String key) { + return redisTemplate.type(key); + } + + /** -------------------string相关操作--------------------- */ + + /** + * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout + * + * @param key + * @param value + * @param timeout 过期时间 + * @param unit 时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES + * 秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS + */ + public void set(String key, String value, long timeout, TimeUnit unit) { + redisTemplate.opsForValue().set(key, value, timeout, unit); + } + + /** + * 获取指定 key 的值 + * + * @param key + * @return + */ + public String get(String key) { + return redisTemplate.opsForValue().get(key); + } + + /** + * 返回 key 中字符串值的子字符 + * + * @param key + * @param start + * @param end + * @return + */ + public String getRange(String key, long start, long end) { + return redisTemplate.opsForValue().get(key, start, end); + } + + /** + * 将给定 key 的值设为 value ,并返回 key 的旧值(old value) + * + * @param key + * @param value + * @return + */ + public String getAndSet(String key, String value) { + return redisTemplate.opsForValue().getAndSet(key, value); + } + + /** + * 对 key 所储存的字符串值,获取指定偏移量上的位(bit) + * + * @param key + * @param offset + * @return + */ + public Boolean getBit(String key, long offset) { + return redisTemplate.opsForValue().getBit(key, offset); + } + + /** + * 批量获取 + * + * @param keys + * @return + */ + public List multiGet(Collection keys) { + return redisTemplate.opsForValue().multiGet(keys); + } + + /** + * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value + * + * @param key + * @param value 值,true为1, false为0 + * @return + */ + public boolean setBit(String key, long offset, boolean value) { + return redisTemplate.opsForValue().setBit(key, offset, value); + } + + /** + * 只有在 key 不存在时设置 key 的值 + * + * @param key + * @param value + * @return 之前已经存在返回false, 不存在返回true + */ + public boolean setIfAbsent(String key, String value) { + return redisTemplate.opsForValue().setIfAbsent(key, value); + } + + /** + * 只有在 key 不存在时设置 key 的值 + * + * @param key + * @param value + * @return 之前已经存在返回false, 不存在返回true + */ + public boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) { + return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit); + } + + /** + * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始 + * + * @param key + * @param value + * @param offset 从指定位置开始覆写 + */ + public void setRange(String key, String value, long offset) { + redisTemplate.opsForValue().set(key, value, offset); + } + + /** + * 获取字符串的长度 + * + * @param key + * @return + */ + public Long size(String key) { + return redisTemplate.opsForValue().size(key); + } + + /** + * 批量添加 + * + * @param maps + */ + public void multiSet(Map maps) { + redisTemplate.opsForValue().multiSet(maps); + } + + /** + * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在 + * + * @param maps + * @return 之前已经存在返回false, 不存在返回true + */ + public boolean multiSetIfAbsent(Map maps) { + return redisTemplate.opsForValue().multiSetIfAbsent(maps); + } + + /** + * 增加(自增长), 负数则为自减 + * + * @param key + * @param key + * @return + */ + public Long increment(String key, long increment) { + return redisTemplate.opsForValue().increment(key, increment); + } + + /** + * 追加到末尾 + * + * @param key + * @param value + * @return + */ + public Integer append(String key, String value) { + return redisTemplate.opsForValue().append(key, value); + } + + /** -------------------hash相关操作------------------------- */ + + /** + * 获取存储在哈希表中指定字段的值 + * + * @param key + * @param field + * @return + */ + public Object hGet(String key, String field) { + return redisTemplate.opsForHash().get(key, field); + } + + /** + * 获取所有给定字段的值 + * + * @param key + * @return + */ + public Map hGetAll(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 获取所有给定字段的值 + * + * @param key + * @param fields + * @return + */ + public List hMultiGet(String key, Collection fields) { + return redisTemplate.opsForHash().multiGet(key, fields); + } + + public void hPut(String key, String hashKey, String value) { + redisTemplate.opsForHash().put(key, hashKey, value); + } + + public void hPutAll(String key, Map maps) { + redisTemplate.opsForHash().putAll(key, maps); + } + + /** + * 仅当hashKey不存在时才设置 + * + * @param key + * @param hashKey + * @param value + * @return + */ + public Boolean hPutIfAbsent(String key, String hashKey, String value) { + return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value); + } + + /** + * 删除一个或多个哈希表字段 + * + * @param key + * @param fields + * @return + */ + public Long hDelete(String key, Object... fields) { + return redisTemplate.opsForHash().delete(key, fields); + } + + /** + * 查看哈希表 key 中,指定的字段是否存在 + * + * @param key + * @param field + * @return + */ + public boolean hExists(String key, String field) { + return redisTemplate.opsForHash().hasKey(key, field); + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key + * @param field + * @param increment + * @return + */ + public Long hIncrBy(String key, Object field, long increment) { + return redisTemplate.opsForHash().increment(key, field, increment); + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key + * @param field + * @param delta + * @return + */ + public Double hIncrByFloat(String key, Object field, double delta) { + return redisTemplate.opsForHash().increment(key, field, delta); + } + + /** + * 获取所有哈希表中的字段 + * + * @param key + * @return + */ + public Set hKeys(String key) { + return redisTemplate.opsForHash().keys(key); + } + + /** + * 获取哈希表中字段的数量 + * + * @param key + * @return + */ + public Long hSize(String key) { + return redisTemplate.opsForHash().size(key); + } + + /** + * 获取哈希表中所有值 + * + * @param key + * @return + */ + public List hValues(String key) { + return redisTemplate.opsForHash().values(key); + } + + /** + * 迭代哈希表中的键值对 + * + * @param key + * @param options + * @return + */ + public Cursor> hScan(String key, ScanOptions options) { + return redisTemplate.opsForHash().scan(key, options); + } + + /** ------------------------list相关操作---------------------------- */ + + /** + * 通过索引获取列表中的元素 + * + * @param key + * @param index + * @return + */ + public String lIndex(String key, long index) { + return redisTemplate.opsForList().index(key, index); + } + + /** + * 获取列表指定范围内的元素 + * + * @param key + * @param start 开始位置, 0是开始位置 + * @param end 结束位置, -1返回所有 + * @return + */ + public List lRange(String key, long start, long end) { + return redisTemplate.opsForList().range(key, start, end); + } + + /** + * 存储在list头部 + * + * @param key + * @param value + * @return + */ + public Long lLeftPush(String key, String value) { + return redisTemplate.opsForList().leftPush(key, value); + } + + /** + * 存储在list头部 + * + * @param key + * @param value + * @return + */ + public Long lLeftPushAll(String key, String... value) { + return redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * 存储在list头部 + * + * @param key + * @param value + * @return + */ + public Long lLeftPushAll(String key, Collection value) { + return redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * 当list存在的时候才加入 + * + * @param key + * @param value + * @return + */ + public Long lLeftPushIfPresent(String key, String value) { + return redisTemplate.opsForList().leftPushIfPresent(key, value); + } + + /** + * 如果pivot存在,再pivot前面添加 + * + * @param key + * @param pivot + * @param value + * @return + */ + public Long lLeftPush(String key, String pivot, String value) { + return redisTemplate.opsForList().leftPush(key, pivot, value); + } + + /** + * 存储在list尾部 + * + * @param key + * @param value + * @return + */ + public Long lRightPush(String key, String value) { + return redisTemplate.opsForList().rightPush(key, value); + } + + /** + * 存储在list尾部 + * + * @param key + * @param value + * @return + */ + public Long lRightPushAll(String key, String... value) { + return redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * 存储在list尾部 + * + * @param key + * @param value + * @return + */ + public Long lRightPushAll(String key, Collection value) { + return redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * 为已存在的列表添加值 + * + * @param key + * @param value + * @return + */ + public Long lRightPushIfPresent(String key, String value) { + return redisTemplate.opsForList().rightPushIfPresent(key, value); + } + + /** + * 在pivot元素的右边添加值 + * + * @param key + * @param pivot + * @param value + * @return + */ + public Long lRightPush(String key, String pivot, String value) { + return redisTemplate.opsForList().rightPush(key, pivot, value); + } + + /** + * 通过索引设置列表元素的值 + * + * @param key + * @param index 位置 + * @param value + */ + public void lSet(String key, long index, String value) { + redisTemplate.opsForList().set(key, index, value); + } + + /** + * 移出并获取列表的第一个元素 + * + * @param key + * @return 删除的元素 + */ + public String lLeftPop(String key) { + return redisTemplate.opsForList().leftPop(key); + } + + /** + * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param key + * @param timeout 等待时间 + * @param unit 时间单位 + * @return + */ + public String lBLeftPop(String key, long timeout, TimeUnit unit) { + return redisTemplate.opsForList().leftPop(key, timeout, unit); + } + + /** + * 移除并获取列表最后一个元素 + * + * @param key + * @return 删除的元素 + */ + public String lRightPop(String key) { + return redisTemplate.opsForList().rightPop(key); + } + + /** + * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param key + * @param timeout 等待时间 + * @param unit 时间单位 + * @return + */ + public String lBRightPop(String key, long timeout, TimeUnit unit) { + return redisTemplate.opsForList().rightPop(key, timeout, unit); + } + + /** + * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 + * + * @param sourceKey + * @param destinationKey + * @return + */ + public String lRightPopAndLeftPush(String sourceKey, String destinationKey) { + return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, + destinationKey); + } + + /** + * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param sourceKey + * @param destinationKey + * @param timeout + * @param unit + * @return + */ + public String lBRightPopAndLeftPush(String sourceKey, String destinationKey, + long timeout, TimeUnit unit) { + return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, + destinationKey, timeout, unit); + } + + /** + * 删除集合中值等于value得元素 + * + * @param key + * @param index index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素; + * index<0, 从尾部开始删除第一个值等于value的元素; + * @param value + * @return + */ + public Long lRemove(String key, long index, String value) { + return redisTemplate.opsForList().remove(key, index, value); + } + + /** + * 裁剪list + * + * @param key + * @param start + * @param end + */ + public void lTrim(String key, long start, long end) { + redisTemplate.opsForList().trim(key, start, end); + } + + /** + * 获取列表长度 + * + * @param key + * @return + */ + public Long lLen(String key) { + return redisTemplate.opsForList().size(key); + } + + /** + * 获取列表长度 + * + * @param key + * @return + */ + public List lAll(String key) { + Long size = lLen(key); + if (size.equals(0L)) { + return new ArrayList<>(); + } + return redisTemplate.opsForList().range(key, 0, size); + } + + /** --------------------set相关操作-------------------------- */ + + /** + * set添加元素 + * + * @param key + * @param values + * @return + */ + public Long sAdd(String key, String... values) { + return redisTemplate.opsForSet().add(key, values); + } + + /** + * set移除元素 + * + * @param key + * @param values + * @return + */ + public Long sRemove(String key, Object... values) { + return redisTemplate.opsForSet().remove(key, values); + } + + /** + * 移除并返回集合的一个随机元素 + * + * @param key + * @return + */ + public String sPop(String key) { + return redisTemplate.opsForSet().pop(key); + } + + /** + * 将元素value从一个集合移到另一个集合 + * + * @param key + * @param value + * @param destKey + * @return + */ + public Boolean sMove(String key, String value, String destKey) { + return redisTemplate.opsForSet().move(key, value, destKey); + } + + /** + * 获取集合的大小 + * + * @param key + * @return + */ + public Long sSize(String key) { + return redisTemplate.opsForSet().size(key); + } + + /** + * 判断集合是否包含value + * + * @param key + * @param value + * @return + */ + public Boolean sIsMember(String key, Object value) { + return redisTemplate.opsForSet().isMember(key, value); + } + + /** + * 获取两个集合的交集 + * + * @param key + * @param otherKey + * @return + */ + public Set sIntersect(String key, String otherKey) { + return redisTemplate.opsForSet().intersect(key, otherKey); + } + + /** + * 获取key集合与多个集合的交集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sIntersect(String key, Collection otherKeys) { + return redisTemplate.opsForSet().intersect(key, otherKeys); + } + + /** + * key集合与otherKey集合的交集存储到destKey集合中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sIntersectAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().intersectAndStore(key, otherKey, + destKey); + } + + /** + * key集合与多个集合的交集存储到destKey集合中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sIntersectAndStore(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForSet().intersectAndStore(key, otherKeys, + destKey); + } + + /** + * 获取两个集合的并集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sUnion(String key, String otherKeys) { + return redisTemplate.opsForSet().union(key, otherKeys); + } + + /** + * 获取key集合与多个集合的并集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sUnion(String key, Collection otherKeys) { + return redisTemplate.opsForSet().union(key, otherKeys); + } + + /** + * key集合与otherKey集合的并集存储到destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sUnionAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey); + } + + /** + * key集合与多个集合的并集存储到destKey中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sUnionAndStore(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey); + } + + /** + * 获取两个集合的差集 + * + * @param key + * @param otherKey + * @return + */ + public Set sDifference(String key, String otherKey) { + return redisTemplate.opsForSet().difference(key, otherKey); + } + + /** + * 获取key集合与多个集合的差集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sDifference(String key, Collection otherKeys) { + return redisTemplate.opsForSet().difference(key, otherKeys); + } + + /** + * key集合与otherKey集合的差集存储到destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sDifference(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().differenceAndStore(key, otherKey, + destKey); + } + + /** + * key集合与多个集合的差集存储到destKey中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sDifference(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForSet().differenceAndStore(key, otherKeys, + destKey); + } + + /** + * 获取集合所有元素 + * + * @param key + * @param key + * @return + */ + public Set setMembers(String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 随机获取集合中的一个元素 + * + * @param key + * @return + */ + public String sRandomMember(String key) { + return redisTemplate.opsForSet().randomMember(key); + } + + /** + * 随机获取集合中count个元素 + * + * @param key + * @param count + * @return + */ + public List sRandomMembers(String key, long count) { + return redisTemplate.opsForSet().randomMembers(key, count); + } + + /** + * 随机获取集合中count个元素并且去除重复的 + * + * @param key + * @param count + * @return + */ + public Set sDistinctRandomMembers(String key, long count) { + return redisTemplate.opsForSet().distinctRandomMembers(key, count); + } + + /** + * 获取集合 + * + * @param key + * @param options + * @return + */ + public Cursor sScan(String key, ScanOptions options) { + return redisTemplate.opsForSet().scan(key, options); + } + + /**------------------zSet相关操作--------------------------------*/ + + /** + * 添加元素,有序集合是按照元素的score值由小到大排列 + * + * @param key + * @param value + * @param score + * @return + */ + public Boolean zAdd(String key, String value, double score) { + return redisTemplate.opsForZSet().add(key, value, score); + } + + /** + * 有序集合 + * + * @param key + * @param values + * @return + */ + public Long zAdd(String key, Set> values) { + return redisTemplate.opsForZSet().add(key, values); + } + + /** + * 移除有序集合 + * + * @param key + * @param values + * @return + */ + public Long zRemove(String key, Object... values) { + return redisTemplate.opsForZSet().remove(key, values); + } + + /** + * 增加元素的score值,并返回增加后的值 + * + * @param key + * @param value + * @param delta + * @return + */ + public Double zIncrementScore(String key, String value, double delta) { + return redisTemplate.opsForZSet().incrementScore(key, value, delta); + } + + /** + * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列 + * + * @param key + * @param value + * @return 0表示第一位 + */ + public Long zRank(String key, Object value) { + return redisTemplate.opsForZSet().rank(key, value); + } + + /** + * 返回元素在集合的排名,按元素的score值由大到小排列 + * + * @param key + * @param value + * @return + */ + public Long zReverseRank(String key, Object value) { + return redisTemplate.opsForZSet().reverseRank(key, value); + } + + /** + * 获取集合的元素, 从小到大排序 + * + * @param key + * @param start 开始位置 + * @param end 结束位置, -1查询所有 + * @return + */ + public Set zRange(String key, long start, long end) { + return redisTemplate.opsForZSet().range(key, start, end); + } + + /** + * 获取集合元素, 并且把score值也获取 + * + * @param key + * @param start + * @param end + * @return + */ + public Set> zRangeWithScores(String key, long start, + long end) { + return redisTemplate.opsForZSet().rangeWithScores(key, start, end); + } + + /** + * 根据Score值查询集合元素 + * + * @param key + * @param min 最小值 + * @param max 最大值 + * @return + */ + public Set zRangeByScore(String key, double min, double max) { + return redisTemplate.opsForZSet().rangeByScore(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从小到大排序 + * + * @param key + * @param min 最小值 + * @param max 最大值 + * @return + */ + public Set> zRangeByScoreWithScores(String key, + double min, double max) { + return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从小到大排序 + * + * @param key + * @param min + * @param max + * @param start + * @param end + * @return + */ + public Set> zRangeByScoreWithScores(String key, + double min, double max, long start, long end) { + return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, + start, end); + } + + /** + * 获取集合的元素, 从大到小排序 + * + * @param key + * @param start + * @param end + * @return + */ + public Set zReverseRange(String key, long start, long end) { + return redisTemplate.opsForZSet().reverseRange(key, start, end); + } + + /** + * 获取集合的元素, 从大到小排序, 并返回score值 + * + * @param key + * @param start + * @param end + * @return + */ + public Set> zReverseRangeWithScores(String key, + long start, long end) { + return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, + end); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key + * @param min + * @param max + * @return + */ + public Set zReverseRangeByScore(String key, double min, + double max) { + return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key + * @param min + * @param max + * @return + */ + public Set> zReverseRangeByScoreWithScores( + String key, double min, double max) { + return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, + min, max); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key + * @param min + * @param max + * @param start + * @param end + * @return + */ + public Set zReverseRangeByScore(String key, double min, + double max, long start, long end) { + return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, + start, end); + } + + /** + * 根据score值获取集合元素数量 + * + * @param key + * @param min + * @param max + * @return + */ + public Long zCount(String key, double min, double max) { + return redisTemplate.opsForZSet().count(key, min, max); + } + + /** + * 获取集合大小 + * + * @param key + * @return + */ + public Long zSize(String key) { + return redisTemplate.opsForZSet().size(key); + } + + /** + * 获取集合大小 + * + * @param key + * @return + */ + public Long zZCard(String key) { + return redisTemplate.opsForZSet().zCard(key); + } + + /** + * 获取集合中value元素的score值 + * + * @param key + * @param value + * @return + */ + public Double zScore(String key, Object value) { + return redisTemplate.opsForZSet().score(key, value); + } + + /** + * 移除指定索引位置的成员 + * + * @param key + * @param start + * @param end + * @return + */ + public Long zRemoveRange(String key, long start, long end) { + return redisTemplate.opsForZSet().removeRange(key, start, end); + } + + /** + * 根据指定的score值的范围来移除成员 + * + * @param key + * @param min + * @param max + * @return + */ + public Long zRemoveRangeByScore(String key, double min, double max) { + return redisTemplate.opsForZSet().removeRangeByScore(key, min, max); + } + + /** + * 获取key和otherKey的并集并存储在destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long zUnionAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey); + } + + /** + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long zUnionAndStore(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForZSet() + .unionAndStore(key, otherKeys, destKey); + } + + /** + * 交集 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long zIntersectAndStore(String key, String otherKey, + String destKey) { + return redisTemplate.opsForZSet().intersectAndStore(key, otherKey, + destKey); + } + + /** + * 交集 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long zIntersectAndStore(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys, + destKey); + } + + /** + * @param key + * @param options + * @return + */ + public Cursor> zScan(String key, ScanOptions options) { + return redisTemplate.opsForZSet().scan(key, options); + } + + /** + * 发布订阅 + * + * @param channel + * @param message + */ + public void convertAndSend(String channel, Object message) { + redisTemplate.convertAndSend(channel, message); + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/shiro/LoginUser.java b/src/main/java/com/platform/common/shiro/LoginUser.java new file mode 100644 index 0000000..219a05a --- /dev/null +++ b/src/main/java/com/platform/common/shiro/LoginUser.java @@ -0,0 +1,33 @@ +package com.platform.common.shiro; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 登录用户身份权限 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class LoginUser { + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户手机号 + */ + private String phone; + + /** + * 登录IP地址 + */ + private String ipAddr; + +} diff --git a/src/main/java/com/platform/common/shiro/Md5Utils.java b/src/main/java/com/platform/common/shiro/Md5Utils.java new file mode 100644 index 0000000..e7e47a3 --- /dev/null +++ b/src/main/java/com/platform/common/shiro/Md5Utils.java @@ -0,0 +1,49 @@ +package com.platform.common.shiro; + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.SecureUtil; +import org.apache.shiro.crypto.hash.Md5Hash; +import org.apache.shiro.util.ByteSource; + +/** + * Md5Util + */ +public class Md5Utils { + + /** + * credentials + */ + public static final String credentials(String password, String salt) { + //盐:为了即使相同的密码不同的盐加密后的结果也不同 + ByteSource byteSalt = ByteSource.Util.bytes(salt); + Md5Hash result = new Md5Hash(password, byteSalt); + return result.toString(); + } + + /** + * md5 + */ + public static final String md5(String str) { + return SecureUtil.md5(str); + } + + /** + * 加密盐 + */ + public static String salt() { + return RandomUtil.randomString(4); + } + + /** + * 基础密码 + */ + private static final String baseStr = "abcdefghgkmnprstwxyz123456789"; + + /** + * 密码 + */ + public static String password() { + return RandomUtil.randomString(baseStr, 8); + } + +} diff --git a/src/main/java/com/platform/common/shiro/ShiroConfiguration.java b/src/main/java/com/platform/common/shiro/ShiroConfiguration.java new file mode 100644 index 0000000..cd343e3 --- /dev/null +++ b/src/main/java/com/platform/common/shiro/ShiroConfiguration.java @@ -0,0 +1,110 @@ +package com.platform.common.shiro; + +import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.Filter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * ShiroConfiguration + */ +@Configuration +public class ShiroConfiguration { + + /** + * 下面两个方法对 注解权限起作用有很大的关系,请把这两个方法,放在配置的最上面 + */ + @Bean(name = "lifecycleBeanPostProcessor") + public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + return new LifecycleBeanPostProcessor(); + } + + /** + * 身份认证Realm,此处的注入不可以缺少。否则会在UserRealm中注入对象会报空指针. + * 将自己的验证方式加入容器 + */ + @Bean + public ShiroRealm shiroRealm() { + ShiroRealm realm = new ShiroRealm(); + realm.setCredentialsMatcher(hashedCredentialsMatcher()); + return realm; + } + + /** + * 配置shiro session 的一个管理器 + */ + @Bean + public DefaultWebSessionManager sessionManager() { + DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); + sessionManager.setSessionListeners(new ArrayList<>()); + return sessionManager; + } + + /** + * 核心的安全事务管理器 + * 设置realm、cacheManager等 + */ + @Bean + public SecurityManager securityManager() { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + //设置realm + securityManager.setRealm(shiroRealm()); + // 设置sessionManager + securityManager.setSessionManager(sessionManager()); + return securityManager; + } + + /** + * 开启shiro aop注解支持. + * 使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效 + */ + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); + authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); + return authorizationAttributeSourceAdvisor; + } + + /** + * 哈希密码比较器。在myShiroRealm中作用参数使用 + * 登陆时会比较用户输入的密码,跟数据库密码配合盐值salt解密后是否一致。 + */ + @Bean + public HashedCredentialsMatcher hashedCredentialsMatcher() { + HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); + hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用md5算法; + hashedCredentialsMatcher.setHashIterations(1);//散列的次数,比如散列两次,相当于 md5( md5("")); + return hashedCredentialsMatcher; + } + + @Bean + public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { + ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); + //设置安全管理器 + shiroFilterFactoryBean.setSecurityManager(securityManager); + //oauth过滤 + Map filters = new HashMap<>(16); + filters.put("oauth2", new ShiroTokenFilter()); + shiroFilterFactoryBean.setFilters(filters); + //权限控制map + Map filterChainDefinitionMap = new LinkedHashMap<>(); + // 自定义拦截全部写到下面↓↓↓ + // 免登录接口,增加@IgnoreAuth注解 + // 自定义拦截全部写到上面↑↑↑ + filterChainDefinitionMap.put("/**", "oauth2"); + shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); + return shiroFilterFactoryBean; + } + +} diff --git a/src/main/java/com/platform/common/shiro/ShiroLoginAuth.java b/src/main/java/com/platform/common/shiro/ShiroLoginAuth.java new file mode 100644 index 0000000..b7521fa --- /dev/null +++ b/src/main/java/com/platform/common/shiro/ShiroLoginAuth.java @@ -0,0 +1,30 @@ +package com.platform.common.shiro; + +import lombok.Data; +import org.apache.shiro.authc.AuthenticationToken; + +/** + * token + */ +@Data +public class ShiroLoginAuth implements AuthenticationToken { + + private String phone; + private char[] password; + + public ShiroLoginAuth(String phone, String password) { + this.phone = phone; + this.password = password.toCharArray(); + } + + @Override + public Object getPrincipal() { + return phone; + } + + @Override + public Object getCredentials() { + return password; + } + +} diff --git a/src/main/java/com/platform/common/shiro/ShiroLoginPhone.java b/src/main/java/com/platform/common/shiro/ShiroLoginPhone.java new file mode 100644 index 0000000..2022591 --- /dev/null +++ b/src/main/java/com/platform/common/shiro/ShiroLoginPhone.java @@ -0,0 +1,28 @@ +package com.platform.common.shiro; + +import lombok.Data; +import org.apache.shiro.authc.AuthenticationToken; + +/** + * token + */ +@Data +public class ShiroLoginPhone implements AuthenticationToken { + + private String phone; + + public ShiroLoginPhone(String phone) { + this.phone = phone; + } + + @Override + public Object getPrincipal() { + return phone; + } + + @Override + public Object getCredentials() { + return phone; + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/shiro/ShiroLoginToken.java b/src/main/java/com/platform/common/shiro/ShiroLoginToken.java new file mode 100644 index 0000000..f688e7f --- /dev/null +++ b/src/main/java/com/platform/common/shiro/ShiroLoginToken.java @@ -0,0 +1,26 @@ +package com.platform.common.shiro; + +import org.apache.shiro.authc.AuthenticationToken; + +/** + * token + */ +public class ShiroLoginToken implements AuthenticationToken { + private static final long serialVersionUID = 1L; + + private String token; + + public ShiroLoginToken(String token) { + this.token = token; + } + + @Override + public Object getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } +} diff --git a/src/main/java/com/platform/common/shiro/ShiroRealm.java b/src/main/java/com/platform/common/shiro/ShiroRealm.java new file mode 100644 index 0000000..70415f1 --- /dev/null +++ b/src/main/java/com/platform/common/shiro/ShiroRealm.java @@ -0,0 +1,129 @@ +package com.platform.common.shiro; + +import cn.hutool.core.util.RandomUtil; +import com.platform.common.enums.ResultCodeEnum; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.exception.LoginException; +import com.platform.common.utils.IpUtils; +import com.platform.common.utils.ServletUtils; +import com.platform.modules.auth.service.TokenService; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.service.ChatUserService; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.util.ByteSource; +import org.springframework.context.annotation.Lazy; + +import javax.annotation.Resource; + +/** + * ShiroRealm + */ +public class ShiroRealm extends AuthorizingRealm { + + @Lazy + @Resource + private TokenService tokenService; + + @Lazy + @Resource + private ChatUserService chatUserService; + + /** + * 提供用户信息,返回权限信息 + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + Object object = ShiroUtils.getSubject().getPrincipal(); + if (object == null) { + return null; + } + // 后台管理 + if (object instanceof LoginUser) { + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + return info; + } + return null; + } + + /** + * 必须重写此方法,不然会报错 + */ + @Override + public boolean supports(AuthenticationToken authenticationToken) { + return authenticationToken instanceof ShiroLoginToken + || authenticationToken instanceof ShiroLoginAuth + || authenticationToken instanceof ShiroLoginPhone; + } + + /** + * 身份认证/登录,验证用户是不是拥有相应的身份; 用于登陆认证 + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { + // token + if (authenticationToken instanceof ShiroLoginToken) { + String token = (String) authenticationToken.getPrincipal(); + LoginUser loginUser = tokenService.queryByToken(token); + if (loginUser == null) { + throw new LoginException(ResultCodeEnum.UNAUTHORIZED); + } + // 加密盐 + String salt = Md5Utils.salt(); + // 对token加密 + String credentials = Md5Utils.credentials(token, salt); + return new SimpleAuthenticationInfo(loginUser, credentials, ByteSource.Util.bytes(salt), getName()); + } + // 手机+密码登录 + if (authenticationToken instanceof ShiroLoginAuth) { + ShiroLoginAuth token = (ShiroLoginAuth) authenticationToken; + return makeLoginUser(token.getPhone(), true); + } + // 手机+验证码登录 + if (authenticationToken instanceof ShiroLoginPhone) { + ShiroLoginPhone token = (ShiroLoginPhone) authenticationToken; + return makeLoginUser(token.getPhone(), false); + } + return null; + } + + /** + * 组装登录对象 + */ + private SimpleAuthenticationInfo makeLoginUser(String phone, boolean isPassword) { + // 查询用户 + ChatUser chatUser = chatUserService.queryByPhone(phone); + // 处理用户 + if (chatUser == null) { + throw new AuthenticationException("手机号或密码不正确"); // 手机不存在 + } + // 查询权限 + LoginUser loginUser = new LoginUser() + .setUserId(chatUser.getUserId()) + .setPhone(chatUser.getPhone()) + .setIpAddr(IpUtils.getIpAddr(ServletUtils.getRequest())) + .setToken(RandomUtil.randomString(32)); + String credentials = chatUser.getPassword(); + String salt = chatUser.getSalt(); + if (!isPassword) { + // 加密盐 + salt = Md5Utils.salt(); + // 对token加密 + credentials = Md5Utils.credentials(phone, salt); + } + // 登录 + return new SimpleAuthenticationInfo(loginUser, credentials, ByteSource.Util.bytes(salt), getName()); + } + + @Override + public boolean isPermitted(PrincipalCollection principals, String permission) { + return super.isPermitted(principals, permission); + } + +} diff --git a/src/main/java/com/platform/common/shiro/ShiroTokenFilter.java b/src/main/java/com/platform/common/shiro/ShiroTokenFilter.java new file mode 100644 index 0000000..128f51d --- /dev/null +++ b/src/main/java/com/platform/common/shiro/ShiroTokenFilter.java @@ -0,0 +1,126 @@ +package com.platform.common.shiro; + +import cn.hutool.json.JSONUtil; +import com.platform.common.aspectj.IgnoreAuth; +import com.platform.common.constant.HeadConstant; +import com.platform.common.enums.ResultCodeEnum; +import com.platform.common.exception.BaseException; +import com.platform.common.exception.LoginException; +import com.platform.common.web.domain.AjaxResult; +import lombok.SneakyThrows; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.web.filter.authc.AuthenticatingFilter; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.springframework.web.servlet.support.RequestContextUtils; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.annotation.Annotation; + +/** + * auth2过滤器 + */ +public class ShiroTokenFilter extends AuthenticatingFilter { + + @Override + protected AuthenticationToken createToken(ServletRequest request, ServletResponse servletResponse) { + //获取请求token + ShiroLoginToken token = getToken(request); + if (token == null) { + return null; + } + return token; + } + + @SneakyThrows + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + if (RequestMethod.OPTIONS.name().equalsIgnoreCase(httpServletRequest.getMethod())) { + return true; + } + if ("/favicon.ico".equals(httpServletRequest.getRequestURI())) { + return error(request, response, ResultCodeEnum.SUCCESS); + } + try { + WebApplicationContext ctx = RequestContextUtils.findWebApplicationContext(httpServletRequest); + RequestMappingHandlerMapping mapping = ctx.getBean(RequestMappingHandlerMapping.class); + HandlerExecutionChain handler = mapping.getHandler(httpServletRequest); + if (handler == null) { + return true; + } + Annotation[] declaredAnnotations = ((HandlerMethod) handler.getHandler()).getMethod().getDeclaredAnnotations(); + if (declaredAnnotations.length != 0) { + for (Annotation annotation : declaredAnnotations) { + if (IgnoreAuth.class.equals(annotation.annotationType())) { + return true; + } + } + } + } catch (BaseException e) { + return error(request, response, e.getResultCode()); + } catch (Exception e) { + return error(request, response, ResultCodeEnum.FAIL); + } + return false; + } + + private boolean error(ServletRequest request, ServletResponse response, ResultCodeEnum resultCode) { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + try { + // 请求转发401controller + httpServletRequest.getRequestDispatcher("/error/" + resultCode.getCode()).forward(request, response); + } catch (Exception e) { + } + return true; + } + + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { + // 请求token + ShiroLoginToken token = getToken(request); + if (token == null) { + return error(response, ResultCodeEnum.UNAUTHORIZED, null); + } + try { + getSubject(request, response).login(token); + return true; + } catch (LoginException e) { + return error(response, ResultCodeEnum.UNAUTHORIZED, e.getMessage()); + } catch (AuthenticationException e) { + return error(response, ResultCodeEnum.UNAUTHORIZED, null); + } + } + + private boolean error(ServletResponse response, ResultCodeEnum resultCode, String msg) throws IOException { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setContentType("application/json;charset=utf-8"); + httpResponse.getWriter().print(JSONUtil.toJsonStr(AjaxResult.result(resultCode, msg))); + return false; + } + + /** + * 请求的token + */ + private ShiroLoginToken getToken(ServletRequest request) { + HttpServletRequest httpRequest = (HttpServletRequest) request; + String token = httpRequest.getHeader(HeadConstant.TOKEN_HEADER_ADMIN); + if (StringUtils.isEmpty(token)) { + token = httpRequest.getParameter(HeadConstant.TOKEN_HEADER_ADMIN); + } + if (!StringUtils.isEmpty(token)) { + return new ShiroLoginToken(token); + } + return null; + } + +} diff --git a/src/main/java/com/platform/common/shiro/ShiroUtils.java b/src/main/java/com/platform/common/shiro/ShiroUtils.java new file mode 100644 index 0000000..0cd81a1 --- /dev/null +++ b/src/main/java/com/platform/common/shiro/ShiroUtils.java @@ -0,0 +1,45 @@ + +package com.platform.common.shiro; + +import com.platform.common.constant.HeadConstant; +import com.platform.common.utils.ServletUtils; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; + +/** + * Shiro工具类 + */ +public class ShiroUtils { + + public static Subject getSubject() { + return SecurityUtils.getSubject(); + } + + public static LoginUser getLoginUser() { + return (LoginUser) getSubject().getPrincipal(); + } + + /** + * 是否登录 + */ + public static boolean isLogin() { + return getLoginUser() != null; + } + + public static String getToken() { + LoginUser loginUser = getLoginUser(); + if (loginUser != null) { + return loginUser.getToken(); + } + return ServletUtils.getRequest().getHeader(HeadConstant.TOKEN_HEADER_ADMIN); + } + + public static String getPhone() { + return getLoginUser().getPhone(); + } + + public static Long getUserId() { + return getLoginUser().getUserId(); + } + +} diff --git a/src/main/java/com/platform/common/upload/config/UploadConfig.java b/src/main/java/com/platform/common/upload/config/UploadConfig.java new file mode 100644 index 0000000..16d8663 --- /dev/null +++ b/src/main/java/com/platform/common/upload/config/UploadConfig.java @@ -0,0 +1,45 @@ +package com.platform.common.upload.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 文件上传 + */ +@Component +@Data +public class UploadConfig { + + /** + * 服务端域名 + */ + @Value("${upload.serverUrl}") + private String serverUrl; + /** + * accessKey + */ + @Value("${upload.accessKey}") + private String accessKey; + /** + * secretKey + */ + @Value("${upload.secretKey}") + private String secretKey; + /** + * bucket + */ + @Value("${upload.bucket}") + private String bucket; + /** + * region + */ + @Value("${upload.region}") + private String region; + /** + * 封面 + */ + @Value("${upload.post}") + private String post; + +} diff --git a/src/main/java/com/platform/common/upload/enums/UploadTypeEnum.java b/src/main/java/com/platform/common/upload/enums/UploadTypeEnum.java new file mode 100644 index 0000000..ac0a596 --- /dev/null +++ b/src/main/java/com/platform/common/upload/enums/UploadTypeEnum.java @@ -0,0 +1,53 @@ +package com.platform.common.upload.enums; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 上传类型枚举 + */ +@Getter +public enum UploadTypeEnum { + + /** + * 阿里oss + */ + OSS("oss", "阿里oss"), + + /** + * 腾讯cos + */ + COS("cos", "腾讯cos"), + + /** + * 七牛kodo + */ + KODO("kodo", "七牛kodo"), + + /** + * FAST + */ + FAST("fast", "FAST_DFS"), + + /** + * MINIO + */ + MINIO("minio", "MINIO"), + + /** + * 本地local + */ + LOCAL("local", "本地上传"), + + ; + + @JsonValue + private final String code; + private final String info; + + UploadTypeEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/common/upload/service/UploadService.java b/src/main/java/com/platform/common/upload/service/UploadService.java new file mode 100644 index 0000000..8fb6da2 --- /dev/null +++ b/src/main/java/com/platform/common/upload/service/UploadService.java @@ -0,0 +1,54 @@ +package com.platform.common.upload.service; + +import cn.hutool.core.lang.Dict; +import com.platform.common.upload.vo.UploadFileVo; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.InputStream; + +/** + * 上传服务 + */ +public interface UploadService { + + /** + * 获取服务端域名 + */ + String getServerUrl(); + + /** + * 获取上传凭证 + */ + Dict getToken(String fileType); + + /** + * 文件上传 + */ + UploadFileVo uploadFile(MultipartFile file); + + /** + * 文件上传 + */ + UploadFileVo uploadFile(MultipartFile file, String folder); + + /** + * 文件上传 + */ + UploadFileVo uploadFile(File file); + + /** + * 文件上传 + */ + UploadFileVo uploadFile(File file, String folder); + + /** + * 获取文件流 + */ + InputStream getInputStream(String urlPath); + + /** + * 删除本地文件 + */ + boolean delFile(File file); +} diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadBaseService.java b/src/main/java/com/platform/common/upload/service/impl/UploadBaseService.java new file mode 100644 index 0000000..8a5e78c --- /dev/null +++ b/src/main/java/com/platform/common/upload/service/impl/UploadBaseService.java @@ -0,0 +1,171 @@ +package com.platform.common.upload.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileTypeUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.util.IdUtil; +import com.platform.common.upload.vo.UploadFileVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * 基础上传 + */ +@Slf4j +public class UploadBaseService { + + /** + * 获取文件名称 + */ + protected static String getFileName(MultipartFile file) { + String fileName = file.getOriginalFilename(); + if (StringUtils.isEmpty(fileName)) { + // 随机名称 + fileName = IdUtil.objectId() + "." + getFileType(file); + } + return fileName; + } + + /** + * 获取文件名称 + */ + protected static String getFileName(File file) { + String fileName = file.getName(); + if (StringUtils.isEmpty(fileName)) { + // 随机名称 + fileName = IdUtil.objectId() + "." + getFileType(file); + } + return fileName; + } + + /** + * 获取文件全名 + */ + protected static String getFileKey(MultipartFile file) { + return IdUtil.objectId() + "." + getFileType(file); + } + + /** + * 获取文件全名 + */ + protected static String getFileKey(MultipartFile file, String folder) { + String fileKey = IdUtil.objectId() + "." + getFileType(file); + if (StringUtils.isEmpty(folder)) { + return fileKey; + } + return folder + "/" + fileKey; + } + + /** + * 获取文件全名 + */ + protected static String getFileKey(File file) { + return IdUtil.objectId() + "." + getFileType(file); + } + + /** + * 获取文件全名 + */ + protected static String getFileKey(File file, String folder) { + String fileKey = IdUtil.objectId() + "." + getFileType(file); + if (StringUtils.isEmpty(folder)) { + return fileKey; + } + return folder + "/" + fileKey; + } + + /** + * 获取文件全名 + */ + protected static String getFileKey(String uploadPath, String fileType) { + // 文件路径 + String filePath = DateUtil.format(DateUtil.date(), "yyyy/MM/dd"); + // 生成文件夹 + FileUtil.mkdir(uploadPath + FileNameUtil.UNIX_SEPARATOR + filePath); + return filePath + FileNameUtil.UNIX_SEPARATOR + IdUtil.objectId() + "." + fileType; + } + + /** + * 获取文件全名 + */ + protected static String getFileType(MultipartFile file) { + // 文件扩展名 + String fileType = FileNameUtil.extName(file.getOriginalFilename()); + if (StringUtils.isEmpty(fileType)) { + // 文件后缀 + try { + fileType = FileTypeUtil.getType(file.getInputStream()); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + return fileType; + } + + /** + * 获取文件全名 + */ + protected static String getFileType(File file) { + // 文件扩展名 + String fileType = FileNameUtil.extName(file.getName()); + if (StringUtils.isEmpty(fileType)) { + // 文件后缀 + fileType = FileTypeUtil.getType(file); + } + if (StringUtils.isEmpty(fileType)) { + fileType = "txt"; + } + return fileType; + } + + /** + * 获取文件流 + */ + public InputStream getInputStream(String urlPath) { + try { + URL url = new URL(urlPath); + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + // 设置网络连接超时时间 + httpURLConnection.setConnectTimeout(5000); + // 设置应用程序要从网络连接读取数据 + httpURLConnection.setDoInput(true); + // 从服务器返回一个输入流 + return httpURLConnection.getInputStream(); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("获取文件流失败"); + } + } + + /** + * 封装对象 + */ + protected static UploadFileVo format(String fileName, String serverUrl, String fileKey, String fileType) { + // 服务器地址 + UploadFileVo fileVo = new UploadFileVo() + .setFileName(fileName) + .setFullPath(serverUrl + FileNameUtil.UNIX_SEPARATOR + fileKey) + .setFileType(fileType); + return fileVo; + } + + /** + * 删除本地文件 + */ + public boolean delFile(File file) { + try { + return FileUtil.del(file); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("删除上传失败"); + } + } + +} diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadCosServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadCosServiceImpl.java new file mode 100644 index 0000000..90f4117 --- /dev/null +++ b/src/main/java/com/platform/common/upload/service/impl/UploadCosServiceImpl.java @@ -0,0 +1,154 @@ +package com.platform.common.upload.service.impl; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.platform.common.upload.config.UploadConfig; +import com.platform.common.upload.enums.UploadTypeEnum; +import com.platform.common.upload.service.UploadService; +import com.platform.common.upload.vo.UploadFileVo; +import com.qcloud.cos.COSClient; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.model.ObjectMetadata; +import com.qcloud.cos.model.PutObjectRequest; +import com.qcloud.cos.region.Region; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.InputStream; + +/** + * 腾讯云上传 + */ +@Slf4j +@Service("uploadCosService") +@Configuration +@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "cos") +public class UploadCosServiceImpl extends UploadBaseService implements UploadService { + + @Resource + private UploadConfig uploadConfig; + + /** + * 初始化cos + */ + private COSClient initCOS() { + return new COSClient(new BasicCOSCredentials(uploadConfig.getAccessKey(), uploadConfig.getSecretKey()), + new com.qcloud.cos.ClientConfig(new Region(uploadConfig.getRegion()))); + } + + @Override + public String getServerUrl() { + return uploadConfig.getServerUrl(); + } + + @Override + public Dict getToken(String fileType) { + // 1、默认固定值 + Long expire = 1800L; + String algorithm = "sha1"; + String accessKey = uploadConfig.getAccessKey(); + String post = uploadConfig.getPost(); + String serverUrl = uploadConfig.getServerUrl(); + // 2、生成KeyTime + Long startTime = DateUtil.currentSeconds(); + Long endTime = startTime + expire; + String keyTime = StrUtil.format("{};{}", startTime, endTime); + // 3、构造“策略”(Policy) + JSONArray conditions = new JSONArray() + .set(new JSONObject().set("q-sign-algorithm", algorithm)) + .set(new JSONObject().set("q-ak", accessKey)) + .set(new JSONObject().set("q-sign-time", keyTime)); + JSONObject policyObj = new JSONObject() + .set("expiration", DateUtil.format(DateUtil.date(endTime * 1000), DatePattern.UTC_MS_PATTERN)) + .set("conditions", conditions); + String policy = JSONUtil.toJsonStr(policyObj); + // 4、生成 SignKey + String signKey = SecureUtil.hmacSha1(uploadConfig.getSecretKey()).digestHex(keyTime); + // 5、生成 Signature + String signature = SecureUtil.hmacSha1(signKey).digestHex(SecureUtil.sha1(policy)); + return Dict.create() + .set("uploadType", UploadTypeEnum.COS) + .set("serverUrl", serverUrl) + .set("fileName", IdUtil.objectId() + "." + fileType) + .set("accessKey", accessKey) + .set("policy", Base64.encode(policy)) + .set("signature", signature) + .set("keyTime", keyTime) + .set("algorithm", algorithm) + .set("post", post); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file, String folder) { + String fileName = getFileName(file); + String fileKey = getFileKey(file, folder); + String fileType = getFileType(file); + // 3 生成 cos 客户端。 + COSClient client = null; + try { + client = initCOS(); + //上传到腾讯云 + PutObjectRequest putObjectRequest = new PutObjectRequest(uploadConfig.getBucket() + , fileKey, file.getInputStream(), new ObjectMetadata()); + client.putObject(putObjectRequest); + return format(fileName, uploadConfig.getServerUrl(), fileKey, fileType); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } finally { + if (client != null) { + client.shutdown(); + } + } + } + + @Override + public UploadFileVo uploadFile(File file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(File file, String folder) { + String fileName = getFileName(file); + String fileKey = getFileKey(file, folder); + String fileType = getFileType(file); + InputStream inputStream = FileUtil.getInputStream(file); + // 3 生成 cos 客户端。 + COSClient client = null; + try { + client = initCOS(); + //上传到腾讯云 + PutObjectRequest putObjectRequest = new PutObjectRequest(uploadConfig.getBucket() + , fileKey, inputStream, new ObjectMetadata()); + client.putObject(putObjectRequest); + return format(fileName, uploadConfig.getServerUrl(), fileKey, fileType); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } finally { + if (client != null) { + client.shutdown(); + } + } + } + +} diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadFastServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadFastServiceImpl.java new file mode 100644 index 0000000..a8d697d --- /dev/null +++ b/src/main/java/com/platform/common/upload/service/impl/UploadFastServiceImpl.java @@ -0,0 +1,92 @@ +package com.platform.common.upload.service.impl; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.IdUtil; +import com.github.tobato.fastdfs.domain.fdfs.StorePath; +import com.platform.common.upload.config.UploadConfig; +import com.platform.common.upload.enums.UploadTypeEnum; +import com.platform.common.upload.service.UploadService; +import com.platform.common.upload.utils.FastUtils; +import com.platform.common.upload.vo.UploadFileVo; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; + +/** + * fast上传 + */ +@Slf4j +@Service("uploadFastService") +@Configuration +@NoArgsConstructor +@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "fast") +public class UploadFastServiceImpl extends UploadBaseService implements UploadService { + + @Resource + private UploadConfig uploadConfig; + + @Override + public String getServerUrl() { + return uploadConfig.getServerUrl(); + } + + @Override + public Dict getToken(String fileType) { + String serverUrl = uploadConfig.getServerUrl(); + String post = uploadConfig.getPost(); + return Dict.create() + .set("uploadType", UploadTypeEnum.LOCAL) + .set("serverUrl", serverUrl) + .set("fileName", IdUtil.objectId() + "." + fileType) + .set("post", post); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file, String folder) { + StorePath storePath; + try { + storePath = FastUtils.uploadFile(file); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } + String fileKey = storePath.getFullPath(); + String fileName = getFileName(file); + String fileType = getFileType(file); + String serverUrl = uploadConfig.getServerUrl(); + return format(fileName, serverUrl, fileKey, fileType); + } + + @Override + public UploadFileVo uploadFile(File file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(File file, String folder) { + StorePath storePath; + try { + storePath = FastUtils.uploadFile(file); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } + String fileKey = storePath.getFullPath(); + String fileType = getFileType(file); + String fileName = getFileName(file); + String serverUrl = uploadConfig.getServerUrl(); + return format(fileName, serverUrl, fileKey, fileType); + } + +} diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadKodoServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadKodoServiceImpl.java new file mode 100644 index 0000000..fdae5fc --- /dev/null +++ b/src/main/java/com/platform/common/upload/service/impl/UploadKodoServiceImpl.java @@ -0,0 +1,130 @@ +package com.platform.common.upload.service.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.IdUtil; +import com.platform.common.upload.config.UploadConfig; +import com.platform.common.upload.enums.UploadTypeEnum; +import com.platform.common.upload.service.UploadService; +import com.platform.common.upload.vo.UploadFileVo; +import com.qiniu.http.Response; +import com.qiniu.storage.UploadManager; +import com.qiniu.util.Auth; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.InputStream; + +/** + * 七牛云上传 + */ +@Slf4j +@Service("uploadKodoService") +@Configuration +@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "kodo") +public class UploadKodoServiceImpl extends UploadBaseService implements UploadService { + + @Resource + private UploadConfig uploadConfig; + + /** + * 获取Auth + */ + private Auth getAuth() { + return Auth.create(uploadConfig.getAccessKey(), uploadConfig.getSecretKey()); + } + + /** + * 获取Token + */ + private String getUploadToken(String fileType) { + return getAuth().uploadToken(uploadConfig.getBucket(), fileType); + } + + @Override + public String getServerUrl() { + return uploadConfig.getServerUrl(); + } + + @Override + public Dict getToken(String fileType) { + String serverUrl = uploadConfig.getServerUrl(); + String region = uploadConfig.getRegion(); + String post = uploadConfig.getPost(); + String fileName = IdUtil.objectId(); + String token; + if (StringUtils.isEmpty(fileType)) { + token = getUploadToken(fileType); + } else { + fileName += "." + fileType; + token = getUploadToken(fileName); + } + return Dict.create() + .set("uploadType", UploadTypeEnum.KODO) + .set("serverUrl", serverUrl) + .set("fileName", fileName) + .set("region", region) + .set("uploadToken", token) + .set("post", post); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file, String folder) { + String fileName = getFileName(file); + String fileKey = getFileKey(file, folder); + String fileType = getFileType(file); + String token = getUploadToken(fileKey); + Response response = null; + try { + UploadManager uploadManager = new UploadManager(new com.qiniu.storage.Configuration()); + response = uploadManager.put(file.getInputStream(), fileKey, token, null, fileType); + return format(fileName, uploadConfig.getServerUrl(), fileKey, fileType); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } finally { + if (response != null) { + response.close(); + } + } + } + + @Override + public UploadFileVo uploadFile(File file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(File file, String folder) { + String fileName = getFileName(file); + String fileKey = getFileKey(file, folder); + String fileType = getFileType(file); + String token = getUploadToken(fileKey); + InputStream inputStream = FileUtil.getInputStream(file); + Response response = null; + try { + UploadManager uploadManager = new UploadManager(new com.qiniu.storage.Configuration()); + response = uploadManager.put(inputStream, fileKey, token, null, fileType); + return format(fileName, uploadConfig.getServerUrl(), fileKey, fileType); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } finally { + if (response != null) { + response.close(); + } + } + } + +} diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadLocalServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadLocalServiceImpl.java new file mode 100644 index 0000000..383d27b --- /dev/null +++ b/src/main/java/com/platform/common/upload/service/impl/UploadLocalServiceImpl.java @@ -0,0 +1,89 @@ +package com.platform.common.upload.service.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.lang.Dict; +import com.platform.common.upload.config.UploadConfig; +import com.platform.common.upload.enums.UploadTypeEnum; +import com.platform.common.upload.service.UploadService; +import com.platform.common.upload.vo.UploadFileVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; + +/** + * 本地上传 + */ +@Slf4j +@Service("uploadLocalService") +@Configuration +@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "local") +public class UploadLocalServiceImpl extends UploadBaseService implements UploadService { + + @Resource + private UploadConfig uploadConfig; + + @Override + public String getServerUrl() { + return uploadConfig.getServerUrl(); + } + + @Override + public Dict getToken(String fileType) { + return Dict.create() + .set("uploadType", UploadTypeEnum.LOCAL); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file, String folder) { + String serverUrl = uploadConfig.getServerUrl(); + String uploadPath = uploadConfig.getRegion(); + String fileName = getFileName(file); + String fileType = getFileType(file); + // 文件路径 + String fileKey = getFileKey(uploadPath, fileType); + try { + // 文件拷贝 + file.transferTo(new File(uploadPath + FileNameUtil.UNIX_SEPARATOR + fileKey)); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } + // 组装对象 + UploadFileVo fileVo = format(fileName, serverUrl, fileKey, fileType) + .setFullPath(serverUrl + fileKey); + return fileVo; + } + + @Override + public UploadFileVo uploadFile(File file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(File file, String folder) { + String serverUrl = uploadConfig.getServerUrl(); + String uploadPath = uploadConfig.getRegion(); + String fileName = getFileName(file); + String fileType = getFileType(file); + // 文件路径 + String fileKey = getFileKey(uploadPath, fileType); + // 文件拷贝 + FileUtil.copyFile(file, new File(uploadPath + FileNameUtil.UNIX_SEPARATOR + fileKey)); + // 组装对象 + UploadFileVo fileVo = format(fileName, serverUrl, fileKey, fileType) + .setFullPath(serverUrl + fileKey); + return fileVo; + } + +} diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadMinioServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadMinioServiceImpl.java new file mode 100644 index 0000000..6405258 --- /dev/null +++ b/src/main/java/com/platform/common/upload/service/impl/UploadMinioServiceImpl.java @@ -0,0 +1,119 @@ +package com.platform.common.upload.service.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.IdUtil; +import com.platform.common.upload.config.UploadConfig; +import com.platform.common.upload.enums.UploadTypeEnum; +import com.platform.common.upload.service.UploadService; +import com.platform.common.upload.vo.UploadFileVo; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; + +/** + * minio上传 + */ +@Slf4j +@Service("uploadMinioService") +@Configuration +@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "minio") +public class UploadMinioServiceImpl extends UploadBaseService implements UploadService { + + @Resource + private UploadConfig uploadConfig; + + /** + * 获取上传凭证 + */ + private MinioClient initClient() { + return MinioClient.builder() + .endpoint(uploadConfig.getRegion()) + .credentials(uploadConfig.getAccessKey(), uploadConfig.getSecretKey()) + .build(); + } + + @Override + public String getServerUrl() { + return uploadConfig.getServerUrl(); + } + + @Override + public Dict getToken(String fileType) { + String serverUrl = uploadConfig.getServerUrl(); + String post = uploadConfig.getPost(); + return Dict.create() + .set("uploadType", UploadTypeEnum.LOCAL) + .set("serverUrl", serverUrl) + .set("fileName", IdUtil.objectId() + "." + fileType) + .set("post", post); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file, String folder) { + String serverUrl = uploadConfig.getServerUrl(); + MinioClient client = initClient(); + String fileName = getFileName(file); + String fileType = getFileType(file); + String fileKey = getFileKey(file); + try { + PutObjectArgs args = PutObjectArgs.builder() + .bucket(uploadConfig.getBucket()) + .object(fileName) + .stream(file.getInputStream(), file.getSize(), -1) + .contentType(fileType) + .build(); + client.putObject(args); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } + // 组装对象 + UploadFileVo fileVo = format(fileName, serverUrl, fileKey, fileType) + .setFullPath(serverUrl + fileKey); + return fileVo; + } + + @Override + public UploadFileVo uploadFile(File file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(File file, String folder) { + String serverUrl = uploadConfig.getServerUrl(); + MinioClient client = initClient(); + String fileName = getFileName(file); + String fileType = getFileType(file); + String fileKey = getFileKey(file); + try { + PutObjectArgs args = PutObjectArgs.builder() + .bucket(uploadConfig.getBucket()) + .object(fileName) + .stream(FileUtil.getInputStream(file), FileUtil.size(file), -1) + .contentType(fileType) + .build(); + client.putObject(args); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } + // 组装对象 + UploadFileVo fileVo = format(fileName, serverUrl, fileKey, fileType) + .setFullPath(serverUrl + fileKey); + return fileVo; + } + +} diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadOssServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadOssServiceImpl.java new file mode 100644 index 0000000..b3eed0f --- /dev/null +++ b/src/main/java/com/platform/common/upload/service/impl/UploadOssServiceImpl.java @@ -0,0 +1,126 @@ +package com.platform.common.upload.service.impl; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.IdUtil; +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.model.PolicyConditions; +import com.platform.common.upload.config.UploadConfig; +import com.platform.common.upload.enums.UploadTypeEnum; +import com.platform.common.upload.service.UploadService; +import com.platform.common.upload.vo.UploadFileVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.InputStream; +import java.util.Date; + +/** + * 阿里云上传 + */ +@Slf4j +@Service("uploadOssService") +@Configuration +@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "oss") +public class UploadOssServiceImpl extends UploadBaseService implements UploadService { + + @Resource + private UploadConfig uploadConfig; + + /** + * 初始化oss + */ + private OSS initOSS() { + return new OSSClientBuilder() + .build(uploadConfig.getRegion(), uploadConfig.getAccessKey(), uploadConfig.getSecretKey()); + } + + @Override + public String getServerUrl() { + return uploadConfig.getServerUrl(); + } + + @Override + public Dict getToken(String fileType) { + // 1、默认固定值,分钟 + Integer expire = 30; + String accessKey = uploadConfig.getAccessKey(); + String serverUrl = uploadConfig.getServerUrl(); + String post = uploadConfig.getPost(); + // 2、过期时间 + Date expiration = DateUtil.offsetMinute(DateUtil.date(), expire); + // 3、构造“策略”(Policy) + OSS ossClient = initOSS(); + PolicyConditions policyConditions = new PolicyConditions(); + policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000); + String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions); + String postSignature = ossClient.calculatePostSignature(postPolicy); + // 4、生成 Signature + return Dict.create() + .set("uploadType", UploadTypeEnum.OSS) + .set("serverUrl", serverUrl) + .set("fileName", IdUtil.objectId() + "." + fileType) + .set("accessKey", accessKey) + .set("policy", Base64.encode(postPolicy)) + .set("signature", postSignature) + .set("post", post); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(MultipartFile file, String folder) { + OSS client = initOSS(); + try { + String fileName = getFileName(file); + String fileKey = getFileKey(file, folder); + String fileType = getFileType(file); + client.putObject(uploadConfig.getBucket(), fileKey, file.getInputStream()); + // 服务器地址 + String serverUrl = uploadConfig.getServerUrl(); + return format(fileName, serverUrl, fileKey, fileType); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } finally { + client.shutdown(); + } + } + + @Override + public UploadFileVo uploadFile(File file) { + return uploadFile(file, null); + } + + @Override + public UploadFileVo uploadFile(File file, String folder) { + OSS client = initOSS(); + try { + String fileName = getFileName(file); + String fileKey = getFileKey(file, folder); + String fileType = getFileType(file); + InputStream inputStream = FileUtil.getInputStream(file); + client.putObject(uploadConfig.getBucket(), fileKey, inputStream); + // 服务器地址 + String serverUrl = uploadConfig.getServerUrl(); + return format(fileName, serverUrl, fileKey, fileType); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("文件上传失败"); + } finally { + client.shutdown(); + } + } + +} diff --git a/src/main/java/com/platform/common/upload/utils/FastUtils.java b/src/main/java/com/platform/common/upload/utils/FastUtils.java new file mode 100644 index 0000000..41df49c --- /dev/null +++ b/src/main/java/com/platform/common/upload/utils/FastUtils.java @@ -0,0 +1,48 @@ +package com.platform.common.upload.utils; + +import com.github.tobato.fastdfs.domain.conn.FdfsWebServer; +import com.github.tobato.fastdfs.domain.fdfs.StorePath; +import com.github.tobato.fastdfs.domain.fdfs.ThumbImageConfig; +import com.github.tobato.fastdfs.service.FastFileStorageClient; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Set; + +@Slf4j +@Component +public class FastUtils { + private static FastFileStorageClient fastFileStorageClient; + + public FastUtils(ThumbImageConfig thumbImageConfig, FastFileStorageClient fastFileStorageClient, FdfsWebServer fdfsWebServer) { + FastUtils.fastFileStorageClient = fastFileStorageClient; + } + + public static StorePath uploadFile(MultipartFile multipartFile) { + StorePath storePath; + try { + storePath = fastFileStorageClient.uploadFile(multipartFile.getInputStream(), multipartFile.getSize(), FilenameUtils.getExtension(multipartFile.getOriginalFilename()), (Set) null); + } catch (Exception var6) { + log.error(var6.getMessage(), var6); + throw new RuntimeException("文件上传失败"); + } + return storePath; + } + + public static StorePath uploadFile(File file) { + StorePath storePath; + try { + FileInputStream inputStream = new FileInputStream(file); + storePath = fastFileStorageClient.uploadFile(inputStream, file.length(), FilenameUtils.getExtension(file.getName()), null); + } catch (Exception var6) { + log.error(var6.getMessage(), var6); + throw new RuntimeException("文件上传失败"); + } + return storePath; + } + +} diff --git a/src/main/java/com/platform/common/upload/vo/UploadAudioVo.java b/src/main/java/com/platform/common/upload/vo/UploadAudioVo.java new file mode 100644 index 0000000..f76b146 --- /dev/null +++ b/src/main/java/com/platform/common/upload/vo/UploadAudioVo.java @@ -0,0 +1,18 @@ +package com.platform.common.upload.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 文件上传 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class UploadAudioVo extends UploadFileVo { + + /** + * 识别文字 + */ + private String sourceText; + +} diff --git a/src/main/java/com/platform/common/upload/vo/UploadFileVo.java b/src/main/java/com/platform/common/upload/vo/UploadFileVo.java new file mode 100644 index 0000000..613d747 --- /dev/null +++ b/src/main/java/com/platform/common/upload/vo/UploadFileVo.java @@ -0,0 +1,26 @@ +package com.platform.common.upload.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 文件上传 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class UploadFileVo { + + /** + * 文件名称 + */ + private String fileName; + /** + * 文件地址 + */ + private String fullPath; + /** + * 文件类型 + */ + private String fileType; + +} diff --git a/src/main/java/com/platform/common/upload/vo/UploadVideoVo.java b/src/main/java/com/platform/common/upload/vo/UploadVideoVo.java new file mode 100644 index 0000000..98d4dd9 --- /dev/null +++ b/src/main/java/com/platform/common/upload/vo/UploadVideoVo.java @@ -0,0 +1,30 @@ +package com.platform.common.upload.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 文件上传 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class UploadVideoVo { + + /** + * 文件名称 + */ + private String fileName; + /** + * 文件地址 + */ + private String fullPath; + /** + * 文件类型 + */ + private String fileType; + /** + * 文件封面 + */ + private String screenShot; + +} diff --git a/src/main/java/com/platform/common/utils/BeanCopyUtils.java b/src/main/java/com/platform/common/utils/BeanCopyUtils.java new file mode 100644 index 0000000..74c1806 --- /dev/null +++ b/src/main/java/com/platform/common/utils/BeanCopyUtils.java @@ -0,0 +1,34 @@ +package com.platform.common.utils; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; + +/** + * java bean 复制操作的工具类 + */ +public class BeanCopyUtils { + + /** + * 包含方法 + */ + public static T include(Object source, String... fields) { + JSONObject jsonObject = JSONUtil.parseObj(source); + JSONObject target = new JSONObject(); + for (String field : fields) { + target.set(field.trim(), jsonObject.get(field.trim())); + } + return (T) target.toBean(source.getClass()); + } + + /** + * 排除方法 + */ + public static T exclude(Object source, String... fields) { + JSONObject jsonObject = JSONUtil.parseObj(source); + for (String field : fields) { + jsonObject.remove(field.trim()); + } + return (T) jsonObject.toBean(source.getClass()); + } + +} diff --git a/src/main/java/com/platform/common/utils/IpUtils.java b/src/main/java/com/platform/common/utils/IpUtils.java new file mode 100644 index 0000000..06ce44c --- /dev/null +++ b/src/main/java/com/platform/common/utils/IpUtils.java @@ -0,0 +1,154 @@ +package com.platform.common.utils; + +import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 获取IP方法 + */ +public class IpUtils { + + public static String getIpAddr(HttpServletRequest request) { + if (request == null) { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; + } + + public static boolean internalIp(String ip) { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + private static boolean internalIp(byte[] addr) { + if (addr == null || addr.length < 2) { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) { + return true; + } + case SECTION_5: + switch (b1) { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + *

+ * text IPv4地址 + */ + public static byte[] textToNumericFormatV4(String text) { + if (text.length() == 0) { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try { + long l; + int i; + switch (elements.length) { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + return null; + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + return null; + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + return null; + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + return null; + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + return null; + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + return null; + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } catch (NumberFormatException e) { + return null; + } + return bytes; + } + + public static String getHostIp() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + } + return "127.0.0.1"; + } + + public static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + } + return "未知"; + } +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/utils/MessageUtils.java b/src/main/java/com/platform/common/utils/MessageUtils.java new file mode 100644 index 0000000..761ac1d --- /dev/null +++ b/src/main/java/com/platform/common/utils/MessageUtils.java @@ -0,0 +1,22 @@ +package com.platform.common.utils; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * 获取i18n资源文件 + */ +public class MessageUtils { + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + *

+ * msgCode 消息键 + * args 参数 + * 获取国际化翻译值 + */ + public static String message(String msgCode, Object... args) { + MessageSource messageSource = SpringUtil.getBean(MessageSource.class); + return messageSource.getMessage(msgCode, args, LocaleContextHolder.getLocale()); + } +} diff --git a/src/main/java/com/platform/common/utils/ServletUtils.java b/src/main/java/com/platform/common/utils/ServletUtils.java new file mode 100644 index 0000000..8ebeff8 --- /dev/null +++ b/src/main/java/com/platform/common/utils/ServletUtils.java @@ -0,0 +1,74 @@ +package com.platform.common.utils; + +import cn.hutool.core.convert.Convert; +import org.springframework.util.StringUtils; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 客户端工具类 + */ +public class ServletUtils { + /** + * 获取String参数 + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) { + String value = getParameter(name); + if (StringUtils.isEmpty(value)) { + value = defaultValue; + } + return value; + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) { + return getParameterToInt(name, null); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + /** + * 获取sessionId + */ + public static String getSessionId() { + return getRequestAttributes().getSessionId(); + } + + public static ServletRequestAttributes getRequestAttributes() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + +} diff --git a/src/main/java/com/platform/common/utils/TimerUtils.java b/src/main/java/com/platform/common/utils/TimerUtils.java new file mode 100644 index 0000000..59ca612 --- /dev/null +++ b/src/main/java/com/platform/common/utils/TimerUtils.java @@ -0,0 +1,39 @@ +package com.platform.common.utils; + +import io.netty.util.HashedWheelTimer; +import io.netty.util.TimerTask; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.TimeUnit; + +/** + * 环形队列--定时器 + * + * @author 考拉 + * @email 939313737@qq.com + * @date 2017年8月26日 All rights Reserved, Designed By www.q3z3.com + */ +@Slf4j +public class TimerUtils { + + private HashedWheelTimer timer; + + private TimerUtils() { + // 精度1秒,一共512格 + this.timer = new HashedWheelTimer(1, TimeUnit.SECONDS); + } + + private static class SingletonHolder { + private static final TimerUtils singleton = new TimerUtils(); + } + + public static TimerUtils instance() { + return SingletonHolder.singleton; + } + + public void addTask(TimerTask task, long delay, TimeUnit unit) { + log.info("添加定时任务, 延迟:{} {}", delay, unit.name()); + timer.newTimeout(task, delay, unit); + } + +} diff --git a/src/main/java/com/platform/common/version/ApiVersion.java b/src/main/java/com/platform/common/version/ApiVersion.java new file mode 100644 index 0000000..238ec0e --- /dev/null +++ b/src/main/java/com/platform/common/version/ApiVersion.java @@ -0,0 +1,20 @@ +package com.platform.common.version; + +import java.lang.annotation.*; + +/** + * 自定义一个注解,给需要版本控制的方法加上该注解 + *

+ * 使用:@SubmitRepeat + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ApiVersion { + + /** + * 版本号 + */ + VersionEnum value(); + +} diff --git a/src/main/java/com/platform/common/version/DeviceInterceptor.java b/src/main/java/com/platform/common/version/DeviceInterceptor.java new file mode 100644 index 0000000..540203c --- /dev/null +++ b/src/main/java/com/platform/common/version/DeviceInterceptor.java @@ -0,0 +1,52 @@ +package com.platform.common.version; + +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.json.JSONUtil; +import com.platform.common.constant.HeadConstant; +import com.platform.common.core.EnumUtils; +import com.platform.common.enums.DeviceEnum; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.domain.AjaxResult; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 设备拦截器 + */ +@Component +public class DeviceInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + if (YesOrNoEnum.NO.equals(VersionConfig.ENABLED)) { + return true; + } + String currentUrl = request.getServletPath(); + if (VersionUtils.verifyUrl(currentUrl, VersionConfig.EXCLUDES)) { + return true; + } + String device = ServletUtil.getHeader(request, HeadConstant.DEVICE, CharsetUtil.UTF_8); + if (StringUtils.isEmpty(device)) { + return error(response); + } + DeviceEnum deviceEnum = EnumUtils.toEnum(DeviceEnum.class, device); + if (deviceEnum == null) { + return error(response); + } + return true; + } + + private boolean error(HttpServletResponse response) throws IOException { + response.setContentType("application/json;charset=utf-8"); + response.getWriter().print(JSONUtil.toJsonStr(AjaxResult.fail("请求不正确"))); + return false; + } + +} diff --git a/src/main/java/com/platform/common/version/SignInterceptor.java b/src/main/java/com/platform/common/version/SignInterceptor.java new file mode 100644 index 0000000..069bb26 --- /dev/null +++ b/src/main/java/com/platform/common/version/SignInterceptor.java @@ -0,0 +1,80 @@ +package com.platform.common.version; + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.json.JSONUtil; +import com.platform.common.constant.HeadConstant; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.domain.AjaxResult; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLDecoder; +import java.util.Date; + +/** + * 签名拦截器 + */ +@Component +public class SignInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + if (YesOrNoEnum.NO.equals(VersionConfig.ENABLED)) { + return true; + } + String currentUrl = request.getServletPath(); + if (VersionUtils.verifyUrl(currentUrl, VersionConfig.EXCLUDES)) { + return true; + } + String sign = ServletUtil.getHeader(request, HeadConstant.SIGN, CharsetUtil.UTF_8); + String timestamp = ServletUtil.getHeader(request, HeadConstant.TIMESTAMP, CharsetUtil.UTF_8); + String appId = ServletUtil.getHeader(request, HeadConstant.APP_ID, CharsetUtil.UTF_8); + if (StringUtils.isEmpty(sign) + || StringUtils.isEmpty(appId) + || StringUtils.isEmpty(timestamp)) { + return error(response); + } + String appSecret = VersionConfig.SIGN.get(appId); + if (appSecret == null) { + return error(response); + } + if (!NumberUtil.isLong(timestamp)) { + return error(response); + } + Date date = DateUtil.date(NumberUtil.parseLong(timestamp)); + if (DateUtil.between(date, DateUtil.date(), DateUnit.MINUTE) > 5) { + return error(response); + } + String path = request.getRequestURI(); + if ("GET".equalsIgnoreCase(request.getMethod())) { + String query = request.getQueryString(); + if (!StringUtils.isEmpty(query)) { + path += "?" + URLDecoder.decode(query, "UTF-8"); + } + } + String param = SecureUtil.md5(appId + path + timestamp); + // 此处密钥如果有非ASCII字符,考虑编码 + String result = SecureUtil.hmacMd5(appSecret).digestHex(param); + if (!sign.equalsIgnoreCase(result)) { + return error(response); + } + return true; + } + + private boolean error(HttpServletResponse response) throws IOException { + response.setContentType("application/json;charset=utf-8"); + response.getWriter().print(JSONUtil.toJsonStr(AjaxResult.fail("请求不正确"))); + return false; + } + +} diff --git a/src/main/java/com/platform/common/version/VersionCondition.java b/src/main/java/com/platform/common/version/VersionCondition.java new file mode 100644 index 0000000..af4c1bd --- /dev/null +++ b/src/main/java/com/platform/common/version/VersionCondition.java @@ -0,0 +1,43 @@ +package com.platform.common.version; + +import com.platform.common.constant.HeadConstant; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.servlet.mvc.condition.RequestCondition; + +import javax.servlet.http.HttpServletRequest; + +@Slf4j +@Getter +public class VersionCondition implements RequestCondition { + + private String version; + + public VersionCondition(String version) { + this.version = version; + } + + //将不同的筛选条件合并,这里采用的覆盖,即后来的规则生效 + @Override + public VersionCondition combine(VersionCondition other) { + return new VersionCondition(other.getVersion()); + } + + //根据request查找匹配到的筛选条件 + @Override + public VersionCondition getMatchingCondition(HttpServletRequest request) { + String version = request.getHeader(HeadConstant.VERSION); + // 如果请求的版本号大于配置版本号, 则满足,即与请求的 + if (VersionUtils.compareTo(version, this.version, request.getRequestURI()) >= 0) { + return this; + } + return null; + } + + //实现不同条件类的比较,从而实现优先级排序,返回值最小匹配this + @Override + public int compareTo(VersionCondition other, HttpServletRequest request) { + return VersionUtils.compareTo(other.getVersion(), this.version, request.getRequestURI()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/version/VersionConfig.java b/src/main/java/com/platform/common/version/VersionConfig.java new file mode 100644 index 0000000..f4d552d --- /dev/null +++ b/src/main/java/com/platform/common/version/VersionConfig.java @@ -0,0 +1,76 @@ +package com.platform.common.version; + +import com.platform.common.core.EnumUtils; +import com.platform.common.enums.YesOrNoEnum; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 读取项目相关配置 + */ +@Component +@ConfigurationProperties(prefix = "version") +public class VersionConfig { + + /** + * 版本开关 + */ + public static YesOrNoEnum ENABLED = YesOrNoEnum.NO; + + /** + * 最低版本 + */ + public static String VERSION = "1.0.0"; + + /** + * 过滤请求 + */ + public static List EXCLUDES = new ArrayList<>(); + + /** + * 过滤请求 + */ + public static List BANS = new ArrayList<>(); + + /** + * 签名 + */ + public static Map SIGN = new HashMap<>(); + + public void setEnabled(String enabled) { + if (!StringUtils.isEmpty(enabled)) { + VersionConfig.ENABLED = EnumUtils.toEnum(YesOrNoEnum.class, enabled, YesOrNoEnum.NO); + } + } + + public void setVersion(String version) { + if (!StringUtils.isEmpty(version)) { + VersionConfig.VERSION = version; + } + } + + public void setExcludes(List excludes) { + if (!CollectionUtils.isEmpty(excludes)) { + VersionConfig.EXCLUDES = excludes; + } + } + + public void setBans(List bans) { + if (!CollectionUtils.isEmpty(bans)) { + VersionConfig.BANS = bans; + } + } + + public void setSign(Map sign) { + if (!CollectionUtils.isEmpty(sign)) { + VersionConfig.SIGN = sign; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/version/VersionEnum.java b/src/main/java/com/platform/common/version/VersionEnum.java new file mode 100644 index 0000000..4d4bb64 --- /dev/null +++ b/src/main/java/com/platform/common/version/VersionEnum.java @@ -0,0 +1,27 @@ +package com.platform.common.version; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 版本枚举值 + */ +@Getter +public enum VersionEnum { + + V1_0_0("1.0.0", "初始化版本"), + V1_0_1("1.0.1", "下一个版本"), + ; + + @EnumValue + @JsonValue + private final String code; + private final String info; + + VersionEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/common/version/VersionHandlerMapping.java b/src/main/java/com/platform/common/version/VersionHandlerMapping.java new file mode 100644 index 0000000..1cadbf5 --- /dev/null +++ b/src/main/java/com/platform/common/version/VersionHandlerMapping.java @@ -0,0 +1,25 @@ +package com.platform.common.version; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.servlet.mvc.condition.RequestCondition; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.lang.reflect.Method; + +public class VersionHandlerMapping extends RequestMappingHandlerMapping { + + @Override + protected RequestCondition getCustomMethodCondition(Method method) { + ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class); + return createCondition(apiVersion); + } + + // 实例化RequestCondition + private RequestCondition createCondition(ApiVersion apiVersion) { + if (apiVersion == null) { + return null; + } + return new VersionCondition(apiVersion.value().getCode()); + } + +} diff --git a/src/main/java/com/platform/common/version/VersionInterceptor.java b/src/main/java/com/platform/common/version/VersionInterceptor.java new file mode 100644 index 0000000..6ab7fc9 --- /dev/null +++ b/src/main/java/com/platform/common/version/VersionInterceptor.java @@ -0,0 +1,51 @@ +package com.platform.common.version; + +import com.platform.common.constant.HeadConstant; +import com.platform.common.enums.ResultCodeEnum; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.exception.BaseException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 版本验证 + */ +@Component +@Slf4j +public class VersionInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + /** + * 如果不属于HandlerMethod,则放行 + */ + if (!(handler instanceof HandlerMethod)) { + return true; + } + if (YesOrNoEnum.NO.equals(VersionConfig.ENABLED)) { + return true; + } + String currentUrl = request.getServletPath(); + if (VersionUtils.verifyUrl(currentUrl, VersionConfig.EXCLUDES)) { + return true; + } + //获取用户接口版本 + String version = request.getHeader(HeadConstant.VERSION); + if (VersionUtils.compareTo(version, VersionConfig.VERSION, request.getRequestURI()) < 0) { + throw new BaseException(ResultCodeEnum.VERSION); + } + return true; + + } + + @Override + public void afterCompletion(HttpServletRequest request, + HttpServletResponse response, Object object, Exception e) { + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/version/VersionUtils.java b/src/main/java/com/platform/common/version/VersionUtils.java new file mode 100644 index 0000000..93261a3 --- /dev/null +++ b/src/main/java/com/platform/common/version/VersionUtils.java @@ -0,0 +1,103 @@ +package com.platform.common.version; + +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import com.platform.common.enums.ResultCodeEnum; +import com.platform.common.exception.BaseException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import org.springframework.util.StringUtils; + +import java.util.List; + +/** + * 版本比较 + */ +@Slf4j +@Component +public class VersionUtils { + + /** + * 比较版本大小 + *

+ * 说明:支n位基础版本号+1位子版本号 + * 示例:1.0.2>1.0.1 + * + * @param version1 版本1 + * @param version2 版本2 + * @return 0:相同 >0:大于 <0:小于 + */ + public static int compareTo(String version1, String version2, String uri) { + if (!matchVersion(version1)) { + log.error("RequestURI:" + uri); + log.error("传入版本格式有误version1-{}", version1); + throw new BaseException(ResultCodeEnum.VERSION); + } + if (!matchVersion(version2)) { + log.error("RequestURI:" + uri); + log.error("系统版本格式有误version2-{}", version2); + throw new BaseException(ResultCodeEnum.VERSION); + } + if (version1.equals(version2)) { + return 0; + } + return versionStrToNum(version1) - versionStrToNum(version2); + } + + /** + * 比较版本大小 + *

+ * 说明:支n位基础版本号+1位子版本号 + * 示例:1.0.2>1.0.1 + * + * @param version1 版本1 + * @param version2 版本2 + * @return 0:相同 >0:大于 <0:小于 + */ + public static int compareTo(String version1, String version2) { + return compareTo(version1, version2, "-"); + } + + /** + * 版本号转换为数字 + * + * @param versionStr + * @return + */ + public static int versionStrToNum(String versionStr) { + List dataList = StrUtil.splitTrim(versionStr, "."); + StringBuilder builder = new StringBuilder() + .append(dataList.get(0)) + .append(dataList.get(1)) + .append(String.format("%03d", Integer.valueOf(dataList.get(2)))); + return Integer.valueOf(builder.toString()); + } + + /** + * 匹配版本 + * + * @param version + * @return + */ + private static boolean matchVersion(String version) { + return ReUtil.isMatch("\\d{1,3}(\\.\\d{1,3}){2}", StringUtils.isEmpty(version) ? "" : version); + } + + // 匹配器 + private static final PathMatcher pathMatcher = new AntPathMatcher(); + + /** + * 验证地址 + */ + public static boolean verifyUrl(String currentUrl, List whiteList) { + for (String url : whiteList) { + if (pathMatcher.match(url, currentUrl)) { + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/web/aspectj/MybatisAspectj.java b/src/main/java/com/platform/common/web/aspectj/MybatisAspectj.java new file mode 100644 index 0000000..1213a5f --- /dev/null +++ b/src/main/java/com/platform/common/web/aspectj/MybatisAspectj.java @@ -0,0 +1,27 @@ +package com.platform.common.web.aspectj; + +import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class MybatisAspectj { + + // 配置织入点 + @Pointcut("execution(public * com.baomidou.mybatisplus.core.mapper.BaseMapper.selectOne(..))") + public void selectOneAspect() { + } + + @Before("selectOneAspect()") + public void beforeSelect(JoinPoint point) { + Object arg = point.getArgs()[0]; + if (arg instanceof AbstractWrapper) { + ((AbstractWrapper) arg).last("limit 1"); + } + } + +} diff --git a/src/main/java/com/platform/common/web/controller/BaseController.java b/src/main/java/com/platform/common/web/controller/BaseController.java new file mode 100644 index 0000000..2f48c59 --- /dev/null +++ b/src/main/java/com/platform/common/web/controller/BaseController.java @@ -0,0 +1,107 @@ +package com.platform.common.web.controller; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.platform.common.core.EnumUtils; +import com.platform.common.enums.GenderEnum; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.domain.AjaxResult; +import com.platform.common.web.domain.JsonDateDeserializer; +import com.platform.common.web.page.PageDomain; +import com.platform.common.web.page.TableDataInfo; +import com.platform.common.web.page.TableSupport; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; + +/** + * web层通用数据处理 + */ +@Slf4j +public class BaseController { + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { + @Override + public void setAsText(String text) { + setValue(JsonDateDeserializer.parseDate(text)); + } + }); + // YesOrNoEnum 类型转换 + binder.registerCustomEditor(YesOrNoEnum.class, new PropertyEditorSupport() { + @Override + public void setAsText(String text) { + setValue(EnumUtils.toEnum(YesOrNoEnum.class, text)); + } + }); + // GenderTypeEnum 类型转换 + binder.registerCustomEditor(GenderEnum.class, new PropertyEditorSupport() { + @Override + public void setAsText(String text) { + setValue(EnumUtils.toEnum(GenderEnum.class, text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() { + PageDomain pageDomain = TableSupport.getPageDomain(); + startPage(PageDomain.escapeOrderBySql(pageDomain.getOrderBy())); + } + + /** + * 设置请求分页数据 + */ + protected void startPage(String orderBy) { + PageDomain pageDomain = TableSupport.getPageDomain(); + PageHelper.startPage(pageDomain.getPageNum(), pageDomain.getPageSize(), StrUtil.toUnderlineCase(orderBy)); + } + + /** + * 设置排序分页数据 + */ + protected void orderBy(String orderBy) { + PageHelper.orderBy(StrUtil.toUnderlineCase(orderBy)); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected TableDataInfo getDataTable(List list) { + return new TableDataInfo(list, new PageInfo(list).getTotal()); + } + + protected TableDataInfo getDataTable(List list, PageDomain pageDomain) { + return getDataTable(CollUtil.sub(list, pageDomain.getPageStart(), pageDomain.getPageEnd())); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected TableDataInfo getDataTable(PageInfo list) { + return new TableDataInfo(list.getList(), list.getTotal()); + } + + /** + * 响应返回结果 + */ + protected AjaxResult toAjax(int rows) { + return rows > 0 ? AjaxResult.success() : AjaxResult.fail(); + } + +} diff --git a/src/main/java/com/platform/common/web/controller/ErrorController.java b/src/main/java/com/platform/common/web/controller/ErrorController.java new file mode 100644 index 0000000..2156f5d --- /dev/null +++ b/src/main/java/com/platform/common/web/controller/ErrorController.java @@ -0,0 +1,33 @@ +package com.platform.common.web.controller; + +import com.platform.common.aspectj.IgnoreAuth; +import com.platform.common.core.EnumUtils; +import com.platform.common.enums.ResultCodeEnum; +import com.platform.common.web.domain.AjaxResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 错误请求处理 + */ +@RestController +@RequestMapping("/error") +@Slf4j +public class ErrorController { + + @IgnoreAuth + @RequestMapping("/{code}") + public AjaxResult error(@PathVariable String code) { + ResultCodeEnum resultCode = EnumUtils.toEnum(ResultCodeEnum.class, code, ResultCodeEnum.FAIL); + switch (resultCode) { + case SUCCESS: + return AjaxResult.success(); + default: + return AjaxResult.result(resultCode); + } + + } + +} diff --git a/src/main/java/com/platform/common/web/dao/BaseDao.java b/src/main/java/com/platform/common/web/dao/BaseDao.java new file mode 100644 index 0000000..f7a0620 --- /dev/null +++ b/src/main/java/com/platform/common/web/dao/BaseDao.java @@ -0,0 +1,44 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.common.web.dao; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +/** + * Dao基类 + */ +public interface BaseDao extends BaseMapper { + + /** + * 批量插入 仅适用于mysql + */ + Integer insertBatchSomeColumn(Collection entityList); + + /** + * 注意:使用此方法时,sql末尾需要增加${ew.customSqlSegment} + *

+ * 支持单表、和联查 + */ + List search(@Param("ew") Wrapper queryWrapper); + +} diff --git a/src/main/java/com/platform/common/web/domain/AjaxResult.java b/src/main/java/com/platform/common/web/domain/AjaxResult.java new file mode 100644 index 0000000..4a99373 --- /dev/null +++ b/src/main/java/com/platform/common/web/domain/AjaxResult.java @@ -0,0 +1,129 @@ +package com.platform.common.web.domain; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.platform.common.core.EnumUtils; +import com.platform.common.enums.ResultCodeEnum; +import org.springframework.util.StringUtils; + +import java.nio.charset.Charset; +import java.util.HashMap; + +/** + * 操作消息提醒 + */ +public class AjaxResult extends HashMap { + + private static final long serialVersionUID = 1L; + + /** + * 状态码 + */ + public static final String CODE_TAG = "code"; + + /** + * 返回内容 + */ + public static final String MSG_TAG = "msg"; + + /** + * 数据对象 + */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象 + */ + public AjaxResult(ResultCodeEnum resultCode, String msg, Object data) { + super.put(CODE_TAG, resultCode.getCode()); + super.put(MSG_TAG, StringUtils.isEmpty(msg) ? resultCode.getInfo() : msg); + if (data != null) { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + */ + public static AjaxResult success() { + return new AjaxResult(ResultCodeEnum.SUCCESS, null, null); + } + + /** + * 返回成功数据 + */ + public static AjaxResult success(Object data) { + return new AjaxResult(ResultCodeEnum.SUCCESS, null, data); + } + + /** + * 返回成功消息 + */ + public static AjaxResult successMsg(String msg) { + return new AjaxResult(ResultCodeEnum.SUCCESS, msg, null); + } + + /** + * 返回错误消息 + */ + public static AjaxResult fail() { + return new AjaxResult(ResultCodeEnum.FAIL, null, null); + } + + /** + * 返回错误消息 + */ + public static AjaxResult fail(String msg) { + return new AjaxResult(ResultCodeEnum.FAIL, msg, null); + } + + /** + * 返回错误消息 + */ + public static AjaxResult result(ResultCodeEnum resultCode) { + return new AjaxResult(resultCode, resultCode.getInfo(), null); + } + + /** + * 返回错误消息 + */ + public static AjaxResult result(ResultCodeEnum resultCode, String msg) { + if (StringUtils.isEmpty(msg)) { + msg = resultCode.getInfo(); + } + return new AjaxResult(resultCode, msg, null); + } + + @Override + public AjaxResult put(String key, Object value) { + super.put(key, value); + return this; + } + + public ResultCodeEnum getCode() { + Object code = this.get(CODE_TAG); + return EnumUtils.toEnum(ResultCodeEnum.class, code.toString(), ResultCodeEnum.FAIL); + } + + public Object getData() { + return this.get(DATA_TAG); + } + + public String getDataStr() { + Object data = getData(); + return StrUtil.str(data, Charset.defaultCharset()); + } + + public static AjaxResult ajax(String json) { + if (StringUtils.isEmpty(json)) { + return AjaxResult.fail(); + } + JSONObject jsonObject = JSONUtil.parseObj(json); + String code = StrUtil.str(jsonObject.get(CODE_TAG), Charset.defaultCharset()); + String msg = StrUtil.str(jsonObject.get(MSG_TAG), Charset.defaultCharset()); + ResultCodeEnum resultCode = EnumUtils.toEnum(ResultCodeEnum.class, code, ResultCodeEnum.FAIL); + return new AjaxResult(resultCode, msg, jsonObject.get(DATA_TAG)); + } + +} diff --git a/src/main/java/com/platform/common/web/domain/BaseEntity.java b/src/main/java/com/platform/common/web/domain/BaseEntity.java new file mode 100644 index 0000000..85a9bc3 --- /dev/null +++ b/src/main/java/com/platform/common/web/domain/BaseEntity.java @@ -0,0 +1,63 @@ +package com.platform.common.web.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +/** + * Entity基类 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 开始时间 + */ + @TableField(exist = false) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Date beginTime; + + /** + * 结束时间 + */ + @TableField(exist = false) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Date endTime; + + /** + * 请求参数 + */ + @TableField(exist = false) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String param; + + /** + * 请求参数 + */ + @TableField(exist = false) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Map params; + + /** + * 返回参数 + */ + @TableField(exist = false) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String label; + + /** + * 返回参数 + */ + @TableField(exist = false) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Long count; + +} diff --git a/src/main/java/com/platform/common/web/domain/JsonDateDeserializer.java b/src/main/java/com/platform/common/web/domain/JsonDateDeserializer.java new file mode 100644 index 0000000..3fcf282 --- /dev/null +++ b/src/main/java/com/platform/common/web/domain/JsonDateDeserializer.java @@ -0,0 +1,46 @@ +package com.platform.common.web.domain; + +import cn.hutool.core.date.DateException; +import cn.hutool.core.date.DateUtil; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.util.Date; + +/** + * 自定义时间转换 + */ +public class JsonDateDeserializer extends JsonDeserializer { + + @Override + public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException { + String date = jsonParser.getText(); + return parseDate(date); + } + + /** + * 前台日期格式 + */ + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object obj) { + if (obj == null) { + return null; + } + try { + return DateUtil.parse(obj.toString(), parsePatterns); + } catch (DateException e) { + return null; + } + } + +} diff --git a/src/main/java/com/platform/common/web/exception/ErrorAttributesCustom.java b/src/main/java/com/platform/common/web/exception/ErrorAttributesCustom.java new file mode 100644 index 0000000..6b50019 --- /dev/null +++ b/src/main/java/com/platform/common/web/exception/ErrorAttributesCustom.java @@ -0,0 +1,18 @@ +package com.platform.common.web.exception; + +import com.platform.common.web.domain.AjaxResult; +import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.WebRequest; + +import java.util.Map; + +@Component +public class ErrorAttributesCustom extends DefaultErrorAttributes { + + @Override + public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { + return AjaxResult.fail(); + } + +} diff --git a/src/main/java/com/platform/common/web/exception/GlobalExceptionHandler.java b/src/main/java/com/platform/common/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..6950851 --- /dev/null +++ b/src/main/java/com/platform/common/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,47 @@ +package com.platform.common.web.exception; + +import com.platform.common.enums.ResultCodeEnum; +import com.platform.common.exception.BaseException; +import com.platform.common.web.domain.AjaxResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.NoHandlerFoundException; + +/** + * 全局异常处理器 + */ +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e) { + log.error("全局异常:" + e.getMessage(), e); + /** + * 路径不存在 + */ + if (e instanceof NoHandlerFoundException + || e instanceof org.springframework.web.HttpRequestMethodNotSupportedException) { + return AjaxResult.result(ResultCodeEnum.NOT_FOUND); + } + /** + * 校验异常 + */ + if (e instanceof MethodArgumentNotValidException) { + return AjaxResult.fail(((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage()); + } + /** + * 自定义异常 + */ + if (e instanceof BaseException) { + if (ResultCodeEnum.VERSION.equals(((BaseException) e).getResultCode())) { + return AjaxResult.result(ResultCodeEnum.VERSION); + } + return AjaxResult.fail(e.getMessage()); + } + return AjaxResult.fail(); + } + +} diff --git a/src/main/java/com/platform/common/web/interceptor/CustomizationBean.java b/src/main/java/com/platform/common/web/interceptor/CustomizationBean.java new file mode 100644 index 0000000..112c984 --- /dev/null +++ b/src/main/java/com/platform/common/web/interceptor/CustomizationBean.java @@ -0,0 +1,20 @@ +package com.platform.common.web.interceptor; + +import io.undertow.server.DefaultByteBufferPool; +import io.undertow.websockets.jsr.WebSocketDeploymentInfo; +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class CustomizationBean implements WebServerFactoryCustomizer { + + @Override + public void customize(UndertowServletWebServerFactory factory) { + factory.addDeploymentInfoCustomizers(deploymentInfo -> { + WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); + webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024)); + deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); + }); + } +} diff --git a/src/main/java/com/platform/common/web/page/PageDomain.java b/src/main/java/com/platform/common/web/page/PageDomain.java new file mode 100644 index 0000000..e146206 --- /dev/null +++ b/src/main/java/com/platform/common/web/page/PageDomain.java @@ -0,0 +1,62 @@ +package com.platform.common.web.page; + +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.util.StringUtils; + +/** + * 分页数据 + */ +@Data +@NoArgsConstructor +public class PageDomain { + /** + * 当前记录起始索引 + */ + private Integer pageNum; + /** + * 每页显示记录数 + */ + private Integer pageSize; + /** + * 排序列 + */ + private String orderBy; + /** + * 排序方向 "desc" 或者 "asc" + */ + private String orderSort; + + public String getOrderBy() { + if (StringUtils.isEmpty(orderBy)) { + return ""; + } + return StrUtil.toUnderlineCase(orderBy) + " " + orderSort; + } + + public Integer getPageStart() { + return (getPageNum() - 1) * getPageSize(); + } + + public Integer getPageEnd() { + return getPageStart() + getPageSize(); + } + + /** + * 记录总数 + */ + private Long total; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) { + // 仅支持字母、数字、下划线、空格、逗号(支持多个字段排序) + String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,]+"; + if (!StringUtils.isEmpty(value) && !value.matches(SQL_PATTERN)) { + return ""; + } + return StrUtil.toUnderlineCase(value); + } +} diff --git a/src/main/java/com/platform/common/web/page/TableDataInfo.java b/src/main/java/com/platform/common/web/page/TableDataInfo.java new file mode 100644 index 0000000..fdb22f8 --- /dev/null +++ b/src/main/java/com/platform/common/web/page/TableDataInfo.java @@ -0,0 +1,55 @@ +package com.platform.common.web.page; + +import com.platform.common.enums.ResultCodeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.HashMap; +import java.util.List; + +/** + * 表格分页数据对象 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class TableDataInfo extends HashMap { + private static final long serialVersionUID = 1L; + + /** + * 状态码 + */ + public static final String CODE_TAG = "code"; + + /** + * 返回内容 + */ + public static final String MSG_TAG = "msg"; + + /** + * 数据对象 + */ + public static final String ROWS_TAG = "rows"; + + /** + * 数据对象 + */ + public static final String TOTAL_TAG = "total"; + + /** + * 分页 + */ + public TableDataInfo(List list, Long total) { + ResultCodeEnum result = ResultCodeEnum.SUCCESS; + super.put(CODE_TAG, result.getCode()); + super.put(MSG_TAG, result.getInfo()); + super.put(ROWS_TAG, list); + super.put(TOTAL_TAG, total.intValue()); + } + + @Override + public TableDataInfo put(String key, Object value) { + super.put(key, value); + return this; + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/common/web/page/TableSupport.java b/src/main/java/com/platform/common/web/page/TableSupport.java new file mode 100644 index 0000000..db15f24 --- /dev/null +++ b/src/main/java/com/platform/common/web/page/TableSupport.java @@ -0,0 +1,41 @@ +package com.platform.common.web.page; + +import com.platform.common.utils.ServletUtils; + +/** + * 表格数据处理 + */ +public class TableSupport { + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY = "orderBy"; + + /** + * 排序方向 "desc" 或者 "asc" + */ + public static final String ORDER_SORT = "orderSort"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM, 1)); + pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE, 10)); + pageDomain.setOrderBy(ServletUtils.getParameter(ORDER_BY)); + pageDomain.setOrderSort(ServletUtils.getParameter(ORDER_SORT, "asc")); + return pageDomain; + } + +} diff --git a/src/main/java/com/platform/common/web/service/BaseService.java b/src/main/java/com/platform/common/web/service/BaseService.java new file mode 100644 index 0000000..238991b --- /dev/null +++ b/src/main/java/com/platform/common/web/service/BaseService.java @@ -0,0 +1,120 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.common.web.service; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + * 基础service接口层基类 + */ +public interface BaseService { + + /** + * 新增(并返回id) + */ + Integer add(T entity); + + /** + * 根据 id 删除 + */ + Integer deleteById(Long id); + + /** + * 根据ids 批量删除 + */ + Integer deleteByIds(Long[] ids); + + /** + * 根据ids 批量删除 + */ + Integer deleteByIds(List ids); + + /** + * 根据 id 修改 + */ + Integer updateById(T entity); + + /** + * 根据条件更新 + */ + Integer update(Wrapper wrapper); + + /** + * 根据 id 查询 + */ + T getById(Long id); + + /** + * 根据 id 查询 + */ + T findById(Long id); + + /** + * 根据 ids 查询 + */ + List getByIds(Collection idList); + + /** + * 查询总记录数 + */ + Long queryCount(T t); + + /** + * 查询总记录数 + */ + Long queryCount(Wrapper wrapper); + + /** + * 查询全部记录 + */ + List queryList(T t); + + /** + * 查询全部记录 + */ + List queryList(Wrapper wrapper); + + /** + * 查询一个 + */ + T queryOne(T t); + + /** + * 查询一个 + */ + T queryOne(Wrapper wrapper); + + /** + * 批量新增 + */ + Integer batchAdd(List list); + + /** + * 批量新增(仅mysql可以使用) + */ + Integer batchAdd(List list, Integer batchCount); + + /** + * 修改状态 + */ + void changeStatus(T t, String... param); +} diff --git a/src/main/java/com/platform/common/web/service/impl/BaseServiceImpl.java b/src/main/java/com/platform/common/web/service/impl/BaseServiceImpl.java new file mode 100644 index 0000000..4c49def --- /dev/null +++ b/src/main/java/com/platform/common/web/service/impl/BaseServiceImpl.java @@ -0,0 +1,206 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.common.web.service.impl; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.github.pagehelper.PageInfo; +import com.platform.common.exception.BaseException; +import com.platform.common.utils.BeanCopyUtils; +import com.platform.common.web.dao.BaseDao; +import com.platform.common.web.service.BaseService; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * 基础service实现层基类 + */ +public class BaseServiceImpl implements BaseService { + + protected final static String EXIST_MSG = "数据不存在"; + + private BaseDao baseDao; + + public void setBaseDao(BaseDao baseDao) { + this.baseDao = baseDao; + } + + private static final Integer batchCount = 1000; + + @Override + public Integer add(T entity) { + return baseDao.insert(entity); + } + + @Override + public Integer deleteById(Long id) { + return baseDao.deleteById(id); + } + + @Override + public Integer deleteByIds(Long[] ids) { + return baseDao.deleteBatchIds(Arrays.asList(ids)); + } + + @Override + public Integer deleteByIds(List ids) { + return baseDao.deleteBatchIds(ids); + } + + @Override + public Integer updateById(T entity) { + return baseDao.updateById(entity); + } + + @Override + public T getById(Long id) { + return baseDao.selectById(id); + } + + /** + * eq: Wrapper wrapper = Wrappers.lambdaUpdate().set(ChatFriend::getRemark, null).eq(ChatFriend::getId, friend.getId()); + */ + @Override + public Integer update(Wrapper wrapper) { + return baseDao.update(null, wrapper); + } + + @Override + public T findById(Long id) { + T t = getById(id); + if (t == null) { + throw new BaseException(EXIST_MSG); + } + return t; + } + + @Override + public List getByIds(Collection idList) { + return baseDao.selectBatchIds(idList); + } + + @Override + public Long queryCount(T t) { + Wrapper wrapper = new QueryWrapper<>(t); + return queryCount(wrapper); + } + + @Override + public Long queryCount(Wrapper wrapper) { + return baseDao.selectCount(wrapper).longValue(); + } + + @Override + public List queryList(T t) { + Wrapper wrapper = new QueryWrapper<>(t); + return queryList(wrapper); + } + + @Override + public List queryList(Wrapper wrapper) { + return baseDao.selectList(wrapper); + } + + @Override + public T queryOne(T t) { + Wrapper wrapper = new QueryWrapper<>(t); + return queryOne(wrapper); + } + + @Override + public T queryOne(Wrapper wrapper) { + return baseDao.selectOne(wrapper); + } + + @Override + public Integer batchAdd(List list) { + return batchAdd(list, batchCount); + } + + @Transactional + @Override + public Integer batchAdd(List list, Integer batchCount) { + if (CollectionUtils.isEmpty(list)) { + return 0; + } + List batchList = new ArrayList<>(); + AtomicReference result = new AtomicReference<>(0); + list.forEach((o) -> { + batchList.add(o); + if (batchList.size() == batchCount) { + result.updateAndGet(v -> v + baseDao.insertBatchSomeColumn(batchList)); + batchList.clear(); + } + }); + if (!CollectionUtils.isEmpty(batchList)) { + result.updateAndGet(v -> v + baseDao.insertBatchSomeColumn(batchList)); + batchList.clear(); + } + return result.get(); + } + + /** + * 循环工具类 + */ + public static Consumer forEach(BiConsumer consumer) { + class Obj { + int i; + } + Obj obj = new Obj(); + return t -> { + int index = obj.i++; + consumer.accept(t, index); + }; + } + + @Override + public void changeStatus(T t, String... param) { + if (param.length == 0) { + this.updateById(BeanCopyUtils.include(t, "id", "status")); + } else { + this.updateById(BeanCopyUtils.include(t, param)); + } + } + + /** + * 响应请求分页数据 + */ + protected PageInfo getPageInfo(List list, List oldList) { + Long total = new PageInfo(oldList).getTotal(); + return getPageInfo(list, total); + } + + /** + * 格式化分页 + */ + public PageInfo getPageInfo(List list, long total) { + PageInfo pageInfo = new PageInfo(list); + pageInfo.setTotal(total); + return pageInfo; + } + +} diff --git a/src/main/java/com/platform/modules/auth/controller/AuthController.java b/src/main/java/com/platform/modules/auth/controller/AuthController.java new file mode 100644 index 0000000..7b8d1db --- /dev/null +++ b/src/main/java/com/platform/modules/auth/controller/AuthController.java @@ -0,0 +1,131 @@ +package com.platform.modules.auth.controller; + +import cn.hutool.core.lang.Dict; +import com.platform.common.aspectj.IgnoreAuth; +import com.platform.common.aspectj.SubmitRepeat; +import com.platform.common.exception.BaseException; +import com.platform.common.shiro.ShiroLoginAuth; +import com.platform.common.shiro.ShiroLoginPhone; +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.auth.vo.AuthVo01; +import com.platform.modules.auth.vo.AuthVo02; +import com.platform.modules.auth.vo.AuthVo03; +import com.platform.modules.auth.vo.AuthVo04; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.sms.enums.SmsTypeEnum; +import com.platform.modules.sms.service.SmsService; +import com.platform.modules.sms.vo.SmsVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * 认证 + */ +@RestController +@Slf4j +@RequestMapping("/auth") +public class AuthController extends BaseController { + + @Resource + private ChatUserService chatUserService; + + @Resource + private SmsService smsService; + + /** + * 发送验证码(登录/注册/忘记密码) + */ + @IgnoreAuth + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping(value = "/sendCode") + @SubmitRepeat + public AjaxResult sendCode(@Validated @RequestBody SmsVo smsVo) { + ChatUser chatUser = chatUserService.queryByPhone(smsVo.getPhone()); + switch (smsVo.getType()) { + case LOGIN: + case FORGET: + if (chatUser == null) { + throw new BaseException("用户未注册,请先注册"); + } + break; + case REGISTERED: + if (chatUser != null) { + throw new BaseException("用户已注册,请直接登录"); + } + break; + } + return AjaxResult.success(smsService.sendSms(smsVo)).put("msg", "验证码已发送"); + } + + /** + * 注册方法 + */ + @IgnoreAuth + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping(value = "/register") + public AjaxResult register(@Validated @RequestBody AuthVo01 authVo) { + // 验证 + smsService.verifySms(authVo.getPhone(), authVo.getCode(), SmsTypeEnum.REGISTERED); + // 注册 + chatUserService.register(authVo); + return AjaxResult.successMsg("注册成功,请登录"); + } + + /** + * 登录方法(根据手机+密码登录) + */ + @IgnoreAuth + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/login") + public AjaxResult login(@Validated @RequestBody AuthVo02 authVo) { + // 执行登录 + ShiroLoginAuth loginAuth = new ShiroLoginAuth(authVo.getPhone(), authVo.getPassword()); + Dict dict = chatUserService.doLogin(loginAuth); + return AjaxResult.success(dict); + } + + /** + * 登录方法(根据手机+验证码登录) + */ + @IgnoreAuth + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/loginByCode") + public AjaxResult loginByCode(@Validated @RequestBody AuthVo03 authVo) { + // 验证 + smsService.verifySms(authVo.getPhone(), authVo.getCode(), SmsTypeEnum.LOGIN); + // 执行登录 + ShiroLoginPhone loginPhone = new ShiroLoginPhone(authVo.getPhone()); + Dict dict = chatUserService.doLogin(loginPhone); + return AjaxResult.success(dict); + } + + /** + * 找回密码(根据手机) + */ + @IgnoreAuth + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/forget") + public AjaxResult forget(@Validated @RequestBody AuthVo04 authVo) { + // 验证 + smsService.verifySms(authVo.getPhone(), authVo.getCode(), SmsTypeEnum.FORGET); + // 查询用户 + ChatUser cu = chatUserService.queryByPhone(authVo.getPhone()); + if (cu == null) { + throw new BaseException("手机号不存在"); + } + // 重置密码 + chatUserService.resetPass(cu.getUserId(), authVo.getPassword()); + return AjaxResult.successMsg("您的密码重置成功,请重新登录"); + } + +} diff --git a/src/main/java/com/platform/modules/auth/service/TokenService.java b/src/main/java/com/platform/modules/auth/service/TokenService.java new file mode 100644 index 0000000..5c10c44 --- /dev/null +++ b/src/main/java/com/platform/modules/auth/service/TokenService.java @@ -0,0 +1,44 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.modules.auth.service; + +import com.platform.common.shiro.LoginUser; + +/** + *

+ * token 服务层 + *

+ */ +public interface TokenService { + + /** + * 生成token + */ + String generateToken(); + + /** + * 通过token查询 + */ + LoginUser queryByToken(String token); + + /** + * 删除token + */ + void deleteToken(String token); + +} diff --git a/src/main/java/com/platform/modules/auth/service/impl/TokenServiceImpl.java b/src/main/java/com/platform/modules/auth/service/impl/TokenServiceImpl.java new file mode 100644 index 0000000..d717ed9 --- /dev/null +++ b/src/main/java/com/platform/modules/auth/service/impl/TokenServiceImpl.java @@ -0,0 +1,75 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.modules.auth.service.impl; + +import cn.hutool.json.JSONUtil; +import com.platform.common.config.PlatformConfig; +import com.platform.common.constant.HeadConstant; +import com.platform.common.redis.RedisUtils; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.shiro.LoginUser; +import com.platform.modules.auth.service.TokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.concurrent.TimeUnit; + +/** + * token 服务层 + */ +@Service("tokenService") +public class TokenServiceImpl implements TokenService { + + @Autowired + private RedisUtils redisUtils; + + @Override + public String generateToken() { + LoginUser loginUser = ShiroUtils.getLoginUser(); + String token = loginUser.getToken(); + // 存储redis + redisUtils.set(makeToken(token), JSONUtil.toJsonStr(loginUser), PlatformConfig.TIMEOUT, TimeUnit.MINUTES); + return token; + } + + @Override + public LoginUser queryByToken(String token) { + String key = makeToken(token); + if (!redisUtils.hasKey(key)) { + return null; + } + // 续期 + redisUtils.expire(key, PlatformConfig.TIMEOUT, TimeUnit.MINUTES); + // 转换 + return JSONUtil.toBean(redisUtils.get(key), LoginUser.class); + } + + @Override + public void deleteToken(String token) { + if (StringUtils.isEmpty(token)) { + return; + } + redisUtils.delete(makeToken(token)); + } + + private String makeToken(String token) { + return HeadConstant.TOKEN_REDIS_APP + token; + } + +} diff --git a/src/main/java/com/platform/modules/auth/vo/AuthVo01.java b/src/main/java/com/platform/modules/auth/vo/AuthVo01.java new file mode 100644 index 0000000..c4ef122 --- /dev/null +++ b/src/main/java/com/platform/modules/auth/vo/AuthVo01.java @@ -0,0 +1,36 @@ +package com.platform.modules.auth.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class AuthVo01 { + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空") + private String phone; + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空") + private String password; + + /** + * 昵称 + */ + @NotBlank(message = "昵称不能为空") + @Size(max = 20, message = "昵称长度不能大于20") + private String nickName; + + /** + * 验证码 + */ + @NotBlank(message = "验证码不能为空") + private String code; + +} diff --git a/src/main/java/com/platform/modules/auth/vo/AuthVo02.java b/src/main/java/com/platform/modules/auth/vo/AuthVo02.java new file mode 100644 index 0000000..89b0d33 --- /dev/null +++ b/src/main/java/com/platform/modules/auth/vo/AuthVo02.java @@ -0,0 +1,22 @@ +package com.platform.modules.auth.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class AuthVo02 { + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空") + private String phone; + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空") + private String password; + +} diff --git a/src/main/java/com/platform/modules/auth/vo/AuthVo03.java b/src/main/java/com/platform/modules/auth/vo/AuthVo03.java new file mode 100644 index 0000000..85b58aa --- /dev/null +++ b/src/main/java/com/platform/modules/auth/vo/AuthVo03.java @@ -0,0 +1,22 @@ +package com.platform.modules.auth.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class AuthVo03 { + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空") + private String phone; + + /** + * 验证码 + */ + @NotBlank(message = "验证码不能为空") + private String code; + +} diff --git a/src/main/java/com/platform/modules/auth/vo/AuthVo04.java b/src/main/java/com/platform/modules/auth/vo/AuthVo04.java new file mode 100644 index 0000000..87e691b --- /dev/null +++ b/src/main/java/com/platform/modules/auth/vo/AuthVo04.java @@ -0,0 +1,28 @@ +package com.platform.modules.auth.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class AuthVo04 { + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空") + private String phone; + + /** + * 验证码 + */ + @NotBlank(message = "验证码不能为空") + private String code; + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空") + private String password; + +} diff --git a/src/main/java/com/platform/modules/chat/config/AmapConfig.java b/src/main/java/com/platform/modules/chat/config/AmapConfig.java new file mode 100644 index 0000000..3faca2d --- /dev/null +++ b/src/main/java/com/platform/modules/chat/config/AmapConfig.java @@ -0,0 +1,17 @@ +package com.platform.modules.chat.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 读取高德地图相关配置 + */ +@Component +@Data +public class AmapConfig { + + @Value("${amap.key}") + private String key; + +} \ No newline at end of file diff --git a/src/main/java/com/platform/modules/chat/config/TencentConfig.java b/src/main/java/com/platform/modules/chat/config/TencentConfig.java new file mode 100644 index 0000000..c5eb46c --- /dev/null +++ b/src/main/java/com/platform/modules/chat/config/TencentConfig.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 腾讯nlp配置 + */ +@Component +@Data +public class TencentConfig { + + @Value("${tencent.appId}") + private String appId; + + @Value("${tencent.appKey}") + private String appKey; + + @Value("${tencent.appSecret}") + private String appSecret; + +} \ No newline at end of file diff --git a/src/main/java/com/platform/modules/chat/controller/ApplyController.java b/src/main/java/com/platform/modules/chat/controller/ApplyController.java new file mode 100644 index 0000000..42e9149 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/controller/ApplyController.java @@ -0,0 +1,91 @@ +package com.platform.modules.chat.controller; + +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.common.web.page.TableDataInfo; +import com.platform.modules.chat.service.ChatApplyService; +import com.platform.modules.chat.service.ChatFriendService; +import com.platform.modules.chat.vo.ApplyVo01; +import com.platform.modules.chat.vo.FriendVo02; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * 申请 + */ +@RestController +@Slf4j +@RequestMapping("/apply") +public class ApplyController extends BaseController { + + @Resource + private ChatFriendService chatFriendService; + + @Resource + private ChatApplyService chatApplyService; + + /** + * 申请添加 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/add") + public AjaxResult add(@Validated @RequestBody FriendVo02 friendVo) { + chatFriendService.applyFriend(friendVo); + return AjaxResult.success(); + } + + /** + * 申请记录 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/list") + public TableDataInfo list() { + startPage("create_time desc"); + return getDataTable(chatApplyService.list()); + } + + /** + * 申请详情 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/info/{applyId}") + public AjaxResult getInfo(@PathVariable Long applyId) { + return AjaxResult.success(chatApplyService.getInfo(applyId)); + } + + /** + * 同意申请 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/agree") + public AjaxResult agree(@Validated @RequestBody ApplyVo01 applyVo) { + chatFriendService.agree(applyVo.getApplyId()); + return AjaxResult.success(); + } + + /** + * 拒绝申请 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/refused") + public AjaxResult refused(@Validated @RequestBody ApplyVo01 applyVo) { + chatFriendService.refused(applyVo.getApplyId()); + return AjaxResult.success(); + } + + /** + * 忽略申请 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/ignore") + public AjaxResult ignore(@Validated @RequestBody ApplyVo01 applyVo) { + chatFriendService.ignore(applyVo.getApplyId()); + return AjaxResult.success(); + } + +} diff --git a/src/main/java/com/platform/modules/chat/controller/ChatController.java b/src/main/java/com/platform/modules/chat/controller/ChatController.java new file mode 100644 index 0000000..a6af0ca --- /dev/null +++ b/src/main/java/com/platform/modules/chat/controller/ChatController.java @@ -0,0 +1,35 @@ +package com.platform.modules.chat.controller; + +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.chat.service.ChatMsgService; +import com.platform.modules.chat.vo.ChatVo01; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * 聊天 + */ +@RestController +@Slf4j +@RequestMapping("/chat") +public class ChatController extends BaseController { + + @Resource + private ChatMsgService chatMsgService; + + /** + * 发送信息 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/sendMsg") + public AjaxResult sendMsg(@Validated @RequestBody ChatVo01 chatVo) { + return AjaxResult.success(chatMsgService.sendFriendMsg(chatVo)); + } + +} diff --git a/src/main/java/com/platform/modules/chat/controller/FriendController.java b/src/main/java/com/platform/modules/chat/controller/FriendController.java new file mode 100644 index 0000000..6d9fe89 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/controller/FriendController.java @@ -0,0 +1,95 @@ +package com.platform.modules.chat.controller; + +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.chat.service.ChatFriendService; +import com.platform.modules.chat.vo.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 好友 + */ +@RestController +@Slf4j +@RequestMapping("/friend") +public class FriendController extends BaseController { + + @Resource + private ChatFriendService chatFriendService; + + /** + * 搜索好友 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/findFriend") + public AjaxResult findFriend(@Validated @RequestBody FriendVo01 friendVo) { + return AjaxResult.success(chatFriendService.findFriend(friendVo.getParam())); + } + + /** + * 好友列表 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/friendList") + public AjaxResult friendList(@Validated @RequestBody FriendVo08 friendVo) { + List list = chatFriendService.friendList(friendVo.getParam()); + return AjaxResult.success(list); + } + + /** + * 好友详情 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/info/{friendId}") + public AjaxResult getInfo(@PathVariable Long friendId) { + return AjaxResult.success(chatFriendService.getInfo(friendId)); + } + + /** + * 设置黑名单 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/black") + public AjaxResult black(@Validated @RequestBody FriendVo03 friendVo) { + chatFriendService.setBlack(friendVo); + return AjaxResult.success(); + } + + /** + * 删除好友 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/delFriend") + public AjaxResult delFriend(@Validated @RequestBody FriendVo04 friendVo) { + chatFriendService.delFriend(friendVo.getUserId()); + return AjaxResult.success(); + } + + /** + * 设置备注 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/remark") + public AjaxResult remark(@Validated @RequestBody FriendVo05 friendVo) { + chatFriendService.setRemark(friendVo); + return AjaxResult.success(); + } + + /** + * 设置是否置顶 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/top") + public AjaxResult top(@Validated @RequestBody FriendVo09 friendVo) { + chatFriendService.setTop(friendVo); + return AjaxResult.success(); + } + +} diff --git a/src/main/java/com/platform/modules/chat/controller/GroupController.java b/src/main/java/com/platform/modules/chat/controller/GroupController.java new file mode 100644 index 0000000..c401bd7 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/controller/GroupController.java @@ -0,0 +1,193 @@ +package com.platform.modules.chat.controller; + +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.chat.service.ChatGroupService; +import com.platform.modules.chat.service.ChatMsgService; +import com.platform.modules.chat.vo.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 群组 + */ +@RestController +@Slf4j +@RequestMapping("/group") +public class GroupController extends BaseController { + + @Resource + private ChatGroupService chatGroupService; + + @Resource + private ChatMsgService chatMsgService; + + /** + * 建立群组 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/createGroup") + public AjaxResult createGroup(@Validated @RequestBody List list) { + chatGroupService.createGroup(list); + return AjaxResult.success(); + } + + /** + * 获取群详情 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/getInfo/{groupId}") + public AjaxResult getInfo(@PathVariable Long groupId) { + return AjaxResult.success(chatGroupService.getInfo(groupId)); + } + + /** + * 邀请进群 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/invitationGroup") + public AjaxResult invitationGroup(@Validated @RequestBody GroupVo01 groupVo) { + Long groupId = groupVo.getGroupId(); + List list = groupVo.getList(); + chatGroupService.invitationGroup(groupId, list); + return AjaxResult.success(); + } + + /** + * 踢出群组 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/kickedGroup") + public AjaxResult kickedGroup(@Validated @RequestBody GroupVo01 groupVo) { + Long groupId = groupVo.getGroupId(); + List list = groupVo.getList(); + chatGroupService.kickedGroup(groupId, list); + return AjaxResult.success(); + } + + /** + * 修改群名 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editGroupName") + public AjaxResult editGroupName(@Validated @RequestBody GroupVo02 groupVo) { + chatGroupService.editGroupName(groupVo); + return AjaxResult.success(); + } + + /** + * 获取群组二维码 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/getGroupQrCode/{groupId}") + public AjaxResult getGroupQrCode(@PathVariable Long groupId) { + return AjaxResult.success(chatGroupService.getGroupQrCode(groupId)); + } + + /** + * 修改群公告 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editGroupNotice") + public AjaxResult editGroupNotice(@Validated @RequestBody GroupVo03 groupVo) { + chatGroupService.editGroupNotice(groupVo); + return AjaxResult.success(); + } + + /** + * 是否置顶 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editTop") + public AjaxResult editTop(@Validated @RequestBody GroupVo04 groupVo) { + Long groupId = groupVo.getGroupId(); + chatGroupService.editTop(groupId, groupVo.getTop()); + return AjaxResult.success(); + } + + /** + * 是否免打扰 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editDisturb") + public AjaxResult editDisturb(@Validated @RequestBody GroupVo05 groupVo) { + Long groupId = groupVo.getGroupId(); + chatGroupService.editDisturb(groupId, groupVo.getDisturb()); + return AjaxResult.success(); + } + + /** + * 是否保存群组 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editKeepGroup") + public AjaxResult editKeepGroup(@Validated @RequestBody GroupVo06 groupVo) { + Long groupId = groupVo.getGroupId(); + chatGroupService.editKeepGroup(groupId, groupVo.getKeepGroup()); + return AjaxResult.success(); + } + + /** + * 退出群组 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/logoutGroup/{groupId}") + public AjaxResult logoutGroup(@PathVariable Long groupId) { + chatGroupService.logoutGroup(groupId); + return AjaxResult.success(); + } + + /** + * 解散群组 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/removeGroup/{groupId}") + public AjaxResult removeGroup(@PathVariable Long groupId) { + chatGroupService.removeGroup(groupId); + return AjaxResult.success(); + } + + /** + * 扫码查询 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/scanCode/{param}") + public AjaxResult scanCode(@PathVariable String param) { + return AjaxResult.success(chatGroupService.scanCode(param)); + } + + /** + * 加入群组 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/joinGroup/{groupId}") + public AjaxResult joinGroup(@PathVariable Long groupId) { + chatGroupService.joinGroup(groupId); + return AjaxResult.success(); + } + + /** + * 查询群列表 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/groupList") + public AjaxResult groupList() { + return AjaxResult.success(chatGroupService.groupList()); + } + + /** + * 发送信息 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/sendMsg") + public AjaxResult sendMsg(@Validated @RequestBody ChatVo02 chatVo) { + return AjaxResult.success(chatMsgService.sendGroupMsg(chatVo)); + } + +} diff --git a/src/main/java/com/platform/modules/chat/controller/MyController.java b/src/main/java/com/platform/modules/chat/controller/MyController.java new file mode 100644 index 0000000..765a557 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/controller/MyController.java @@ -0,0 +1,183 @@ +package com.platform.modules.chat.controller; + +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.service.ChatFeedbackService; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.chat.vo.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * 我的 + */ +@RestController +@Slf4j +@RequestMapping("/my") +public class MyController extends BaseController { + + @Resource + private ChatUserService chatUserService; + + @Resource + private ChatFeedbackService feedbackService; + + /** + * 修改密码 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editPass") + public AjaxResult editPass(@Validated @RequestBody MyVo01 myVo) { + // 执行重置 + chatUserService.editPass(myVo.getPassword(), myVo.getPwd()); + return AjaxResult.successMsg("修改成功"); + } + + /** + * 退出系统 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/logout") + public AjaxResult logout() { + chatUserService.logout(); + return AjaxResult.success(); + } + + /** + * 获取二维码 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/getQrCode") + public AjaxResult getQrCode() { + return AjaxResult.success(chatUserService.getQrCode()); + } + + /** + * 获取基本信息 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/getInfo") + public AjaxResult getInfo() { + return AjaxResult.success(chatUserService.getInfo()); + } + + /** + * 修改头像 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editPortrait") + public AjaxResult editPortrait(@Validated @RequestBody MyVo02 myVo) { + // 执行修改 + ChatUser chatUser = new ChatUser() + .setUserId(ShiroUtils.getUserId()) + .setPortrait(myVo.getPortrait()); + chatUserService.updateById(chatUser); + return AjaxResult.successMsg("修改成功"); + } + + /** + * 修改昵称 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editNick") + public AjaxResult editNick(@Validated @RequestBody MyVo03 myVo) { + ChatUser chatUser = new ChatUser() + .setUserId(ShiroUtils.getUserId()) + .setNickName(myVo.getNickName()); + chatUserService.updateById(chatUser); + return AjaxResult.successMsg("修改成功"); + } + + /** + * 修改性别 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editGender") + public AjaxResult editGender(@Validated @RequestBody MyVo05 myVo) { + // 执行修改 + ChatUser chatUser = new ChatUser() + .setUserId(ShiroUtils.getUserId()) + .setGender(myVo.getGender()); + chatUserService.updateById(chatUser); + return AjaxResult.successMsg("修改成功"); + } + + /** + * 修改微聊号 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editChatNo") + public AjaxResult editChatNo(@Validated @RequestBody MyVo06 myVo) { + // 执行修改 + chatUserService.editChatNo(myVo.getChatNo()); + return AjaxResult.successMsg("修改成功"); + } + + /** + * 修改个性签名 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editIntro") + public AjaxResult editIntro(@Validated @RequestBody MyVo07 myVo) { + // 执行修改 + ChatUser chatUser = new ChatUser() + .setUserId(ShiroUtils.getUserId()) + .setIntro(myVo.getIntro()); + chatUserService.updateById(chatUser); + return AjaxResult.successMsg("修改成功"); + } + + /** + * 修改省市 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editCity") + public AjaxResult editCity(@Validated @RequestBody MyVo08 myVo) { + // 执行修改 + ChatUser chatUser = new ChatUser() + .setUserId(ShiroUtils.getUserId()) + .setProvinces(myVo.getProvinces()) + .setCity(myVo.getCity()); + chatUserService.updateById(chatUser); + return AjaxResult.successMsg("修改成功"); + } + + /** + * 用户注销 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/deleted") + public AjaxResult deleted() { + chatUserService.deleted(); + return AjaxResult.successMsg("注销成功"); + } + + /** + * 建议反馈 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/feedback") + public AjaxResult feedback(@Validated @RequestBody MyVo04 myVo) { + feedbackService.addFeedback(myVo); + return AjaxResult.success(); + } + + /** + * 刷新 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/refresh") + public AjaxResult refresh() { + chatUserService.refresh(); + return AjaxResult.success(); + } + + +} diff --git a/src/main/java/com/platform/modules/chat/dao/ChatApplyDao.java b/src/main/java/com/platform/modules/chat/dao/ChatApplyDao.java new file mode 100644 index 0000000..3216eb8 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/dao/ChatApplyDao.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.dao; + +import com.platform.modules.chat.domain.ChatApply; +import org.springframework.stereotype.Repository; +import com.platform.common.web.dao.BaseDao; + +import java.util.List; + +/** + *

+ * 好友申请表 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatApplyDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatApply chatApply); + +} diff --git a/src/main/java/com/platform/modules/chat/dao/ChatFeedbackDao.java b/src/main/java/com/platform/modules/chat/dao/ChatFeedbackDao.java new file mode 100644 index 0000000..e3d7794 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/dao/ChatFeedbackDao.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.dao; + +import com.platform.modules.chat.domain.ChatFeedback; +import org.springframework.stereotype.Repository; +import com.platform.common.web.dao.BaseDao; + +import java.util.List; + +/** + *

+ * 建议反馈 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatFeedbackDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatFeedback chatFeedback); + +} diff --git a/src/main/java/com/platform/modules/chat/dao/ChatFriendDao.java b/src/main/java/com/platform/modules/chat/dao/ChatFriendDao.java new file mode 100644 index 0000000..dff5a4a --- /dev/null +++ b/src/main/java/com/platform/modules/chat/dao/ChatFriendDao.java @@ -0,0 +1,34 @@ +package com.platform.modules.chat.dao; + +import com.platform.common.web.dao.BaseDao; +import com.platform.modules.chat.domain.ChatFriend; +import com.platform.modules.chat.vo.FriendVo06; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * 好友表 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatFriendDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatFriend chatFriend); + + /** + * 查询好友列表 + */ + List friendList(Long userId); + + /** + * 查询好友id + */ + List queryFriendId(Long userId); + +} diff --git a/src/main/java/com/platform/modules/chat/dao/ChatGroupDao.java b/src/main/java/com/platform/modules/chat/dao/ChatGroupDao.java new file mode 100644 index 0000000..a77b4fa --- /dev/null +++ b/src/main/java/com/platform/modules/chat/dao/ChatGroupDao.java @@ -0,0 +1,30 @@ +package com.platform.modules.chat.dao; + +import com.platform.common.web.dao.BaseDao; +import com.platform.modules.chat.domain.ChatGroup; +import com.platform.modules.push.vo.PushParamVo; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * 群组 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatGroupDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatGroup chatGroup); + + /** + * 查询用户 + */ + List queryFriendPushFrom(@Param("groupId") Long groupId, @Param("userId") Long userId); + +} diff --git a/src/main/java/com/platform/modules/chat/dao/ChatGroupInfoDao.java b/src/main/java/com/platform/modules/chat/dao/ChatGroupInfoDao.java new file mode 100644 index 0000000..9a22615 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/dao/ChatGroupInfoDao.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.dao; + +import com.platform.common.web.dao.BaseDao; +import com.platform.modules.chat.domain.ChatGroupInfo; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatGroupInfoDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatGroupInfo chatGroupInfo); + +} diff --git a/src/main/java/com/platform/modules/chat/dao/ChatMsgDao.java b/src/main/java/com/platform/modules/chat/dao/ChatMsgDao.java new file mode 100644 index 0000000..73e4944 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/dao/ChatMsgDao.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.dao; + +import com.platform.modules.chat.domain.ChatMsg; +import org.springframework.stereotype.Repository; +import com.platform.common.web.dao.BaseDao; + +import java.util.List; + +/** + *

+ * 聊天消息 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatMsgDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatMsg chatMsg); + +} diff --git a/src/main/java/com/platform/modules/chat/dao/ChatUserDao.java b/src/main/java/com/platform/modules/chat/dao/ChatUserDao.java new file mode 100644 index 0000000..da37151 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/dao/ChatUserDao.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.dao; + +import com.platform.common.web.dao.BaseDao; +import com.platform.modules.chat.domain.ChatUser; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * 用户表 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatUserDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatUser chatUser); + +} diff --git a/src/main/java/com/platform/modules/chat/dao/ChatVersionDao.java b/src/main/java/com/platform/modules/chat/dao/ChatVersionDao.java new file mode 100644 index 0000000..d430a49 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/dao/ChatVersionDao.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.dao; + +import com.platform.modules.chat.domain.ChatVersion; +import org.springframework.stereotype.Repository; +import com.platform.common.web.dao.BaseDao; + +import java.util.List; + +/** + *

+ * 版本 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatVersionDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatVersion chatVersion); + +} diff --git a/src/main/java/com/platform/modules/chat/domain/ChatApply.java b/src/main/java/com/platform/modules/chat/domain/ChatApply.java new file mode 100644 index 0000000..4074db1 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/domain/ChatApply.java @@ -0,0 +1,65 @@ +package com.platform.modules.chat.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.web.domain.BaseEntity; +import com.platform.modules.chat.enums.ApplySourceEnum; +import com.platform.modules.chat.enums.ApplyStatusEnum; +import com.platform.modules.chat.enums.ApplyTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 好友申请表实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_apply") +@Accessors(chain = true) // 链式调用 +public class ChatApply extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 发起id + */ + private Long fromId; + /** + * 接收id + */ + private Long toId; + /** + * 目标id + */ + private Long targetId; + /** + * 申请类型1好友2群组 + */ + private ApplyTypeEnum applyType; + /** + * 申请状态0无1同意2拒绝 + */ + private ApplyStatusEnum applyStatus; + /** + * 申请来源 + */ + private ApplySourceEnum applySource; + /** + * 理由 + */ + private String reason; + /** + * 申请时间 + */ + private Date createTime; + +} diff --git a/src/main/java/com/platform/modules/chat/domain/ChatFeedback.java b/src/main/java/com/platform/modules/chat/domain/ChatFeedback.java new file mode 100644 index 0000000..1233532 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/domain/ChatFeedback.java @@ -0,0 +1,50 @@ +package com.platform.modules.chat.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.web.domain.BaseEntity; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 建议反馈实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_feedback") +@Accessors(chain = true) // 链式调用 +public class ChatFeedback extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 用户id + */ + private Long userId; + /** + * 图片 + */ + private String images; + /** + * 内容 + */ + private String content; + /** + * 版本 + */ + private String version; + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/src/main/java/com/platform/modules/chat/domain/ChatFriend.java b/src/main/java/com/platform/modules/chat/domain/ChatFriend.java new file mode 100644 index 0000000..5db6eaf --- /dev/null +++ b/src/main/java/com/platform/modules/chat/domain/ChatFriend.java @@ -0,0 +1,63 @@ +package com.platform.modules.chat.domain; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.domain.BaseEntity; +import com.platform.modules.chat.enums.ApplySourceEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 好友表实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_friend") +@Accessors(chain = true) // 链式调用 +public class ChatFriend extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 用户id + */ + private Long fromId; + /** + * 好友id + */ + private Long toId; + /** + * 备注 + */ + private String remark; + /** + * 是否黑名单 + */ + private YesOrNoEnum black; + /** + * 是否置顶 + */ + private YesOrNoEnum top; + /** + * 申请来源 + */ + private ApplySourceEnum source; + /** + * 创建时间 + */ + @TableField(updateStrategy = FieldStrategy.NEVER) + private Date createTime; + +} diff --git a/src/main/java/com/platform/modules/chat/domain/ChatGroup.java b/src/main/java/com/platform/modules/chat/domain/ChatGroup.java new file mode 100644 index 0000000..18c7b7b --- /dev/null +++ b/src/main/java/com/platform/modules/chat/domain/ChatGroup.java @@ -0,0 +1,50 @@ +package com.platform.modules.chat.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.web.domain.BaseEntity; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 群组实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_group") +@Accessors(chain = true) // 链式调用 +public class ChatGroup extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 群名 + */ + private String name; + /** + * 公告 + */ + private String notice; + /** + * 头像 + */ + private String portrait; + /** + * 群主 + */ + private Long master; + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/src/main/java/com/platform/modules/chat/domain/ChatGroupInfo.java b/src/main/java/com/platform/modules/chat/domain/ChatGroupInfo.java new file mode 100644 index 0000000..1835e9c --- /dev/null +++ b/src/main/java/com/platform/modules/chat/domain/ChatGroupInfo.java @@ -0,0 +1,66 @@ +package com.platform.modules.chat.domain; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.domain.BaseEntity; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_group_info") +@Accessors(chain = true) // 链式调用 +@NoArgsConstructor +public class ChatGroupInfo extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long infoId; + /** + * 用户id + */ + private Long userId; + /** + * 群组id + */ + private Long groupId; + /** + * 是否置顶 + */ + private YesOrNoEnum top; + /** + * 是否免打扰 + */ + private YesOrNoEnum disturb; + /** + * 是否保存群组 + */ + private YesOrNoEnum keepGroup; + /** + * 加入时间 + */ + private Date createTime; + + public ChatGroupInfo(Long userId, Long groupId) { + this.userId = userId; + this.groupId = groupId; + this.createTime = DateUtil.date(); + this.top = YesOrNoEnum.NO; + this.disturb = YesOrNoEnum.NO; + this.keepGroup = YesOrNoEnum.NO; + } +} diff --git a/src/main/java/com/platform/modules/chat/domain/ChatMsg.java b/src/main/java/com/platform/modules/chat/domain/ChatMsg.java new file mode 100644 index 0000000..c8c7ba3 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/domain/ChatMsg.java @@ -0,0 +1,59 @@ +package com.platform.modules.chat.domain; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.web.domain.BaseEntity; +import com.platform.modules.push.enums.PushMsgEnum; +import com.platform.modules.push.enums.PushTalkEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 聊天消息实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_msg") +@Accessors(chain = true) // 链式调用 +public class ChatMsg extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 消息主键 + */ + @TableId + private Long id; + /** + * 发送人 + */ + private Long fromId; + /** + * 接收人 + */ + private Long toId; + /** + * 消息类型 + */ + private PushMsgEnum msgType; + /** + * 消息类型 + */ + private PushTalkEnum talkType; + /** + * 消息内容 + */ + private String content; + /** + * 创建时间 + */ + @TableField(updateStrategy = FieldStrategy.NEVER) + private Date createTime; + +} diff --git a/src/main/java/com/platform/modules/chat/domain/ChatUser.java b/src/main/java/com/platform/modules/chat/domain/ChatUser.java new file mode 100644 index 0000000..b3595d7 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/domain/ChatUser.java @@ -0,0 +1,123 @@ +package com.platform.modules.chat.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.platform.common.constant.AppConstants; +import com.platform.common.enums.GenderEnum; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.domain.BaseEntity; +import com.platform.modules.push.vo.PushParamVo; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 用户表实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_user") +@Accessors(chain = true) +public class ChatUser extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long userId; + /** + * 昵称 + */ + private String nickName; + /** + * 介绍 + */ + private String intro; + /** + * 性别1男0女 + */ + private GenderEnum gender; + /** + * 头像 + */ + private String portrait; + /** + * 封面 + */ + private String cover; + /** + * 手机号 + */ + private String phone; + /** + * 省份 + */ + private String provinces; + /** + * 城市 + */ + private String city; + /** + * 微聊号 + */ + private String chatNo; + /** + * 密码 + */ + private String password; + /** + * 盐 + */ + private String salt; + /** + * 用户token + */ + private String token; + /** + * 版本信息 + */ + private String version; + /** + * 注册时间 + */ + @TableField(updateStrategy = FieldStrategy.NEVER) + private Date createTime; + /** + * 注销0正常null注销 + */ + @TableLogic + private Integer deleted; + /** + * 注销时间 + */ + private Date deletedTime; + + /** + * 格式化,防止出错 + */ + public static ChatUser initUser(ChatUser user) { + if (user != null) { + return user; + } + return new ChatUser() + .setGender(GenderEnum.MALE) + .setPortrait(AppConstants.DEFAULT_PORTRAIT) + .setNickName(AppConstants.DELETED_NICK_NAME); + } + + /** + * 格式化,防止出错 + */ + public static PushParamVo initParam(ChatUser user) { + user = initUser(user); + return new PushParamVo() + .setUserId(user.getUserId()) + .setPortrait(user.getPortrait()) + .setNickName(user.getNickName()); + } + +} diff --git a/src/main/java/com/platform/modules/chat/domain/ChatVersion.java b/src/main/java/com/platform/modules/chat/domain/ChatVersion.java new file mode 100644 index 0000000..97821f4 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/domain/ChatVersion.java @@ -0,0 +1,38 @@ +package com.platform.modules.chat.domain; + +import com.platform.common.web.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + *

+ * 版本实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_version") +public class ChatVersion extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 版本 + */ + private String version; + /** + * 地址 + */ + private String url; + /** + * 内容 + */ + private String content; + +} diff --git a/src/main/java/com/platform/modules/chat/enums/ApplySourceEnum.java b/src/main/java/com/platform/modules/chat/enums/ApplySourceEnum.java new file mode 100644 index 0000000..18b0286 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/enums/ApplySourceEnum.java @@ -0,0 +1,57 @@ +package com.platform.modules.chat.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 好友来源 + */ +@Getter +public enum ApplySourceEnum { + + /** + * 扫一扫 + */ + SCAN("1", "扫一扫"), + /** + * 名片 + */ + CARD("2", "名片"), + /** + * 微聊号 + */ + CHAT_NO("3", "微聊号"), + /** + * 手机号 + */ + PHONE("4", "手机号"), + /** + * 摇一摇 + */ + SHAKE("5", "摇一摇"), + /** + * 系统 + */ + SYS("6", "系统"), + /** + * 群聊 + */ + GROUP("7", "群聊"), + /** + * 附近的人 + */ + NEAR("8", "附近的人"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + ApplySourceEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/chat/enums/ApplyStatusEnum.java b/src/main/java/com/platform/modules/chat/enums/ApplyStatusEnum.java new file mode 100644 index 0000000..2e9e7d1 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/enums/ApplyStatusEnum.java @@ -0,0 +1,41 @@ +package com.platform.modules.chat.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 申请状态 + */ +@Getter +public enum ApplyStatusEnum { + + /** + * 无 + */ + NONE("0", "无"), + /** + * 同意 + */ + AGREE("1", "同意"), + /** + * 拒绝 + */ + REFUSED("2", "拒绝"), + /** + * 忽略 + */ + IGNORE("3", "忽略"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + ApplyStatusEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/chat/enums/ApplyTypeEnum.java b/src/main/java/com/platform/modules/chat/enums/ApplyTypeEnum.java new file mode 100644 index 0000000..d624872 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/enums/ApplyTypeEnum.java @@ -0,0 +1,33 @@ +package com.platform.modules.chat.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 申请类型 + */ +@Getter +public enum ApplyTypeEnum { + + /** + * 好友 + */ + FRIEND("1", "好友"), + /** + * 群组 + */ + GROUP("2", "群组"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + ApplyTypeEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/chat/enums/FriendTypeEnum.java b/src/main/java/com/platform/modules/chat/enums/FriendTypeEnum.java new file mode 100644 index 0000000..d5ba978 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/enums/FriendTypeEnum.java @@ -0,0 +1,41 @@ +package com.platform.modules.chat.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 好友类型 + */ +@Getter +public enum FriendTypeEnum { + + /** + * 正常 + */ + NORMAL("normal", "正常"), + /** + * 天气机器人 + */ + WEATHER("weather", "天气机器人"), + /** + * 翻译机器人 + */ + TRANSLATION("translation", "翻译机器人"), + /** + * 自己 + */ + SELF("self", "自己"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + FriendTypeEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/chat/enums/MsgStatusEnum.java b/src/main/java/com/platform/modules/chat/enums/MsgStatusEnum.java new file mode 100644 index 0000000..3395079 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/enums/MsgStatusEnum.java @@ -0,0 +1,53 @@ +package com.platform.modules.chat.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 消息状态 + */ +@Getter +public enum MsgStatusEnum { + + /** + * 正常 + */ + NORMAL("0", "正常"), + /** + * 对方不是自己朋友 + */ + FRIEND_TO("1", "对方不是你的好友,消息发送失败"), + /** + * 自己不是对方朋友 + */ + FRIEND_FROM("2", "你不是对方的好友,消息发送失败"), + /** + * 黑名单 + */ + FRIEND_BLACK("3", "消息已发出,但被对方拒收了"), + /** + * 注销 + */ + FRIEND_DELETED("4", "对方已注销,消息发送失败"), + /** + * 群不存在 + */ + GROUP_NOT_EXIST("5", "当前群不存在,消息发送失败"), + /** + * 群明细不存在 + */ + GROUP_INFO_NOT_EXIST("6", "你不在当前群中,消息发送失败"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + MsgStatusEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/chat/enums/VersionTypeEnum.java b/src/main/java/com/platform/modules/chat/enums/VersionTypeEnum.java new file mode 100644 index 0000000..120cc26 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/enums/VersionTypeEnum.java @@ -0,0 +1,33 @@ +package com.platform.modules.chat.enums; + +import lombok.Getter; + +/** + * 版本类型枚举 + */ +@Getter +public enum VersionTypeEnum { + + /** + * 用户协议 + */ + AGREEMENT(1L, "agreement"), + /** + * 安卓 + */ + ANDROID(2L, "android"), + /** + * iOS + */ + IOS(3L, "iOS"), + ; + + private Long code; + private String name; + + VersionTypeEnum(Long code, String name) { + this.code = code; + this.name = name; + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatApplyService.java b/src/main/java/com/platform/modules/chat/service/ChatApplyService.java new file mode 100644 index 0000000..0ba4fff --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatApplyService.java @@ -0,0 +1,31 @@ +package com.platform.modules.chat.service; + +import com.github.pagehelper.PageInfo; +import com.platform.common.web.service.BaseService; +import com.platform.modules.chat.domain.ChatApply; +import com.platform.modules.chat.enums.ApplySourceEnum; +import com.platform.modules.chat.vo.ApplyVo03; + +/** + *

+ * 好友申请表 服务层 + * q3z3 + *

+ */ +public interface ChatApplyService extends BaseService { + + /** + * 申请好友 + */ + void applyFriend(Long acceptId, ApplySourceEnum source, String reason); + + /** + * 申请记录 + */ + PageInfo list(); + + /** + * 查询详情 + */ + ApplyVo03 getInfo(Long applyId); +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatFeedbackService.java b/src/main/java/com/platform/modules/chat/service/ChatFeedbackService.java new file mode 100644 index 0000000..9f8d907 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatFeedbackService.java @@ -0,0 +1,20 @@ +package com.platform.modules.chat.service; + +import com.platform.common.web.service.BaseService; +import com.platform.modules.chat.domain.ChatFeedback; +import com.platform.modules.chat.vo.MyVo04; + +/** + *

+ * 建议反馈 服务层 + * q3z3 + *

+ */ +public interface ChatFeedbackService extends BaseService { + + /** + * 添加建议反馈 + */ + void addFeedback(MyVo04 myVo); + +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatFriendService.java b/src/main/java/com/platform/modules/chat/service/ChatFriendService.java new file mode 100644 index 0000000..9f3fe58 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatFriendService.java @@ -0,0 +1,82 @@ +package com.platform.modules.chat.service; + +import com.platform.common.web.service.BaseService; +import com.platform.modules.chat.domain.ChatFriend; +import com.platform.modules.chat.vo.*; + +import java.util.List; + +/** + *

+ * 好友表 服务层 + * q3z3 + *

+ */ +public interface ChatFriendService extends BaseService { + + /** + * 搜索好友 + */ + FriendVo07 findFriend(String param); + + /** + * 添加好友 + */ + void applyFriend(FriendVo02 friendVo); + + /** + * 同意申请 + */ + void agree(Long applyId); + + /** + * 拒绝申请 + */ + void refused(Long applyId); + + /** + * 忽略申请 + */ + void ignore(Long applyId); + + /** + * 设置黑名单 + */ + void setBlack(FriendVo03 friendVo); + + /** + * 删除好友 + */ + void delFriend(Long friendId); + + /** + * 设置备注 + */ + void setRemark(FriendVo05 friendVo); + + /** + * 是否置顶 + */ + void setTop(FriendVo09 friendVo); + + /** + * 好友列表 + */ + List friendList(String param); + + /** + * 好友详情 + */ + FriendVo07 getInfo(Long friendId); + + /** + * 获取好友信息 + */ + ChatFriend getFriend(Long userId, Long friendId); + + /** + * 查询好友id + */ + List queryFriendId(Long userId); + +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatGroupInfoService.java b/src/main/java/com/platform/modules/chat/service/ChatGroupInfoService.java new file mode 100644 index 0000000..c9ce5e5 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatGroupInfoService.java @@ -0,0 +1,53 @@ +package com.platform.modules.chat.service; + +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.service.BaseService; +import com.platform.modules.chat.domain.ChatGroupInfo; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 服务层 + * q3z3 + *

+ */ +public interface ChatGroupInfoService extends BaseService { + + /** + * 查询详情 + */ + ChatGroupInfo getGroupInfo(Long groupId, Long userId, YesOrNoEnum verify); + + /** + * 删除缓存 + */ + void delGroupInfoCache(Long groupId, List userList); + + /** + * 查询数量 + */ + Long countByGroup(Long groupId); + + /** + * 查询用户id + */ + List queryUserList(Long groupId); + + /** + * 查询用户id + */ + List queryUserList(Long groupId, List userList); + + /** + * 查询用户id + */ + Map queryUserMap(Long groupId); + + /** + * 通过群组删除 + */ + void delByGroup(Long groupId); + +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatGroupService.java b/src/main/java/com/platform/modules/chat/service/ChatGroupService.java new file mode 100644 index 0000000..9d56656 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatGroupService.java @@ -0,0 +1,108 @@ +package com.platform.modules.chat.service; + +import cn.hutool.core.lang.Dict; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.service.BaseService; +import com.platform.modules.chat.domain.ChatGroup; +import com.platform.modules.chat.domain.ChatMsg; +import com.platform.modules.chat.vo.GroupVo02; +import com.platform.modules.chat.vo.GroupVo03; +import com.platform.modules.chat.vo.GroupVo07; +import com.platform.modules.chat.vo.GroupVo08; +import com.platform.modules.push.vo.PushParamVo; + +import java.util.List; + +/** + *

+ * 群组 服务层 + * q3z3 + *

+ */ +public interface ChatGroupService extends BaseService { + + /** + * 建群 + */ + void createGroup(List list); + + /** + * 群详情 + */ + Dict getInfo(Long groupId); + + /** + * 邀请群组 + */ + void invitationGroup(Long groupId, List list); + + /** + * 踢出群组 + */ + void kickedGroup(Long groupId, List list); + + /** + * 获取群二维码 + */ + String getGroupQrCode(Long groupId); + + /** + * 修改置顶 + */ + void editTop(Long groupId, YesOrNoEnum top); + + /** + * 修改免打扰 + */ + void editDisturb(Long groupId, YesOrNoEnum disturb); + + /** + * 修改保存群组 + */ + void editKeepGroup(Long groupId, YesOrNoEnum keepGroup); + + /** + * 退出群组 + */ + void logoutGroup(Long groupId); + + /** + * 解散群组 + */ + void removeGroup(Long groupId); + + /** + * 扫码查询 + */ + GroupVo07 scanCode(String param); + + /** + * 加入群组 + */ + void joinGroup(Long groupId); + + /** + * 查询群列表 + */ + List groupList(); + + /** + * 查询用户id + */ + List queryFriendPushFrom(ChatMsg chatMsg); + + /** + * 查询用户id + */ + List queryGroupPushFrom(Long groupId, List list, String content); + + /** + * 修改群名 + */ + void editGroupName(GroupVo02 groupVo); + + /** + * 修改群公告 + */ + void editGroupNotice(GroupVo03 groupVo); +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatMsgService.java b/src/main/java/com/platform/modules/chat/service/ChatMsgService.java new file mode 100644 index 0000000..a65ad8a --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatMsgService.java @@ -0,0 +1,27 @@ +package com.platform.modules.chat.service; + +import com.platform.common.web.service.BaseService; +import com.platform.modules.chat.domain.ChatMsg; +import com.platform.modules.chat.vo.ChatVo01; +import com.platform.modules.chat.vo.ChatVo02; +import com.platform.modules.chat.vo.ChatVo03; + +/** + *

+ * 聊天消息 服务层 + * q3z3 + *

+ */ +public interface ChatMsgService extends BaseService { + + /** + * 发送消息 + */ + ChatVo03 sendFriendMsg(ChatVo01 chatVo); + + /** + * 发送群消息 + */ + ChatVo03 sendGroupMsg(ChatVo02 chatVo); + +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatTalkService.java b/src/main/java/com/platform/modules/chat/service/ChatTalkService.java new file mode 100644 index 0000000..8ada897 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatTalkService.java @@ -0,0 +1,32 @@ +package com.platform.modules.chat.service; + +import com.platform.modules.chat.vo.FriendVo06; +import com.platform.modules.chat.vo.FriendVo07; +import com.platform.modules.push.vo.PushParamVo; + +import java.util.List; + +/** + *

+ * 系统聊天 服务层 + * q3z3 + *

+ */ +public interface ChatTalkService { + + /** + * 查询好友列表 + */ + List queryFriendList(); + + /** + * 查询好友详情 + */ + FriendVo07 queryFriendInfo(Long userId); + + /** + * 发送聊天 + */ + PushParamVo talk(Long userId, String content); + +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatUserService.java b/src/main/java/com/platform/modules/chat/service/ChatUserService.java new file mode 100644 index 0000000..fc0619a --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatUserService.java @@ -0,0 +1,73 @@ +package com.platform.modules.chat.service; + +import cn.hutool.core.lang.Dict; +import com.platform.common.web.service.BaseService; +import com.platform.modules.auth.vo.AuthVo01; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.vo.MyVo09; +import org.apache.shiro.authc.AuthenticationToken; + +/** + *

+ * 用户表 服务层 + * q3z3 + *

+ */ +public interface ChatUserService extends BaseService { + + /** + * 通过手机+密码注册 + */ + void register(AuthVo01 authVo); + + /** + * 通过手机号查询 + */ + ChatUser queryByPhone(String phone); + + /** + * 重置密码 + */ + void resetPass(Long userId, String password); + + /** + * 修改密码 + */ + void editPass(String password, String pwd); + + /** + * 修改微聊号 + */ + void editChatNo(String chatNo); + + /** + * 获取基本信息 + */ + MyVo09 getInfo(); + + /** + * 获取二维码 + */ + String getQrCode(); + + /** + * 用户注销 + */ + void deleted(); + + /** + * 执行登录/返回token + */ + Dict doLogin(AuthenticationToken authenticationToken); + + /** + * 退出登录 + */ + void logout(); + + /** + * 刷新 + */ + void refresh(); + +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatVersionService.java b/src/main/java/com/platform/modules/chat/service/ChatVersionService.java new file mode 100644 index 0000000..0f2cefd --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatVersionService.java @@ -0,0 +1,25 @@ +package com.platform.modules.chat.service; + +import com.platform.common.web.service.BaseService; +import com.platform.modules.chat.domain.ChatVersion; +import com.platform.modules.chat.vo.VersionVo; + +/** + *

+ * 版本 服务层 + * q3z3 + *

+ */ +public interface ChatVersionService extends BaseService { + + /** + * 用户协议 + */ + String getAgreement(); + + /** + * 获取版本 + */ + VersionVo getVersion(String version, String device); + +} diff --git a/src/main/java/com/platform/modules/chat/service/ChatWeatherService.java b/src/main/java/com/platform/modules/chat/service/ChatWeatherService.java new file mode 100644 index 0000000..dba56d1 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/ChatWeatherService.java @@ -0,0 +1,20 @@ +package com.platform.modules.chat.service; + +import cn.hutool.json.JSONObject; + +import java.util.List; + +/** + *

+ * 待办事项 服务层 + * q3z3 + *

+ */ +public interface ChatWeatherService { + + /** + * 预报天气 + */ + List queryByCityName(String cityName); + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatApplyServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatApplyServiceImpl.java new file mode 100644 index 0000000..dd00a25 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatApplyServiceImpl.java @@ -0,0 +1,137 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import com.github.pagehelper.PageInfo; +import com.platform.common.constant.AppConstants; +import com.platform.common.exception.BaseException; +import com.platform.common.redis.RedisUtils; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.dao.ChatApplyDao; +import com.platform.modules.chat.domain.ChatApply; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.enums.ApplySourceEnum; +import com.platform.modules.chat.enums.ApplyStatusEnum; +import com.platform.modules.chat.enums.ApplyTypeEnum; +import com.platform.modules.chat.service.ChatApplyService; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.chat.vo.ApplyVo02; +import com.platform.modules.chat.vo.ApplyVo03; +import com.platform.modules.push.enums.PushNoticeEnum; +import com.platform.modules.push.service.ChatPushService; +import com.platform.modules.push.vo.PushParamVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 好友申请表 服务层实现 + * q3z3 + *

+ */ +@Service("chatApplyService") +public class ChatApplyServiceImpl extends BaseServiceImpl implements ChatApplyService { + + @Resource + private ChatApplyDao chatApplyDao; + + @Lazy + @Resource + private ChatUserService chatUserService; + + @Resource + private ChatPushService chatPushService; + + @Autowired + private RedisUtils redisUtils; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatApplyDao); + } + + @Override + public List queryList(ChatApply t) { + List dataList = chatApplyDao.queryList(t); + return dataList; + } + + @Override + public void applyFriend(Long acceptId, ApplySourceEnum source, String reason) { + Date now = DateUtil.date(); + Long fromId = ShiroUtils.getUserId(); + // 查询 + ChatApply query = new ChatApply() + .setFromId(fromId) + .setToId(acceptId) + .setTargetId(acceptId) + .setApplyType(ApplyTypeEnum.FRIEND) + .setApplyStatus(ApplyStatusEnum.NONE); + ChatApply apply = this.queryOne(query); + query.setApplySource(source).setReason(reason).setCreateTime(now); + if (apply == null) { + this.add(query); + } else { + this.updateById(query.setId(apply.getId())); + } + // 给好友发送通知 + PushParamVo paramVo = new PushParamVo().setToId(acceptId); + chatPushService.pushNotice(paramVo, PushNoticeEnum.FRIEND_APPLY); + } + + @Override + public PageInfo list() { + Long userId = ShiroUtils.getUserId(); + // 清空角标 + redisUtils.delete(AppConstants.REDIS_FRIEND_NOTICE + userId); + // 查询 + List dataList = queryList(new ChatApply().setToId(userId)); + // 获取申请人 + List fromList = dataList.stream().map(ChatApply::getFromId).collect(Collectors.toList()); + // 集合判空 + if (CollectionUtils.isEmpty(fromList)) { + return new PageInfo(); + } + // 查询申请人 + HashMap dataMap = chatUserService.getByIds(fromList).stream().collect(HashMap::new, (x, y) -> { + x.put(y.getUserId(), y); + }, HashMap::putAll); + // 转换 + List dictList = new ArrayList<>(); + for (ChatApply apply : dataList) { + ChatUser chatUser = ChatUser.initUser(dataMap.get(apply.getFromId())); + ApplyVo02 applyVo = BeanUtil.toBean(apply, ApplyVo02.class) + .setApplyId(apply.getId()) + .setUserId(apply.getFromId()) + .setPortrait(chatUser.getPortrait()) + .setNickName(chatUser.getNickName()); + dictList.add(applyVo); + } + return getPageInfo(dictList, dataList); + } + + @Override + public ApplyVo03 getInfo(Long applyId) { + ChatApply apply = getById(applyId); + if (apply == null) { + throw new BaseException("申请已过期,请刷新后重试"); + } + ChatUser chatUser = ChatUser.initUser(chatUserService.getById(apply.getFromId())); + return BeanUtil.toBean(chatUser, ApplyVo03.class) + .setApplyId(applyId) + .setApplySource(apply.getApplySource()) + .setApplyStatus(apply.getApplyStatus()) + .setReason(apply.getReason()); + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatFeedbackServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatFeedbackServiceImpl.java new file mode 100644 index 0000000..475b7bd --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatFeedbackServiceImpl.java @@ -0,0 +1,51 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import com.platform.common.constant.HeadConstant; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.utils.ServletUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.dao.ChatFeedbackDao; +import com.platform.modules.chat.domain.ChatFeedback; +import com.platform.modules.chat.service.ChatFeedbackService; +import com.platform.modules.chat.vo.MyVo04; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + *

+ * 建议反馈 服务层实现 + * q3z3 + *

+ */ +@Service("chatFeedbackService") +public class ChatFeedbackServiceImpl extends BaseServiceImpl implements ChatFeedbackService { + + @Resource + private ChatFeedbackDao chatFeedbackDao; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatFeedbackDao); + } + + @Override + public List queryList(ChatFeedback t) { + List dataList = chatFeedbackDao.queryList(t); + return dataList; + } + + @Override + public void addFeedback(MyVo04 myVo) { + String version = ServletUtils.getRequest().getHeader(HeadConstant.VERSION); + ChatFeedback feedback = BeanUtil.toBean(myVo, ChatFeedback.class) + .setUserId(ShiroUtils.getUserId()) + .setVersion(version) + .setCreateTime(DateUtil.date()); + this.add(feedback); + } +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatFriendServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatFriendServiceImpl.java new file mode 100644 index 0000000..6d92909 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatFriendServiceImpl.java @@ -0,0 +1,400 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.PatternPool; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.platform.common.constant.AppConstants; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.exception.BaseException; +import com.platform.common.redis.RedisUtils; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.dao.ChatFriendDao; +import com.platform.modules.chat.domain.*; +import com.platform.modules.chat.enums.ApplySourceEnum; +import com.platform.modules.chat.enums.ApplyStatusEnum; +import com.platform.modules.chat.enums.ApplyTypeEnum; +import com.platform.modules.chat.enums.FriendTypeEnum; +import com.platform.modules.chat.service.*; +import com.platform.modules.chat.vo.*; +import com.platform.modules.push.enums.PushMsgEnum; +import com.platform.modules.push.service.ChatPushService; +import com.platform.modules.push.vo.PushParamVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + *

+ * 好友表 服务层实现 + * q3z3 + *

+ */ +@Service("chatFriendService") +public class ChatFriendServiceImpl extends BaseServiceImpl implements ChatFriendService { + + @Resource + private ChatFriendDao chatFriendDao; + + @Resource + @Lazy + private ChatUserService chatUserService; + + @Resource + private ChatApplyService chatApplyService; + + @Resource + private ChatPushService chatPushService; + + @Resource + private ChatGroupService groupService; + + @Resource + private ChatGroupInfoService groupInfoService; + + @Resource + private ChatTalkService chatTalkService; + + @Autowired + private RedisUtils redisUtils; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatFriendDao); + } + + @Override + public List queryList(ChatFriend t) { + List dataList = chatFriendDao.queryList(t); + return dataList; + } + + @Override + public FriendVo07 findFriend(String param) { + // 好友 + ChatUser chatUser; + // 来源 + ApplySourceEnum sourceEnum = null; + // 按扫码加好友 + if (StrUtil.startWith(param, AppConstants.QR_CODE_USER)) { + Long userId = Convert.toLong(ReUtil.get(PatternPool.NUMBERS, param, 0), null); + chatUser = chatUserService.getById(userId); + sourceEnum = ApplySourceEnum.SCAN; + } + // 按手机搜索 + else if ((chatUser = chatUserService.queryByPhone(param)) != null) { + sourceEnum = ApplySourceEnum.PHONE; + } + // 按微信号搜索 + else if ((chatUser = chatUserService.queryOne(new ChatUser().setChatNo(param))) != null) { + sourceEnum = ApplySourceEnum.CHAT_NO; + } + if (chatUser == null) { + throw new BaseException("暂无结果"); + } + if (ShiroUtils.getPhone().equals(chatUser.getPhone())) { + throw new BaseException("不能添加自己为好友"); + } + FriendVo07 friendVo = formatFriendVo(chatUser); + if (friendVo.getSource() == null) { + friendVo.setSource(sourceEnum); + } + return friendVo; + } + + @Transactional + @Override + public void applyFriend(FriendVo02 friendVo) { + //当前登录人 + Long userId = ShiroUtils.getUserId(); + Long friendId = friendVo.getUserId(); + // 验证是否是自己 + if (userId.equals(friendId)) { + throw new BaseException("你不能添加自己为好友"); + } + // 查询好友 + ChatUser user = chatUserService.getById(friendId); + if (user == null) { + throw new BaseException("好友不存在"); + } + ChatFriend friend1 = getFriend(userId, friendId); + ChatFriend friend2 = getFriend(friendId, userId); + if (friend1 != null && friend2 != null) { + throw new BaseException("已经是你的好友了,不能重复添加"); + } + // 申请好友 + chatApplyService.applyFriend(friendId, friendVo.getSource(), friendVo.getReason()); + } + + @Transactional + @Override + public void agree(Long applyId) { + ChatApply apply = verifyApply(applyId); + ChatUser fromUser = chatUserService.getById(apply.getFromId()); + // 更新申请 + chatApplyService.updateById(new ChatApply() + .setId(apply.getId()) + .setApplyStatus(ApplyStatusEnum.AGREE)); + if (fromUser == null) { + return; + } + if (ApplyTypeEnum.FRIEND.equals(apply.getApplyType())) { + agreeFriend(apply, fromUser); + } else { + agreeGroup(apply, fromUser); + } + } + + /** + * 同意朋友 + */ + private void agreeFriend(ChatApply apply, ChatUser fromUser) { + Long toId = ShiroUtils.getUserId(); + Long fromId = apply.getFromId(); + Date now = DateUtil.date(); + ApplySourceEnum source = apply.getApplySource(); + ChatUser toUser = chatUserService.getById(toId); + // 添加好友列表 + List friendList = new ArrayList<>(); + ChatFriend friend1 = new ChatFriend().setFromId(toId).setToId(fromId); + if (this.queryOne(friend1) == null) { + friendList.add(friend1.setCreateTime(now) + .setSource(source) + .setBlack(YesOrNoEnum.NO) + .setTop(YesOrNoEnum.NO) + .setRemark(fromUser.getNickName())); + } + ChatFriend friend2 = new ChatFriend().setFromId(fromId).setToId(toId); + if (this.queryOne(friend2) == null) { + friendList.add(friend2.setCreateTime(now) + .setSource(source) + .setTop(YesOrNoEnum.NO) + .setBlack(YesOrNoEnum.NO) + .setRemark(toUser.getNickName())); + } + if (CollectionUtils.isEmpty(friendList)) { + return; + } + // 增加好友数据 + this.batchAdd(friendList); + // 发送通知 + chatPushService.pushMsg(ChatUser.initParam(fromUser).setContent(AppConstants.NOTICE_FRIEND_CREATE).setToId(toId), PushMsgEnum.ALERT); + chatPushService.pushMsg(ChatUser.initParam(toUser).setContent(AppConstants.NOTICE_FRIEND_CREATE).setToId(fromId), PushMsgEnum.ALERT); + } + + /** + * 同意群组 + */ + private void agreeGroup(ChatApply apply, ChatUser fromUser) { + Long toId = ShiroUtils.getUserId(); + Long fromId = apply.getFromId(); + Long groupId = apply.getTargetId(); + // 查询群 + ChatGroup group = groupService.getById(groupId); + if (group == null) { + return; + } + if (!group.getMaster().equals(toId)) { + throw new BaseException("你不是群主,不能操作"); + } + ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, fromId, YesOrNoEnum.NO); + // 加群 + if (groupInfo == null) { + groupInfoService.add(new ChatGroupInfo(fromId, groupId)); + } + // 发送通知 + String content = StrUtil.format(AppConstants.NOTICE_GROUP_JOIN, fromUser.getNickName()); + List pushParamList = groupService.queryGroupPushFrom(groupId, null, content); + chatPushService.pushMsg(pushParamList, PushMsgEnum.ALERT); + } + + @Override + public void refused(Long applyId) { + ChatApply apply = verifyApply(applyId); + // 更新申请 + chatApplyService.updateById(new ChatApply().setId(apply.getId()).setApplyStatus(ApplyStatusEnum.REFUSED)); + } + + @Override + public void ignore(Long applyId) { + ChatApply apply = verifyApply(applyId); + // 更新申请 + chatApplyService.updateById(new ChatApply().setId(apply.getId()).setApplyStatus(ApplyStatusEnum.IGNORE)); + } + + @Override + public void setBlack(FriendVo03 friendVo) { + Long userId = ShiroUtils.getUserId(); + Long friendId = friendVo.getUserId(); + // 校验是否是好友 + ChatFriend friend = getFriend(userId, friendId); + if (friend == null) { + throw new BaseException(AppConstants.FRIEND_NOT_EXIST); + } + this.updateById(new ChatFriend().setId(friend.getId()).setBlack(friendVo.getBlack())); + // 移除缓存 + this.delFriendCache(userId, friendId); + } + + @Transactional + @Override + public void delFriend(Long friendId) { + Long userId = ShiroUtils.getUserId(); + // 校验是否是好友 + ChatFriend friend = getFriend(userId, friendId); + if (friend == null) { + throw new BaseException(AppConstants.FRIEND_NOT_EXIST); + } + this.deleteById(friend.getId()); + // 移除缓存 + this.delFriendCache(userId, friendId); + } + + @Override + public void setRemark(FriendVo05 friendVo) { + Long userId = ShiroUtils.getUserId(); + Long friendId = friendVo.getUserId(); + // 校验是否是好友 + ChatFriend friend = getFriend(userId, friendId); + if (friend == null) { + throw new BaseException(AppConstants.FRIEND_NOT_EXIST); + } + ChatFriend cf = new ChatFriend().setId(friend.getId()).setRemark(friendVo.getRemark()); + this.updateById(cf); + // 移除缓存 + this.delFriendCache(userId, friendId); + } + + @Override + public void setTop(FriendVo09 friendVo) { + Long userId = ShiroUtils.getUserId(); + Long friendId = friendVo.getUserId(); + // 校验是否是好友 + ChatFriend friend = getFriend(userId, friendId); + if (friend == null) { + throw new BaseException(AppConstants.FRIEND_NOT_EXIST); + } + this.updateById(new ChatFriend().setId(friend.getId()).setTop(friendVo.getTop())); + // 移除缓存 + this.delFriendCache(userId, friendId); + } + + @Override + public List friendList(String param) { + List list1 = chatTalkService.queryFriendList(); + List list2 = chatFriendDao.friendList(ShiroUtils.getUserId()); + List dataList = new ArrayList<>(); + if (!CollectionUtils.isEmpty(list1)) { + dataList.addAll(list1); + } + if (!CollectionUtils.isEmpty(list2)) { + dataList.addAll(list2); + } + if (!StringUtils.isEmpty(param)) { + // 过滤 + dataList = dataList.stream().filter(data -> data.getNickName().contains(param)).collect(Collectors.toList()); + } + return dataList; + } + + /** + * 格式化好友 + */ + private FriendVo07 formatFriendVo(ChatUser chatUser) { + Long userId = ShiroUtils.getUserId(); + Long friendId = chatUser.getUserId(); + FriendVo07 friendVo = BeanUtil.toBean(chatUser, FriendVo07.class); + // 校验是否是好友 + ChatFriend friend = getFriend(userId, friendId); + if (friend == null) { + return friendVo; + } + if (getFriend(friendId, userId) != null) { + friendVo.setIsFriend(YesOrNoEnum.YES); + } + return friendVo.setBlack(friend.getBlack()) + .setNickName(friend.getRemark()) + .setSource(friend.getSource()); + } + + @Override + public FriendVo07 getInfo(Long friendId) { + Long userId = ShiroUtils.getUserId(); + FriendVo07 talk = chatTalkService.queryFriendInfo(friendId); + if (talk != null) { + return talk; + } + ChatUser chatUser = chatUserService.getById(friendId); + if (chatUser == null) { + throw new BaseException("用户信息不存在"); + } + if (userId.equals(friendId)) { + FriendVo07 friendVo = BeanUtil.toBean(chatUser, FriendVo07.class); + return friendVo.setIsFriend(YesOrNoEnum.YES) + .setSource(ApplySourceEnum.SYS) + .setUserType(FriendTypeEnum.SELF); + } + return formatFriendVo(chatUser); + } + + /** + * 校验申请 + */ + private ChatApply verifyApply(Long applyId) { + ChatApply apply = chatApplyService.getById(applyId); + if (apply == null + || !ShiroUtils.getUserId().equals(apply.getToId()) + || !ApplyStatusEnum.NONE.equals(apply.getApplyStatus())) { + throw new BaseException("申请已过期,请刷新后重试"); + } + return apply; + } + + @Override + public ChatFriend getFriend(Long userId, Long friendId) { + String key = makeFriendKey(userId, friendId); + if (redisUtils.hasKey(key)) { + return JSONUtil.toBean(redisUtils.get(key), ChatFriend.class); + } + ChatFriend friend = queryOne(new ChatFriend().setFromId(userId).setToId(friendId)); + if (friend == null) { + return null; + } + redisUtils.set(key, JSONUtil.toJsonStr(friend), AppConstants.REDIS_FRIEND_TIME, TimeUnit.DAYS); + return friend; + } + + @Override + public List queryFriendId(Long userId) { + return chatFriendDao.queryFriendId(userId); + } + + /** + * 生成好友缓存 + */ + private String makeFriendKey(Long userId, Long friendId) { + return StrUtil.format(AppConstants.REDIS_FRIEND, userId, friendId); + } + + /** + * 删除好友缓存 + */ + private void delFriendCache(Long userId, Long friendId) { + redisUtils.delete(makeFriendKey(userId, friendId)); + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatGroupInfoServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatGroupInfoServiceImpl.java new file mode 100644 index 0000000..b5eb984 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatGroupInfoServiceImpl.java @@ -0,0 +1,114 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.platform.common.constant.AppConstants; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.exception.BaseException; +import com.platform.common.redis.RedisUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.dao.ChatGroupInfoDao; +import com.platform.modules.chat.domain.ChatGroupInfo; +import com.platform.modules.chat.service.ChatGroupInfoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + *

+ * 服务层实现 + * q3z3 + *

+ */ +@Service("chatGroupInfoService") +public class ChatGroupInfoServiceImpl extends BaseServiceImpl implements ChatGroupInfoService { + + @Resource + private ChatGroupInfoDao chatGroupInfoDao; + + @Autowired + private RedisUtils redisUtils; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatGroupInfoDao); + } + + @Override + public List queryList(ChatGroupInfo t) { + List dataList = chatGroupInfoDao.queryList(t); + return dataList; + } + + @Override + public ChatGroupInfo getGroupInfo(Long groupId, Long userId, YesOrNoEnum verify) { + String key = StrUtil.format(AppConstants.REDIS_GROUP_INFO, groupId, userId); + ChatGroupInfo info; + // 缓存存在 + if (redisUtils.hasKey(key)) { + info = JSONUtil.toBean(redisUtils.get(key), ChatGroupInfo.class); + } + // 缓存不存在 + else if ((info = this.queryOne(new ChatGroupInfo().setUserId(userId).setGroupId(groupId))) != null) { + redisUtils.set(key, JSONUtil.toJsonStr(info), AppConstants.REDIS_GROUP_TIME, TimeUnit.DAYS); + } + if (YesOrNoEnum.NO.equals(verify)) { + return info; + } + if (info == null) { + throw new BaseException("你不在当前群中"); + } + return info; + } + + @Override + public void delGroupInfoCache(Long groupId, List userList) { + userList.forEach(e -> { + redisUtils.delete(StrUtil.format(AppConstants.REDIS_GROUP_INFO, groupId, e)); + }); + } + + @Override + public Long countByGroup(Long groupId) { + return queryCount(new ChatGroupInfo().setGroupId(groupId)); + } + + @Override + public List queryUserList(Long groupId) { + // 查询所有成员 + List infoList = this.queryList(new ChatGroupInfo().setGroupId(groupId)); + return infoList.stream().map(ChatGroupInfo::getUserId).collect(Collectors.toList()); + } + + @Override + public List queryUserList(Long groupId, List userList) { + List dataList = this.queryList(new ChatGroupInfo().setGroupId(groupId)); + if (!CollectionUtils.isEmpty(userList)) { + dataList = dataList.stream().filter(data -> userList.contains(data.getUserId())).collect(Collectors.toList()); + } + return dataList; + } + + @Override + public Map queryUserMap(Long groupId) { + // 查询所有成员 + List dataList = this.queryList(new ChatGroupInfo().setGroupId(groupId)); + return dataList.stream().collect(Collectors.toMap(ChatGroupInfo::getUserId, a -> a, (k1, k2) -> k1)); + } + + @Override + public void delByGroup(Long groupId) { + chatGroupInfoDao.delete(new QueryWrapper<>(new ChatGroupInfo().setGroupId(groupId))); + // 删除群成员 + String redisKey = StrUtil.format(AppConstants.REDIS_GROUP_INFO, groupId, "*"); + redisUtils.delete(redisKey); + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatGroupServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatGroupServiceImpl.java new file mode 100644 index 0000000..2a6d82f --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatGroupServiceImpl.java @@ -0,0 +1,541 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.lang.PatternPool; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.github.pagehelper.PageHelper; +import com.platform.common.constant.AppConstants; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.exception.BaseException; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.dao.ChatGroupDao; +import com.platform.modules.chat.domain.*; +import com.platform.modules.chat.enums.ApplyStatusEnum; +import com.platform.modules.chat.enums.ApplyTypeEnum; +import com.platform.modules.chat.service.ChatApplyService; +import com.platform.modules.chat.service.ChatGroupInfoService; +import com.platform.modules.chat.service.ChatGroupService; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.chat.vo.GroupVo02; +import com.platform.modules.chat.vo.GroupVo03; +import com.platform.modules.chat.vo.GroupVo07; +import com.platform.modules.chat.vo.GroupVo08; +import com.platform.modules.push.enums.PushMsgEnum; +import com.platform.modules.push.service.ChatPushService; +import com.platform.modules.push.vo.PushParamVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 群组 服务层实现 + * q3z3 + *

+ */ +@Service("chatGroupService") +public class ChatGroupServiceImpl extends BaseServiceImpl implements ChatGroupService { + + @Resource + private ChatGroupDao chatGroupDao; + + @Resource + private ChatUserService chatUserService; + + @Resource + private ChatGroupInfoService groupInfoService; + + @Resource + private ChatApplyService chatApplyService; + + @Resource + private ChatPushService chatPushService; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatGroupDao); + } + + @Override + public List queryList(ChatGroup t) { + List dataList = chatGroupDao.queryList(t); + return dataList; + } + + @Transactional + @Override + public void createGroup(List list) { + Long userId = ShiroUtils.getUserId(); + // 传入用户 + List userList = verifyUserList(list); + // 当前登录人 + ChatUser master = chatUserService.getById(userId); + // 当前时间 + Date now = DateUtil.date(); + // 建群 + String portrait = "https://img.alicdn.com/imgextra/i3/87413133/O1CN01mHA9DJ1Z0xlORnKuW_!!87413133.png"; + ChatGroup group = new ChatGroup() + .setMaster(master.getUserId()) + .setName(StrUtil.format(AppConstants.GROUP_CREATE_NAME, master.getNickName(), RandomUtil.randomString(4))) + .setPortrait(JSONUtil.toJsonStr(new JSONArray().put(portrait))) + .setCreateTime(now); + this.add(group); + // 群明细 + List infoList = new ArrayList<>(); + for (ChatUser user : userList) { + infoList.add(new ChatGroupInfo(user.getUserId(), group.getId()).setKeepGroup(YesOrNoEnum.YES)); + } + infoList.add(new ChatGroupInfo(master.getUserId(), group.getId()).setKeepGroup(YesOrNoEnum.YES)); + groupInfoService.batchAdd(infoList); + String groupName = formatGroupName(group.getId(), group.getName()); + PushParamVo paramVo1 = new PushParamVo() + .setUserId(group.getId()) + .setNickName(groupName) + .setPortrait(group.getPortrait()) + .setContent(StrUtil.format(AppConstants.NOTICE_GROUP_CREATE_MEMBER, master.getNickName())); + // 通知组员 + chatPushService.pushGroupMsg(formatFrom(list, paramVo1), paramVo1, PushMsgEnum.ALERT); + // 通知群主 + List nickList = userList.stream().map(ChatUser::getNickName).collect(Collectors.toList()); + String content = StrUtil.format(AppConstants.NOTICE_GROUP_CREATE_MASTER, CollUtil.join(nickList, "、")); + PushParamVo paramVo2 = new PushParamVo() + .setUserId(group.getId()) + .setNickName(groupName) + .setPortrait(group.getPortrait()) + .setContent(content) + .setToId(userId); + chatPushService.pushGroupMsg(Arrays.asList(paramVo2), paramVo2, PushMsgEnum.ALERT); + } + + /** + * 计算群名 + */ + private String formatGroupName(Long groupId, String groupName) { + if (StringUtils.isEmpty(groupName)) { + groupName = getById(groupId).getName(); + } + Long count = groupInfoService.countByGroup(groupId); + return StrUtil.format("{}({})", groupName, count); + } + + @Override + public Dict getInfo(Long groupId) { + // 校验 + ChatGroup group = this.getById(groupId); + if (group == null) { + throw new BaseException("当前群不存在"); + } + Long userId = ShiroUtils.getUserId(); + ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + // 组装返回值 + Dict groupDict = Dict.create().parseBean(group) + .filter("name", "notice", "portrait") + .set("groupId", group.getId()); + Dict set = Dict.create().parseBean(info) + .filter("top", "disturb", "keepGroup"); + List userList = groupInfoService.queryUserList(groupId); + List userDict = chatUserService.getByIds(userList).stream().collect(ArrayList::new, (x, y) -> { + x.add(Dict.create().parseBean(y).filter("userId", "nickName", "portrait")); + }, ArrayList::addAll); + return Dict.create() + .set("user", userDict) + .set("group", groupDict) + .set("set", set) + .set("master", group.getMaster().equals(userId) ? YesOrNoEnum.YES : YesOrNoEnum.NO); + } + + @Transactional + @Override + public void invitationGroup(Long groupId, List list) { + // 查询用户列表 + List userList = verifyUserList(list); + // 当前用户 + Long userId = ShiroUtils.getUserId(); + // 验证群 + groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + // 集合 + Map infoMap = groupInfoService.queryUserMap(groupId); + // 群明细 + List infoList = new ArrayList<>(); + List newUserList = new ArrayList<>(); + for (ChatUser chatUser : userList) { + ChatGroupInfo groupInfo = infoMap.get(chatUser.getUserId()); + // 新增 + if (groupInfo == null) { + infoList.add(new ChatGroupInfo(chatUser.getUserId(), groupId)); + newUserList.add(chatUser); + } + } + // 批量信息 + groupInfoService.batchAdd(infoList); + // 通知 + ChatGroup group = getById(groupId); + List nickList = newUserList.stream().map(ChatUser::getNickName).collect(Collectors.toList()); + String content = StrUtil.format(AppConstants.NOTICE_GROUP_JOIN, CollUtil.join(nickList, "、")); + List pushParamList = queryPushParam(group, content); + chatPushService.pushGroupMsg(pushParamList, initGroupParam(group), PushMsgEnum.ALERT); + } + + /** + * 更新群主 + */ + @Transactional + protected void updMaster(ChatGroup group) { + Long groupId = group.getId(); + // 执行分页 + PageHelper.startPage(1, 1, "info_id"); + // 查询所有成员 + List userList = groupInfoService.queryUserList(groupId); + if (CollectionUtils.isEmpty(userList)) { + // 删除群 + this.deleteById(groupId); + return; + } + Long userId = userList.get(0); + // 更新群主 + this.updateById(new ChatGroup().setId(groupId).setMaster(userId)); + groupInfoService.updateById(new ChatGroupInfo().setInfoId(userId).setKeepGroup(YesOrNoEnum.YES)); + // 更新申请人 + ChatApply query = new ChatApply().setTargetId(groupId).setApplyType(ApplyTypeEnum.GROUP).setApplyStatus(ApplyStatusEnum.NONE); + chatApplyService.queryList(query).forEach(e -> { + chatApplyService.updateById(new ChatApply().setId(e.getId()).setToId(userId)); + }); + ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.NO); + String groupName = formatGroupName(group.getId(), group.getName()); + // 通知新群主 + PushParamVo paramVo = new PushParamVo() + .setUserId(group.getId()) + .setNickName(groupName) + .setPortrait(group.getPortrait()) + .setDisturb(groupInfo.getDisturb()) + .setTop(groupInfo.getTop()) + .setContent(AppConstants.NOTICE_GROUP_TRANSFER); + chatPushService.pushGroupMsg(paramVo.setToId(userId), initGroupParam(group), PushMsgEnum.ALERT); + } + + /** + * 验证人员信息 + */ + private List verifyUserList(List list) { + // 验证 + if (CollectionUtils.isEmpty(list)) { + throw new BaseException("好友列表不能为空"); + } + if (list.contains(ShiroUtils.getUserId())) { + throw new BaseException("好友列表不能包含自己"); + } + // 去重 + list = list.stream().distinct().collect(Collectors.toList()); + // 验证 + if (CollectionUtils.isEmpty(list)) { + throw new BaseException("好友列表不能为空"); + } + // 查询用户列表 + return chatUserService.getByIds(list); + } + + @Transactional + @Override + public void kickedGroup(Long groupId, List list) { + // 查询用户列表 + List userList = verifyUserList(list); + // 当前用户 + Long userId = ShiroUtils.getUserId(); + // 验证群 + ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + // 查询群 + ChatGroup group = getById(groupId); + if (!group.getMaster().equals(userId)) { + throw new BaseException("你不是群主,不能操作"); + } + // 删除成员 + groupInfoService.queryUserList(groupId, list).forEach(e -> { + groupInfoService.deleteById(e.getInfoId()); + }); + String groupName = formatGroupName(group.getId(), group.getName()); + // 群主 + List nickList = userList.stream().map(ChatUser::getNickName).collect(Collectors.toList()); + PushParamVo paramVo = new PushParamVo() + .setUserId(group.getId()) + .setNickName(groupName) + .setPortrait(group.getPortrait()) + .setDisturb(groupInfo.getDisturb()) + .setTop(groupInfo.getTop()) + .setContent(StrUtil.format(AppConstants.NOTICE_GROUP_KICKED_MASTER, CollUtil.join(nickList, "、"))); + chatPushService.pushGroupMsg(paramVo.setToId(group.getMaster()), paramVo, PushMsgEnum.ALERT); + // 组员 + List paramList = queryGroupPushFrom(groupId, list, AppConstants.NOTICE_GROUP_KICKED_MEMBER); + chatPushService.pushGroupMsg(paramList, paramVo, PushMsgEnum.ALERT); + // 删除缓存 + groupInfoService.delGroupInfoCache(groupId, list); + } + + @Override + public String getGroupQrCode(Long groupId) { + return AppConstants.QR_CODE_GROUP + groupId; + } + + @Override + public void editTop(Long groupId, YesOrNoEnum top) { + Long userId = ShiroUtils.getUserId(); + ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + groupInfoService.updateById(new ChatGroupInfo().setInfoId(info.getInfoId()).setTop(top)); + // 删除缓存 + groupInfoService.delGroupInfoCache(groupId, Arrays.asList(userId)); + } + + @Override + public void editDisturb(Long groupId, YesOrNoEnum disturb) { + Long userId = ShiroUtils.getUserId(); + ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + groupInfoService.updateById(new ChatGroupInfo().setInfoId(info.getInfoId()).setDisturb(disturb)); + // 删除缓存 + groupInfoService.delGroupInfoCache(groupId, Arrays.asList(userId)); + } + + @Override + public void editKeepGroup(Long groupId, YesOrNoEnum keepGroup) { + Long userId = ShiroUtils.getUserId(); + ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + groupInfoService.updateById(new ChatGroupInfo().setInfoId(info.getInfoId()).setKeepGroup(keepGroup)); + // 删除缓存 + groupInfoService.delGroupInfoCache(groupId, Arrays.asList(userId)); + } + + @Transactional + @Override + public void logoutGroup(Long groupId) { + Long userId = ShiroUtils.getUserId(); + ChatUser chatUser = chatUserService.getById(userId); + ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + ChatGroup group = getById(groupId); + // 删除数据 + groupInfoService.deleteById(groupInfo.getInfoId()); + // 删除缓存 + groupInfoService.delGroupInfoCache(groupId, Arrays.asList(userId)); + if (group.getMaster().equals(userId)) { + // 变更群主 + ThreadUtil.execAsync(() -> { + updMaster(group); + }); + } else { + String groupName = formatGroupName(groupId, group.getName()); + // 通知 + PushParamVo paramVo = new PushParamVo() + .setUserId(group.getId()) + .setPortrait(group.getPortrait()) + .setNickName(groupName) + .setDisturb(groupInfo.getDisturb()) + .setTop(groupInfo.getTop()) + .setContent(StrUtil.format(AppConstants.NOTICE_GROUP_LOGOUT, chatUser.getNickName())) + .setToId(group.getMaster()); + chatPushService.pushGroupMsg(paramVo, paramVo, PushMsgEnum.ALERT); + } + } + + @Transactional + @Override + public void removeGroup(Long groupId) { + Long userId = ShiroUtils.getUserId(); + ChatGroup group = getById(groupId); + if (group == null) { + throw new BaseException("当前群组不存在"); + } + if (!group.getMaster().equals(userId)) { + throw new BaseException("你不是群主,不能操作"); + } + List userList = queryPushParam(group, AppConstants.NOTICE_GROUP_DISSOLVE); + this.deleteById(groupId); + // 删除数据 + groupInfoService.delByGroup(groupId); + // 通知 + chatPushService.pushGroupMsg(userList, initGroupParam(group), PushMsgEnum.ALERT); + } + + private List queryPushParam(ChatGroup group, String content) { + List dataList = groupInfoService.queryList(new ChatGroupInfo().setGroupId(group.getId())); + String groupName = formatGroupName(group.getId(), group.getName()); + List arrayList = new ArrayList<>(); + dataList.forEach(e -> { + PushParamVo paramVo = new PushParamVo() + .setUserId(group.getId()) + .setNickName(groupName) + .setPortrait(group.getPortrait()) + .setToId(e.getUserId()) + .setDisturb(e.getDisturb()) + .setTop(e.getTop()) + .setContent(content); + arrayList.add(paramVo); + }); + return arrayList; + } + + @Override + public GroupVo07 scanCode(String param) { + // 校验前缀 + if (!StrUtil.startWith(param, AppConstants.QR_CODE_GROUP)) { + throw new BaseException("参数错误"); + } + Long groupId = Convert.toLong(ReUtil.get(PatternPool.NUMBERS, param, 0), null); + ChatGroup group = getById(groupId); + if (group == null) { + throw new BaseException("当前群组不存在"); + } + Long count = groupInfoService.countByGroup(groupId); + return BeanUtil.toBean(group, GroupVo07.class) + .setPortrait(JSONUtil.toList(group.getPortrait(), String.class)) + .setCount(count); + } + + @Override + public void joinGroup(Long groupId) { + Long userId = ShiroUtils.getUserId(); + ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.NO); + ChatGroup group = this.getById(groupId); + // 加群 + if (info == null) { + groupInfoService.add(new ChatGroupInfo(userId, groupId)); + // 全员 + // 查询当前登录f + ChatUser chatUser = chatUserService.getById(userId); + String content = StrUtil.format(AppConstants.NOTICE_GROUP_JOIN, chatUser.getNickName()); + List userList = queryPushParam(group, content); + // 通知 + chatPushService.pushGroupMsg(userList, initGroupParam(group), PushMsgEnum.ALERT); + } + } + + /** + * 组装群对象 + */ + private PushParamVo initGroupParam(ChatGroup group) { + return new PushParamVo() + .setUserId(group.getId()) + .setNickName(group.getName()) + .setPortrait(group.getPortrait()); + } + + @Override + public List groupList() { + // 结果 + List dataList = new ArrayList<>(); + // 查询明细 + List groupList = groupInfoService.queryList(new ChatGroupInfo().setUserId(ShiroUtils.getUserId())) + .stream().map(ChatGroupInfo::getGroupId).collect(Collectors.toList()); + // 集合判空 + if (CollectionUtils.isEmpty(groupList)) { + return dataList; + } + // 查询群组 + this.getByIds(groupList).forEach(e -> { + GroupVo08 groupVo = BeanUtil.toBean(e, GroupVo08.class) + .setPortrait(JSONUtil.toList(e.getPortrait(), String.class)) + .setGroupId(e.getId()); + dataList.add(groupVo); + }); + return dataList; + } + + @Override + public List queryFriendPushFrom(ChatMsg chatMsg) { + Long userId = ShiroUtils.getUserId(); + List paramList = chatGroupDao.queryFriendPushFrom(chatMsg.getToId(), userId); + ChatUser fromUser = chatUserService.getById(userId); + paramList.forEach(e -> { + e.setUserId(fromUser.getUserId()); + if (StringUtils.isEmpty(e.getNickName())) { + e.setNickName(fromUser.getNickName()); + } + e.setPortrait(fromUser.getPortrait()); + e.setContent(chatMsg.getContent()); + e.setMsgId(chatMsg.getId()); + }); + return paramList; + } + + @Override + public List queryGroupPushFrom(Long groupId, List list, String content) { + // 查询群 + ChatGroup group = getById(groupId); + String groupName = formatGroupName(groupId, group.getName()); + // 成员 + List groupInfoList = groupInfoService.queryUserList(groupId, list); + List paramList = new ArrayList<>(); + groupInfoList.forEach(e -> { + PushParamVo paramVo = new PushParamVo() + .setUserId(group.getId()) + .setNickName(groupName) + .setPortrait(group.getPortrait()) + .setToId(e.getUserId()) + .setDisturb(e.getDisturb()) + .setTop(e.getTop()) + .setContent(AppConstants.NOTICE_GROUP_KICKED_MEMBER); + paramList.add(paramVo); + }); + return paramList; + } + + @Override + public void editGroupName(GroupVo02 groupVo) { + Long userId = ShiroUtils.getUserId(); + Long groupId = groupVo.getGroupId(); + ChatUser chatUser = chatUserService.getById(userId); + groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + updateById(new ChatGroup().setId(groupId).setName(groupVo.getName())); + String groupName = formatGroupName(groupId, groupVo.getName()); + PushParamVo paramVo = new PushParamVo() + .setUserId(groupId) + .setNickName(groupName) + .setPortrait(getById(groupId).getPortrait()) + .setContent(StrUtil.format(AppConstants.NOTICE_GROUP_EDIT, chatUser.getNickName(), groupVo.getName())); + // 通知组员 + List userList = groupInfoService.queryUserList(groupId); + chatPushService.pushGroupMsg(formatFrom(userList, paramVo), paramVo, PushMsgEnum.ALERT); + } + + @Override + public void editGroupNotice(GroupVo03 groupVo) { + Long userId = ShiroUtils.getUserId(); + Long groupId = groupVo.getGroupId(); + groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES); + ChatGroup group = new ChatGroup() + .setId(groupVo.getGroupId()) + .setNotice(groupVo.getNotice()); + updateById(group); + String groupName = formatGroupName(groupId, null); + ChatUser chatUser = chatUserService.getById(userId); + PushParamVo paramVo = new PushParamVo() + .setUserId(groupId) + .setNickName(groupName) + .setPortrait(getById(groupId).getPortrait()) + .setContent(StrUtil.format(AppConstants.NOTICE_GROUP_NOTICE, chatUser.getNickName(), group.getNotice())); + // 通知组员 + List userList = groupInfoService.queryUserList(groupId); + chatPushService.pushGroupMsg(formatFrom(userList, paramVo), paramVo, PushMsgEnum.ALERT); + } + + private List formatFrom(List userList, PushParamVo paramVo) { + List paramList = new ArrayList<>(); + userList.forEach(e -> { + paramList.add(BeanUtil.toBean(paramVo, PushParamVo.class).setToId(e)); + }); + return paramList; + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatMsgServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatMsgServiceImpl.java new file mode 100644 index 0000000..c3e2549 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatMsgServiceImpl.java @@ -0,0 +1,253 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.platform.common.constant.AppConstants; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.utils.TimerUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.dao.ChatMsgDao; +import com.platform.modules.chat.domain.*; +import com.platform.modules.chat.enums.FriendTypeEnum; +import com.platform.modules.chat.enums.MsgStatusEnum; +import com.platform.modules.chat.service.*; +import com.platform.modules.chat.vo.ChatVo01; +import com.platform.modules.chat.vo.ChatVo02; +import com.platform.modules.chat.vo.ChatVo03; +import com.platform.modules.chat.vo.ChatVo04; +import com.platform.modules.push.enums.PushMsgEnum; +import com.platform.modules.push.enums.PushTalkEnum; +import com.platform.modules.push.service.ChatPushService; +import com.platform.modules.push.vo.PushParamVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + *

+ * 聊天消息 服务层实现 + * q3z3 + *

+ */ +@Service("chatMsgService") +public class ChatMsgServiceImpl extends BaseServiceImpl implements ChatMsgService { + + @Resource + private ChatMsgDao chatMsgDao; + + @Resource + private ChatFriendService friendService; + + @Resource + private ChatGroupService groupService; + + @Resource + private ChatGroupInfoService groupInfoService; + + @Resource + private ChatPushService chatPushService; + + @Resource + private ChatUserService chatUserService; + + @Resource + private ChatTalkService chatTalkService; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatMsgDao); + } + + @Override + public List queryList(ChatMsg t) { + List dataList = chatMsgDao.queryList(t); + return dataList; + } + + @Transactional + @Override + public ChatVo03 sendFriendMsg(ChatVo01 chatVo) { + Long userId = ShiroUtils.getUserId(); + Long friendId = chatVo.getUserId(); + // 系统好友 + if (friendId.equals(10002L) || friendId.equals(10003L)) { + return sys(chatVo); + } + // 自己给自己发消息 + if (userId.equals(friendId)) { + return self(chatVo); + } + // 发送给好友的消息 + return friend(chatVo); + } + + /** + * 保存消息 + */ + private ChatMsg saveMsg(ChatVo01 chatVo) { + Long userId = ShiroUtils.getUserId(); + ChatMsg chatMsg = new ChatMsg() + .setFromId(userId) + .setToId(chatVo.getUserId()) + .setMsgType(chatVo.getMsgType()) + .setTalkType(PushTalkEnum.SINGLE) + .setContent(chatVo.getContent()) + .setCreateTime(DateUtil.date()); + this.add(chatMsg); + return chatMsg; + } + + /** + * 系统消息 + */ + private ChatVo03 sys(ChatVo01 chatVo) { + // 保存消息 + ChatMsg chatMsg = this.saveMsg(chatVo); + Long userId = ShiroUtils.getUserId(); + Long friendId = chatVo.getUserId(); + String content = chatVo.getContent(); + // 异步执行 + TimerUtils.instance().addTask((timeout) -> { + // 发送聊天 + PushParamVo paramVo = chatTalkService.talk(friendId, content); + if (paramVo == null) { + return; + } + // 推送 + chatPushService.pushMsg(paramVo.setToId(userId).setMsgId(IdWorker.getId()), PushMsgEnum.TEXT); + }, 2, TimeUnit.SECONDS); + // 返回结果 + return doResult(MsgStatusEnum.NORMAL) + .setMsgId(chatMsg.getId()); + } + + /** + * 自己消息 + */ + private ChatVo03 self(ChatVo01 chatVo) { + // 保存消息 + ChatMsg chatMsg = this.saveMsg(chatVo); + Long userId = ShiroUtils.getUserId(); + Long friendId = chatVo.getUserId(); + String content = chatVo.getContent(); + PushMsgEnum msgType = chatVo.getMsgType(); + // 组装推送 + PushParamVo paramVo = ChatUser.initParam(chatUserService.getById(userId)) + .setUserType(FriendTypeEnum.SELF) + .setContent(content) + .setToId(friendId) + .setMsgId(IdWorker.getId()); + // 推送 + // 异步执行 + TimerUtils.instance().addTask((timeout) -> { + // 推送 + chatPushService.pushMsg(paramVo, msgType); + }, 2, TimeUnit.SECONDS); + // 返回结果 + return doResult(MsgStatusEnum.NORMAL) + .setMsgId(chatMsg.getId()); + } + + /** + * 好友消息 + */ + private ChatVo03 friend(ChatVo01 chatVo) { + Long userId = ShiroUtils.getUserId(); + Long friendId = chatVo.getUserId(); + String content = chatVo.getContent(); + PushMsgEnum msgType = chatVo.getMsgType(); + // 校验好友 + ChatFriend friend1 = friendService.getFriend(userId, friendId); + if (friend1 == null) { + return doResult(MsgStatusEnum.FRIEND_TO); + } + // 校验好友 + ChatFriend friend2 = friendService.getFriend(friendId, userId); + if (friend2 == null) { + return doResult(MsgStatusEnum.FRIEND_FROM); + } + // 校验黑名单 + if (YesOrNoEnum.YES.equals(friend2.getBlack())) { + return doResult(MsgStatusEnum.FRIEND_BLACK); + } + // 校验好友 + ChatUser toUser = chatUserService.getById(friendId); + if (toUser == null) { + return doResult(MsgStatusEnum.FRIEND_DELETED); + } + // 保存消息 + ChatMsg chatMsg = this.saveMsg(chatVo); + // 组装推送 + PushParamVo paramVo = ChatUser.initParam(chatUserService.getById(userId)) + .setNickName(friend2.getRemark()) + .setTop(friend2.getTop()) + .setContent(content) + .setToId(friendId) + .setMsgId(chatMsg.getId()); + ChatVo04 chatVo04 = null; + if (PushMsgEnum.TRTC_VOICE_START.equals(msgType) + || PushMsgEnum.TRTC_VIDEO_START.equals(msgType)) { + chatVo04 = new ChatVo04() + .setUserId(friendId) + .setTrtcId(AppConstants.REDIS_TRTC_USER + friendId) + .setPortrait(toUser.getPortrait()) + .setNickName(friend1.getRemark()); + } + // 推送 + chatPushService.pushMsg(paramVo, msgType); + return doResult(MsgStatusEnum.NORMAL) + .setMsgId(chatMsg.getId()) + .setUserInfo(chatVo04); + } + + + /** + * 返回发送结果 + */ + private ChatVo03 doResult(MsgStatusEnum status) { + return new ChatVo03().setStatus(status); + } + + @Override + public ChatVo03 sendGroupMsg(ChatVo02 chatVo) { + String content = chatVo.getContent(); + Long fromId = ShiroUtils.getUserId(); + Long groupId = chatVo.getGroupId(); + // 查询群组 + ChatGroup group = groupService.getById(groupId); + if (group == null) { + return doResult(MsgStatusEnum.GROUP_NOT_EXIST); + } + // 查询群明细 + ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, fromId, YesOrNoEnum.NO); + if (groupInfo == null) { + return doResult(MsgStatusEnum.GROUP_INFO_NOT_EXIST); + } + // 保存数据 + ChatMsg chatMsg = new ChatMsg() + .setFromId(fromId) + .setToId(groupId) + .setMsgType(chatVo.getMsgType()) + .setTalkType(PushTalkEnum.GROUP) + .setContent(content) + .setCreateTime(DateUtil.date()); + this.add(chatMsg); + // 查询群列表 + List userList = groupService.queryFriendPushFrom(chatMsg); + // 群信息 + PushParamVo groupUser = new PushParamVo() + .setUserId(group.getId()) + .setNickName(group.getName()) + .setPortrait(group.getPortrait()); + // 推送 + chatPushService.pushGroupMsg(userList, groupUser, chatVo.getMsgType()); + return doResult(MsgStatusEnum.NORMAL) + .setMsgId(chatMsg.getId()); + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatTalkServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatTalkServiceImpl.java new file mode 100644 index 0000000..b089358 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatTalkServiceImpl.java @@ -0,0 +1,156 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Console; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.shiro.ShiroUtils; +import com.platform.modules.chat.config.TencentConfig; +import com.platform.modules.chat.enums.ApplySourceEnum; +import com.platform.modules.chat.enums.FriendTypeEnum; +import com.platform.modules.chat.service.ChatTalkService; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.chat.service.ChatWeatherService; +import com.platform.modules.chat.utils.TencentUtils; +import com.platform.modules.chat.vo.FriendVo06; +import com.platform.modules.chat.vo.FriendVo07; +import com.platform.modules.push.vo.PushParamVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 系统聊天 服务层实现 + * q3z3 + *

+ */ +@Service("chatTalkService") +public class ChatTalkServiceImpl implements ChatTalkService { + + @Autowired + private TencentConfig tencentConfig; + + @Resource + private ChatWeatherService weatherService; + + @Resource + private ChatUserService chatUserService; + + /** + * 好友列表 + */ + private static List friendList() { + // 天气机器人 + Long weatherId = 10002L; + FriendTypeEnum weatherType = FriendTypeEnum.WEATHER; + FriendVo06 weather = new FriendVo06() + .setUserId(weatherId) + .setChatNo(NumberUtil.toStr(weatherId)) + .setNickName(weatherType.getInfo()) + .setPortrait("http://q3z3-im.oss-cn-beijing.aliyuncs.com/0295f6edc9de43748c0d2f25b5057893.png") + .setUserType(weatherType); + // 翻译机器人 + Long translationId = 10003L; + FriendTypeEnum translationType = FriendTypeEnum.TRANSLATION; + FriendVo06 translation = new FriendVo06() + .setUserId(translationId) + .setChatNo(NumberUtil.toStr(translationId)) + .setNickName(translationType.getInfo()) + .setPortrait("http://q3z3-im.oss-cn-beijing.aliyuncs.com/18ac0b6aa3d147e6b1a65c2eb838707e.png") + .setUserType(translationType); + return CollUtil.newArrayList(weather, translation); + } + + @Override + public List queryFriendList() { + Long userId = ShiroUtils.getUserId(); + List userList = friendList(); + userList.add(BeanUtil.toBean(chatUserService.findById(userId), FriendVo06.class).setUserType(FriendTypeEnum.SELF)); + return userList; + } + + @Override + public FriendVo07 queryFriendInfo(Long userId) { + Map dataList = friendList().stream().collect(Collectors.toMap(FriendVo06::getUserId, a -> a, (k1, k2) -> k1)); + FriendVo06 friendVo = dataList.get(userId); + if (friendVo == null) { + return null; + } + return BeanUtil.toBean(friendVo, FriendVo07.class) + .setIsFriend(YesOrNoEnum.YES) + .setSource(ApplySourceEnum.SYS); + } + + @Override + public PushParamVo talk(Long userId, String content) { + Map dataList = friendList().stream().collect(Collectors.toMap(FriendVo06::getUserId, a -> a, (k1, k2) -> k1)); + FriendVo06 friendVo = dataList.get(userId); + if (friendVo == null) { + return null; + } + PushParamVo paramVo = new PushParamVo() + .setUserId(friendVo.getUserId()) + .setPortrait(friendVo.getPortrait()) + .setNickName(friendVo.getNickName()) + .setContent(content) + .setUserType(friendVo.getUserType()); + switch (friendVo.getUserType()) { + case WEATHER: + content = weather(content); + break; + case TRANSLATION: + content = TencentUtils.translation(tencentConfig, content); + break; + } + return paramVo.setContent(content); + } + + /** + * 天气预报 + */ + private String weather(String content) { + List dataList = weatherService.queryByCityName(content); + if (CollectionUtils.isEmpty(dataList)) { + return "暂未找到结果,格式:北京市"; + } + StringBuilder builder = new StringBuilder(); + dataList.forEach(e -> { + builder.append("城市:"); + builder.append(e.getStr("province")); + builder.append(e.getStr("city")); + builder.append("\n"); + builder.append("天气:"); + builder.append(e.getStr("weather")); + builder.append("\n"); + builder.append("温度:"); + builder.append(e.getStr("temperature")); + builder.append("℃"); + builder.append("\n"); + builder.append("风力:"); + builder.append(e.getStr("windpower")); + builder.append("级"); + builder.append("\n"); + builder.append("湿度:"); + builder.append(e.getStr("temperature")); + builder.append("RH"); + builder.append("\n"); + builder.append("\n"); + }); + return StrUtil.removeSuffix(builder.toString(), "\n\n"); + } + + public static void main(String[] args) { + Console.log(IdUtil.simpleUUID()); + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatUserServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatUserServiceImpl.java new file mode 100644 index 0000000..f8d3e98 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatUserServiceImpl.java @@ -0,0 +1,245 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.DesensitizedUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.RandomUtil; +import com.platform.common.constant.AppConstants; +import com.platform.common.constant.HeadConstant; +import com.platform.common.enums.GenderEnum; +import com.platform.common.exception.BaseException; +import com.platform.common.exception.LoginException; +import com.platform.common.redis.GeoHashUtils; +import com.platform.common.redis.RedisUtils; +import com.platform.common.shiro.Md5Utils; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.utils.ServletUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.auth.service.TokenService; +import com.platform.modules.auth.vo.AuthVo01; +import com.platform.modules.chat.dao.ChatUserDao; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.chat.vo.MyVo09; +import com.platform.modules.push.service.ChatPushService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.List; + +/** + *

+ * 用户表 服务层实现 + * q3z3 + *

+ */ +@Slf4j +@Service("chatUserService") +public class ChatUserServiceImpl extends BaseServiceImpl implements ChatUserService { + + @Resource + private ChatUserDao chatUserDao; + + @Resource + private TokenService tokenService; + + @Resource + private ChatPushService chatPushService; + + @Autowired + private RedisUtils redisUtils; + + @Autowired + private GeoHashUtils geoHashUtils; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatUserDao); + } + + @Override + public List queryList(ChatUser t) { + List dataList = chatUserDao.queryList(t); + return dataList; + } + + @Override + public void register(AuthVo01 authVo) { + String phone = authVo.getPhone(); + String password = authVo.getPassword(); + String nickName = authVo.getNickName(); + String msg = "此手机号码已注册过,请勿重复注册"; + // 验证手机号是否注册过 + if (this.queryCount(new ChatUser().setPhone(phone)) > 0) { + throw new BaseException(msg); + } + String salt = RandomUtil.randomString(4); + String chatNo = IdUtil.simpleUUID(); + ChatUser cu = new ChatUser() + .setNickName(nickName) + .setChatNo(chatNo) + .setGender(GenderEnum.MALE) + .setPortrait(AppConstants.DEFAULT_PORTRAIT) + .setSalt(salt) + .setPhone(phone) + .setPassword(Md5Utils.credentials(password, salt)) + .setCreateTime(DateUtil.date()); + try { + this.add(cu); + } catch (org.springframework.dao.DuplicateKeyException e) { + throw new BaseException(msg); + } + } + + @Override + public ChatUser queryByPhone(String phone) { + return this.queryOne(new ChatUser().setPhone(phone)); + } + + @Override + public void resetPass(Long userId, String password) { + String salt = RandomUtil.randomString(4); + ChatUser chatUser = new ChatUser() + .setUserId(userId) + .setSalt(salt) + .setPassword(Md5Utils.credentials(password, salt)); + this.updateById(chatUser); + } + + @Override + public void editPass(String password, String pwd) { + // 当前用户 + ChatUser cu = getById(ShiroUtils.getUserId()); + if (!Md5Utils.credentials(password, cu.getSalt()).equalsIgnoreCase(cu.getPassword())) { + throw new BaseException("旧密码不正确"); + } + String salt = RandomUtil.randomString(4); + ChatUser chatUser = new ChatUser() + .setUserId(cu.getUserId()) + .setSalt(salt) + .setPassword(Md5Utils.credentials(pwd, salt)); + this.updateById(chatUser); + } + + @Override + public void editChatNo(String chatNo) { + Long userId = ShiroUtils.getUserId(); + String errMsg = "微聊号已被占用,请重新输入"; + // 校验 + ChatUser cu = this.queryOne(new ChatUser().setChatNo(chatNo)); + if (cu != null && !userId.equals(cu.getUserId())) { + throw new BaseException(errMsg); + } + try { + // 更新 + this.updateById(new ChatUser().setUserId(userId).setChatNo(chatNo)); + } catch (org.springframework.dao.DuplicateKeyException e) { + throw new BaseException(errMsg); + } + + } + + @Override + public MyVo09 getInfo() { + // 当前用户 + ChatUser cu = findById(ShiroUtils.getUserId()); + return BeanUtil.toBean(cu, MyVo09.class) + .setPhone(DesensitizedUtil.mobilePhone(cu.getPhone())); + } + + @Override + public String getQrCode() { + Long userId = ShiroUtils.getUserId(); + return AppConstants.QR_CODE_USER + userId; + } + + @Transactional + @Override + public void deleted() { + // 移除缓存 + removeCache(); + // 更新用户 + ChatUser cu = new ChatUser() + .setUserId(ShiroUtils.getUserId()) + .setDeletedTime(DateUtil.date()); + this.updateById(cu); + } + + @Transactional + @Override + public Dict doLogin(AuthenticationToken authenticationToken) { + String msg = null; + try { + ShiroUtils.getSubject().login(authenticationToken); + } catch (LoginException e) { + msg = e.getMessage(); + } catch (AuthenticationException e) { + msg = "手机号或密码不正确"; + } catch (Exception e) { + msg = "未知异常"; + log.error(msg, e); + } + if (!StringUtils.isEmpty(msg)) { + throw new BaseException(msg); + } + Long userId = ShiroUtils.getUserId(); + tokenService.deleteToken(this.getById(userId).getToken()); + // 生成新TOKEN + String token = tokenService.generateToken(); + String version = ServletUtils.getRequest().getHeader(HeadConstant.VERSION); + ChatUser chatUser = new ChatUser() + .setUserId(userId) + .setToken(token) + .setVersion(version); + // 更新token + this.updateById(chatUser); + return Dict.create().set("token", token); + } + + @Override + public void logout() { + try { + // 移除缓存 + this.removeCache(); + // 执行退出 + ShiroUtils.getSubject().logout(); + log.info("退出成功。。。。"); + } catch (Exception ex) { + log.error("退出异常", ex); + } + } + + @Override + public void refresh() { + Long userId = ShiroUtils.getUserId(); + // 拉取离线消息 + chatPushService.pullMsg(userId); + } + + /** + * 移除缓存 + */ + private void removeCache() { + Long userId = ShiroUtils.getUserId(); + ChatUser chatUser = this.getById(userId); + if (chatUser != null) { + // 清理token + tokenService.deleteToken(chatUser.getToken()); + } + String userStr = NumberUtil.toStr(userId); + // 附近的人 + geoHashUtils.remove(AppConstants.REDIS_NEAR, userStr); + // 摇一摇 + redisUtils.lRemove(AppConstants.REDIS_SHAKE, 0, userStr); + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatVersionServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatVersionServiceImpl.java new file mode 100644 index 0000000..b188f65 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatVersionServiceImpl.java @@ -0,0 +1,72 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.version.VersionUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.dao.ChatVersionDao; +import com.platform.modules.chat.domain.ChatVersion; +import com.platform.modules.chat.enums.VersionTypeEnum; +import com.platform.modules.chat.service.ChatVersionService; +import com.platform.modules.chat.vo.VersionVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + *

+ * 版本 服务层实现 + * q3z3 + *

+ */ +@Service("chatVersionService") +public class ChatVersionServiceImpl extends BaseServiceImpl implements ChatVersionService { + + @Resource + private ChatVersionDao chatVersionDao; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatVersionDao); + } + + @Override + public List queryList(ChatVersion t) { + List dataList = chatVersionDao.queryList(t); + return dataList; + } + + @Override + public String getAgreement() { + ChatVersion obj = this.findById(VersionTypeEnum.AGREEMENT.getCode()); + return obj.getUrl(); + } + + @Value("${platform.version}") + private String version; + + @Override + public VersionVo getVersion(String version, String device) { + VersionTypeEnum versionType = initDevice(device); + ChatVersion chatVersion = this.findById(versionType.getCode()); + YesOrNoEnum upgrade = VersionUtils.compareTo(version, chatVersion.getVersion()) < 0 ? YesOrNoEnum.YES : YesOrNoEnum.NO; + YesOrNoEnum forceUpgrade = VersionUtils.compareTo(version, this.version) < 0 ? YesOrNoEnum.YES : YesOrNoEnum.NO; + return BeanUtil.toBean(chatVersion, VersionVo.class) + .setUpgrade(upgrade) + .setForceUpgrade(forceUpgrade); + } + + /** + * 计算版本 + */ + private VersionTypeEnum initDevice(String device) { + if (VersionTypeEnum.ANDROID.getName().equalsIgnoreCase(device)) { + return VersionTypeEnum.ANDROID; + } + return VersionTypeEnum.IOS; + } + +} diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatWeatherServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatWeatherServiceImpl.java new file mode 100644 index 0000000..30d8aa6 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/service/impl/ChatWeatherServiceImpl.java @@ -0,0 +1,67 @@ +package com.platform.modules.chat.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.platform.common.constant.AppConstants; +import com.platform.common.exception.BaseException; +import com.platform.common.redis.RedisUtils; +import com.platform.modules.chat.config.AmapConfig; +import com.platform.modules.chat.service.ChatWeatherService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + *

+ * 天气预报-服务层实现 + * q3z3 + *

+ */ +@Service("chatWeatherService") +public class ChatWeatherServiceImpl implements ChatWeatherService { + + // 文档地址 https://lbs.amap.com/api/webservice/guide/api/weatherinfo + + /** + * 接口地址 + */ + private final static String URL = "https://restapi.amap.com/v3/weather/weatherInfo?city=CITY&&key=KEY&extensions=EXT"; + private final static String EXT_BASE = "base"; + + @Autowired + private AmapConfig amapConfig; + + @Autowired + private RedisUtils redisUtils; + + private JSONArray doQuery(String city, String extensions) { + String key = StrUtil.format(AppConstants.REDIS_MP_WEATHER, city, extensions); + if (redisUtils.hasKey(key)) { + return JSONUtil.parseArray(redisUtils.get(key)); + } + String url = URL.replace("CITY", city).replace("KEY", amapConfig.getKey()).replace("EXT", extensions); + String result = HttpUtil.get(url); + JSONObject jsonObject = JSONUtil.parseObj(result); + if (1 != jsonObject.getInt("status")) { + throw new BaseException("天气接口异常,请稍后再试"); + } + JSONArray jsonArray = jsonObject.getJSONArray(EXT_BASE.equals(extensions) ? "lives" : "forecasts"); + if ("[[]]".equals(jsonArray.toString())) { + return new JSONArray(); + } + redisUtils.set(key, JSONUtil.toJsonStr(jsonArray), AppConstants.REDIS_MP_WEATHER_TIME, TimeUnit.MINUTES); + return jsonArray; + } + + @Override + public List queryByCityName(String cityName) { + JSONArray jsonArray = doQuery(cityName, EXT_BASE); + return jsonArray.toList(JSONObject.class); + } + +} diff --git a/src/main/java/com/platform/modules/chat/utils/TencentUtils.java b/src/main/java/com/platform/modules/chat/utils/TencentUtils.java new file mode 100644 index 0000000..d88d2e3 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/utils/TencentUtils.java @@ -0,0 +1,89 @@ +package com.platform.modules.chat.utils; + +import cn.hutool.core.util.ReUtil; +import com.platform.common.exception.BaseException; +import com.platform.modules.chat.config.TencentConfig; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.tmt.v20180321.TmtClient; +import com.tencentcloudapi.tmt.v20180321.models.SpeechTranslateRequest; +import com.tencentcloudapi.tmt.v20180321.models.SpeechTranslateResponse; +import com.tencentcloudapi.tmt.v20180321.models.TextTranslateRequest; +import com.tencentcloudapi.tmt.v20180321.models.TextTranslateResponse; + +/** + * 腾讯工具类 + */ +public class TencentUtils { + + /** + * 腾讯翻译 + */ + public static String translation(TencentConfig tencentConfig, String content) { + String target = "zh"; + String source = "auto"; + if (ReUtil.contains("[\\u4e00-\\u9fa5]", content)) { + target = "en"; + source = "zh"; + } + Credential cred = new Credential(tencentConfig.getAppKey(), tencentConfig.getAppSecret()); + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint("tmt.tencentcloudapi.com"); + // 实例化一个client选项,可选的,没有特殊需求可以跳过 + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + // 实例化要请求产品的client对象,clientProfile是可选的 + TmtClient client = new TmtClient(cred, "ap-beijing", clientProfile); + // 实例化一个请求对象,每个接口都会对应一个request对象 + TextTranslateRequest req = new TextTranslateRequest(); + req.setSourceText(content); + req.setSource(source); + req.setTarget(target); + req.setProjectId(0L); + TextTranslateResponse resp; + try { + resp = client.TextTranslate(req); + } catch (TencentCloudSDKException e) { + throw new BaseException("翻译机器人接口调用异常,请稍后再试"); + } + StringBuilder builder = new StringBuilder(); + builder.append("翻译结果:"); + builder.append("\n"); + builder.append("原文:"); + builder.append(content); + builder.append("\n"); + builder.append("译文:"); + builder.append(resp.getTargetText()); + return builder.toString(); + } + + /** + * 语音识别 + */ + public static String audio2Text(TencentConfig tencentConfig, String content) { + try { + Credential cred = new Credential(tencentConfig.getAppKey(), tencentConfig.getAppSecret()); + ClientProfile clientProfile = new ClientProfile(); + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint("tmt.tencentcloudapi.com"); + clientProfile.setHttpProfile(httpProfile); + // 实例化要请求产品的client对象,clientProfile是可选的 + TmtClient client = new TmtClient(cred, "ap-beijing", clientProfile); + // 实例化一个请求对象,每个接口都会对应一个request对象 + SpeechTranslateRequest req = new SpeechTranslateRequest(); + req.setSessionUuid("1"); + req.setSource("zh"); + req.setTarget("zh"); + req.setAudioFormat(83886080L); + req.setSeq(0L); + req.setIsEnd(1L); + req.setData(content); + SpeechTranslateResponse resp = client.SpeechTranslate(req); + return resp.getSourceText(); + } catch (Exception e) { + throw new BaseException("语音识别接口调用异常,请稍后再试"); + } + } +} diff --git a/src/main/java/com/platform/modules/chat/vo/ApplyVo01.java b/src/main/java/com/platform/modules/chat/vo/ApplyVo01.java new file mode 100644 index 0000000..15459b9 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/ApplyVo01.java @@ -0,0 +1,13 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class ApplyVo01 { + + @NotNull(message = "申请id不能为空") + private Long applyId; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/ApplyVo02.java b/src/main/java/com/platform/modules/chat/vo/ApplyVo02.java new file mode 100644 index 0000000..f6db0c2 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/ApplyVo02.java @@ -0,0 +1,59 @@ +package com.platform.modules.chat.vo; + +import com.platform.modules.chat.enums.ApplySourceEnum; +import com.platform.modules.chat.enums.ApplyStatusEnum; +import com.platform.modules.chat.enums.ApplyTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@Accessors(chain = true) // 链式调用 +public class ApplyVo02 { + + /** + * 主键 + */ + private Long applyId; + /** + * 申请状态0无1同意2拒绝 + */ + private ApplyStatusEnum applyStatus; + /** + * 申请来源 + */ + private ApplySourceEnum applySource; + /** + * 申请类型1好友2群组 + */ + private ApplyTypeEnum applyType; + /** + * 理由 + */ + private String reason; + /** + * 申请时间 + */ + private Date createTime; + /** + * 用户id + */ + private Long userId; + /** + * 用户头像 + */ + private String portrait; + /** + * 用户昵称 + */ + private String nickName; + + public String getApplySourceLabel() { + if (applySource == null) { + return null; + } + return applySource.getInfo(); + } + +} diff --git a/src/main/java/com/platform/modules/chat/vo/ApplyVo03.java b/src/main/java/com/platform/modules/chat/vo/ApplyVo03.java new file mode 100644 index 0000000..b6d21fd --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/ApplyVo03.java @@ -0,0 +1,82 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.GenderEnum; +import com.platform.modules.chat.enums.ApplySourceEnum; +import com.platform.modules.chat.enums.ApplyStatusEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class ApplyVo03 { + + /** + * 用户id + */ + private Long userId; + /** + * 用户头像 + */ + private String portrait; + /** + * 用户昵称 + */ + private String nickName; + /** + * 介绍 + */ + private String intro; + /** + * 性别1男0女 + */ + private GenderEnum gender; + /** + * 微聊号 + */ + private String chatNo; + /** + * 省份 + */ + private String provinces; + /** + * 城市 + */ + private String city; + /** + * 主键 + */ + private Long applyId; + /** + * 理由 + */ + private String reason; + /** + * 申请来源 + */ + private ApplySourceEnum applySource; + /** + * 申请状态0无1同意2拒绝 + */ + private ApplyStatusEnum applyStatus; + + public String getApplySourceLabel() { + if (applySource == null) { + return null; + } + return applySource.getInfo(); + } + + public String getGenderLabel() { + if (gender == null) { + return null; + } + return gender.getInfo(); + } + + public String getApplyStatusLabel() { + if (applyStatus == null) { + return null; + } + return applyStatus.getInfo(); + } +} diff --git a/src/main/java/com/platform/modules/chat/vo/ChatVo01.java b/src/main/java/com/platform/modules/chat/vo/ChatVo01.java new file mode 100644 index 0000000..55288a9 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/ChatVo01.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.vo; + +import com.platform.modules.push.enums.PushMsgEnum; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class ChatVo01 { + + @NotNull(message = "用户不能为空") + private Long userId; + + @NotNull(message = "消息类型不能为空") + private PushMsgEnum msgType; + + @NotBlank(message = "消息内容不能为空") + @Size(max = 20000, message = "消息内容长度不能大于20000") + private String content; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/ChatVo02.java b/src/main/java/com/platform/modules/chat/vo/ChatVo02.java new file mode 100644 index 0000000..235a15d --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/ChatVo02.java @@ -0,0 +1,22 @@ +package com.platform.modules.chat.vo; + +import com.platform.modules.push.enums.PushMsgEnum; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class ChatVo02 { + + @NotNull(message = "群组不能为空") + private Long groupId; + + @NotNull(message = "消息类型不能为空") + private PushMsgEnum msgType; + + @NotBlank(message = "消息内容不能为空") + @Size(max = 20000, message = "消息内容长度不能大于20000") + private String content; +} diff --git a/src/main/java/com/platform/modules/chat/vo/ChatVo03.java b/src/main/java/com/platform/modules/chat/vo/ChatVo03.java new file mode 100644 index 0000000..e927431 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/ChatVo03.java @@ -0,0 +1,33 @@ +package com.platform.modules.chat.vo; + +import com.platform.modules.chat.enums.MsgStatusEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class ChatVo03 { + + /** + * 发送状态 + */ + private MsgStatusEnum status; + + /** + * 消息id + */ + private Long msgId; + + /** + * 好友详情 + */ + private ChatVo04 userInfo; + + public String getStatusLabel() { + if (status == null) { + return null; + } + return status.getInfo(); + } + +} diff --git a/src/main/java/com/platform/modules/chat/vo/ChatVo04.java b/src/main/java/com/platform/modules/chat/vo/ChatVo04.java new file mode 100644 index 0000000..b26868b --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/ChatVo04.java @@ -0,0 +1,27 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class ChatVo04 { + + /** + * 用户id + */ + private Long userId; + /** + * 用户id + */ + private String trtcId; + /** + * 昵称 + */ + private String nickName; + /** + * 头像 + */ + private String portrait; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo01.java b/src/main/java/com/platform/modules/chat/vo/FriendVo01.java new file mode 100644 index 0000000..c44fbf2 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo01.java @@ -0,0 +1,13 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class FriendVo01 { + + @NotBlank(message = "搜索参数不能为空") + private String param; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo02.java b/src/main/java/com/platform/modules/chat/vo/FriendVo02.java new file mode 100644 index 0000000..546f4b9 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo02.java @@ -0,0 +1,23 @@ +package com.platform.modules.chat.vo; + +import com.platform.modules.chat.enums.ApplySourceEnum; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class FriendVo02 { + + @NotNull(message = "用户id不能为空") + private Long userId; + + @NotBlank(message = "申请理由不能为空") + @Size(max = 20, message = "申请理由长度不能大于20") + private String reason; + + @NotNull(message = "好友来源不能为空") + private ApplySourceEnum source; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo03.java b/src/main/java/com/platform/modules/chat/vo/FriendVo03.java new file mode 100644 index 0000000..58df4c7 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo03.java @@ -0,0 +1,17 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.YesOrNoEnum; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class FriendVo03 { + + @NotNull(message = "用户id不能为空") + private Long userId; + + @NotNull(message = "状态不能为空") + private YesOrNoEnum black; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo04.java b/src/main/java/com/platform/modules/chat/vo/FriendVo04.java new file mode 100644 index 0000000..ca620ed --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo04.java @@ -0,0 +1,13 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class FriendVo04 { + + @NotNull(message = "用户id不能为空") + private Long userId; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo05.java b/src/main/java/com/platform/modules/chat/vo/FriendVo05.java new file mode 100644 index 0000000..bc8219f --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo05.java @@ -0,0 +1,19 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class FriendVo05 { + + @NotNull(message = "用户id不能为空") + private Long userId; + + @NotBlank(message = "备注不能为空") + @Size(max = 32, message = "备注长度不能大于32") + private String remark; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo06.java b/src/main/java/com/platform/modules/chat/vo/FriendVo06.java new file mode 100644 index 0000000..d6ef648 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo06.java @@ -0,0 +1,35 @@ +package com.platform.modules.chat.vo; + +import com.platform.modules.chat.enums.FriendTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 好友对象 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class FriendVo06 { + + /** + * 用户id + */ + private Long userId; + /** + * 头像 + */ + private String portrait; + /** + * 微聊号 + */ + private String chatNo; + /** + * 昵称 + */ + private String nickName; + /** + * 好友类型 + */ + private FriendTypeEnum userType = FriendTypeEnum.NORMAL; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo07.java b/src/main/java/com/platform/modules/chat/vo/FriendVo07.java new file mode 100644 index 0000000..b8e555f --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo07.java @@ -0,0 +1,84 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.GenderEnum; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.modules.chat.enums.ApplySourceEnum; +import com.platform.modules.chat.enums.FriendTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 好友详情 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class FriendVo07 { + + /** + * 用户id + */ + private Long userId; + /** + * 昵称 + */ + private String nickName; + /** + * 头像 + */ + private String portrait; + /** + * 性别1男0女 + */ + private GenderEnum gender; + /** + * 封面 + */ + private String cover; + /** + * 微聊号 + */ + private String chatNo; + /** + * 省份 + */ + private String provinces; + /** + * 城市 + */ + private String city; + /** + * 介绍 + */ + private String intro; + /** + * 是否是好友 + */ + private YesOrNoEnum isFriend = YesOrNoEnum.NO; + /** + * 是否黑名单 + */ + private YesOrNoEnum black = YesOrNoEnum.NO; + /** + * 好友来源 + */ + private ApplySourceEnum source; + /** + * 好友类型 + */ + private FriendTypeEnum userType = FriendTypeEnum.NORMAL; + + public String getGenderLabel() { + if (gender == null) { + return null; + } + return gender.getInfo(); + } + + public String getSourceLabel() { + if (source == null) { + return null; + } + return source.getInfo(); + } + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo08.java b/src/main/java/com/platform/modules/chat/vo/FriendVo08.java new file mode 100644 index 0000000..64eefe0 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo08.java @@ -0,0 +1,10 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +@Data +public class FriendVo08 { + + private String param; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/FriendVo09.java b/src/main/java/com/platform/modules/chat/vo/FriendVo09.java new file mode 100644 index 0000000..d829a35 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/FriendVo09.java @@ -0,0 +1,17 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.YesOrNoEnum; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class FriendVo09 { + + @NotNull(message = "用户id不能为空") + private Long userId; + + @NotNull(message = "状态不能为空") + private YesOrNoEnum top; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/GroupVo01.java b/src/main/java/com/platform/modules/chat/vo/GroupVo01.java new file mode 100644 index 0000000..01bc5b2 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/GroupVo01.java @@ -0,0 +1,16 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@Data +public class GroupVo01 { + + @NotNull(message = "群id不能为空") + private Long groupId; + + @NotNull(message = "好友列表不能为空") + private List list; +} diff --git a/src/main/java/com/platform/modules/chat/vo/GroupVo02.java b/src/main/java/com/platform/modules/chat/vo/GroupVo02.java new file mode 100644 index 0000000..b3a51cd --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/GroupVo02.java @@ -0,0 +1,18 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class GroupVo02 { + + @NotNull(message = "群id不能为空") + private Long groupId; + + @NotBlank(message = "群组名称不能为空") + @Size(max = 20, message = "群组名称长度不能大于20") + private String name; +} diff --git a/src/main/java/com/platform/modules/chat/vo/GroupVo03.java b/src/main/java/com/platform/modules/chat/vo/GroupVo03.java new file mode 100644 index 0000000..d5e46ff --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/GroupVo03.java @@ -0,0 +1,18 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class GroupVo03 { + + @NotNull(message = "群id不能为空") + private Long groupId; + + @NotBlank(message = "群组公告不能为空") + @Size(max = 200, message = "群组公告长度不能大于200") + private String notice; +} diff --git a/src/main/java/com/platform/modules/chat/vo/GroupVo04.java b/src/main/java/com/platform/modules/chat/vo/GroupVo04.java new file mode 100644 index 0000000..ca66d98 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/GroupVo04.java @@ -0,0 +1,16 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.YesOrNoEnum; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class GroupVo04 { + + @NotNull(message = "群id不能为空") + private Long groupId; + + @NotNull(message = "状态不能为空") + private YesOrNoEnum top; +} diff --git a/src/main/java/com/platform/modules/chat/vo/GroupVo05.java b/src/main/java/com/platform/modules/chat/vo/GroupVo05.java new file mode 100644 index 0000000..c6774b5 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/GroupVo05.java @@ -0,0 +1,16 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.YesOrNoEnum; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class GroupVo05 { + + @NotNull(message = "群id不能为空") + private Long groupId; + + @NotNull(message = "状态不能为空") + private YesOrNoEnum disturb; +} diff --git a/src/main/java/com/platform/modules/chat/vo/GroupVo06.java b/src/main/java/com/platform/modules/chat/vo/GroupVo06.java new file mode 100644 index 0000000..74e6f80 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/GroupVo06.java @@ -0,0 +1,16 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.YesOrNoEnum; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class GroupVo06 { + + @NotNull(message = "群id不能为空") + private Long groupId; + + @NotNull(message = "状态不能为空") + private YesOrNoEnum keepGroup; +} diff --git a/src/main/java/com/platform/modules/chat/vo/GroupVo07.java b/src/main/java/com/platform/modules/chat/vo/GroupVo07.java new file mode 100644 index 0000000..c38975d --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/GroupVo07.java @@ -0,0 +1,29 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(chain = true) // 链式调用 +public class GroupVo07 { + + /** + * 群id + */ + private Long groupId; + /** + * 群名 + */ + private String name; + /** + * 头像 + */ + private List portrait; + /** + * 人数 + */ + private Long count; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/GroupVo08.java b/src/main/java/com/platform/modules/chat/vo/GroupVo08.java new file mode 100644 index 0000000..fc7b00b --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/GroupVo08.java @@ -0,0 +1,25 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(chain = true) // 链式调用 +public class GroupVo08 { + + /** + * 群id + */ + private Long groupId; + /** + * 群名 + */ + private String name; + /** + * 头像 + */ + private List portrait; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo01.java b/src/main/java/com/platform/modules/chat/vo/MyVo01.java new file mode 100644 index 0000000..3e473bd --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo01.java @@ -0,0 +1,22 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class MyVo01 { + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空") + private String password; + + /** + * 新密码 + */ + @NotBlank(message = "新密码不能为空") + private String pwd; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo02.java b/src/main/java/com/platform/modules/chat/vo/MyVo02.java new file mode 100644 index 0000000..d3d1afd --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo02.java @@ -0,0 +1,15 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class MyVo02 { + + @NotBlank(message = "头像不能为空") + @Size(max = 2000, message = "头像长度不能大于2000") + private String portrait; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo03.java b/src/main/java/com/platform/modules/chat/vo/MyVo03.java new file mode 100644 index 0000000..ab12eba --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo03.java @@ -0,0 +1,15 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class MyVo03 { + + @NotBlank(message = "昵称不能为空") + @Size(max = 20, message = "昵称长度不能大于20") + private String nickName; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo04.java b/src/main/java/com/platform/modules/chat/vo/MyVo04.java new file mode 100644 index 0000000..5b8a1ff --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo04.java @@ -0,0 +1,19 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class MyVo04 { + + @NotBlank(message = "图片不能为空") + @Size(max = 2000, message = "昵称长度不能大于2000") + private String images; + + @NotBlank(message = "内容不能为空") + @Size(max = 2000, message = "内容长度不能大于2000") + private String content; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo05.java b/src/main/java/com/platform/modules/chat/vo/MyVo05.java new file mode 100644 index 0000000..e0174df --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo05.java @@ -0,0 +1,14 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.GenderEnum; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class MyVo05 { + + @NotNull(message = "性别不能为空") + private GenderEnum gender; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo06.java b/src/main/java/com/platform/modules/chat/vo/MyVo06.java new file mode 100644 index 0000000..ebe4445 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo06.java @@ -0,0 +1,15 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class MyVo06 { + + @NotBlank(message = "微聊号不能为空") + @Size(min = 6, max = 20, message = "微聊号长度限6-20位") + private String chatNo; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo07.java b/src/main/java/com/platform/modules/chat/vo/MyVo07.java new file mode 100644 index 0000000..4fe8009 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo07.java @@ -0,0 +1,15 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class MyVo07 { + + @NotBlank(message = "个性签名不能为空") + @Size(min = 1, max = 200, message = "个性签名长度限1-200位") + private String intro; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo08.java b/src/main/java/com/platform/modules/chat/vo/MyVo08.java new file mode 100644 index 0000000..dca8376 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo08.java @@ -0,0 +1,19 @@ +package com.platform.modules.chat.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class MyVo08 { + + @NotBlank(message = "省份不能为空") + @Size(max = 20, message = "省份长度限1-20位") + private String provinces; + + @NotBlank(message = "城市不能为空") + @Size(max = 20, message = "城市长度限1-20位") + private String city; + +} diff --git a/src/main/java/com/platform/modules/chat/vo/MyVo09.java b/src/main/java/com/platform/modules/chat/vo/MyVo09.java new file mode 100644 index 0000000..b72614d --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/MyVo09.java @@ -0,0 +1,59 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.GenderEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class MyVo09 { + + /** + * 主键 + */ + private Long userId; + /** + * 昵称 + */ + private String nickName; + /** + * 头像 + */ + private String portrait; + /** + * 封面 + */ + private String cover; + /** + * 性别1男0女 + */ + private GenderEnum gender; + /** + * 手机号 + */ + private String phone; + /** + * 微聊号 + */ + private String chatNo; + /** + * 介绍 + */ + private String intro; + /** + * 省份 + */ + private String provinces; + /** + * 城市 + */ + private String city; + + public String getGenderLabel() { + if (gender == null) { + return null; + } + return gender.getInfo(); + } + +} diff --git a/src/main/java/com/platform/modules/chat/vo/VersionVo.java b/src/main/java/com/platform/modules/chat/vo/VersionVo.java new file mode 100644 index 0000000..6e529a0 --- /dev/null +++ b/src/main/java/com/platform/modules/chat/vo/VersionVo.java @@ -0,0 +1,32 @@ +package com.platform.modules.chat.vo; + +import com.platform.common.enums.YesOrNoEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class VersionVo { + + /** + * 是否升级 + */ + private YesOrNoEnum upgrade; + /** + * 版本 + */ + private String version; + /** + * 地址 + */ + private String url; + /** + * 内容 + */ + private String content; + /** + * 是否强制升级 + */ + private YesOrNoEnum forceUpgrade; + +} diff --git a/src/main/java/com/platform/modules/collect/controller/CollectController.java b/src/main/java/com/platform/modules/collect/controller/CollectController.java new file mode 100644 index 0000000..1508bd8 --- /dev/null +++ b/src/main/java/com/platform/modules/collect/controller/CollectController.java @@ -0,0 +1,58 @@ +package com.platform.modules.collect.controller; + +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.common.web.page.TableDataInfo; +import com.platform.modules.collect.domain.ChatCollect; +import com.platform.modules.collect.service.ChatCollectService; +import com.platform.modules.collect.vo.CollectVo01; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * 收藏 + */ +@RestController +@Slf4j +@RequestMapping("/collect") +public class CollectController extends BaseController { + + @Resource + private ChatCollectService collectService; + + /** + * 增加 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/add") + public AjaxResult addCollect(@Validated @RequestBody CollectVo01 collectVo) { + collectService.addCollect(collectVo); + return AjaxResult.success(); + } + + /** + * 删除 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/remove/{collectId}") + public AjaxResult remove(@PathVariable Long collectId) { + collectService.deleteCollect(collectId); + return AjaxResult.success(); + } + + /** + * 列表 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/list") + public TableDataInfo list(ChatCollect collect) { + startPage("create_time desc"); + return getDataTable(collectService.collectList(collect)); + } + +} diff --git a/src/main/java/com/platform/modules/collect/dao/ChatCollectDao.java b/src/main/java/com/platform/modules/collect/dao/ChatCollectDao.java new file mode 100644 index 0000000..1fa641f --- /dev/null +++ b/src/main/java/com/platform/modules/collect/dao/ChatCollectDao.java @@ -0,0 +1,23 @@ +package com.platform.modules.collect.dao; + +import com.platform.common.web.dao.BaseDao; +import com.platform.modules.collect.domain.ChatCollect; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * 收藏表 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatCollectDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatCollect chatCollect); + +} diff --git a/src/main/java/com/platform/modules/collect/domain/ChatCollect.java b/src/main/java/com/platform/modules/collect/domain/ChatCollect.java new file mode 100644 index 0000000..332d8dc --- /dev/null +++ b/src/main/java/com/platform/modules/collect/domain/ChatCollect.java @@ -0,0 +1,47 @@ +package com.platform.modules.collect.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.web.domain.BaseEntity; +import com.platform.modules.collect.enums.CollectTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 收藏表实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_collect") +@Accessors(chain = true) // 链式调用 +public class ChatCollect extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 用户id + */ + private Long userId; + /** + * 收藏类型 + */ + private CollectTypeEnum collectType; + /** + * 内容 + */ + private String content; + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/src/main/java/com/platform/modules/collect/enums/CollectTypeEnum.java b/src/main/java/com/platform/modules/collect/enums/CollectTypeEnum.java new file mode 100644 index 0000000..1f00b95 --- /dev/null +++ b/src/main/java/com/platform/modules/collect/enums/CollectTypeEnum.java @@ -0,0 +1,53 @@ +package com.platform.modules.collect.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 收藏类型 + */ +@Getter +public enum CollectTypeEnum { + + /** + * 文字/表情 + */ + TEXT("TEXT", "文字/表情"), + /** + * 图片/拍照 + */ + IMAGE("IMAGE", "图片/拍照"), + /** + * 声音 + */ + VOICE("VOICE", "声音"), + /** + * 视频 + */ + VIDEO("VIDEO", "视频"), + /** + * 位置 + */ + LOCATION("LOCATION", "位置"), + /** + * 名片 + */ + CARD("CARD", "名片"), + /** + * 文件 + */ + FILE("FILE", "文件"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + CollectTypeEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/collect/service/ChatCollectService.java b/src/main/java/com/platform/modules/collect/service/ChatCollectService.java new file mode 100644 index 0000000..2887076 --- /dev/null +++ b/src/main/java/com/platform/modules/collect/service/ChatCollectService.java @@ -0,0 +1,31 @@ +package com.platform.modules.collect.service; + +import com.github.pagehelper.PageInfo; +import com.platform.common.web.service.BaseService; +import com.platform.modules.collect.domain.ChatCollect; +import com.platform.modules.collect.vo.CollectVo01; + +/** + *

+ * 收藏表 服务层 + * q3z3 + *

+ */ +public interface ChatCollectService extends BaseService { + + /** + * 新增收藏 + */ + void addCollect(CollectVo01 collectVo); + + /** + * 删除收藏 + */ + void deleteCollect(Long collectId); + + /** + * 列表 + */ + PageInfo collectList(ChatCollect collect); + +} diff --git a/src/main/java/com/platform/modules/collect/service/impl/ChatCollectServiceImpl.java b/src/main/java/com/platform/modules/collect/service/impl/ChatCollectServiceImpl.java new file mode 100644 index 0000000..0785717 --- /dev/null +++ b/src/main/java/com/platform/modules/collect/service/impl/ChatCollectServiceImpl.java @@ -0,0 +1,77 @@ +package com.platform.modules.collect.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import com.github.pagehelper.PageInfo; +import com.platform.common.exception.BaseException; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.collect.dao.ChatCollectDao; +import com.platform.modules.collect.domain.ChatCollect; +import com.platform.modules.collect.service.ChatCollectService; +import com.platform.modules.collect.vo.CollectVo01; +import com.platform.modules.collect.vo.CollectVo02; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * 收藏表 服务层实现 + * q3z3 + *

+ */ +@Service("chatCollectService") +public class ChatCollectServiceImpl extends BaseServiceImpl implements ChatCollectService { + + @Resource + private ChatCollectDao chatCollectDao; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatCollectDao); + } + + @Override + public List queryList(ChatCollect t) { + List dataList = chatCollectDao.queryList(t); + return dataList; + } + + @Override + public void addCollect(CollectVo01 collectVo) { + ChatCollect collect = new ChatCollect() + .setUserId(ShiroUtils.getUserId()) + .setCollectType(collectVo.getCollectType()) + .setContent(collectVo.getContent()) + .setCreateTime(DateUtil.date()); + this.add(collect); + } + + @Override + public void deleteCollect(Long collectId) { + ChatCollect collect = this.getById(collectId); + if (collect == null) { + return; + } + if (!ShiroUtils.getUserId().equals(collect.getUserId())) { + throw new BaseException("删除失败,不能删除别人的收藏"); + } + this.deleteById(collectId); + } + + @Override + public PageInfo collectList(ChatCollect collect) { + collect.setUserId(ShiroUtils.getUserId()); + List collectList = queryList(collect); + List dataList = new ArrayList<>(); + collectList.forEach(e -> { + dataList.add(BeanUtil.toBean(e, CollectVo02.class).setCollectId(e.getId())); + }); + return getPageInfo(dataList, collectList); + } + +} diff --git a/src/main/java/com/platform/modules/collect/vo/CollectVo01.java b/src/main/java/com/platform/modules/collect/vo/CollectVo01.java new file mode 100644 index 0000000..62f6090 --- /dev/null +++ b/src/main/java/com/platform/modules/collect/vo/CollectVo01.java @@ -0,0 +1,20 @@ +package com.platform.modules.collect.vo; + +import com.platform.modules.collect.enums.CollectTypeEnum; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class CollectVo01 { + + @NotNull(message = "收藏类型不能为空") + private CollectTypeEnum collectType; + + @NotBlank(message = "收藏内容不能为空") + @Size(max = 20000, message = "收藏内容长度不能大于20000") + private String content; + +} diff --git a/src/main/java/com/platform/modules/collect/vo/CollectVo02.java b/src/main/java/com/platform/modules/collect/vo/CollectVo02.java new file mode 100644 index 0000000..f9bca82 --- /dev/null +++ b/src/main/java/com/platform/modules/collect/vo/CollectVo02.java @@ -0,0 +1,36 @@ +package com.platform.modules.collect.vo; + +import com.platform.modules.collect.enums.CollectTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@Accessors(chain = true) // 链式调用 +public class CollectVo02 { + + /** + * 主键 + */ + private Long collectId; + /** + * 收藏类型 + */ + private CollectTypeEnum collectType; + /** + * 内容 + */ + private String content; + /** + * 创建时间 + */ + private Date createTime; + + public String getCollectTypeLabel() { + if (collectType == null) { + return null; + } + return collectType.getInfo(); + } +} diff --git a/src/main/java/com/platform/modules/common/config/TrtcConfig.java b/src/main/java/com/platform/modules/common/config/TrtcConfig.java new file mode 100644 index 0000000..1a47b4b --- /dev/null +++ b/src/main/java/com/platform/modules/common/config/TrtcConfig.java @@ -0,0 +1,21 @@ +package com.platform.modules.common.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 读取trtc相关配置 + */ +@Component +@Data +public class TrtcConfig { + + @Value("${trtc.appId}") + private String appId; + @Value("${trtc.expire}") + private String expire; + @Value("${trtc.secret}") + private String secret; + +} \ No newline at end of file diff --git a/src/main/java/com/platform/modules/common/controller/CommonController.java b/src/main/java/com/platform/modules/common/controller/CommonController.java new file mode 100644 index 0000000..7c372cb --- /dev/null +++ b/src/main/java/com/platform/modules/common/controller/CommonController.java @@ -0,0 +1,52 @@ +package com.platform.modules.common.controller; + +import com.platform.common.aspectj.IgnoreAuth; +import com.platform.common.constant.HeadConstant; +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.chat.service.ChatVersionService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +/** + * 通用请求处理 + */ +@RestController +@RequestMapping("/common") +@Slf4j +public class CommonController { + + @Resource + private ChatVersionService versionService; + + /** + * 校验版本号 + */ + @IgnoreAuth + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/getVersion") + public AjaxResult getVersion(HttpServletRequest request) { + // 请求的版本 + String version = request.getHeader(HeadConstant.VERSION); + // 请求的设备 + String device = request.getHeader(HeadConstant.DEVICE); + return AjaxResult.success(versionService.getVersion(version, device)); + } + + /** + * 用户协议 + */ + @IgnoreAuth + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/getAgreement") + public AjaxResult getAgreement() { + return AjaxResult.success(versionService.getAgreement()); + } + +} diff --git a/src/main/java/com/platform/modules/common/controller/FileController.java b/src/main/java/com/platform/modules/common/controller/FileController.java new file mode 100644 index 0000000..9c0f34f --- /dev/null +++ b/src/main/java/com/platform/modules/common/controller/FileController.java @@ -0,0 +1,65 @@ +package com.platform.modules.common.controller; + +import com.platform.common.exception.BaseException; +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.common.service.FileService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; + +/** + * 文件处理 + */ +@RestController +@RequestMapping("/file") +@Slf4j +public class FileController { + + @Resource + private FileService fileService; + + /** + * 通用上传请求 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/upload") + public AjaxResult upload(MultipartFile file) { + if (file == null) { + throw new BaseException("上传文件不能为空"); + } + return AjaxResult.success(fileService.uploadFile(file)); + } + + /** + * 生成视频封面图 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/uploadVideo") + public AjaxResult createVideoCover(MultipartFile file) { + if (file == null) { + throw new BaseException("上传文件不能为空"); + } + // 调用视频处理工具类 + return AjaxResult.success(fileService.uploadVideo(file)); + } + + /** + * 生成音频文字 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/uploadAudio") + public AjaxResult uploadAudio(MultipartFile file) { + if (file == null) { + throw new BaseException("上传文件不能为空"); + } + // 调用视频处理工具类 + return AjaxResult.success(fileService.uploadAudio(file)); + } + +} diff --git a/src/main/java/com/platform/modules/common/controller/TrtcController.java b/src/main/java/com/platform/modules/common/controller/TrtcController.java new file mode 100644 index 0000000..b00fefb --- /dev/null +++ b/src/main/java/com/platform/modules/common/controller/TrtcController.java @@ -0,0 +1,34 @@ +package com.platform.modules.common.controller; + +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.common.service.TrtcService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * 实时语音/视频 + */ +@RestController +@RequestMapping("/trtc") +@Slf4j +public class TrtcController { + + @Resource + private TrtcService trtcService; + + /** + * 获取签名 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/getSign") + public AjaxResult getSign() { + return AjaxResult.success(trtcService.getSign()); + } + +} diff --git a/src/main/java/com/platform/modules/common/service/FileService.java b/src/main/java/com/platform/modules/common/service/FileService.java new file mode 100644 index 0000000..6b0dca4 --- /dev/null +++ b/src/main/java/com/platform/modules/common/service/FileService.java @@ -0,0 +1,28 @@ +package com.platform.modules.common.service; + +import com.platform.common.upload.vo.UploadAudioVo; +import com.platform.common.upload.vo.UploadFileVo; +import com.platform.common.upload.vo.UploadVideoVo; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件服务 + */ +public interface FileService { + + /** + * 文件上传 + */ + UploadFileVo uploadFile(MultipartFile file); + + /** + * 文件视频 + */ + UploadVideoVo uploadVideo(MultipartFile file); + + /** + * 文件音频 + */ + UploadAudioVo uploadAudio(MultipartFile file); + +} diff --git a/src/main/java/com/platform/modules/common/service/TrtcService.java b/src/main/java/com/platform/modules/common/service/TrtcService.java new file mode 100644 index 0000000..9537a81 --- /dev/null +++ b/src/main/java/com/platform/modules/common/service/TrtcService.java @@ -0,0 +1,16 @@ +package com.platform.modules.common.service; + + +import com.platform.modules.common.vo.TrtcVo; + +/** + * 实时语音/视频 + */ +public interface TrtcService { + + /** + * 实时语音/视频 + */ + TrtcVo getSign(); + +} diff --git a/src/main/java/com/platform/modules/common/service/impl/FileServiceImpl.java b/src/main/java/com/platform/modules/common/service/impl/FileServiceImpl.java new file mode 100644 index 0000000..6af6f36 --- /dev/null +++ b/src/main/java/com/platform/modules/common/service/impl/FileServiceImpl.java @@ -0,0 +1,64 @@ +package com.platform.modules.common.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.util.StrUtil; +import com.platform.common.constant.AppConstants; +import com.platform.common.exception.BaseException; +import com.platform.common.upload.service.UploadService; +import com.platform.common.upload.vo.UploadAudioVo; +import com.platform.common.upload.vo.UploadFileVo; +import com.platform.common.upload.vo.UploadVideoVo; +import com.platform.modules.chat.config.TencentConfig; +import com.platform.modules.chat.utils.TencentUtils; +import com.platform.modules.common.service.FileService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.IOException; + +@Service("fileService") +public class FileServiceImpl implements FileService { + + @Resource + private UploadService uploadService; + + @Autowired + private TencentConfig tencentConfig; + + @Override + public UploadFileVo uploadFile(MultipartFile file) { + String fileType = FileNameUtil.extName(file.getOriginalFilename()); + if ("webp".equalsIgnoreCase(fileType)) { + throw new BaseException(StrUtil.format("暂不支持{}格式上传", fileType)); + } + // 上传 + return uploadService.uploadFile(file); + } + + @Override + public UploadVideoVo uploadVideo(MultipartFile videoFile) { + // 上传视频文件 + UploadFileVo videoFileVo = uploadService.uploadFile(videoFile); + // screenShot + return BeanUtil.toBean(videoFileVo, UploadVideoVo.class) + .setScreenShot(videoFileVo.getFullPath() + AppConstants.VIDEO_PARAM); + } + + @Override + public UploadAudioVo uploadAudio(MultipartFile audioFile) { + // 上传音频文件 + UploadFileVo audioFileVo = uploadService.uploadFile(audioFile); + String data; + try { + data = Base64.encode(audioFile.getInputStream()); + } catch (IOException e) { + throw new BaseException("语音识别接口调用异常,请稍后再试"); + } + return BeanUtil.toBean(audioFileVo, UploadAudioVo.class).setSourceText(TencentUtils.audio2Text(tencentConfig, data)); + } + +} diff --git a/src/main/java/com/platform/modules/common/service/impl/TrtcServiceImpl.java b/src/main/java/com/platform/modules/common/service/impl/TrtcServiceImpl.java new file mode 100644 index 0000000..107902e --- /dev/null +++ b/src/main/java/com/platform/modules/common/service/impl/TrtcServiceImpl.java @@ -0,0 +1,90 @@ +package com.platform.modules.common.service.impl; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.HMac; +import cn.hutool.crypto.digest.HmacAlgorithm; +import cn.hutool.json.JSONUtil; +import com.platform.common.constant.AppConstants; +import com.platform.common.redis.RedisUtils; +import com.platform.common.shiro.ShiroUtils; +import com.platform.modules.common.config.TrtcConfig; +import com.platform.modules.common.service.TrtcService; +import com.platform.modules.common.vo.TrtcVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; +import java.util.zip.Deflater; + +@Service("trtcService") +public class TrtcServiceImpl implements TrtcService { + + @Autowired + private TrtcConfig trtcConfig; + + @Autowired + private RedisUtils redisUtils; + + @Override + public TrtcVo getSign() { + String key = AppConstants.REDIS_TRTC_SIGN + ShiroUtils.getUserId(); + if (redisUtils.hasKey(key)) { + return JSONUtil.toBean(redisUtils.get(key), TrtcVo.class); + } + String userId = AppConstants.REDIS_TRTC_USER + ShiroUtils.getUserId(); + long currTime = DateUtil.currentSeconds(); + Dict doc = Dict.create() + .set("TLS.ver", "2.0") + .set("TLS.identifier", userId) + .set("TLS.sdkappid", trtcConfig.getAppId()) + .set("TLS.expire", trtcConfig.getExpire()) + .set("TLS.time", currTime) + .set("TLS.sig", hmacsha256(userId, currTime)); + Deflater compressor = new Deflater(); + compressor.setInput(JSONUtil.toJsonStr(doc).getBytes(StandardCharsets.UTF_8)); + compressor.finish(); + byte[] bytes = new byte[2048]; + int length = compressor.deflate(bytes); + compressor.end(); + TrtcVo trtcVo = new TrtcVo().setUserId(userId) + .setAppId(trtcConfig.getAppId()) + .setExpire(trtcConfig.getExpire()) + .setSign(base64EncodeUrl(ArrayUtil.resize(bytes, length))); + redisUtils.set(key, JSONUtil.toJsonStr(trtcVo), 5, TimeUnit.DAYS); + return trtcVo; + } + + private String hmacsha256(String userId, long currTime) { + String contentToBeSigned = "TLS.identifier:" + userId + "\n" + + "TLS.sdkappid:" + trtcConfig.getAppId() + "\n" + + "TLS.time:" + currTime + "\n" + + "TLS.expire:" + trtcConfig.getExpire() + "\n"; + HMac mac = new HMac(HmacAlgorithm.HmacSHA256, StrUtil.bytes(trtcConfig.getSecret(), StandardCharsets.UTF_8)); + byte[] signed = mac.digest(contentToBeSigned); + return cn.hutool.core.codec.Base64.encode(signed); + } + + private String base64EncodeUrl(byte[] input) { + byte[] base64 = Base64.encode(input).getBytes(StandardCharsets.UTF_8); + for (int i = 0; i < base64.length; ++i) + switch (base64[i]) { + case '+': + base64[i] = '*'; + break; + case '/': + base64[i] = '-'; + break; + case '=': + base64[i] = '_'; + break; + default: + break; + } + return new String(base64); + } +} diff --git a/src/main/java/com/platform/modules/common/vo/TrtcVo.java b/src/main/java/com/platform/modules/common/vo/TrtcVo.java new file mode 100644 index 0000000..9799031 --- /dev/null +++ b/src/main/java/com/platform/modules/common/vo/TrtcVo.java @@ -0,0 +1,27 @@ +package com.platform.modules.common.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class TrtcVo { + + /** + * 用户id + */ + private String userId; + /** + * appId + */ + private String appId; + /** + * 过期时间 + */ + private String expire; + /** + * 签名 + */ + private String sign; + +} diff --git a/src/main/java/com/platform/modules/push/enums/PushBodyEnum.java b/src/main/java/com/platform/modules/push/enums/PushBodyEnum.java new file mode 100644 index 0000000..e4d09ae --- /dev/null +++ b/src/main/java/com/platform/modules/push/enums/PushBodyEnum.java @@ -0,0 +1,29 @@ +package com.platform.modules.push.enums; + +import lombok.Getter; + +/** + * 推送消息类型 + */ +@Getter +public enum PushBodyEnum { + + /** + * 普通消息 + */ + MSG("MSG", "普通消息"), + /** + * 通知消息 + */ + NOTICE("NOTICE", "通知消息"), + ; + + private String code; + private String info; + + PushBodyEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/push/enums/PushMsgEnum.java b/src/main/java/com/platform/modules/push/enums/PushMsgEnum.java new file mode 100644 index 0000000..bba22ea --- /dev/null +++ b/src/main/java/com/platform/modules/push/enums/PushMsgEnum.java @@ -0,0 +1,77 @@ +package com.platform.modules.push.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 消息类型枚举 + */ +@Getter +public enum PushMsgEnum { + + /** + * 通知 + */ + ALERT("ALERT", "通知"), + /** + * 文字/表情 + */ + TEXT("TEXT", "文字/表情"), + /** + * 图片/拍照 + */ + IMAGE("IMAGE", "图片/拍照"), + /** + * 声音 + */ + VOICE("VOICE", "声音"), + /** + * 视频 + */ + VIDEO("VIDEO", "视频"), + /** + * 位置 + */ + LOCATION("LOCATION", "位置"), + /** + * 收藏 + */ + COLLECTION("COLLECTION", "收藏"), + /** + * 名片 + */ + CARD("CARD", "名片"), + /** + * 文件 + */ + FILE("FILE", "文件"), + /** + * 实时语音开始 + */ + TRTC_VOICE_START("TRTC_VOICE_START", "实时语音开始"), + /** + * 实时语音结束 + */ + TRTC_VOICE_END("TRTC_VOICE_END", "实时语音结束"), + /** + * 实时视频开始 + */ + TRTC_VIDEO_START("TRTC_VIDEO_START", "实时视频开始"), + /** + * 实时视频结束 + */ + TRTC_VIDEO_END("TRTC_VIDEO_END", "实时视频结束"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + PushMsgEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/push/enums/PushNoticeEnum.java b/src/main/java/com/platform/modules/push/enums/PushNoticeEnum.java new file mode 100644 index 0000000..afa0c23 --- /dev/null +++ b/src/main/java/com/platform/modules/push/enums/PushNoticeEnum.java @@ -0,0 +1,37 @@ +package com.platform.modules.push.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 消息类型枚举 + */ +@Getter +public enum PushNoticeEnum { + + /** + * 帖子_小红点 + */ + TOPIC_RED("TOPIC_RED", "帖子_小红点"), + /** + * 帖子_回复 + */ + TOPIC_REPLY("TOPIC_REPLY", "帖子_回复"), + /** + * 好友_申请 + */ + FRIEND_APPLY("FRIEND_APPLY", "好友_申请"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + PushNoticeEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/push/enums/PushTalkEnum.java b/src/main/java/com/platform/modules/push/enums/PushTalkEnum.java new file mode 100644 index 0000000..45235e6 --- /dev/null +++ b/src/main/java/com/platform/modules/push/enums/PushTalkEnum.java @@ -0,0 +1,33 @@ +package com.platform.modules.push.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 消息聊天枚举 + */ +@Getter +public enum PushTalkEnum { + + /** + * 单聊 + */ + SINGLE("SINGLE", "单聊"), + /** + * 群聊 + */ + GROUP("GROUP", "群聊"), + ; + + @EnumValue + @JsonValue + private String code; + private String info; + + PushTalkEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/push/service/ChatPushService.java b/src/main/java/com/platform/modules/push/service/ChatPushService.java new file mode 100644 index 0000000..de10cce --- /dev/null +++ b/src/main/java/com/platform/modules/push/service/ChatPushService.java @@ -0,0 +1,52 @@ +package com.platform.modules.push.service; + +import com.platform.modules.push.enums.PushMsgEnum; +import com.platform.modules.push.enums.PushNoticeEnum; +import com.platform.modules.push.vo.PushParamVo; + +import java.util.List; + +/** + *

+ * 用户推送 服务层 + * q3z3 + *

+ */ +public interface ChatPushService { + + /** + * 发送个人消息 + */ + void pushMsg(PushParamVo from, PushMsgEnum msgType); + + /** + * 发送个人消息 + */ + void pushMsg(List userList, PushMsgEnum msgType); + + /** + * 发送群组消息 + */ + void pushGroupMsg(PushParamVo from, PushParamVo group, PushMsgEnum msgType); + + /** + * 发送群组消息 + */ + void pushGroupMsg(List userList, PushParamVo group, PushMsgEnum msgType); + + /** + * 拉取离线消息 + */ + void pullMsg(Long userId); + + /** + * 发送通知 + */ + void pushNotice(PushParamVo paramVo, PushNoticeEnum pushNotice); + + /** + * 发送通知 + */ + void pushNotice(List userList, PushNoticeEnum pushNotice); + +} diff --git a/src/main/java/com/platform/modules/push/service/impl/ChatPushServiceImpl.java b/src/main/java/com/platform/modules/push/service/impl/ChatPushServiceImpl.java new file mode 100644 index 0000000..d8d5fff --- /dev/null +++ b/src/main/java/com/platform/modules/push/service/impl/ChatPushServiceImpl.java @@ -0,0 +1,169 @@ +package com.platform.modules.push.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.platform.common.constant.AppConstants; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.redis.RedisUtils; +import com.platform.modules.push.enums.PushBodyEnum; +import com.platform.modules.push.enums.PushMsgEnum; +import com.platform.modules.push.enums.PushNoticeEnum; +import com.platform.modules.push.service.ChatPushService; +import com.platform.modules.push.vo.*; +import com.platform.modules.ws.BootWebSocketHandler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + *

+ * 用户推送 服务层 + * q3z3 + *

+ */ +@Service("chatPushService") +@Slf4j +public class ChatPushServiceImpl implements ChatPushService { + + @Autowired + private RedisUtils redisUtils; + + @Autowired + private BootWebSocketHandler bootWebSocketHandler; + + @Override + public void pushMsg(PushParamVo from, PushMsgEnum msgType) { + this.doMsg(from, null, msgType); + } + + @Override + public void pushMsg(List userList, PushMsgEnum msgType) { + userList.forEach(e -> { + this.doMsg(e, e, msgType); + }); + } + + @Override + public void pushGroupMsg(PushParamVo from, PushParamVo group, PushMsgEnum msgType) { + this.doMsg(from, group, msgType); + } + + @Override + public void pushGroupMsg(List userList, PushParamVo group, PushMsgEnum msgType) { + userList.forEach(e -> { + this.doMsg(e, group, msgType); + }); + } + + /** + * 发送消息 + */ + private void doMsg(PushParamVo from, PushParamVo group, PushMsgEnum msgType) { + Long userId = from.getToId(); + // 组装消息体 + PushMsgVo pushMsgVo = new PushMsgVo() + .setMsgType(msgType.getCode()) + .setContent(from.getContent()); + YesOrNoEnum top = from.getTop(); + if (top != null) { + pushMsgVo.setTop(top.getCode()); + } + YesOrNoEnum disturb = from.getDisturb(); + if (disturb != null) { + pushMsgVo.setDisturb(disturb.getCode()); + } + Long msgId = from.getMsgId(); + if (msgId == null) { + msgId = IdWorker.getId(); + } + PushBodyVo pushBodyVo = new PushBodyVo(msgId, PushBodyEnum.MSG, pushMsgVo); + // 发送人 + pushBodyVo.setFromInfo(BeanUtil.toBean(from, PushFromVo.class).setUserType(from.getUserType().getCode())); + // 接收人 + if (group != null) { + pushBodyVo.setGroupInfo(BeanUtil.toBean(group, PushToVo.class)); + } + // 发送消息 + this.push(userId, pushBodyVo); + } + + @Override + public void pullMsg(Long userId) { + String redisKey = StrUtil.format(AppConstants.REDIS_MSG, userId, "*"); + Set redisKeys = this.redisUtils.keys(redisKey); + if (CollectionUtils.isEmpty(redisKeys)) { + return; + } + // 获取消息 + List redisValues = this.redisUtils.multiGet(CollUtil.sort(redisKeys, Comparator.naturalOrder())); + // 循环发送 + redisValues.forEach(content -> { + // 发送消息 + bootWebSocketHandler.sendMsg(userId, content); + }); + // 移除离线消息 + redisUtils.delete(redisKeys); + } + + @Override + public void pushNotice(PushParamVo paramVo, PushNoticeEnum pushNotice) { + this.pushNotice(Arrays.asList(paramVo), pushNotice); + } + + @Override + public void pushNotice(List userList, PushNoticeEnum pushNotice) { + userList.forEach(e -> { + this.doNotice(e.getToId(), e, pushNotice); + }); + } + + /** + * 发送通知 + */ + private void doNotice(Long userId, PushParamVo paramVo, PushNoticeEnum pushNotice) { + // 组装消息体 + PushNoticeVo pushNoticeVo = new PushNoticeVo(); + switch (pushNotice) { + case TOPIC_RED: + pushNoticeVo.setTopicRed(Dict.create().set("portrait", paramVo.getPortrait())); + break; + case TOPIC_REPLY: + Long topicCount = redisUtils.increment(AppConstants.REDIS_TOPIC_NOTICE + userId, 1); + pushNoticeVo.setTopicReply(Dict.create().set("count", topicCount).set("portrait", paramVo.getPortrait())); + break; + case FRIEND_APPLY: + Long applyCount = redisUtils.increment(AppConstants.REDIS_FRIEND_NOTICE + userId, 1); + pushNoticeVo.setFriendApply(Dict.create().set("count", applyCount)); + break; + } + Long msgId = IdWorker.getId(); + PushBodyVo pushBodyVo = new PushBodyVo(msgId, PushBodyEnum.NOTICE, pushNoticeVo); + // 发送消息 + this.push(userId, pushBodyVo); + } + + /** + * 推送 + */ + private void push(Long userId, PushBodyVo pushBodyVo) { + // 组装消息 + String content = JSONUtil.toJsonStr(pushBodyVo); + // 发送消息 + bootWebSocketHandler.sendMsg(userId, content); + // 离线消息 + String redisKey = StrUtil.format(AppConstants.REDIS_MSG, userId, pushBodyVo.getMsgId()); + redisUtils.set(redisKey, content, AppConstants.REDIS_MSG_TIME, TimeUnit.DAYS); + } + +} diff --git a/src/main/java/com/platform/modules/push/vo/PushBigVo.java b/src/main/java/com/platform/modules/push/vo/PushBigVo.java new file mode 100644 index 0000000..07825c2 --- /dev/null +++ b/src/main/java/com/platform/modules/push/vo/PushBigVo.java @@ -0,0 +1,18 @@ +package com.platform.modules.push.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 大消息 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class PushBigVo { + + /** + * 消息内容(消息Id) + */ + private String content; + +} diff --git a/src/main/java/com/platform/modules/push/vo/PushBodyVo.java b/src/main/java/com/platform/modules/push/vo/PushBodyVo.java new file mode 100644 index 0000000..d3157f3 --- /dev/null +++ b/src/main/java/com/platform/modules/push/vo/PushBodyVo.java @@ -0,0 +1,56 @@ +package com.platform.modules.push.vo; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import com.platform.modules.push.enums.PushBodyEnum; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 推送消息体 + */ +@Data +@Accessors(chain = true) // 链式调用 +@NoArgsConstructor +public class PushBodyVo { + + /** + * 消息id + */ + private String msgId; + + /** + * 消息类型(MSG/NOTICE) + */ + private String pushType; + + /** + * 消息时间 + */ + private String createTime; + + /** + * 消息数据 + */ + private Object msgContent; + + /** + * 发送人数据 + */ + private PushFromVo fromInfo; + + /** + * 接收人数据 + */ + private PushToVo groupInfo; + + public PushBodyVo(Long msgId, PushBodyEnum pushType, Object data) { + this.msgId = String.valueOf(msgId); + this.pushType = pushType.getCode(); + this.msgContent = data; + this.createTime = DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_FORMAT); + this.groupInfo = new PushToVo(); + } + +} diff --git a/src/main/java/com/platform/modules/push/vo/PushFromVo.java b/src/main/java/com/platform/modules/push/vo/PushFromVo.java new file mode 100644 index 0000000..787671c --- /dev/null +++ b/src/main/java/com/platform/modules/push/vo/PushFromVo.java @@ -0,0 +1,33 @@ +package com.platform.modules.push.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 消息发送人 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class PushFromVo { + + /** + * 发送人 + */ + private String userId; + + /** + * 用户头像 + */ + private String portrait; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(normal、self、weather、translation) + */ + private String userType; + +} diff --git a/src/main/java/com/platform/modules/push/vo/PushMsgVo.java b/src/main/java/com/platform/modules/push/vo/PushMsgVo.java new file mode 100644 index 0000000..d54b652 --- /dev/null +++ b/src/main/java/com/platform/modules/push/vo/PushMsgVo.java @@ -0,0 +1,34 @@ +package com.platform.modules.push.vo; + +import com.platform.common.enums.YesOrNoEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 普通消息 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class PushMsgVo { + + /** + * 是否置顶 + */ + private String top = YesOrNoEnum.NO.getCode(); + + /** + * 免打扰 + */ + private String disturb = YesOrNoEnum.NO.getCode(); + + /** + * 消息类型 + */ + private String msgType; + + /** + * 消息内容 + */ + private String content; + +} diff --git a/src/main/java/com/platform/modules/push/vo/PushNoticeVo.java b/src/main/java/com/platform/modules/push/vo/PushNoticeVo.java new file mode 100644 index 0000000..192d85a --- /dev/null +++ b/src/main/java/com/platform/modules/push/vo/PushNoticeVo.java @@ -0,0 +1,27 @@ +package com.platform.modules.push.vo; + +import cn.hutool.core.lang.Dict; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 推送对象 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class PushNoticeVo { + + /** + * 帖子_小红点 + */ + private Dict topicRed = Dict.create(); + /** + * 帖子_回复 + */ + private Dict topicReply = Dict.create(); + /** + * 好友_申请 + */ + private Dict friendApply = Dict.create(); + +} diff --git a/src/main/java/com/platform/modules/push/vo/PushParamVo.java b/src/main/java/com/platform/modules/push/vo/PushParamVo.java new file mode 100644 index 0000000..f0323df --- /dev/null +++ b/src/main/java/com/platform/modules/push/vo/PushParamVo.java @@ -0,0 +1,60 @@ +package com.platform.modules.push.vo; + +import com.platform.common.enums.YesOrNoEnum; +import com.platform.modules.chat.enums.FriendTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 消息发送人 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class PushParamVo { + + /** + * 消息id + */ + private Long msgId; + + /** + * 用户id + */ + private Long userId; + + /** + * 用户头像 + */ + private String portrait; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 发送内容 + */ + private String content; + + /** + * 是否静默 + */ + private YesOrNoEnum disturb = YesOrNoEnum.NO; + + /** + * 是否置顶 + */ + private YesOrNoEnum top = YesOrNoEnum.NO; + + /** + * 好友类型 + */ + private FriendTypeEnum userType = FriendTypeEnum.NORMAL; + + /** + * 临时参数 + */ + private Long toId; + +} diff --git a/src/main/java/com/platform/modules/push/vo/PushToVo.java b/src/main/java/com/platform/modules/push/vo/PushToVo.java new file mode 100644 index 0000000..5c977f5 --- /dev/null +++ b/src/main/java/com/platform/modules/push/vo/PushToVo.java @@ -0,0 +1,28 @@ +package com.platform.modules.push.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 消息接收人 + */ +@Data +@Accessors(chain = true) // 链式调用 +public class PushToVo { + + /** + * 接收人id + */ + private String userId; + + /** + * 用户头像 + */ + private String portrait; + + /** + * 用户昵称 + */ + private String nickName; + +} diff --git a/src/main/java/com/platform/modules/shake/controller/NearController.java b/src/main/java/com/platform/modules/shake/controller/NearController.java new file mode 100644 index 0000000..90abf9a --- /dev/null +++ b/src/main/java/com/platform/modules/shake/controller/NearController.java @@ -0,0 +1,45 @@ +package com.platform.modules.shake.controller; + +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.shake.service.NearService; +import com.platform.modules.shake.vo.NearVo01; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * 附近的人 + */ +@RestController +@Slf4j +@RequestMapping("/near") +public class NearController extends BaseController { + + @Resource + private NearService nearService; + + /** + * 发送附近的人 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping(value = "/doNear") + public AjaxResult doNear(@Validated @RequestBody NearVo01 nearVo) { + return AjaxResult.success(nearService.doNear(nearVo)); + } + + /** + * 关闭附近的人 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping(value = "/closeNear") + public AjaxResult closeNear() { + nearService.closeNear(); + return AjaxResult.success(); + } + +} diff --git a/src/main/java/com/platform/modules/shake/controller/ShakeController.java b/src/main/java/com/platform/modules/shake/controller/ShakeController.java new file mode 100644 index 0000000..0c1616d --- /dev/null +++ b/src/main/java/com/platform/modules/shake/controller/ShakeController.java @@ -0,0 +1,38 @@ +package com.platform.modules.shake.controller; + +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.modules.shake.service.ShakeService; +import com.platform.modules.shake.vo.ShakeVo01; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * 摇一摇 + */ +@RestController +@Slf4j +@RequestMapping("/shake") +public class ShakeController extends BaseController { + + @Resource + private ShakeService shakeService; + + /** + * 发送摇一摇 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping(value = "/doShake") + public AjaxResult sendShake(@Validated @RequestBody ShakeVo01 shakeVo) { + return AjaxResult.success(shakeService.doShake(shakeVo)); + } + +} diff --git a/src/main/java/com/platform/modules/shake/service/NearService.java b/src/main/java/com/platform/modules/shake/service/NearService.java new file mode 100644 index 0000000..4b5e31b --- /dev/null +++ b/src/main/java/com/platform/modules/shake/service/NearService.java @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.modules.shake.service; + + +import com.platform.modules.shake.vo.NearVo01; +import com.platform.modules.shake.vo.NearVo02; + +import java.util.List; + +/** + *

+ * 附近的人 服务层 + *

+ */ +public interface NearService { + + /** + * 发送经纬度 + */ + List doNear(NearVo01 nearVo); + + /** + * 关闭附近的人 + */ + void closeNear(); + +} diff --git a/src/main/java/com/platform/modules/shake/service/ShakeService.java b/src/main/java/com/platform/modules/shake/service/ShakeService.java new file mode 100644 index 0000000..91ed2a9 --- /dev/null +++ b/src/main/java/com/platform/modules/shake/service/ShakeService.java @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.modules.shake.service; + +import com.platform.modules.shake.vo.ShakeVo01; +import com.platform.modules.shake.vo.ShakeVo02; + +/** + *

+ * 摇一摇 服务层 + *

+ */ +public interface ShakeService { + + /** + * 发送经纬度 + */ + ShakeVo02 doShake(ShakeVo01 shakeVo); + +} diff --git a/src/main/java/com/platform/modules/shake/service/impl/NearServiceImpl.java b/src/main/java/com/platform/modules/shake/service/impl/NearServiceImpl.java new file mode 100644 index 0000000..a2978eb --- /dev/null +++ b/src/main/java/com/platform/modules/shake/service/impl/NearServiceImpl.java @@ -0,0 +1,107 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.modules.shake.service.impl; + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.json.JSONUtil; +import com.platform.common.constant.AppConstants; +import com.platform.common.redis.GeoHashUtils; +import com.platform.common.redis.GeoVo; +import com.platform.common.shiro.ShiroUtils; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.shake.service.NearService; +import com.platform.modules.shake.vo.NearVo01; +import com.platform.modules.shake.vo.NearVo02; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.geo.GeoResult; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * 附近的人 服务层 + */ +@Service("nearService") +public class NearServiceImpl implements NearService { + + @Autowired + private GeoHashUtils geoHashUtils; + + @Resource + private ChatUserService chatUserService; + + @Override + public List doNear(NearVo01 nearVo) { + sendNear(nearVo); + return getNear(); + } + + @Override + public void closeNear() { + String userId = NumberUtil.toStr(ShiroUtils.getUserId()); + geoHashUtils.remove(AppConstants.REDIS_NEAR, userId); + } + + private void sendNear(NearVo01 nearVo) { + // 当前用户ID + String userId = NumberUtil.toStr(ShiroUtils.getUserId()); + // 保存坐标 + geoHashUtils.add(AppConstants.REDIS_NEAR, nearVo.getLongitude(), nearVo.getLatitude(), userId); + } + + private List getNear() { + // 当前用户 + String userId = NumberUtil.toStr(ShiroUtils.getUserId()); + // 1000公里内的9999个用户 + List> geoResults = geoHashUtils.radius(AppConstants.REDIS_NEAR, userId, 100, 99); + // 过滤 + List userList = new ArrayList<>(); + List dataList = geoResults.stream().collect(ArrayList::new, (x, y) -> { + String name = JSONUtil.parseObj(y.getContent()).getStr("name"); + if (!userId.equals(name)) { + userList.add(name); + NearVo02 nearVo = new NearVo02() + .setUserId(NumberUtil.parseLong(name)) + .setDistance(y.getDistance().getValue()) + .setDistanceUnit(y.getDistance().getUnit()); + x.add(nearVo); + } + }, ArrayList::addAll); + if (CollectionUtils.isEmpty(userList)) { + return dataList; + } + HashMap mapList = chatUserService.getByIds(userList).stream().collect(HashMap::new, (x, y) -> { + x.put(y.getUserId(), y); + }, HashMap::putAll); + // 转换 + dataList.forEach(e -> { + ChatUser chatUser = ChatUser.initUser(mapList.get(e.getUserId())); + e.setPortrait(chatUser.getPortrait()) + .setIntro(chatUser.getIntro()) + .setNickName(chatUser.getNickName()) + .setGender(chatUser.getGender()); + }); + return dataList; + } + +} diff --git a/src/main/java/com/platform/modules/shake/service/impl/ShakeServiceImpl.java b/src/main/java/com/platform/modules/shake/service/impl/ShakeServiceImpl.java new file mode 100644 index 0000000..7b48b2e --- /dev/null +++ b/src/main/java/com/platform/modules/shake/service/impl/ShakeServiceImpl.java @@ -0,0 +1,87 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.modules.shake.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.NumberUtil; +import com.platform.common.constant.AppConstants; +import com.platform.common.exception.BaseException; +import com.platform.common.redis.GeoHashUtils; +import com.platform.common.redis.RedisUtils; +import com.platform.common.shiro.ShiroUtils; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.shake.service.ShakeService; +import com.platform.modules.shake.vo.ShakeVo01; +import com.platform.modules.shake.vo.ShakeVo02; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.geo.Distance; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * 摇一摇 服务层 + */ +@Service("shakeService") +public class ShakeServiceImpl implements ShakeService { + + @Autowired + private GeoHashUtils geoHashUtils; + + @Autowired + private RedisUtils redisUtils; + + @Resource + private ChatUserService chatUserService; + + private final static String ERR_MSG = "暂无匹配到的结果"; + + @Override + public ShakeVo02 doShake(ShakeVo01 shakeVo) { + sendShake(shakeVo); + return getShake(); + } + + private void sendShake(ShakeVo01 shakeVo) { + // 当前用户ID + String userId = NumberUtil.toStr(ShiroUtils.getUserId()); + // 保存集合 + redisUtils.lRightPush(AppConstants.REDIS_SHAKE, userId); + // 保存经纬度 + geoHashUtils.add(AppConstants.REDIS_GEO, shakeVo.getLongitude(), shakeVo.getLatitude(), userId); + } + + private ShakeVo02 getShake() { + if (!redisUtils.hasKey(AppConstants.REDIS_SHAKE)) { + throw new BaseException(ERR_MSG); + } + String userId = redisUtils.lLeftPop(AppConstants.REDIS_SHAKE); + String current = NumberUtil.toStr(ShiroUtils.getUserId()); + if (current.equals(userId)) { + throw new BaseException(ERR_MSG); + } + redisUtils.lRightPush(AppConstants.REDIS_SHAKE, userId); + ChatUser chatUser = ChatUser.initUser(chatUserService.getById(NumberUtil.parseLong(userId))); + Distance distance = geoHashUtils.dist(AppConstants.REDIS_GEO, userId, current); + return BeanUtil.toBean(chatUser, ShakeVo02.class) + .setDistance(distance.getValue()) + .setDistanceUnit(distance.getUnit()); + } + +} diff --git a/src/main/java/com/platform/modules/shake/vo/NearVo01.java b/src/main/java/com/platform/modules/shake/vo/NearVo01.java new file mode 100644 index 0000000..b208406 --- /dev/null +++ b/src/main/java/com/platform/modules/shake/vo/NearVo01.java @@ -0,0 +1,21 @@ +package com.platform.modules.shake.vo; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class NearVo01 { + + /** + * 经度 + */ + @NotNull(message = "经度不能为空") + private Double longitude; + /** + * 纬度 + */ + @NotNull(message = "纬度不能为空") + private Double latitude; + +} diff --git a/src/main/java/com/platform/modules/shake/vo/NearVo02.java b/src/main/java/com/platform/modules/shake/vo/NearVo02.java new file mode 100644 index 0000000..d94f30d --- /dev/null +++ b/src/main/java/com/platform/modules/shake/vo/NearVo02.java @@ -0,0 +1,47 @@ +package com.platform.modules.shake.vo; + +import com.platform.common.enums.GenderEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class NearVo02 { + + /** + * 用户ID + */ + private Long userId; + /** + * 头像 + */ + private String portrait; + /** + * 昵称 + */ + private String nickName; + /** + * 介绍 + */ + private String intro; + /** + * 性别1男0女 + */ + private GenderEnum gender; + /** + * 距离 + */ + private double distance; + /** + * 距离单位 + */ + private String distanceUnit; + + public String getGenderLabel() { + if (gender == null) { + return null; + } + return gender.getInfo(); + } + +} diff --git a/src/main/java/com/platform/modules/shake/vo/ShakeVo01.java b/src/main/java/com/platform/modules/shake/vo/ShakeVo01.java new file mode 100644 index 0000000..abceaff --- /dev/null +++ b/src/main/java/com/platform/modules/shake/vo/ShakeVo01.java @@ -0,0 +1,21 @@ +package com.platform.modules.shake.vo; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class ShakeVo01 { + + /** + * 经度 + */ + @NotNull(message = "经度不能为空") + private Double longitude; + /** + * 纬度 + */ + @NotNull(message = "纬度不能为空") + private Double latitude; + +} diff --git a/src/main/java/com/platform/modules/shake/vo/ShakeVo02.java b/src/main/java/com/platform/modules/shake/vo/ShakeVo02.java new file mode 100644 index 0000000..d7f8bc4 --- /dev/null +++ b/src/main/java/com/platform/modules/shake/vo/ShakeVo02.java @@ -0,0 +1,43 @@ +package com.platform.modules.shake.vo; + +import com.platform.common.enums.GenderEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class ShakeVo02 { + + /** + * 用户ID + */ + private Long userId; + /** + * 头像 + */ + private String portrait; + /** + * 昵称 + */ + private String nickName; + /** + * 性别1男0女 + */ + private GenderEnum gender; + /** + * 距离 + */ + private double distance; + /** + * 距离单位 + */ + private String distanceUnit; + + public String getGenderLabel() { + if (gender == null) { + return null; + } + return gender.getInfo(); + } + +} diff --git a/src/main/java/com/platform/modules/sms/enums/SmsTemplateEnum.java b/src/main/java/com/platform/modules/sms/enums/SmsTemplateEnum.java new file mode 100644 index 0000000..371751f --- /dev/null +++ b/src/main/java/com/platform/modules/sms/enums/SmsTemplateEnum.java @@ -0,0 +1,25 @@ +package com.platform.modules.sms.enums; + +import lombok.Getter; + +/** + * 短信模板枚举 + */ +@Getter +public enum SmsTemplateEnum { + + /** + * 验证码-00 + */ + VERIFY_CODE("SMS_209335004", "验证码-00"), + ; + + private String code; + private String info; + + SmsTemplateEnum(String code, String info) { + this.code = code; + this.info = info; + } + +} diff --git a/src/main/java/com/platform/modules/sms/enums/SmsTypeEnum.java b/src/main/java/com/platform/modules/sms/enums/SmsTypeEnum.java new file mode 100644 index 0000000..b9043e8 --- /dev/null +++ b/src/main/java/com/platform/modules/sms/enums/SmsTypeEnum.java @@ -0,0 +1,39 @@ +package com.platform.modules.sms.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 短信类型枚举 + */ +@Getter +public enum SmsTypeEnum { + + /** + * 注册 + */ + REGISTERED("1", "chat:code:reg:", 5), + /** + * 登录 + */ + LOGIN("2", "chat:code:login:", 5), + /** + * 忘记密码 + */ + FORGET("3", "chat:code:forget:", 5), + ; + + @EnumValue + @JsonValue + private String code; + private String prefix; + private Integer timeout; + + SmsTypeEnum(String code, String prefix, Integer timeout) { + this.code = code; + this.prefix = prefix; + this.timeout = timeout; + } + +} diff --git a/src/main/java/com/platform/modules/sms/service/SmsService.java b/src/main/java/com/platform/modules/sms/service/SmsService.java new file mode 100644 index 0000000..013c341 --- /dev/null +++ b/src/main/java/com/platform/modules/sms/service/SmsService.java @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.modules.sms.service; + +import cn.hutool.core.lang.Dict; +import com.platform.modules.sms.enums.SmsTypeEnum; +import com.platform.modules.sms.vo.SmsVo; + +/** + *

+ * 短信 服务层 + *

+ */ +public interface SmsService { + + /** + * 发送短信 + */ + Dict sendSms(SmsVo smsVo); + + /** + * 验证短信 + */ + void verifySms(String phone, String code, SmsTypeEnum type); + +} diff --git a/src/main/java/com/platform/modules/sms/service/impl/SmsServiceImpl.java b/src/main/java/com/platform/modules/sms/service/impl/SmsServiceImpl.java new file mode 100644 index 0000000..dc8ac6d --- /dev/null +++ b/src/main/java/com/platform/modules/sms/service/impl/SmsServiceImpl.java @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * https://www.q3z3.com + * QQ : 939313737 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platform.modules.sms.service.impl; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.RandomUtil; +import com.platform.common.config.PlatformConfig; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.exception.BaseException; +import com.platform.common.redis.RedisUtils; +import com.platform.modules.sms.enums.SmsTemplateEnum; +import com.platform.modules.sms.enums.SmsTypeEnum; +import com.platform.modules.sms.service.SmsService; +import com.platform.modules.sms.vo.SmsVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +/** + * token 服务层 + */ +@Service("smsService") +@Slf4j +public class SmsServiceImpl implements SmsService { + + @Autowired + private RedisUtils redisUtils; + + @Override + public Dict sendSms(SmsVo smsVo) { + // 验证手机号 + if (!Validator.isMobile(smsVo.getPhone())) { + throw new BaseException("请输入正确的手机号"); + } + SmsTypeEnum smsType = smsVo.getType(); + String key = smsType.getPrefix().concat(smsVo.getPhone()); + // 生成验证码 + String code = String.valueOf(RandomUtil.randomInt(1000, 9999)); + // 发送短信 + if (YesOrNoEnum.YES.equals(PlatformConfig.SMS)) { + Dict dict = Dict.create() + .set("code", code); + doSendSms(smsVo.getPhone(), SmsTemplateEnum.VERIFY_CODE, dict); + } + // 存入缓存 + redisUtils.set(key, code, smsType.getTimeout(), TimeUnit.MINUTES); + return Dict.create().set("code", code).set("expiration", smsType.getTimeout()); + } + + @Override + public void verifySms(String phone, String code, SmsTypeEnum type) { + // 验证手机号 + if (!Validator.isMobile(phone)) { + throw new BaseException("请输入正确的手机号"); + } + String key = type.getPrefix().concat(phone); + if (!redisUtils.hasKey(key)) { + throw new BaseException("验证码已过期,请重新获取"); + } + String value = redisUtils.get(key); + if (value.equalsIgnoreCase(code)) { + redisUtils.delete(key); + } else { + throw new BaseException("验证码不正确,请重新获取"); + } + } + + /** + * 执行发送短信 + * + * @param phone + * @param templateCode + * @param dict + */ + private void doSendSms(String phone, SmsTemplateEnum templateCode, Dict dict) { + // 短信待集成 + } + +} diff --git a/src/main/java/com/platform/modules/sms/vo/SmsVo.java b/src/main/java/com/platform/modules/sms/vo/SmsVo.java new file mode 100644 index 0000000..f6c6024 --- /dev/null +++ b/src/main/java/com/platform/modules/sms/vo/SmsVo.java @@ -0,0 +1,26 @@ +package com.platform.modules.sms.vo; + +import com.platform.modules.sms.enums.SmsTypeEnum; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 用户登录对象 + */ +@Data +public class SmsVo { + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空") + private String phone; + + /** + * 短信类型 + */ + @NotNull(message = "短信类型不能为空") + private SmsTypeEnum type; + +} diff --git a/src/main/java/com/platform/modules/topic/controller/TopicController.java b/src/main/java/com/platform/modules/topic/controller/TopicController.java new file mode 100644 index 0000000..ac3c44b --- /dev/null +++ b/src/main/java/com/platform/modules/topic/controller/TopicController.java @@ -0,0 +1,155 @@ +package com.platform.modules.topic.controller; + +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.version.ApiVersion; +import com.platform.common.version.VersionEnum; +import com.platform.common.web.controller.BaseController; +import com.platform.common.web.domain.AjaxResult; +import com.platform.common.web.page.TableDataInfo; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.topic.service.ChatTopicService; +import com.platform.modules.topic.vo.TopicVo01; +import com.platform.modules.topic.vo.TopicVo02; +import com.platform.modules.topic.vo.TopicVo07; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * 帖子 + */ +@RestController +@Slf4j +@RequestMapping("/topic") +public class TopicController extends BaseController { + + @Resource + private ChatTopicService topicService; + + @Resource + private ChatUserService chatUserService; + + /** + * 修改封面 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/editCover") + public AjaxResult editCover(@Validated @RequestBody TopicVo02 topicVo) { + // 执行修改 + ChatUser chatUser = new ChatUser() + .setUserId(ShiroUtils.getUserId()) + .setCover(topicVo.getCover()); + chatUserService.updateById(chatUser); + return AjaxResult.successMsg("修改成功"); + } + + /** + * 发布帖子 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/sendTopic") + public AjaxResult sendTopic(@Validated @RequestBody TopicVo01 topicVo) { + topicService.sendTopic(topicVo); + return AjaxResult.success(); + } + + /** + * 删除帖子 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/removeTopic/{topicId}") + public AjaxResult removeTopic(@PathVariable Long topicId) { + topicService.delTopic(topicId); + return AjaxResult.success(); + } + + /** + * 指定人的帖子 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/userTopic/{userId}") + public TableDataInfo userTopic(@PathVariable Long userId) { + return getDataTable(topicService.userTopic(userId)); + } + + /** + * 好友的帖子 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/topicList") + public TableDataInfo topicList() { + startPage("create_time desc"); + return getDataTable(topicService.topicList()); + } + + /** + * 帖子详情 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/topicInfo/{topicId}") + public AjaxResult topicInfo(@PathVariable Long topicId) { + return AjaxResult.success(topicService.topicInfo(topicId)); + } + + /** + * 点赞 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/like/{topicId}") + public AjaxResult like(@PathVariable Long topicId) { + topicService.like(topicId); + return AjaxResult.success(); + } + + /** + * 取消点赞 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/cancelLike/{topicId}") + public AjaxResult cancelLike(@PathVariable Long topicId) { + topicService.cancelLike(topicId); + return AjaxResult.success(); + } + + /** + * 回复 + */ + @ApiVersion(VersionEnum.V1_0_0) + @PostMapping("/reply") + public AjaxResult reply(@Validated @RequestBody TopicVo07 topicVo) { + return AjaxResult.success(topicService.reply(topicVo)); + } + + /** + * 删除回复 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/removeReply/{replyId}") + public AjaxResult removeReply(@PathVariable Long replyId) { + topicService.delReply(replyId); + return AjaxResult.success(); + } + + /** + * 查询通知列表 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/noticeList") + public AjaxResult noticeList() { + return AjaxResult.success(topicService.queryNoticeList()); + } + + /** + * 清空通知列表 + */ + @ApiVersion(VersionEnum.V1_0_0) + @GetMapping("/clearNotice") + public AjaxResult clearNotice() { + topicService.clearNotice(); + return AjaxResult.success(); + } + +} diff --git a/src/main/java/com/platform/modules/topic/dao/ChatTopicDao.java b/src/main/java/com/platform/modules/topic/dao/ChatTopicDao.java new file mode 100644 index 0000000..6f1d18c --- /dev/null +++ b/src/main/java/com/platform/modules/topic/dao/ChatTopicDao.java @@ -0,0 +1,29 @@ +package com.platform.modules.topic.dao; + +import com.platform.common.web.dao.BaseDao; +import com.platform.modules.topic.domain.ChatTopic; +import com.platform.modules.topic.vo.TopicVo04; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * 主题 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatTopicDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatTopic chatTopic); + + /** + * 查询 + */ + List topicList(Long userId); + +} diff --git a/src/main/java/com/platform/modules/topic/dao/ChatTopicLikeDao.java b/src/main/java/com/platform/modules/topic/dao/ChatTopicLikeDao.java new file mode 100644 index 0000000..792e09c --- /dev/null +++ b/src/main/java/com/platform/modules/topic/dao/ChatTopicLikeDao.java @@ -0,0 +1,29 @@ +package com.platform.modules.topic.dao; + +import com.platform.common.web.dao.BaseDao; +import com.platform.modules.topic.domain.ChatTopicLike; +import com.platform.modules.topic.vo.TopicVo05; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * 帖子点赞 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatTopicLikeDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatTopicLike chatTopicLike); + + /** + * 查询点赞信息 + */ + List queryTopicLike(@Param("topicId") Long topicId, @Param("userId") Long userId); +} diff --git a/src/main/java/com/platform/modules/topic/dao/ChatTopicReplyDao.java b/src/main/java/com/platform/modules/topic/dao/ChatTopicReplyDao.java new file mode 100644 index 0000000..aab8005 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/dao/ChatTopicReplyDao.java @@ -0,0 +1,29 @@ +package com.platform.modules.topic.dao; + +import com.platform.common.web.dao.BaseDao; +import com.platform.modules.topic.domain.ChatTopicReply; +import com.platform.modules.topic.vo.TopicVo06; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * 帖子回复表 数据库访问层 + * q3z3 + *

+ */ +@Repository +public interface ChatTopicReplyDao extends BaseDao { + + /** + * 查询列表 + */ + List queryList(ChatTopicReply chatTopicReply); + + /** + * 根据帖子查询 + */ + List queryReplyList(@Param("userId") Long userId, @Param("topicId") Long topicId); +} diff --git a/src/main/java/com/platform/modules/topic/domain/ChatTopic.java b/src/main/java/com/platform/modules/topic/domain/ChatTopic.java new file mode 100644 index 0000000..d6e5c75 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/domain/ChatTopic.java @@ -0,0 +1,51 @@ +package com.platform.modules.topic.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.web.domain.BaseEntity; +import com.platform.modules.topic.enums.TopicTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 主题实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_topic") +@Accessors(chain = true) // 链式调用 +public class ChatTopic extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 用户id + */ + private Long userId; + /** + * 类型 + */ + private TopicTypeEnum topicType; + /** + * 内容 + */ + private String content; + /** + * 经纬度 + */ + private String location; + /** + * 时间 + */ + private Date createTime; + +} diff --git a/src/main/java/com/platform/modules/topic/domain/ChatTopicLike.java b/src/main/java/com/platform/modules/topic/domain/ChatTopicLike.java new file mode 100644 index 0000000..419a071 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/domain/ChatTopicLike.java @@ -0,0 +1,41 @@ +package com.platform.modules.topic.domain; + +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + *

+ * 帖子点赞实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_topic_like") +@Accessors(chain = true) // 链式调用 +public class ChatTopicLike extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 帖子id + */ + private Long topicId; + /** + * 用户id + */ + private Long userId; + /** + * 是否点赞 + */ + private YesOrNoEnum hasLike; + +} diff --git a/src/main/java/com/platform/modules/topic/domain/ChatTopicReply.java b/src/main/java/com/platform/modules/topic/domain/ChatTopicReply.java new file mode 100644 index 0000000..a24c127 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/domain/ChatTopicReply.java @@ -0,0 +1,60 @@ +package com.platform.modules.topic.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.web.domain.BaseEntity; +import com.platform.modules.topic.enums.TopicReplyTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + *

+ * 帖子回复表实体类 + * q3z3 + *

+ */ +@Data +@TableName("chat_topic_reply") +@Accessors(chain = true) // 链式调用 +public class ChatTopicReply extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long replyId; + /** + * 回复类型1帖子2用户 + */ + private TopicReplyTypeEnum replyType; + /** + * 回复状态 + */ + private YesOrNoEnum replyStatus; + /** + * 回复内容 + */ + private String content; + /** + * 帖子id + */ + private Long topicId; + /** + * 用户id + */ + private Long userId; + /** + * 目标id + */ + private Long targetId; + /** + * 回复时间 + */ + private Date createTime; + +} diff --git a/src/main/java/com/platform/modules/topic/enums/TopicNoticeTypeEnum.java b/src/main/java/com/platform/modules/topic/enums/TopicNoticeTypeEnum.java new file mode 100644 index 0000000..bbf553c --- /dev/null +++ b/src/main/java/com/platform/modules/topic/enums/TopicNoticeTypeEnum.java @@ -0,0 +1,33 @@ +package com.platform.modules.topic.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 通知类型枚举 + */ +@Getter +public enum TopicNoticeTypeEnum { + + /** + * 点赞 + */ + LIKE("1", "点赞"), + /** + * 回复 + */ + REPLY("2", "回复"), + ; + + @EnumValue + @JsonValue + private String code; + private String name; + + TopicNoticeTypeEnum(String code, String name) { + this.code = code; + this.name = name; + } + +} diff --git a/src/main/java/com/platform/modules/topic/enums/TopicReplyTypeEnum.java b/src/main/java/com/platform/modules/topic/enums/TopicReplyTypeEnum.java new file mode 100644 index 0000000..557310f --- /dev/null +++ b/src/main/java/com/platform/modules/topic/enums/TopicReplyTypeEnum.java @@ -0,0 +1,33 @@ +package com.platform.modules.topic.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 帖子回复类型枚举 + */ +@Getter +public enum TopicReplyTypeEnum { + + /** + * 帖子 + */ + TOPIC("1", "帖子"), + /** + * 用户 + */ + USER("2", "用户"), + ; + + @EnumValue + @JsonValue + private String code; + private String name; + + TopicReplyTypeEnum(String code, String name) { + this.code = code; + this.name = name; + } + +} diff --git a/src/main/java/com/platform/modules/topic/enums/TopicTypeEnum.java b/src/main/java/com/platform/modules/topic/enums/TopicTypeEnum.java new file mode 100644 index 0000000..b50e306 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/enums/TopicTypeEnum.java @@ -0,0 +1,37 @@ +package com.platform.modules.topic.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 帖子类型枚举 + */ +@Getter +public enum TopicTypeEnum { + + /** + * 文字/表情 + */ + TEXT("TEXT", "文字/表情"), + /** + * 图片/拍照 + */ + IMAGE("IMAGE", "图片/拍照"), + /** + * 视频 + */ + VIDEO("VIDEO", "视频"), + ; + + @EnumValue + @JsonValue + private String code; + private String name; + + TopicTypeEnum(String code, String name) { + this.code = code; + this.name = name; + } + +} diff --git a/src/main/java/com/platform/modules/topic/service/ChatTopicLikeService.java b/src/main/java/com/platform/modules/topic/service/ChatTopicLikeService.java new file mode 100644 index 0000000..3832777 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/service/ChatTopicLikeService.java @@ -0,0 +1,32 @@ +package com.platform.modules.topic.service; + +import com.platform.common.web.service.BaseService; +import com.platform.modules.topic.domain.ChatTopicLike; +import com.platform.modules.topic.vo.TopicVo05; + +import java.util.List; + +/** + *

+ * 帖子点赞 服务层 + * q3z3 + *

+ */ +public interface ChatTopicLikeService extends BaseService { + + /** + * 删除帖子 + */ + void delByTopic(Long topicId); + + /** + * 查询点赞信息 + */ + List queryTopicLike(Long topicId); + + /** + * 查询点赞信息 + */ + ChatTopicLike queryUserLike(Long topicId, Long userId); + +} diff --git a/src/main/java/com/platform/modules/topic/service/ChatTopicReplyService.java b/src/main/java/com/platform/modules/topic/service/ChatTopicReplyService.java new file mode 100644 index 0000000..3bd4496 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/service/ChatTopicReplyService.java @@ -0,0 +1,27 @@ +package com.platform.modules.topic.service; + +import com.platform.common.web.service.BaseService; +import com.platform.modules.topic.domain.ChatTopicReply; +import com.platform.modules.topic.vo.TopicVo06; + +import java.util.List; + +/** + *

+ * 帖子回复表 服务层 + * q3z3 + *

+ */ +public interface ChatTopicReplyService extends BaseService { + + /** + * 根据帖子id删除 + */ + void delByTopic(Long topicId); + + /** + * 根据帖子查询 + */ + List queryReplyList(Long topicId); + +} diff --git a/src/main/java/com/platform/modules/topic/service/ChatTopicService.java b/src/main/java/com/platform/modules/topic/service/ChatTopicService.java new file mode 100644 index 0000000..282fb0d --- /dev/null +++ b/src/main/java/com/platform/modules/topic/service/ChatTopicService.java @@ -0,0 +1,72 @@ +package com.platform.modules.topic.service; + +import com.github.pagehelper.PageInfo; +import com.platform.common.web.service.BaseService; +import com.platform.modules.topic.domain.ChatTopic; +import com.platform.modules.topic.vo.*; + +import java.util.List; + +/** + *

+ * 主题 服务层 + * q3z3 + *

+ */ +public interface ChatTopicService extends BaseService { + + /** + * 发布帖子 + */ + void sendTopic(TopicVo01 topicVo); + + /** + * 删除帖子 + */ + void delTopic(Long topicId); + + /** + * 指定人的帖子 + */ + PageInfo userTopic(Long friendId); + + /** + * 好友的帖子 + */ + PageInfo topicList(); + + /** + * 朋友圈详情 + */ + TopicVo03 topicInfo(Long topicId); + + /** + * 查询通知列表 + */ + List queryNoticeList(); + + /** + * 清空通知列表 + */ + void clearNotice(); + + /** + * 点赞 + */ + void like(Long topicId); + + /** + * 取消点赞 + */ + void cancelLike(Long topicId); + + /** + * 回复 + */ + TopicVo06 reply(TopicVo07 topicVo); + + /** + * 删除回复 + */ + void delReply(Long replyId); +} diff --git a/src/main/java/com/platform/modules/topic/service/impl/ChatTopicLikeServiceImpl.java b/src/main/java/com/platform/modules/topic/service/impl/ChatTopicLikeServiceImpl.java new file mode 100644 index 0000000..9879a49 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/service/impl/ChatTopicLikeServiceImpl.java @@ -0,0 +1,57 @@ +package com.platform.modules.topic.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.topic.dao.ChatTopicLikeDao; +import com.platform.modules.topic.domain.ChatTopicLike; +import com.platform.modules.topic.service.ChatTopicLikeService; +import com.platform.modules.topic.vo.TopicVo05; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + *

+ * 帖子点赞 服务层实现 + * q3z3 + *

+ */ +@Service("chatTopicLikeService") +public class ChatTopicLikeServiceImpl extends BaseServiceImpl implements ChatTopicLikeService { + + @Resource + private ChatTopicLikeDao chatTopicLikeDao; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatTopicLikeDao); + } + + @Override + public List queryList(ChatTopicLike t) { + List dataList = chatTopicLikeDao.queryList(t); + return dataList; + } + + @Override + public void delByTopic(Long topicId) { + ChatTopicLike query = new ChatTopicLike().setTopicId(topicId); + chatTopicLikeDao.delete(new QueryWrapper<>(query)); + } + + @Override + public List queryTopicLike(Long topicId) { + Long userId = ShiroUtils.getUserId(); + return chatTopicLikeDao.queryTopicLike(topicId, userId); + } + + @Override + public ChatTopicLike queryUserLike(Long topicId, Long userId) { + ChatTopicLike query = new ChatTopicLike().setTopicId(topicId).setUserId(userId); + return this.queryOne(query); + } + +} diff --git a/src/main/java/com/platform/modules/topic/service/impl/ChatTopicReplyServiceImpl.java b/src/main/java/com/platform/modules/topic/service/impl/ChatTopicReplyServiceImpl.java new file mode 100644 index 0000000..2af16e7 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/service/impl/ChatTopicReplyServiceImpl.java @@ -0,0 +1,72 @@ +package com.platform.modules.topic.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.topic.dao.ChatTopicReplyDao; +import com.platform.modules.topic.domain.ChatTopicReply; +import com.platform.modules.topic.enums.TopicReplyTypeEnum; +import com.platform.modules.topic.service.ChatTopicReplyService; +import com.platform.modules.topic.vo.TopicVo06; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.List; + +/** + *

+ * 帖子回复表 服务层实现 + * q3z3 + *

+ */ +@Service("chatTopicReplyService") +public class ChatTopicReplyServiceImpl extends BaseServiceImpl implements ChatTopicReplyService { + + @Resource + private ChatTopicReplyDao chatTopicReplyDao; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatTopicReplyDao); + } + + @Override + public List queryList(ChatTopicReply t) { + List dataList = chatTopicReplyDao.queryList(t); + return dataList; + } + + @Override + public void delByTopic(Long topicId) { + chatTopicReplyDao.delete(new QueryWrapper(new ChatTopicReply().setTopicId(topicId))); + } + + @Override + public List queryReplyList(Long topicId) { + Long userId = ShiroUtils.getUserId(); + List dataList = chatTopicReplyDao.queryReplyList(userId, topicId); + dataList.forEach(e -> { + // 是否可以删除 + e.setCanDeleted(userId.equals(e.getUserId()) ? YesOrNoEnum.YES : YesOrNoEnum.NO); + // 纠正注销用户 + if (StringUtils.isEmpty(e.getPortrait())) { + ChatUser chatUser = ChatUser.initUser(null); + e.setUserId(chatUser.getUserId()); + e.setNickName(chatUser.getNickName()); + e.setPortrait(chatUser.getPortrait()); + } + // 纠正注销用户 + if (TopicReplyTypeEnum.USER.equals(e.getReplyType()) && StringUtils.isEmpty(e.getToPortrait())) { + ChatUser chatUser = ChatUser.initUser(null); + e.setToNickName(chatUser.getNickName()); + e.setToPortrait(chatUser.getPortrait()); + } + }); + return dataList; + } + +} diff --git a/src/main/java/com/platform/modules/topic/service/impl/ChatTopicServiceImpl.java b/src/main/java/com/platform/modules/topic/service/impl/ChatTopicServiceImpl.java new file mode 100644 index 0000000..423f9e7 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/service/impl/ChatTopicServiceImpl.java @@ -0,0 +1,453 @@ +package com.platform.modules.topic.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.json.JSONUtil; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.platform.common.constant.AppConstants; +import com.platform.common.enums.YesOrNoEnum; +import com.platform.common.exception.BaseException; +import com.platform.common.redis.RedisUtils; +import com.platform.common.shiro.ShiroUtils; +import com.platform.common.web.page.PageDomain; +import com.platform.common.web.page.TableSupport; +import com.platform.common.web.service.impl.BaseServiceImpl; +import com.platform.modules.chat.domain.ChatFriend; +import com.platform.modules.chat.domain.ChatUser; +import com.platform.modules.chat.service.ChatFriendService; +import com.platform.modules.chat.service.ChatUserService; +import com.platform.modules.push.enums.PushNoticeEnum; +import com.platform.modules.push.service.ChatPushService; +import com.platform.modules.push.vo.PushParamVo; +import com.platform.modules.topic.dao.ChatTopicDao; +import com.platform.modules.topic.domain.ChatTopic; +import com.platform.modules.topic.domain.ChatTopicLike; +import com.platform.modules.topic.domain.ChatTopicReply; +import com.platform.modules.topic.enums.TopicNoticeTypeEnum; +import com.platform.modules.topic.enums.TopicReplyTypeEnum; +import com.platform.modules.topic.service.ChatTopicLikeService; +import com.platform.modules.topic.service.ChatTopicReplyService; +import com.platform.modules.topic.service.ChatTopicService; +import com.platform.modules.topic.vo.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 主题 服务层实现 + * q3z3 + *

+ */ +@Service("chatTopicService") +public class ChatTopicServiceImpl extends BaseServiceImpl implements ChatTopicService { + + @Resource + private ChatTopicDao chatTopicDao; + + @Resource + private ChatUserService chatUserService; + + @Resource + private ChatFriendService chatFriendService; + + @Resource + private ChatTopicLikeService chatTopicLikeService; + + @Resource + private ChatTopicReplyService chatTopicReplyService; + + @Resource + private ChatPushService chatPushService; + + @Autowired + private RedisUtils redisUtils; + + private static final Integer DEFAULT_COUNT = 10; + + @Autowired + public void setBaseDao() { + super.setBaseDao(chatTopicDao); + } + + @Override + public List queryList(ChatTopic t) { + List dataList = chatTopicDao.queryList(t); + return dataList; + } + + @Override + public void sendTopic(TopicVo01 topicVo) { + Long userId = ShiroUtils.getUserId(); + ChatTopic topic = new ChatTopic() + .setUserId(userId) + .setTopicType(topicVo.getTopicType()) + .setContent(topicVo.getContent()) + .setLocation(topicVo.getLocation()) + .setCreateTime(DateUtil.date()); + this.add(topic); + // 好友列表 + List userList = chatFriendService.queryFriendId(userId); + if (CollectionUtils.isEmpty(userList)) { + return; + } + ChatUser chatUser = ChatUser.initUser(chatUserService.getById(userId)); + List paramList = new ArrayList<>(); + userList.forEach(e -> { + paramList.add(new PushParamVo().setToId(e).setPortrait(chatUser.getPortrait())); + }); + // 推送 + chatPushService.pushNotice(paramList, PushNoticeEnum.TOPIC_RED); + } + + @Transactional + @Override + public void delTopic(Long topicId) { + ChatTopic topic = this.getById(topicId); + if (topic == null) { + return; + } + // 删除帖子 + this.deleteById(topicId); + // 删除点赞 + chatTopicLikeService.delByTopic(topicId); + // 删除回复 + chatTopicReplyService.delByTopic(topicId); + } + + @Override + public PageInfo userTopic(Long friendId) { + Long userId = ShiroUtils.getUserId(); + // 获取分页对象 + PageDomain pageDomain = TableSupport.getPageDomain(); + // 查询用户 + ChatUser chatUser = ChatUser.initUser(chatUserService.getById(friendId)); + // 昵称 + String nickName = chatUser.getNickName(); + // 判断是否是自己 + if (!userId.equals(friendId)) { + // 判断是否是好友 + ChatFriend chatFriend = chatFriendService.getFriend(userId, friendId); + // 好友 + if (chatFriend != null) { + nickName = chatFriend.getRemark(); + } + // 非好友 + else { + pageDomain.setPageNum(1); + pageDomain.setPageSize(DEFAULT_COUNT); + } + } + // 查询数据 + PageHelper.startPage(pageDomain.getPageNum(), pageDomain.getPageSize(), "create_time desc"); + List topicList = this.queryList(new ChatTopic().setUserId(friendId)); + List dataList = new ArrayList<>(); + String finalNickName = nickName; + topicList.forEach(e -> { + TopicVo04 topic = BeanUtil.copyProperties(e, TopicVo04.class) + .setNickName(finalNickName) + .setPortrait(chatUser.getPortrait()) + .setTopicId(e.getId()); + dataList.add(formatTopic(topic)); + }); + return getPageInfo(dataList, topicList); + } + + @Override + public PageInfo topicList() { + Long userId = ShiroUtils.getUserId(); + // 分页 + List topicList = chatTopicDao.topicList(userId); + List dataList = new ArrayList<>(); + topicList.forEach(e -> { + if (StringUtils.isEmpty(e.getPortrait())) { + ChatUser chatUser = ChatUser.initUser(null); + e.setNickName(chatUser.getNickName()); + e.setPortrait(chatUser.getPortrait()); + } + dataList.add(formatTopic(e)); + }); + return getPageInfo(dataList, topicList); + } + + @Override + public TopicVo03 topicInfo(Long topicId) { + Long userId = ShiroUtils.getUserId(); + ChatTopic topic = this.getById(topicId); + if (topic == null) { + throw new BaseException("帖子不存在"); + } + // 查询用户 + ChatUser chatUser = ChatUser.initUser(chatUserService.getById(topic.getUserId())); + String nickName = chatUser.getNickName(); + // 判断是否是自己 + if (!userId.equals(topic.getUserId())) { + // 判断是否是好友 + ChatFriend chatFriend = chatFriendService.getFriend(userId, topic.getUserId()); + // 好友 + if (chatFriend != null) { + nickName = chatFriend.getRemark(); + } + } + TopicVo04 topicVo = BeanUtil.copyProperties(topic, TopicVo04.class) + .setNickName(nickName) + .setPortrait(chatUser.getPortrait()) + .setTopicId(topic.getId()); + return formatTopic(topicVo); + } + + @Override + public List queryNoticeList() { + Long userId = ShiroUtils.getUserId(); + // 清空通知数 + redisUtils.delete(AppConstants.REDIS_TOPIC_NOTICE + userId); + String key = AppConstants.REDIS_TOPIC_REPLY + userId; + if (!redisUtils.hasKey(key)) { + return new ArrayList<>(); + } + List jsonStr = redisUtils.lAll(key); + List dataList = new ArrayList<>(); + jsonStr.forEach(e -> { + dataList.add(JSONUtil.toBean(e, TopicVo09.class)); + }); + return dataList; + } + + @Override + public void clearNotice() { + String key = AppConstants.REDIS_TOPIC_REPLY + ShiroUtils.getUserId(); + redisUtils.delete(key); + } + + @Override + public void like(Long topicId) { + ChatTopic topic = this.getById(topicId); + if (topic == null) { + return; + } + Long userId = ShiroUtils.getUserId(); + ChatTopicLike topicLike = chatTopicLikeService.queryUserLike(topicId, userId); + // 点过赞 + if (topicLike != null) { + // 未点赞状态 + if (!YesOrNoEnum.YES.equals(topicLike.getHasLike())) { + chatTopicLikeService.updateById(new ChatTopicLike().setId(topicLike.getId()).setHasLike(YesOrNoEnum.YES)); + } + return; + } + // 点赞操作 + chatTopicLikeService.add(new ChatTopicLike().setTopicId(topicId).setUserId(userId).setHasLike(YesOrNoEnum.YES)); + // 如果是自己给自己点赞,则不处理 + if (userId.equals(topic.getUserId())) { + return; + } + // 给贴主发送通知 + ChatUser fromUser = ChatUser.initUser(chatUserService.getById(userId)); + PushParamVo paramVo = new PushParamVo().setToId(topic.getUserId()).setPortrait(fromUser.getPortrait()); + chatPushService.pushNotice(paramVo, PushNoticeEnum.TOPIC_REPLY); + // 通知 + this.addNotice(topic.getUserId(), topic, fromUser, TopicNoticeTypeEnum.LIKE, null); + } + + @Override + public void cancelLike(Long topicId) { + ChatTopic topic = this.getById(topicId); + if (topic == null) { + return; + } + Long userId = ShiroUtils.getUserId(); + ChatTopicLike topicLike = chatTopicLikeService.queryUserLike(topicId, userId); + if (topicLike == null) { + return; + } + if (YesOrNoEnum.YES.equals(topicLike.getHasLike())) { + chatTopicLikeService.updateById(new ChatTopicLike().setId(topicLike.getId()).setHasLike(YesOrNoEnum.NO)); + } + } + + @Override + public TopicVo06 reply(TopicVo07 topicVo) { + // 回复帖子 + if (TopicReplyTypeEnum.TOPIC.equals(topicVo.getReplyType())) { + return replyTopic(topicVo); + } + // 回复回复 + return replyReply(topicVo); + } + + /** + * 回复帖子 + */ + private TopicVo06 replyTopic(TopicVo07 topicVo) { + Long topicId = topicVo.getReplyId(); + String content = topicVo.getContent(); + Long userId = ShiroUtils.getUserId(); + // 查询帖子 + ChatTopic topic = this.getById(topicId); + if (topic == null) { + return new TopicVo06(); + } + ChatTopicReply topicReply = new ChatTopicReply() + .setReplyType(TopicReplyTypeEnum.TOPIC) + .setContent(content) + .setTopicId(topic.getId()) + .setUserId(userId) + .setTargetId(topic.getId()) + .setReplyStatus(YesOrNoEnum.YES) + .setCreateTime(DateUtil.date()); + chatTopicReplyService.add(topicReply); + // 给贴主发送通知 + ChatUser fromUser = ChatUser.initUser(chatUserService.getById(userId)); + TopicVo06 result = BeanUtil.toBean(topicReply, TopicVo06.class) + .setUserId(fromUser.getUserId()) + .setNickName(fromUser.getNickName()) + .setPortrait(fromUser.getPortrait()) + .setCanDeleted(YesOrNoEnum.YES); + if (!topic.getUserId().equals(userId)) { + // 帖主推送 + PushParamVo paramVo = new PushParamVo().setToId(topic.getUserId()).setPortrait(fromUser.getPortrait()); + chatPushService.pushNotice(paramVo, PushNoticeEnum.TOPIC_REPLY); + // 帖主推送 + addNotice(topic.getUserId(), topic, fromUser, TopicNoticeTypeEnum.REPLY, content); + } + return result; + } + + /** + * 回复回复 + */ + private TopicVo06 replyReply(TopicVo07 topicVo) { + Long replyId = topicVo.getReplyId(); + String content = topicVo.getContent(); + Long userId = ShiroUtils.getUserId(); + // 回复评论 + ChatTopicReply reply = chatTopicReplyService.getById(replyId); + if (reply == null) { + return new TopicVo06(); + } + ChatTopic topic = this.getById(reply.getTopicId()); + if (topic == null) { + return new TopicVo06(); + } + ChatTopicReply topicReply = new ChatTopicReply() + .setReplyType(TopicReplyTypeEnum.USER) + .setContent(content) + .setTopicId(reply.getTopicId()) + .setUserId(userId) + .setTargetId(reply.getUserId()) + .setReplyStatus(YesOrNoEnum.YES) + .setCreateTime(DateUtil.date()); + chatTopicReplyService.add(topicReply); + // 给贴主发送通知 + ChatUser fromUser = ChatUser.initUser(chatUserService.getById(userId)); + // 帖主推送 + if (!topic.getUserId().equals(userId)) { + PushParamVo paramVo = new PushParamVo().setToId(topic.getUserId()).setPortrait(fromUser.getPortrait()); + chatPushService.pushNotice(paramVo, PushNoticeEnum.TOPIC_REPLY); + // 帖主推送 + addNotice(topic.getUserId(), topic, fromUser, TopicNoticeTypeEnum.REPLY, content); + } + // 用户推送 + if (!reply.getUserId().equals(userId)) { + PushParamVo paramVo = new PushParamVo().setToId(reply.getUserId()).setPortrait(fromUser.getPortrait()); + chatPushService.pushNotice(paramVo, PushNoticeEnum.TOPIC_REPLY); + // 用户推送 + addNotice(reply.getUserId(), topic, fromUser, TopicNoticeTypeEnum.REPLY, content); + } + ChatUser toUser = ChatUser.initUser(chatUserService.getById(reply.getUserId())); + String nickName = toUser.getNickName(); + ChatFriend chatFriend = chatFriendService.getFriend(userId, reply.getUserId()); + // 好友 + if (chatFriend != null) { + nickName = chatFriend.getRemark(); + } + return BeanUtil.toBean(topicReply, TopicVo06.class) + .setUserId(fromUser.getUserId()) + .setNickName(fromUser.getNickName()) + .setPortrait(fromUser.getPortrait()) + .setCanDeleted(YesOrNoEnum.YES) + .setToUserId(toUser.getUserId()) + .setToNickName(nickName) + .setToPortrait(toUser.getPortrait()); + } + + + @Override + public void delReply(Long replyId) { + Long userId = ShiroUtils.getUserId(); + ChatTopicReply reply = chatTopicReplyService.getById(replyId); + if (reply == null || YesOrNoEnum.NO.equals(reply.getReplyStatus())) { + return; + } + String errMsg = "不能删除此评论"; + if (TopicReplyTypeEnum.TOPIC.equals(reply.getReplyType())) { + ChatTopic topic = this.getById(reply.getTopicId()); + if (topic == null) { + return; + } + if (!topic.getUserId().equals(userId)) { + throw new BaseException(errMsg); + } + } else if (!reply.getUserId().equals(userId)) { + throw new BaseException(errMsg); + } + // 帖主/自己发布的帖子 + chatTopicReplyService.updateById(new ChatTopicReply().setReplyId(replyId).setReplyStatus(YesOrNoEnum.NO)); + } + + /** + * 执行添加 + */ + private void addNotice(Long userId, ChatTopic topic, ChatUser fromUser, TopicNoticeTypeEnum noticeType, String content) { + ThreadUtil.execAsync(() -> { + TopicVo09 topicVo = new TopicVo09() + .setTopicId(topic.getId()) + .setTopicType(topic.getTopicType()) + .setTopicContent(topic.getContent()) + .setNoticeType(noticeType) + .setUserId(fromUser.getUserId()) + .setNickName(fromUser.getNickName()) + .setPortrait(fromUser.getPortrait()) + .setReplyContent(content) + .setReplyTime(DateUtil.date()); + redisUtils.lLeftPush(AppConstants.REDIS_TOPIC_REPLY + userId, JSONUtil.toJsonStr(topicVo)); + }); + } + + /** + * 格式化帖子 + */ + private TopicVo03 formatTopic(TopicVo04 topic) { + // 查询点赞信息 + List likeList = chatTopicLikeService.queryTopicLike(topic.getTopicId()); + // 查询自己点赞 + YesOrNoEnum like = selfLike(likeList); + // 查询评论信息 + List replyList = chatTopicReplyService.queryReplyList(topic.getTopicId()); + TopicVo03 topicVo = new TopicVo03() + .setTopic(topic) + .setLikeList(likeList) + .setLike(like) + .setReplyList(replyList); + return topicVo; + } + + /** + * 是否点赞 + */ + private YesOrNoEnum selfLike(List likeList) { + Long userId = ShiroUtils.getUserId(); + List userList = likeList.stream().map(TopicVo05::getUserId).collect(Collectors.toList()); + return userList.contains(userId) ? YesOrNoEnum.YES : YesOrNoEnum.NO; + } + +} diff --git a/src/main/java/com/platform/modules/topic/vo/TopicVo01.java b/src/main/java/com/platform/modules/topic/vo/TopicVo01.java new file mode 100644 index 0000000..189821f --- /dev/null +++ b/src/main/java/com/platform/modules/topic/vo/TopicVo01.java @@ -0,0 +1,23 @@ +package com.platform.modules.topic.vo; + +import com.platform.modules.topic.enums.TopicTypeEnum; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class TopicVo01 { + + @NotNull(message = "内容类型不能为空") + private TopicTypeEnum topicType; + + @NotBlank(message = "内容不能为空") + @Size(max = 2000, message = "内容长度不能大于2000") + private String content; + + @Size(max = 2000, message = "经纬度长度不能大于2000") + private String location; + +} diff --git a/src/main/java/com/platform/modules/topic/vo/TopicVo02.java b/src/main/java/com/platform/modules/topic/vo/TopicVo02.java new file mode 100644 index 0000000..e7f052e --- /dev/null +++ b/src/main/java/com/platform/modules/topic/vo/TopicVo02.java @@ -0,0 +1,15 @@ +package com.platform.modules.topic.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class TopicVo02 { + + @NotBlank(message = "封面不能为空") + @Size(max = 2000, message = "封面长度不能大于2000") + private String cover; + +} diff --git a/src/main/java/com/platform/modules/topic/vo/TopicVo03.java b/src/main/java/com/platform/modules/topic/vo/TopicVo03.java new file mode 100644 index 0000000..8467ce1 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/vo/TopicVo03.java @@ -0,0 +1,33 @@ +package com.platform.modules.topic.vo; + +import com.platform.common.enums.YesOrNoEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(chain = true) // 链式调用 +public class TopicVo03 { + + /** + * 帖子 + */ + private TopicVo04 topic; + + /** + * 点赞信息 + */ + private List likeList; + + /** + * 是否点赞 + */ + private YesOrNoEnum like; + + /** + * 评论信息 + */ + private List replyList; + +} diff --git a/src/main/java/com/platform/modules/topic/vo/TopicVo04.java b/src/main/java/com/platform/modules/topic/vo/TopicVo04.java new file mode 100644 index 0000000..0cabaa1 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/vo/TopicVo04.java @@ -0,0 +1,46 @@ +package com.platform.modules.topic.vo; + +import com.platform.modules.topic.enums.TopicTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@Accessors(chain = true) // 链式调用 +public class TopicVo04 { + + /** + * 主键 + */ + private Long topicId; + /** + * 用户id + */ + private Long userId; + /** + * 类型 + */ + private TopicTypeEnum topicType; + /** + * 内容 + */ + private String content; + /** + * 经纬度 + */ + private String location; + /** + * 时间 + */ + private Date createTime; + /** + * 昵称 + */ + private String nickName; + /** + * 头像 + */ + private String portrait; + +} diff --git a/src/main/java/com/platform/modules/topic/vo/TopicVo05.java b/src/main/java/com/platform/modules/topic/vo/TopicVo05.java new file mode 100644 index 0000000..3c5fe24 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/vo/TopicVo05.java @@ -0,0 +1,23 @@ +package com.platform.modules.topic.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) // 链式调用 +public class TopicVo05 { + + /** + * 用户id + */ + private Long userId; + /** + * 昵称 + */ + private String nickName; + /** + * 头像 + */ + private String portrait; + +} diff --git a/src/main/java/com/platform/modules/topic/vo/TopicVo06.java b/src/main/java/com/platform/modules/topic/vo/TopicVo06.java new file mode 100644 index 0000000..16e5020 --- /dev/null +++ b/src/main/java/com/platform/modules/topic/vo/TopicVo06.java @@ -0,0 +1,59 @@ +package com.platform.modules.topic.vo; + +import com.platform.common.enums.YesOrNoEnum; +import com.platform.modules.topic.enums.TopicReplyTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@Accessors(chain = true) // 链式调用 +public class TopicVo06 { + + /** + * 回复id + */ + private Long replyId; + /** + * 回复类型1帖子2用户 + */ + private TopicReplyTypeEnum replyType; + /** + * 评论内容 + */ + private String content; + /** + * 回复时间 + */ + private Date createTime; + /** + * 用户id + */ + private Long userId; + /** + * 昵称 + */ + private String nickName; + /** + * 头像 + */ + private String portrait; + /** + * 是否可以删除 + */ + private YesOrNoEnum canDeleted; + /** + * 用户id + */ + private Long toUserId; + /** + * 昵称 + */ + private String toNickName; + /** + * 头像 + */ + private String toPortrait; + +} diff --git a/src/main/java/com/platform/modules/topic/vo/TopicVo07.java b/src/main/java/com/platform/modules/topic/vo/TopicVo07.java new file mode 100644 index 0000000..32270ae --- /dev/null +++ b/src/main/java/com/platform/modules/topic/vo/TopicVo07.java @@ -0,0 +1,34 @@ +package com.platform.modules.topic.vo; + +import com.platform.modules.topic.enums.TopicReplyTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +@Accessors(chain = true) // 链式调用 +public class TopicVo07 { + + /** + * 回复id + */ + @NotNull(message = "回复id不能为空") + private Long replyId; + + /** + * 回复类型 + */ + @NotNull(message = "回复类型不能为空") + private TopicReplyTypeEnum replyType; + + /** + * 内容 + */ + @NotBlank(message = "内容不能为空") + @Size(max = 2000, message = "内容长度不能大于2000") + private String content; + +} diff --git a/src/main/java/com/platform/modules/topic/vo/TopicVo09.java b/src/main/java/com/platform/modules/topic/vo/TopicVo09.java new file mode 100644 index 0000000..a8d9bed --- /dev/null +++ b/src/main/java/com/platform/modules/topic/vo/TopicVo09.java @@ -0,0 +1,51 @@ +package com.platform.modules.topic.vo; + +import com.platform.modules.topic.enums.TopicNoticeTypeEnum; +import com.platform.modules.topic.enums.TopicTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@Accessors(chain = true) // 链式调用 +public class TopicVo09 { + + /** + * 帖子id + */ + private Long topicId; + /** + * 帖子类型 + */ + private TopicTypeEnum topicType; + /** + * 帖子内容 + */ + private String topicContent; + /** + * 通知类型 1点赞 2回复 + */ + private TopicNoticeTypeEnum noticeType; + /** + * 用户id + */ + private Long userId; + /** + * 昵称 + */ + private String nickName; + /** + * 头像 + */ + private String portrait; + /** + * 回复内容 + */ + private String replyContent; + /** + * 回复时间 + */ + private Date replyTime; + +} diff --git a/src/main/java/com/platform/modules/ws/BootWebSocketHandler.java b/src/main/java/com/platform/modules/ws/BootWebSocketHandler.java new file mode 100644 index 0000000..b9780e7 --- /dev/null +++ b/src/main/java/com/platform/modules/ws/BootWebSocketHandler.java @@ -0,0 +1,102 @@ +package com.platform.modules.ws; + +import com.platform.common.constant.AppConstants; +import com.platform.modules.push.service.ChatPushService; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.TextWebSocketHandler; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@Component +public class BootWebSocketHandler extends TextWebSocketHandler { + + private static final ConcurrentHashMap POOL_SESSION = new ConcurrentHashMap<>(); + + @Resource + private ChatPushService chatPushService; + + /** + * socket 建立成功事件 + */ + @Override + public void afterConnectionEstablished(WebSocketSession session) { + // 用户id + Long userId = (Long) session.getAttributes().get(AppConstants.USER_ID); + // 存储 + POOL_SESSION.put(userId, session); + // 离线消息 + chatPushService.pullMsg(userId); + } + + /** + * 接收消息事件 + */ + @SneakyThrows + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage message) { + // 获得客户端传来的消息 + String payload = message.getPayload(); + log.info("server 接收到消息 {}", payload); + session.sendMessage(new TextMessage("ok")); + } + + /** + * socket 断开连接时 + */ + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { + this.closeSession(session); + } + + /** + * socket 异常连接时 + */ + @Override + public void handleTransportError(WebSocketSession session, Throwable exception) { + this.closeSession(session); + } + + /** + * 关闭session + */ + private void closeSession(WebSocketSession session) { + // 用户id + Long userId = (Long) session.getAttributes().get(AppConstants.USER_ID); + if (session.isOpen()) { + try { + session.close(); + } catch (IOException e) { + } + } + // 移除 + POOL_SESSION.remove(userId); + } + + /** + * 给某个用户发送消息 + */ + public void sendMsg(Long userId, String content) { + WebSocketSession session = POOL_SESSION.get(userId); + if (session == null) { + return; + } + if (!session.isOpen()) { + this.closeSession(session); + return; + } + try { + session.sendMessage(new TextMessage(content)); + } catch (IOException e) { + this.closeSession(session); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/platform/modules/ws/BootWebSocketInterceptor.java b/src/main/java/com/platform/modules/ws/BootWebSocketInterceptor.java new file mode 100644 index 0000000..56d72a3 --- /dev/null +++ b/src/main/java/com/platform/modules/ws/BootWebSocketInterceptor.java @@ -0,0 +1,39 @@ +package com.platform.modules.ws; + +import cn.hutool.extra.spring.SpringUtil; +import com.platform.common.constant.AppConstants; +import com.platform.common.constant.HeadConstant; +import com.platform.common.shiro.LoginUser; +import com.platform.modules.auth.service.TokenService; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; + +import java.util.Map; + +@Component +public class BootWebSocketInterceptor extends HttpSessionHandshakeInterceptor { + + @Override + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, + Map attributes) throws Exception { + // 接受前端传来的参数 + String token = ((ServletServerHttpRequest) request).getServletRequest().getParameter(HeadConstant.TOKEN_HEADER_ADMIN); + if (StringUtils.isEmpty(token)) { + return false; + } + TokenService tokenService = SpringUtil.getBean("tokenService"); + LoginUser loginUser = tokenService.queryByToken(token); + if (loginUser == null) { + return false; + } + //将参数放到attributes + attributes.put(AppConstants.USER_ID, loginUser.getUserId()); + return super.beforeHandshake(request, response, wsHandler, attributes); + } + +} diff --git a/src/main/java/com/platform/modules/ws/WebSocketConfig.java b/src/main/java/com/platform/modules/ws/WebSocketConfig.java new file mode 100644 index 0000000..2a3ae81 --- /dev/null +++ b/src/main/java/com/platform/modules/ws/WebSocketConfig.java @@ -0,0 +1,38 @@ +package com.platform.modules.ws; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/* +1、如果使用默认的嵌入式容器 比如Tomcat 则必须手工在上下文提供ServerEndpointExporter。 +2、如果使用外部容器部署war包,则不需要提供提供ServerEndpointExporter,因为此时SpringBoot默认将扫描 服务端的行为交给外部容器处理,所以线上部署的时候要把WebSocketConfig中这段注入bean的代码注掉 +*/ +@Configuration +@EnableWebSocket +public class WebSocketConfig implements WebSocketConfigurer { + + @Autowired + private BootWebSocketHandler bootWebSocketHandler; + + @Autowired + private BootWebSocketInterceptor bootWebSocketInterceptor; + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) { + webSocketHandlerRegistry + .addHandler(bootWebSocketHandler, "/ws") + .addInterceptors(bootWebSocketInterceptor) + .setAllowedOrigins("*"); + } + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..7f03f60 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,128 @@ +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8080 + servlet: + # 应用的访问路径 + context-path: / + undertow: + # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 + io-threads: 8 + # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 + worker-threads: 256 + # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 + # 每块buffer的空间大小,越小的空间被利用越充分 + buffer-size: 512 + # 是否分配的直接内存 + direct-buffers: true + +# 项目相关配置 +platform: + # 富文本路径 示例( Windows配置D:/platform/uploadPath,Linux配置 /home/platform/uploadPath) + rootPath: E:/platform/uploadPath + # 系统版本 + version: 1.2.0 + # 日志地址 + logPath: ./logs + # token超时时间(分钟)默认7天 + timeout: 10080 + # 短信开关(N关Y开) + sms: N + # 跨域开关(N关Y开) + cors: N + +# oss配置 +upload: + uploadType: oss + serverUrl: http://xxxx.oss-cn-beijing.aliyuncs.com + accessKey: xxxxxxxxxxxxxxx + secretKey: xxxxxxxxxxxxxxxxx + bucket: xxxx + region: oss-cn-beijing.aliyuncs.com + post: ?x-oss-process=video/snapshot,t_1,m_fast + +# 实时语音/视频 +trtc: + # appId + appId: xxxxxxxxxx + # 签名过期时间,单位秒,默认604800 + expire: 604800 + # 签名秘钥 + secret: xxxxxxxxxxxxxxxxxxxx + +# 高德地图配置 +amap: + key: xxxxxxxxxxxxxxxxxxxxx + +# 腾讯nlp配置 +tencent: + appId: xxxxxxxxxxxxxxxx + appKey: xxxxxxxxxxxxxxxxxxxxxx + appSecret: xxxxxxxxxxxxxxxxxxxxxxxxxx + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + mvc: + throw-exception-if-no-handler-found: true + resources: + add-mappings: false + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: -1 + # 设置总上传的文件大小 + max-request-size: -1 + + # ShardingSphere 配置项 + shardingsphere: + # 数据源配置 + datasource: + # 所有数据源的名字 + names: master + # 主库的数据源配置 + master: + type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: jdbc:mysql://127.0.0.1:3306/boot-im?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&verifyServerCertificate=false&serverTimezone=GMT%2B8&allowMultiQueries=true + username: root + password: root + # 拓展属性配置 + props: + sql: + show: true # 打印 SQL + sharding: + default-data-source-name: master + + # redis 配置 + redis: + # 地址 + host: 127.0.0.1 + # 端口,默认为6379 + port: 6379 + # 数据库 + database: 0 + # 密码 + password: + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + +# 日志配置 +logging: + level: + com.platform: debug + org.springframework: warn \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..d8eb012 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,30 @@ +spring: + profiles: + active: dev + +# MyBatis配置 +mybatis-plus: + mapperLocations: classpath*:mapper/**/*Dao.xml + #实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: com.platform + typeEnumsPackage: com.platform + configuration: + defaultEnumTypeHandler: org.apache.ibatis.type.EnumOrdinalTypeHandler + mapUnderscoreToCamelCase: true + #日志打印 + #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + cacheEnabled: true + global-config: + banner: false + dbConfig: + #逻辑删除 + logicDeleteValue: "null" # 逻辑已删除值(默认为 1) + logicNotDeleteValue: 0 # 逻辑未删除值(默认为 0) + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + # 合理化分页 + reasonable: false + supportMethodsArguments: true + params: count=countSql \ No newline at end of file diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..b4c1ad4 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,3 @@ +Application Version: ${platform.version} +Spring Boot Version: ${spring-boot.version} +Spring Boot port : ${server.port} \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..90af2ec --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,73 @@ + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/chat/ChatApplyDao.xml b/src/main/resources/mapper/chat/ChatApplyDao.xml new file mode 100644 index 0000000..4f5a1d0 --- /dev/null +++ b/src/main/resources/mapper/chat/ChatApplyDao.xml @@ -0,0 +1,26 @@ + + + + + + + select id, from_id, to_id, target_id, apply_type, apply_status, apply_source, reason, create_time from chat_apply + + + + + + diff --git a/src/main/resources/mapper/chat/ChatFeedbackDao.xml b/src/main/resources/mapper/chat/ChatFeedbackDao.xml new file mode 100644 index 0000000..7031e39 --- /dev/null +++ b/src/main/resources/mapper/chat/ChatFeedbackDao.xml @@ -0,0 +1,42 @@ + + + + + + + select id, user_id, images, content, version, create_time from chat_feedback + + + + + + diff --git a/src/main/resources/mapper/chat/ChatFriendDao.xml b/src/main/resources/mapper/chat/ChatFriendDao.xml new file mode 100644 index 0000000..81c4624 --- /dev/null +++ b/src/main/resources/mapper/chat/ChatFriendDao.xml @@ -0,0 +1,33 @@ + + + + + + + select id, from_id, to_id, remark, black, source, create_time, top from chat_friend + + + + + + + + + + diff --git a/src/main/resources/mapper/chat/ChatGroupDao.xml b/src/main/resources/mapper/chat/ChatGroupDao.xml new file mode 100644 index 0000000..8962a95 --- /dev/null +++ b/src/main/resources/mapper/chat/ChatGroupDao.xml @@ -0,0 +1,27 @@ + + + + + + + select id, name, notice, portrait, master, create_time from chat_group + + + + + + + + diff --git a/src/main/resources/mapper/chat/ChatGroupInfoDao.xml b/src/main/resources/mapper/chat/ChatGroupInfoDao.xml new file mode 100644 index 0000000..7f34544 --- /dev/null +++ b/src/main/resources/mapper/chat/ChatGroupInfoDao.xml @@ -0,0 +1,23 @@ + + + + + + + select info_id, user_id, group_id, top, disturb, keep_group, create_time from chat_group_info + + + + + + diff --git a/src/main/resources/mapper/chat/ChatMsgDao.xml b/src/main/resources/mapper/chat/ChatMsgDao.xml new file mode 100644 index 0000000..2462894 --- /dev/null +++ b/src/main/resources/mapper/chat/ChatMsgDao.xml @@ -0,0 +1,32 @@ + + + + + + + select id, from_id, to_id, msg_type, content, create_time from chat_msg + + + + + + diff --git a/src/main/resources/mapper/chat/ChatUserDao.xml b/src/main/resources/mapper/chat/ChatUserDao.xml new file mode 100644 index 0000000..1e948f1 --- /dev/null +++ b/src/main/resources/mapper/chat/ChatUserDao.xml @@ -0,0 +1,24 @@ + + + + + + + select user_id, nick_name, intro, chat_no, gender, portrait, cover, provinces, city, phone, password, salt, token, version, create_time from chat_user + + + + + + diff --git a/src/main/resources/mapper/chat/ChatVersionDao.xml b/src/main/resources/mapper/chat/ChatVersionDao.xml new file mode 100644 index 0000000..23fbd03 --- /dev/null +++ b/src/main/resources/mapper/chat/ChatVersionDao.xml @@ -0,0 +1,18 @@ + + + + + + + select id, version, url, content from chat_version + + + + + + diff --git a/src/main/resources/mapper/collect/ChatCollectDao.xml b/src/main/resources/mapper/collect/ChatCollectDao.xml new file mode 100644 index 0000000..6fa9896 --- /dev/null +++ b/src/main/resources/mapper/collect/ChatCollectDao.xml @@ -0,0 +1,23 @@ + + + + + + + select id, user_id, collect_type, content, create_time from chat_collect + + + + + + diff --git a/src/main/resources/mapper/topic/ChatTopicDao.xml b/src/main/resources/mapper/topic/ChatTopicDao.xml new file mode 100644 index 0000000..487dd52 --- /dev/null +++ b/src/main/resources/mapper/topic/ChatTopicDao.xml @@ -0,0 +1,43 @@ + + + + + + + select id, user_id, topic_type, content, location, create_time from chat_topic + + + + + + + + diff --git a/src/main/resources/mapper/topic/ChatTopicLikeDao.xml b/src/main/resources/mapper/topic/ChatTopicLikeDao.xml new file mode 100644 index 0000000..52a04a3 --- /dev/null +++ b/src/main/resources/mapper/topic/ChatTopicLikeDao.xml @@ -0,0 +1,29 @@ + + + + + + + select id, topic_id, user_id, has_like from chat_topic_like + + + + + + + + diff --git a/src/main/resources/mapper/topic/ChatTopicReplyDao.xml b/src/main/resources/mapper/topic/ChatTopicReplyDao.xml new file mode 100644 index 0000000..b88c1a3 --- /dev/null +++ b/src/main/resources/mapper/topic/ChatTopicReplyDao.xml @@ -0,0 +1,42 @@ + + + + + + + select reply_id, reply_type, reply_status, content, topic_id, user_id, target_id, create_time from chat_topic_reply + + + + + + + + diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2072d9bd00b9116cd348615f5af70143f928de40 GIT binary patch literal 4286 zcmb`K2~bt%7018#fcH?T5RJG*eFme5E25$z2ta|kanz$4EP1^2$Gqt__2dak#dep)pf$EXL zPo!Vp*h`?N7QaFA-rr?>7$uB_Z9KffA*U5TM2XC3ysWz z$fe+y;s?uE8Dr^dV|8S9>RS>QX`j8AukC)R0Q;ALe;WMz>dE1na(2YqO3|ng<@_5} z^h)0-Cti1|`v>$yZaT4CsDILPsUYPHQutQ@JpDv2rOR zOu0DLK7;g!a_$Y6dTi)hoOQYO`77P9yB)SXjzj*#0Vz&TSfZN4;EpxT^dRAl3BglsoeA9^~@df$oAisp~ zeUFnk&%n-1nRmYFjSrYJn_A?+F*7fsE^+V&Q{yum|EmeYd;OOg-Wyrzb_~vqY=|Es z@q;akpM{YZA@KD;u-1Z8Phx?8-S-~&b3D_{+$KWW|8p*|!Ov>oy)hxpd-GR;wBOPGJCD}2)$-SoZpT$wqm`C#_`9B5kxyQTdO?^hEB zfd33jfGJmPRupSEE1lH@n}zRB^=&!uPvM&%!0tmHRKeHR_YkZQYOoGmDD;i-Ro?&i z9}<(*hE-qQm$G56)k}Pz5#%U&JH#RN`~LiF6*2JF;fr$KrIQC$-c{fa(?5yZPW|*H zhv)@~RrWmkgPa!LuO|uWElk^-5CPZ3k?$$U#Ati^J*yrV5TNbr|7d&VFZjd3zl;3^ zMnR;5Jh0!v|3Y68Be`bVec{oGlj43{;N~tCR=7F8H5=|2?_)duk#Q4ytc_+@Uobl%ucdb|0SD zK@Z5%wkLmszSu|j8s#vOSznJ5x#8xxSW|!A70SP0e&rV$z|8v>Tdn{Euwck zte68%f**=M1$^S-3=Y=gYO1}LTnr~hDMh3Iu3Q=S(EDZPJJoLH1%4O!|BuAqs+>c1 zy!ei`)sAaTqJH1W89_Nd%t^mo0=@-b?eM#?;-U00_)WmyO7Ff;4(ukc8q>>a607l@ zoDs;of#5#?|7=a0jSW48Nx#%TO(6aWeOOKPO*M01P5R_J8~@_i_TZO*f2F$pU z+SFkx^Y2^aZi@GB6J5wvC-OwkEAXSxV}gIAIw89zW;KguCWuqcj0#lFk8&|%1Sl8A zR44L8&nx_&4E{avSNanouSE$jsI3w7g(dhR9bRyA7JIvVubdbrbHhU|M3juiugoAn zb=mXMwM-fRTx|Il`Twdv8G5Abo8i>;MflBvSMI{EGmMYg1;cT&?!=vzLe7Va)xG`K zetUmL;1l8hr~bmp|03#ti}Gb=?HcgwgMYpP-!k(f?}It!#-TbjvM0&- z2U7!+!S`0+Tljnuy+If&yHv{3SA=>xP4a6H{MHG*Q^~Ki?`~uT!I@6_Peb@}3j5~a zhmP9OA@+=3si2x0yT8tE?&RB~BeTJ~*nfbYv;|(A3?C-b58CoA)0%IhZtCGdW3jIQ zexHkf;KmU55tM^5+exwVC)OOD@pWkw- z5!MQ|tXmH2VzJ1cUnqKuoZmDRj`ENsw>5WmsC6Pg)bif;Sj7Pz>pQn6@y>it=q*oO zccELja{_V#T#g)P;?Y89cJXaz_ASe8$yJKZ((;N`);znkEF|$(S>5