Repository: lining90567/leo-im-server Branch: master Commit: 35759b9caf08 Files: 357 Total size: 517.4 KB Directory structure: gitextract_5xra_4nj/ ├── .classpath ├── .gitignore ├── .project ├── .settings/ │ ├── org.eclipse.core.resources.prefs │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.m2e.core.prefs ├── LICENSE ├── README.md ├── assemble/ │ ├── bin/ │ │ ├── run.bat │ │ └── run.sh │ └── package.xml ├── leo-im-api/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── api/ │ │ │ ├── annotation/ │ │ │ │ ├── Cacheable.java │ │ │ │ └── Transactional.java │ │ │ ├── dto/ │ │ │ │ ├── ChannelDTO.java │ │ │ │ ├── ChannelListDTO.java │ │ │ │ ├── ChannelMemberDTO.java │ │ │ │ ├── FileDTO.java │ │ │ │ ├── MessageDTO.java │ │ │ │ ├── UserChannelDTO.java │ │ │ │ └── UserDTO.java │ │ │ ├── exception/ │ │ │ │ └── ServiceException.java │ │ │ └── service/ │ │ │ ├── ChannelService.java │ │ │ ├── MessageService.java │ │ │ ├── UnreadMessageCountService.java │ │ │ ├── UserChannelService.java │ │ │ └── UserService.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── api/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-api/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.api.AppTest.xml │ └── org.leo.im.api.AppTest.txt ├── leo-im-api-provider/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── api/ │ │ │ └── provider/ │ │ │ └── ServiceFactory.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── api/ │ │ └── provider/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-api-provider/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.api.provider.AppTest.xml │ └── org.leo.im.api.provider.AppTest.txt ├── leo-im-common/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── common/ │ │ │ └── data/ │ │ │ └── Page.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── common/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-common/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.common.AppTest.xml │ └── org.leo.im.common.AppTest.txt ├── leo-im-http/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── http/ │ │ │ ├── HttpServer.java │ │ │ ├── cache/ │ │ │ │ ├── Cache.java │ │ │ │ ├── CacheManager.java │ │ │ │ ├── CacheManagerFactory.java │ │ │ │ └── MapCacheManager.java │ │ │ ├── constant/ │ │ │ │ └── CacheKeys.java │ │ │ ├── controller/ │ │ │ │ ├── AuthController.java │ │ │ │ ├── BaseController.java │ │ │ │ ├── ChannelController.java │ │ │ │ ├── ExceptionController.java │ │ │ │ ├── MessageController.java │ │ │ │ ├── UserChannelController.java │ │ │ │ └── UserController.java │ │ │ ├── exception/ │ │ │ │ └── NoSuchSettingException.java │ │ │ ├── file/ │ │ │ │ ├── AbstractLocalFileStorage.java │ │ │ │ ├── AvatarStorage.java │ │ │ │ ├── AvatarStorageFactory.java │ │ │ │ ├── FileStorage.java │ │ │ │ ├── FileStorageFactory.java │ │ │ │ ├── LocalAvatarStorage.java │ │ │ │ └── LocalFileStorage.java │ │ │ ├── interceptor/ │ │ │ │ ├── AuthenticationInterceptor.java │ │ │ │ └── CorsInterceptor.java │ │ │ ├── util/ │ │ │ │ └── JwtUtils.java │ │ │ └── vo/ │ │ │ ├── ChannelListVO.java │ │ │ ├── ChannelVO.java │ │ │ ├── MessageVO.java │ │ │ └── UserChannelVO.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── http/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-http/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.http.AppTest.xml │ └── org.leo.im.http.AppTest.txt ├── leo-im-migration/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── leo/ │ │ │ │ └── im/ │ │ │ │ └── migration/ │ │ │ │ └── FlywayMigration.java │ │ │ └── resources/ │ │ │ └── db/ │ │ │ └── migration/ │ │ │ └── V20180615___Init.sql │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── migration/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ ├── META-INF/ │ │ │ ├── MANIFEST.MF │ │ │ └── maven/ │ │ │ └── org.leo.im/ │ │ │ └── leo-im-migration/ │ │ │ ├── pom.properties │ │ │ └── pom.xml │ │ └── db/ │ │ └── migration/ │ │ └── V20180615___Init.sql │ ├── maven-archiver/ │ │ └── pom.properties │ └── maven-status/ │ └── maven-compiler-plugin/ │ └── compile/ │ └── default-compile/ │ ├── createdFiles.lst │ └── inputFiles.lst ├── leo-im-model/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── model/ │ │ │ ├── Channel.java │ │ │ ├── ChannelMember.java │ │ │ ├── File.java │ │ │ ├── Message.java │ │ │ ├── User.java │ │ │ └── UserChannel.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── model/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-model/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.model.AppTest.xml │ └── org.leo.im.model.AppTest.txt ├── leo-im-notification/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── notification/ │ │ │ ├── ActionNames.java │ │ │ ├── PublishKeys.java │ │ │ ├── Publisher.java │ │ │ ├── PublisherFactory.java │ │ │ ├── QueuePublisher.java │ │ │ ├── Subscriber.java │ │ │ ├── ThreadPoolHolder.java │ │ │ └── event/ │ │ │ ├── AvatarChangedEvent.java │ │ │ ├── ChannelCreatedEvent.java │ │ │ ├── ChannelNameChangedEvent.java │ │ │ ├── ChannelRemovedEvent.java │ │ │ ├── JoinChannelEvent.java │ │ │ ├── LeaveChannelEvent.java │ │ │ ├── MembersCountChangedEvent.java │ │ │ ├── MessageRemovedEvent.java │ │ │ ├── NewMessageEvent.java │ │ │ ├── NicknameChangedEvent.java │ │ │ ├── NotificationEvent.java │ │ │ ├── OnlineStatusChangedEvent.java │ │ │ ├── ReadMessageEvent.java │ │ │ └── RemoveFromChannelEvent.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── notification/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-notification/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.notification.AppTest.xml │ └── org.leo.im.notification.AppTest.txt ├── leo-im-service/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── service/ │ │ │ ├── ChannelServiceImpl.java │ │ │ ├── MessageServiceImpl.java │ │ │ ├── UnreadMessageCountServiceImpl.java │ │ │ ├── UserChannelServiceImpl.java │ │ │ ├── UserServiceImpl.java │ │ │ ├── support/ │ │ │ │ ├── CacheableHolder.java │ │ │ │ └── ServiceProxy.java │ │ │ └── util/ │ │ │ └── PasswordUtils.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── service/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-service/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.service.AppTest.xml │ └── org.leo.im.service.AppTest.txt ├── leo-im-socket/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── socket/ │ │ │ ├── ChannelIdSet.java │ │ │ ├── ChannelsHolder.java │ │ │ ├── SocketChannel.java │ │ │ ├── WebSocketChannelInitializer.java │ │ │ ├── WebSocketServer.java │ │ │ ├── exception/ │ │ │ │ └── MessageHandleException.java │ │ │ ├── handler/ │ │ │ │ └── TextWebSocketFrameHandler.java │ │ │ └── subscription/ │ │ │ ├── QueueSubscriber.java │ │ │ ├── SubscriberFactory.java │ │ │ └── handler/ │ │ │ ├── AbstractMessageHandler.java │ │ │ ├── AvatarChangedHandler.java │ │ │ ├── ChannelCreatedHandler.java │ │ │ ├── ChannelNameChangedHandler.java │ │ │ ├── ChannelRemovedHandler.java │ │ │ ├── JoinChannelHandler.java │ │ │ ├── LeaveChannelHandler.java │ │ │ ├── MembersCountChangedHandler.java │ │ │ ├── MessageHandler.java │ │ │ ├── MessageHandlerFactory.java │ │ │ ├── NewMessageHandler.java │ │ │ ├── NicknameChangedHandler.java │ │ │ ├── OnlineStatusChangedHandler.java │ │ │ ├── ReadMessageHandler.java │ │ │ ├── RemoveFromChannelHandler.java │ │ │ └── RemoveMessageHandler.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── socket/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-socket/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.socket.AppTest.xml │ └── org.leo.im.socket.AppTest.txt ├── leo-im-starter/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ ├── conf/ │ │ │ │ └── app.conf │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── starter/ │ │ │ └── App.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── starter/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-starter/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── leo-im-starter-1.0/ │ │ ├── bin/ │ │ │ ├── run.bat │ │ │ └── run.sh │ │ └── conf/ │ │ └── app.conf │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.starter.AppTest.xml │ └── org.leo.im.starter.AppTest.txt ├── leo-im-store/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── store/ │ │ │ ├── connection/ │ │ │ │ ├── ConnectionFactory.java │ │ │ │ ├── ConnectionProvider.java │ │ │ │ └── impl/ │ │ │ │ └── PoolConnectionFactory.java │ │ │ ├── dao/ │ │ │ │ ├── BaseDAO.java │ │ │ │ ├── ChannelDAO.java │ │ │ │ ├── ChannelMemberDAO.java │ │ │ │ ├── FileDAO.java │ │ │ │ ├── HideChannelDAO.java │ │ │ │ ├── MessageDAO.java │ │ │ │ ├── UnreadMessageCountDAO.java │ │ │ │ ├── UserChannelDAO.java │ │ │ │ ├── UserDAO.java │ │ │ │ └── impl/ │ │ │ │ ├── JdbcChannelDAOImpl.java │ │ │ │ ├── JdbcChannelMemberDAOImpl.java │ │ │ │ ├── JdbcFileDAOImpl.java │ │ │ │ ├── JdbcHideChannelDAOImpl.java │ │ │ │ ├── JdbcMessageDAOImpl.java │ │ │ │ ├── JdbcUnreadMessageCountDAOImpl.java │ │ │ │ ├── JdbcUserChannelDAOImpl.java │ │ │ │ └── JdbcUserDAOImpl.java │ │ │ ├── datasource/ │ │ │ │ ├── ConnectionPool.java │ │ │ │ └── impl/ │ │ │ │ └── DruidConnectionPool.java │ │ │ ├── exception/ │ │ │ │ └── DAOException.java │ │ │ ├── factory/ │ │ │ │ └── DAOFactory.java │ │ │ ├── support/ │ │ │ │ ├── BatchSqlBuildResult.java │ │ │ │ ├── Parameter.java │ │ │ │ ├── ParameterDataTypeEnum.java │ │ │ │ └── SqlBuildResult.java │ │ │ └── util/ │ │ │ ├── DbUtils.java │ │ │ └── FirstLetterUtil.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── store/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-store/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.store.AppTest.xml │ └── org.leo.im.store.AppTest.txt ├── leo-im-util/ │ ├── .classpath │ ├── .project │ ├── .settings/ │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── leo/ │ │ │ └── im/ │ │ │ └── util/ │ │ │ ├── BeanUtils.java │ │ │ └── JwtUtils.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── leo/ │ │ └── im/ │ │ └── util/ │ │ └── AppTest.java │ └── target/ │ ├── classes/ │ │ └── META-INF/ │ │ ├── MANIFEST.MF │ │ └── maven/ │ │ └── org.leo.im/ │ │ └── leo-im-util/ │ │ ├── pom.properties │ │ └── pom.xml │ ├── maven-archiver/ │ │ └── pom.properties │ ├── maven-status/ │ │ └── maven-compiler-plugin/ │ │ ├── compile/ │ │ │ └── default-compile/ │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile/ │ │ └── default-testCompile/ │ │ ├── createdFiles.lst │ │ └── inputFiles.lst │ └── surefire-reports/ │ ├── TEST-org.leo.im.util.AppTest.xml │ └── org.leo.im.util.AppTest.txt └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .classpath ================================================ ================================================ FILE: .gitignore ================================================ # Compiled class file *.class # Log file *.log # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* ================================================ FILE: .project ================================================ leo-im org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: .settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding/=UTF-8 ================================================ FILE: .settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: .settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed 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 http://www.apache.org/licenses/LICENSE-2.0 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. ================================================ FILE: README.md ================================================ # ![Leo-IM](https://raw.githubusercontent.com/wiki/lining90567/leo-im-server/leo-im.png) Leo-IM,开源好用的IM。 Leo-IM是基于Java语言、Netty框架、Vue+Element-UI开发的轻量级IM,服务端可独立运行(无需部署到web容器),HTTP服务和Socket服务可分开部署,使用基于Netty扩展的[netty-rest-server](https://github.com/lining90567/netty-rest-server)RESTful框架提供Web服务,简单易用,方便扩展。 ## 在线演示 演示地址:http://43.138.44.47:8000 建议使用Chrome浏览器 - 演示用户1:用户名 test1,口令 123456 - 演示用户2:用户名 test2,口令 123456 - 演示用户3:用户名 test3,口令 123456 ## 运行环境要求 - 服务端:Java8、MySQL5.5+ - 客户端:Chrome、IE10+ ## 主要功能 - 私聊 - 群聊 - 文字、表情、图片、文件 ## 构建与部署 - 安装netty-rest-server到本地仓库 mvn install:install-file -Dfile=netty-rest-server-1.0.jar -DgroupId=org.leo -DartifactId=netty-rest-server -Dversion=1.0 -Dpackaging=jar - 创建数据库,并设置字符集(my.cnf或my.ini) [client] default-character-set=utf8mb4 [mysqld] character-set-client-handshake = FALSE character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci init_connect=’SET NAMES utf8mb4' [mysql] default-character-set=utf8mb4 - 构建 mvn package - 部署 解压leo-im-1.0.zip,修改conf/app.conf的相关配置 - 启动 nohup bin/run.sh >/dev/null 2>&1 & - Web端代码 https://github.com/lining90567/leo-im-web ## 联系方式 - **邮箱** - lining90567@sina.com - **QQ** - 328616209 ================================================ FILE: assemble/bin/run.bat ================================================ @REM app launcher script @REM @REM Environment: @REM JAVA_HOME - location of a JDK home dir (optional if java on path) @setlocal enabledelayedexpansion @echo off cd %~dp0 cd ../ if "%JAVA_OPS%" == "" set JAVA_OPS=-Dfile.encoding=utf-8 -Dio.netty.noUnsafe=true -server -Xmx128m -Xms128m -Xss256k java %JAVA_OPS% -Dconf.home=%cd%\conf\ -jar %cd%\lib\leo-im-starter-1.0.jar ================================================ FILE: assemble/bin/run.sh ================================================ #!/bin/sh ### ------------------------------- ### ### leo-im-server launcher script ### ### ------------------------------- ### cd `dirname $0` cd ../ if [ -z "$JAVA_OPS" ]; then JAVA_OPS="-Dfile.encoding=utf-8 -Dio.netty.noUnsafe=true -Xms128M -Xmx128M -Xss256K" fi java $JAVA_OPS -Dconf.home=$(pwd)/conf/ -jar $(pwd)/lib/leo-im-starter-1.0.jar ================================================ FILE: assemble/package.xml ================================================ package zip true ../assemble/bin /bin src/main/conf /conf lib runtime ================================================ FILE: leo-im-api/.classpath ================================================ ================================================ FILE: leo-im-api/.project ================================================ leo-im-api org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-api/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-api/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-api/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-api/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-api leo-im-api http://maven.apache.org org.leo.im leo-im-common ${parent.version} ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/annotation/Cacheable.java ================================================ package org.leo.im.api.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 缓存注解 * * @author Leo * @date 2018/3/19 */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @Inherited public @interface Cacheable { } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/annotation/Transactional.java ================================================ package org.leo.im.api.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; /** * 事务注解,该注解需要在服务接口上使用,在实现类上使用无效。 * * @author Leo * @date 2018/3/19 */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @Inherited public @interface Transactional { } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/dto/ChannelDTO.java ================================================ package org.leo.im.api.dto; import java.util.ArrayList; import java.util.List; /** * 频道dto类 * * @author Leo * @date 2018/4/11 */ public final class ChannelDTO { private String id; private String name; private String type; private int memberCount; private long createAt; private String creatorId; private String fromUserId; private String fromUsername; private String fromUserNickname; private String toUserId; private String toUsername; private String toUserNickname; private String toUserOnlineStatus; private String purpose; private List members = new ArrayList<>(128); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public int getMemberCount() { return memberCount; } public void setMemberCount(int memberCount) { this.memberCount = memberCount; } public long getCreateAt() { return createAt; } public void setCreateAt(long createAt) { this.createAt = createAt; } public String getCreatorId() { return creatorId; } public void setCreatorId(String creatorId) { this.creatorId = creatorId; } public String getFromUserId() { return fromUserId; } public void setFromUserId(String fromUserId) { this.fromUserId = fromUserId; } public String getFromUsername() { return fromUsername; } public void setFromUsername(String fromUsername) { this.fromUsername = fromUsername; } public String getFromUserNickname() { return fromUserNickname; } public void setFromUserNickname(String fromUserNickname) { this.fromUserNickname = fromUserNickname; } public String getToUserId() { return toUserId; } public void setToUserId(String toUserId) { this.toUserId = toUserId; } public String getToUsername() { return toUsername; } public void setToUsername(String toUsername) { this.toUsername = toUsername; } public String getToUserNickname() { return toUserNickname; } public void setToUserNickname(String toUserNickname) { this.toUserNickname = toUserNickname; } public String getToUserOnlineStatus() { return toUserOnlineStatus; } public void setToUserOnlineStatus(String toUserOnlineStatus) { this.toUserOnlineStatus = toUserOnlineStatus; } public List getMembers() { return members; } public String getPurpose() { return purpose; } public void setPurpose(String purpose) { this.purpose = purpose; } @Override public String toString() { return "ChannelDTO [id=" + id + ", name=" + name + ", type=" + type + ", memberCount=" + memberCount + ", createAt=" + createAt + ", creatorId=" + creatorId + ", fromUserId=" + fromUserId + ", fromUsername=" + fromUsername + ", fromUserNickname=" + fromUserNickname + ", toUserId=" + toUserId + ", toUsername=" + toUsername + ", toUserNickname=" + toUserNickname + ", toUserOnlineStatus=" + toUserOnlineStatus + ", purpose=" + purpose + ", members=" + members + "]"; } } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/dto/ChannelListDTO.java ================================================ package org.leo.im.api.dto; /** * Channel列表DTO * * @author Leo * @date 2018/3/30 */ public class ChannelListDTO { private String id; private String name; private String displayName; private String otherSideOnlineStatus; private String type; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getOtherSideOnlineStatus() { return otherSideOnlineStatus; } public void setOtherSideOnlineStatus(String otherSideOnlineStatus) { this.otherSideOnlineStatus = otherSideOnlineStatus; } public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String toString() { return "ChannelListDTO [id=" + id + ", name=" + name + ", displayName=" + displayName + ", otherSideOnlineStatus=" + otherSideOnlineStatus + ", type=" + type + "]"; } } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/dto/ChannelMemberDTO.java ================================================ package org.leo.im.api.dto; /** * 频道成员dto类 * * @author Leo * @date 2018/4/11 */ public class ChannelMemberDTO { private String id; private String nickname; private boolean admin; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public boolean getAdmin() { return admin; } public void setAdmin(boolean admin) { this.admin = admin; } @Override public String toString() { return "ChannelMemberDTO [id=" + id + ", nickname=" + nickname + ", admin=" + admin + "]"; } } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/dto/FileDTO.java ================================================ package org.leo.im.api.dto; /** * 文件dto类 * * @author Leo * @date 2018/6/13 */ public class FileDTO { private String id; private String name; private String extension; private int size; private String mimeType; private int width; private int height; private short thumbWidth; private short thumbHeight; private String path; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getExtension() { return extension; } public void setExtension(String extension) { this.extension = extension; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String getMimeType() { return mimeType; } public void setMimeType(String mimeType) { this.mimeType = mimeType; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public short getThumbWidth() { return thumbWidth; } public void setThumbWidth(short thumbWidth) { this.thumbWidth = thumbWidth; } public short getThumbHeight() { return thumbHeight; } public void setThumbHeight(short thumbHeight) { this.thumbHeight = thumbHeight; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } @Override public String toString() { return "FileDTO [id=" + id + ", name=" + name + ", extension=" + extension + ", size=" + size + ", mimeType=" + mimeType + ", width=" + width + ", height=" + height + ", thumbWidth=" + thumbWidth + ", thumbHeight=" + thumbHeight + ", path=" + path + "]"; } } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/dto/MessageDTO.java ================================================ package org.leo.im.api.dto; /** * 消息dto类 * * @author Leo * @date 2018/5/15 */ public class MessageDTO { private long id; private String channelId; private String channelType; private long createAt; private String type; private String senderId; private String senderName; private String senderNickname; private String senderOnlineStatus; private String senderAvatarUrl; private String senderFirstLetterOfName; private String content; private String fileId; private String fileName; private String fileExtension; private int fileSize; private String fileMimeType; private int imageWidth; private int imageHeight; private short imageThumbWidth; private short imageThumbHeight; private String filePath; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getChannelId() { return channelId; } public void setChannelId(String channelId) { this.channelId = channelId; } public String getChannelType() { return channelType; } public void setChannelType(String channelType) { this.channelType = channelType; } public long getCreateAt() { return createAt; } public void setCreateAt(long createAt) { this.createAt = createAt; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSenderId() { return senderId; } public void setSenderId(String senderId) { this.senderId = senderId; } public String getSenderName() { return senderName; } public void setSenderName(String senderName) { this.senderName = senderName; } public String getSenderNickname() { return senderNickname; } public void setSenderNickname(String senderNickname) { this.senderNickname = senderNickname; } public String getSenderOnlineStatus() { return senderOnlineStatus; } public void setSenderOnlineStatus(String senderOnlineStatus) { this.senderOnlineStatus = senderOnlineStatus; } public String getSenderAvatarUrl() { return senderAvatarUrl; } public void setSenderAvatarUrl(String senderAvatarUrl) { this.senderAvatarUrl = senderAvatarUrl; } public String getSenderFirstLetterOfName() { return senderFirstLetterOfName; } public void setSenderFirstLetterOfName(String senderFirstLetterOfName) { this.senderFirstLetterOfName = senderFirstLetterOfName; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getFileMimeType() { return fileMimeType; } public void setFileMimeType(String fileMimeType) { this.fileMimeType = fileMimeType; } public int getImageWidth() { return imageWidth; } public void setImageWidth(int imageWidth) { this.imageWidth = imageWidth; } public int getImageHeight() { return imageHeight; } public void setImageHeight(int imageHeight) { this.imageHeight = imageHeight; } public short getImageThumbWidth() { return imageThumbWidth; } public void setImageThumbWidth(short imageThumbWidth) { this.imageThumbWidth = imageThumbWidth; } public short getImageThumbHeight() { return imageThumbHeight; } public void setImageThumbHeight(short imageThumbHeight) { this.imageThumbHeight = imageThumbHeight; } public String getFileId() { return fileId; } public void setFileId(String fileId) { this.fileId = fileId; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getFileExtension() { return fileExtension; } public void setFileExtension(String fileExtension) { this.fileExtension = fileExtension; } public int getFileSize() { return fileSize; } public void setFileSize(int fileSize) { this.fileSize = fileSize; } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } public String getSenderRealAvatarUrl() { if("http://".equalsIgnoreCase(this.senderAvatarUrl) || "https://".equalsIgnoreCase(this.senderAvatarUrl)) { return this.senderAvatarUrl; } if(this.senderAvatarUrl != null && !this.senderAvatarUrl.trim().isEmpty()) { return this.senderAvatarUrl; } return null; } @Override public String toString() { return "MessageDTO [id=" + id + ", channelId=" + channelId + ", channelType=" + channelType + ", createAt=" + createAt + ", type=" + type + ", senderId=" + senderId + ", senderName=" + senderName + ", senderNickname=" + senderNickname + ", senderOnlineStatus=" + senderOnlineStatus + ", senderAvatarUrl=" + senderAvatarUrl + ", senderFirstLetterOfName=" + senderFirstLetterOfName + ", content=" + content + ", fileId=" + fileId + ", fileName=" + fileName + ", fileExtension=" + fileExtension + ", fileSize=" + fileSize + ", fileMimeType=" + fileMimeType + ", imageWidth=" + imageWidth + ", imageHeight=" + imageHeight + ", imageThumbWidth=" + imageThumbWidth + ", imageThumbHeight=" + imageThumbHeight + ", filePath=" + filePath + "]"; } } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/dto/UserChannelDTO.java ================================================ package org.leo.im.api.dto; /** * 用户频道dto类 * * @author Leo * @date 2018/4/20 */ public class UserChannelDTO { private String channelId; private String channelName; private String channelType; private String channelDisplayName; private String channelDescription; private String toUserId; private String toUserOnlineStatus; private short unreadMessageCount; private int memberCount; private String creatorId; public String getChannelId() { return channelId; } public void setChannelId(String channelId) { this.channelId = channelId; } public String getChannelName() { return channelName; } public void setChannelName(String channelName) { this.channelName = channelName; } public String getChannelType() { return channelType; } public void setChannelType(String channelType) { this.channelType = channelType; } public String getChannelDisplayName() { return channelDisplayName; } public void setChannelDisplayName(String channelDisplayName) { this.channelDisplayName = channelDisplayName; } public String getChannelDescription() { return channelDescription; } public void setChannelDescription(String channelDescription) { this.channelDescription = channelDescription; } public String getToUserId() { return toUserId; } public void setToUserId(String toUserId) { this.toUserId = toUserId; } public String getToUserOnlineStatus() { return toUserOnlineStatus; } public void setToUserOnlineStatus(String toUserOnlineStatus) { this.toUserOnlineStatus = toUserOnlineStatus; } public short getUnreadMessageCount() { return unreadMessageCount; } public void setUnreadMessageCount(short unreadMessageCount) { this.unreadMessageCount = unreadMessageCount; } public int getMemberCount() { return memberCount; } public void setMemberCount(int memberCount) { this.memberCount = memberCount; } public String getCreatorId() { return creatorId; } public void setCreatorId(String creatorId) { this.creatorId = creatorId; } @Override public String toString() { return "UserChannelDTO [channelId=" + channelId + ", channelName=" + channelName + ", channelType=" + channelType + ", channelDisplayName=" + channelDisplayName + ", channelDescription=" + channelDescription + ", toUserId=" + toUserId + ", toUserOnlineStatus=" + toUserOnlineStatus + ", unreadMessageCount=" + unreadMessageCount + ", memberCount=" + memberCount + ", creatorId=" + creatorId + "]"; } } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/dto/UserDTO.java ================================================ package org.leo.im.api.dto; /** * 用户dto类 * * @author Leo * @date 2018/3/30 */ public class UserDTO { private String id; private String name; private String firstLetterOfName; private String nickname; private String password; private String avatarUrl; private Boolean locked; private Long lastPostAt; private String onlineStatus; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetterOfName() { return firstLetterOfName; } public void setFirstLetterOfName(String firstLetterOfName) { this.firstLetterOfName = firstLetterOfName; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAvatarUrl() { return avatarUrl; } public void setAvatarUrl(String avatarUrl) { this.avatarUrl = avatarUrl; } public Boolean getLocked() { return locked; } public void setLocked(Boolean locked) { this.locked = locked; } public Long getLastPostAt() { return lastPostAt; } public void setLastPostAt(Long lastPostAt) { this.lastPostAt = lastPostAt; } public String getOnlineStatus() { return onlineStatus; } public void setOnlineStatus(String onlineStatus) { this.onlineStatus = onlineStatus; } @Override public String toString() { return "UserDTO [id=" + id + ", name=" + name + ", firstLetterOfName=" + firstLetterOfName + ", nickname=" + nickname + ", password=" + password + ", avatarUrl=" + avatarUrl + ", locked=" + locked + ", lastPostAt=" + lastPostAt + ", onlineStatus=" + onlineStatus + "]"; } } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/exception/ServiceException.java ================================================ package org.leo.im.api.exception; /** * 服务异常类 * * @author Leo * @date 2018/3/20 */ public final class ServiceException extends RuntimeException { private static final long serialVersionUID = 2447167264295739984L; public ServiceException() { } public ServiceException(String message) { super(message); } public ServiceException(String message, Throwable cause) { super(message, cause); } public ServiceException(Throwable cause) { super(cause); } } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/service/ChannelService.java ================================================ package org.leo.im.api.service; import java.util.List; import java.util.Map; import org.leo.im.api.annotation.Transactional; import org.leo.im.api.dto.ChannelDTO; import org.leo.im.api.dto.ChannelListDTO; import org.leo.im.api.dto.ChannelMemberDTO; import org.leo.im.common.data.Page; /** * 频道管理服务接口 * * @author Leo * @date 2018/4/11 */ public interface ChannelService { /** * 得到频道列表 * @param parameters * @param limit * @return */ List listChannel(Map parameters, int limit); /** * 得到群组频道列表 * @param parameters * @param limit * @return */ List listGroupChannel(Map parameters, int limit); /** * 添加频道 * @param dto * @param creatorNickname * @return */ @Transactional ChannelDTO saveChannel(ChannelDTO dto, String creatorNickname); /** * 根据id得到频道信息 * @param id * @return */ ChannelDTO getById(String id); /** * 得到用户是否为频道的管理员 * @param userId * @param channelId * @return */ boolean isAdmin(String userId, String channelId); /** * 更新频道名称 * @param channelId * @param name * @return */ @Transactional int updateName(String channelId, String name); /** * 更新频道用途 * @param channelId * @param purpose * @return */ @Transactional int updatePurpose(String channelId, String purpose); /** * 添加频道成员 * @param channelId * @param userIds * @param userNicknames * @param admin * @return */ @Transactional int addMember(String channelId, String[] userIds, String[] userNicknames, String admin); /** * 移除组成员 * @param channelId * @param memberId * @param memberNickname * @param admin * @return */ @Transactional int removeMember(String channelId, String memberId, String memberNickname, String admin); /** * 得到成员列表 * @param channelId * @param username * @param limit * @param offset * @return */ Page listMember(String channelId, String username, int limit, int offset); /** * 变更频道管理员 * @param channelId * @param memberId * @param isAdmin * @return */ @Transactional int changeAdmin(String channelId, String memberId, boolean isAdmin); /** * 离开频道 * @param channelId * @param memberId * @param memberNickname * @return */ @Transactional int leaveChannel(String channelId, String memberId, String memberNickname); /** * 删除频道 * @param channelId * @param adminId * @return */ @Transactional int removeChannel(String channelId, String adminId); } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/service/MessageService.java ================================================ package org.leo.im.api.service; import java.util.List; import org.leo.im.api.annotation.Transactional; import org.leo.im.api.dto.FileDTO; import org.leo.im.api.dto.MessageDTO; /** * 消息服务接口 * * @author Leo * @date 2018/5/16 */ public interface MessageService { /** * 得到消息列表 * @param channelId * @param maxCreateAt * @param limit * @return */ List listMessage(String channelId, long maxCreateAt, int limit); /** * 根据id得到消息 * @param id * @return */ MessageDTO getById(long id); /** * 添加消息 * @param dto * @return */ @Transactional MessageDTO saveMessage(MessageDTO dto); /** * 批量添加消息 * @param dtos * @return */ @Transactional int saveMessage(List dtos); /** * 读取消息 * @param channelId * @param userId * @param total * @return */ @Transactional int readMessage(String channelId, String userId, short total); /** * 删除消息 * @param messageId * @param senderId * @param channelId * @param toUserId * @return */ @Transactional int removeMessage(long messageId, String senderId, String channelId, String toUserId); /** * 添加文件 * @param dto * @return */ @Transactional String saveFile(FileDTO dto); } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/service/UnreadMessageCountService.java ================================================ package org.leo.im.api.service; import org.leo.im.api.annotation.Transactional; /** * 未读消息数量服务接口 * @author Administrator * */ public interface UnreadMessageCountService { /** * 批量添加未读消息数量 * @param userIds * @param channelId * @param total * @return */ @Transactional int batchSaveUnreadMessageCount(String[] userIds, String channelId, short total); /** * 更新未读消息数量 * @param userId * @param channelId * @param total * @return */ @Transactional int updateUnreadMessageCount(String userId, String channelId, short total); /** * 批量更新未读消息数量 * @param userIds * @param channelId * @param total * @return */ int batchUpdateUnreadMessageCount(String[] userIds, String channelId, short total); /** * 批量增加未读消息数量 * @param userIds * @param channelId * @param quantity * @return */ @Transactional int batchIncreaseUnreadMessageCount(String[] userIds, String channelId, short quantity); /** * 增加未读消息数量 * @param userId * @param channelId * @param quantity * @return */ @Transactional int increaseUnreadMessageCount(String userId, String channelId, short quantity); } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/service/UserChannelService.java ================================================ package org.leo.im.api.service; import java.util.List; import org.leo.im.api.annotation.Transactional; import org.leo.im.api.dto.UserChannelDTO; /** * 用户频道服务接口 * * @author Leo * @date 2018/4/20 */ public interface UserChannelService { /** * 得到用户频道列表 * @param userId * @param type * @param limit * @return */ List listUserChannel(String userId, String type, int limit); /** * 得到用户频道 * @param userId * @param channelId * @return */ UserChannelDTO get(String userId, String channelId); /** * 更新用户频道 * @param channelId * @param userId * @param displayName * @return */ @Transactional int updateDisplayName(String channelId, String userId, String displayName); /** * 隐藏频道 * @param userId * @param channelId * @return */ @Transactional int hideChannel(String userId, String channelId); /** * 根据名称得到用户频道列表 * @param userId * @param name * @param type * @return */ List listByName(String userId, String name, String type); } ================================================ FILE: leo-im-api/src/main/java/org/leo/im/api/service/UserService.java ================================================ package org.leo.im.api.service; import java.util.Set; import org.leo.im.api.annotation.Transactional; import org.leo.im.api.dto.UserDTO; import org.leo.im.common.data.Page; /** * 用户服务接口 * * @author Leo * @date 2018/4/9 */ public interface UserService { /** * 验证用户登录 * * @param loginName * @param password * @return */ UserDTO verifyLogin(String loginName, String password); /** * 根据id得到用户 * * @param id * @return */ UserDTO getById(String id); /** * 添加用户 * * @param dto * @return */ @Transactional String saveUser(UserDTO dto); /** * 根据名称或昵称分页查询用户 * * @param name * @param limit * @param offset * @return */ Page listByNameOrNickname(String name, int limit, int offset); /** * 更新用户 * @param dto * @param updateNullValueField * @return */ @Transactional UserDTO updateUser(UserDTO dto, boolean updateNullValueField); /** * * @param channelId * @param username * @param limit * @param offset * @return */ Page listNonMembers(String channelId, String username, int limit, int offset); /** * 批量下线用户 * @param userIds * @return */ @Transactional int batchOffline(Set userIds); /** * 修改用户口令 * @param userId * @param username * @param oldPassword * @param newPassword * @return */ @Transactional int updatePassword(String userId, String username, String oldPassword, String newPassword); } ================================================ FILE: leo-im-api/src/test/java/org/leo/im/api/AppTest.java ================================================ package org.leo.im.api; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-api/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: leo-im-common-1.0.jar slf4j-api-1.7.25.jar logback-classic -1.2.3.jar logback-core-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-api/target/classes/META-INF/maven/org.leo.im/leo-im-api/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:09 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-api m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-api artifactId=leo-im-api ================================================ FILE: leo-im-api/target/classes/META-INF/maven/org.leo.im/leo-im-api/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-api leo-im-api http://maven.apache.org org.leo.im leo-im-common ${parent.version} ================================================ FILE: leo-im-api/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:09 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-api ================================================ FILE: leo-im-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\service\UserChannelService.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\annotation\Cacheable.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\service\UnreadMessageCountService.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\service\UserService.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\dto\FileDTO.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\dto\UserDTO.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\annotation\Transactional.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\exception\ServiceException.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\service\MessageService.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\dto\ChannelDTO.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\dto\ChannelListDTO.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\dto\ChannelMemberDTO.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\dto\MessageDTO.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\dto\UserChannelDTO.java F:\Develop\open-source\leo-im-server\leo-im-api\src\main\java\org\leo\im\api\service\ChannelService.java ================================================ FILE: leo-im-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-api\src\test\java\org\leo\im\api\AppTest.java ================================================ FILE: leo-im-api/target/surefire-reports/TEST-org.leo.im.api.AppTest.xml ================================================ ================================================ FILE: leo-im-api/target/surefire-reports/org.leo.im.api.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.api.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.097 sec ================================================ FILE: leo-im-api-provider/.classpath ================================================ ================================================ FILE: leo-im-api-provider/.project ================================================ leo-im-api-provider org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-api-provider/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-api-provider/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-api-provider/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-api-provider/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-api-provider leo-im-api-provider http://maven.apache.org UTF-8 org.leo.im leo-im-api ${project.version} org.leo.im leo-im-service ${project.version} ================================================ FILE: leo-im-api-provider/src/main/java/org/leo/im/api/provider/ServiceFactory.java ================================================ package org.leo.im.api.provider; import org.leo.im.api.service.ChannelService; import org.leo.im.api.service.MessageService; import org.leo.im.api.service.UserChannelService; import org.leo.im.api.service.UserService; import org.leo.im.service.ChannelServiceImpl; import org.leo.im.service.MessageServiceImpl; import org.leo.im.service.UserChannelServiceImpl; import org.leo.im.service.UserServiceImpl; /** * 服务工厂类 * * @author Leo * @date 2018/3/30 */ public final class ServiceFactory { /** * 创建用户服务的实例 * * @return */ public static UserService createUserService() { return new UserServiceImpl(); } /** * 创建频道服务的实例 * @return */ public static ChannelService createChannelService() { return new ChannelServiceImpl(); } /** * 创建用户频道服务类的实例 * @return */ public static UserChannelService createUserChannelService() { return new UserChannelServiceImpl(); } /** * 创建消息服务的实例 * @return */ public static MessageService createMessageService() { return new MessageServiceImpl(); } } ================================================ FILE: leo-im-api-provider/src/test/java/org/leo/im/api/provider/AppTest.java ================================================ package org.leo.im.api.provider; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-api-provider/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: leo-im-api-1.0.jar leo-im-common-1.0.jar leo-im-service-1. 0.jar leo-im-store-1.0.jar mysql-connector-java-8.0.11.jar protobuf-j ava-2.6.0.jar druid-1.1.9.jar leo-im-model-1.0.jar leo-im-util-1.0.ja r cglib-3.2.6.jar asm-6.0.jar ant-1.9.6.jar ant-launcher-1.9.6.jar jj wt-0.9.0.jar jackson-databind-2.8.9.jar jackson-annotations-2.8.0.jar jackson-core-2.8.9.jar leo-im-notification-1.0.jar jedis-2.9.0.jar c ommons-pool2-2.4.2.jar fastjson-1.2.47.jar slf4j-api-1.7.25.jar logba ck-classic-1.2.3.jar logback-core-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-api-provider/target/classes/META-INF/maven/org.leo.im/leo-im-api-provider/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:17 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-api-provider m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-api-provider artifactId=leo-im-api-provider ================================================ FILE: leo-im-api-provider/target/classes/META-INF/maven/org.leo.im/leo-im-api-provider/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-api-provider leo-im-api-provider http://maven.apache.org UTF-8 org.leo.im leo-im-api ${project.version} org.leo.im leo-im-service ${project.version} ================================================ FILE: leo-im-api-provider/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:18 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-api-provider ================================================ FILE: leo-im-api-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-api-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-api-provider\src\main\java\org\leo\im\api\provider\ServiceFactory.java ================================================ FILE: leo-im-api-provider/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-api-provider/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-api-provider\src\test\java\org\leo\im\api\provider\AppTest.java ================================================ FILE: leo-im-api-provider/target/surefire-reports/TEST-org.leo.im.api.provider.AppTest.xml ================================================ ================================================ FILE: leo-im-api-provider/target/surefire-reports/org.leo.im.api.provider.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.api.provider.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.087 sec ================================================ FILE: leo-im-common/.classpath ================================================ ================================================ FILE: leo-im-common/.project ================================================ leo-im-common org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-common/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-common/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-common/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-common/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-common leo-im-common http://maven.apache.org UTF-8 ================================================ FILE: leo-im-common/src/main/java/org/leo/im/common/data/Page.java ================================================ package org.leo.im.common.data; import java.util.List; /** * 分页查询结果类 * * @author Leo * @date 2018/4/8 * @param */ public final class Page implements java.io.Serializable { private static final long serialVersionUID = 1L; private long total; private List rows; public Page(long total, List rows) { this.total = total; this.rows = rows; } public long getTotal() { return this.total; } public List getRows() { return this.rows; } } ================================================ FILE: leo-im-common/src/test/java/org/leo/im/common/AppTest.java ================================================ package org.leo.im.common; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-common/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: slf4j-api-1.7.25.jar logback-classic-1.2.3.jar logback-cor e-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-common/target/classes/META-INF/maven/org.leo.im/leo-im-common/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:08 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-common m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-common artifactId=leo-im-common ================================================ FILE: leo-im-common/target/classes/META-INF/maven/org.leo.im/leo-im-common/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-common leo-im-common http://maven.apache.org UTF-8 ================================================ FILE: leo-im-common/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:08 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-common ================================================ FILE: leo-im-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-common\src\main\java\org\leo\im\common\data\Page.java ================================================ FILE: leo-im-common/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-common/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-common\src\test\java\org\leo\im\common\AppTest.java ================================================ FILE: leo-im-common/target/surefire-reports/TEST-org.leo.im.common.AppTest.xml ================================================ ================================================ FILE: leo-im-common/target/surefire-reports/org.leo.im.common.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.common.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.084 sec ================================================ FILE: leo-im-http/.classpath ================================================ ================================================ FILE: leo-im-http/.project ================================================ leo-im-http org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-http/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-http/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-http/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-http/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-http leo-im-http http://maven.apache.org UTF-8 org.ow2.asm asm 6.1 net.coobird thumbnailator 0.4.8 io.netty netty-all ${netty.version} com.alibaba fastjson ${fastjson.version} org.leo netty-rest-server 1.0 org.leo.im leo-im-api ${parent.version} org.leo.im leo-im-api-provider ${project.version} org.leo.im leo-im-util ${project.version} org.leo.im leo-im-notification ${project.version} org.leo.im leo-im-common ${project.version} ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/HttpServer.java ================================================ package org.leo.im.http; import org.leo.im.http.controller.ExceptionController; import org.leo.im.http.interceptor.AuthenticationInterceptor; import org.leo.im.http.interceptor.CorsInterceptor; import org.leo.web.core.WebServer; /** * Leo IM REST API Server * * @author Leo * @date 2018/4/2 */ public final class HttpServer { /** * 启动api server * @throws InterruptedException */ public void start() throws InterruptedException { // 忽略指定url WebServer.getIgnoreUrls().add("/favicon.ico"); // 全局异常处理 WebServer.setExceptionHandler(new ExceptionController()); // 设置监听端口号 WebServer server = new WebServer(Integer.getInteger("http.port")); // 设置boss与worker线程数 server.setBossThreads(Integer.getInteger("http.boss.threads")); server.setWorkerThreads(Integer.getInteger("http.worker.threads")); // 设置Http最大内容长度(默认 为10M) server.setMaxContentLength(1024 * 1024 * Integer.getInteger("http.max.content.length")); // 设置Controller所在包 server.setControllerBasePackage("org.leo.im.http.controller"); // 添加拦截器,按照添加的顺序执行。 // 跨域拦截器 server.addInterceptor(new CorsInterceptor()); // 身份认证拦截器,设置例外url server.addInterceptor(new AuthenticationInterceptor(), "/auth/login", "/auth/verificationCode", "/users", "/messages/files"); server.start(); } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/cache/Cache.java ================================================ package org.leo.im.http.cache; /** * 缓存 * * @author Leo * @date 2018/4/1 */ final class Cache { private Object value; /** * 缓存的生存时间,单位:秒 */ private int timeout; private long expireAt; public Cache(Object value) { this.value = value; } public Cache(Object value, int timeout) { this.value = value; this.timeout = timeout; this.expireAt = System.currentTimeMillis() + this.timeout * 1000; } public Object getValue() { return this.value; } public void setValue(Object value) { this.value = value; } public int getTimeout() { return this.timeout; } public void setTimeout(int timeout) { this.timeout = timeout; this.expireAt = System.currentTimeMillis() + this.timeout * 1000; } public long getExpireAt() { return this.expireAt; } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/cache/CacheManager.java ================================================ package org.leo.im.http.cache; /** * 缓存管理接口 * * @author Leo * @date 2018/3/27 */ public interface CacheManager { /** * 得到缓存 * @param key * @return */ Object get(String key); /** * 设置缓存 * @param key * @param value */ void put(String key, Object value); /** * 设置缓存 * @param key * @param value * @param timeout 生存时间,单位为秒。 */ void put(String key, Object value, int timeout); /** * 删除缓存 * @param key */ void remove(String key); /** * 清除缓存 */ void clear(); /** * 清理超时缓存 */ void clearExpired(); } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/cache/CacheManagerFactory.java ================================================ package org.leo.im.http.cache; /** * 缓存管理器工厂类 * * @author Leo * @date 2018/3/27 */ public final class CacheManagerFactory { /** * 得到缓存管理器的实例 * * @return */ public static CacheManager getCacheManager() { String cacheType = System.getProperty("cache.type") == null ? "map" : System.getProperty("cache.type").toLowerCase(); switch (cacheType) { case "map": return MapCacheManager.getInstance(); default: return MapCacheManager.getInstance(); } } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/cache/MapCacheManager.java ================================================ package org.leo.im.http.cache; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; /** * 基于Java Map的缓存管理器 * * @author Leo * @date 2018/3/27 */ final class MapCacheManager implements CacheManager { private final static int CLEAR_INTERVAL = 10; private Map caches = new ConcurrentHashMap<>(64); private MapCacheManager() { } public static class InstanceHolder { private static MapCacheManager instance = new MapCacheManager(); static { instance.clearExpired(); } } public static MapCacheManager getInstance() { return InstanceHolder.instance; } /** * 得到缓存 * @param key * @return */ @Override public Object get(String key) { Cache cache = caches.get(key); return cache == null ? null : cache.getValue(); } /** * 添加缓存 * * @param key * @param value */ @Override public void put(String key, Object value) { Cache cache = new Cache(value); caches.put(key, cache); } /** * 添加缓存 * * @param key * @param value * @param timeout * 生存时间,单位为秒。 */ @Override public void put(String key, Object value, int timeout) { Cache cache = new Cache(value, timeout); caches.put(key, cache); } /** * 删除缓存 * * @param key */ @Override public void remove(String key) { this.caches.remove(key); } /** * 清除缓存 */ @Override public void clear() { this.caches.clear(); } /** * 清理超时缓存 */ @Override public void clearExpired() { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { Set> entrySet = caches.entrySet(); long currentTime = System.currentTimeMillis(); for (Entry entry : entrySet) { if (entry.getValue().getExpireAt() > 0 && currentTime >= entry.getValue().getExpireAt()) { caches.remove(entry.getKey()); } } } }, 1 * 1000, CLEAR_INTERVAL * 1000); } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/constant/CacheKeys.java ================================================ package org.leo.im.http.constant; /** * 缓存key常量类 * * @author Leo * @date 2018/4/1 */ public final class CacheKeys { /** * JSession key前缀 */ public static final String JSESSIONID = "JSESSIONID"; /** * 验证码缓存key前缀 */ public static final String VERIFICATION_CODE_PREFIX = "v-code-"; /** * 登录失败次数缓存前缀 */ public static final String LOGIN_FAILURE_COUNT = "login-failure-count_"; } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/controller/AuthController.java ================================================ package org.leo.im.http.controller; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.UUID; import org.leo.im.api.service.UserService; import org.leo.im.api.dto.UserDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.http.cache.CacheManagerFactory; import org.leo.im.http.constant.CacheKeys; import org.leo.im.http.util.JwtUtils; import org.leo.im.service.support.ServiceProxy; import org.leo.web.annotation.GetMapping; import org.leo.web.annotation.PostMapping; import org.leo.web.annotation.RequestMapping; import org.leo.web.annotation.RestController; import org.leo.web.annotation.UrlEncodedForm; import org.leo.web.rest.HttpResponse; import org.leo.web.rest.HttpStatus; import org.leo.web.rest.ResponseEntity; import com.alibaba.fastjson.JSONObject; import io.netty.handler.codec.http.cookie.ServerCookieDecoder; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.cookie.Cookie; /** * 认证控制器 * * @author Leo * @date 2018/4/4 */ @RestController @RequestMapping("/auth") public final class AuthController extends BaseController { /** * Session超时时间,单位:秒。 */ private static final int SESSION_TIMEOUT = 5 * 60; /** * 验证码超时时间,默认为120秒。 */ private static final int VERIFICATION_CODE_TIMEOUT = 2 * 60; /** * 登录重试次数 */ private static final int LOGIN_RETRY_COUNT = 5; /** * 登录失败次数缓存超时时间,单位:秒 */ private static final int LOGIN_FAILURE_COUNT_TIMEOUT = 5 * 60; @PostMapping("/login") public ResponseEntity login(FullHttpRequest request, HttpResponse response, @UrlEncodedForm Map form) { String sessionId = getJSessionId(request); if(sessionId == null) { // Session不存在 sessionId = UUID.randomUUID().toString().replaceAll("-", ""); response.getCookies().put(CacheKeys.JSESSIONID, sessionId); } CacheManagerFactory.getCacheManager().put(sessionId, (byte)0, SESSION_TIMEOUT); // 检查登录失败次数 Object loginFailureCount = CacheManagerFactory.getCacheManager().get(CacheKeys.LOGIN_FAILURE_COUNT + sessionId); if(loginFailureCount != null) { if((int)loginFailureCount >= LOGIN_RETRY_COUNT) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build("登录失败次数过多,请稍后重试"); } } // 检查验证码 /*String verificationCode = form.get("verificationCode"); if(verificationCode == null || verificationCode.trim().isEmpty()) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build("验证码为空"); } if(sessionId == null || sessionId.trim().isEmpty()) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build("无效的请求"); } Object verificationCodeInCache = CacheManagerFactory.getCacheManager().get(CacheKeys.VERIFICATION_CODE_PREFIX + sessionId); if(verificationCodeInCache == null || verificationCodeInCache.toString().trim().isEmpty()) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build("无效的验证码"); } if(!verificationCode.equals(verificationCodeInCache)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build("无效的验证码"); }*/ UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); UserDTO dto = serviceProxy.verifyLogin(form.get("username"), form.get("password")); if (dto == null) { int userLoginFailureCount = (loginFailureCount == null ? 0 : (int)loginFailureCount); CacheManagerFactory.getCacheManager().put(CacheKeys.LOGIN_FAILURE_COUNT + sessionId, ++userLoginFailureCount, LOGIN_FAILURE_COUNT_TIMEOUT); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } JSONObject json = new JSONObject(); json.put("userId", dto.getId()); String jwt = JwtUtils.createJWT(json.toJSONString(), System.getProperty("jwt.secret"), Long.getLong("jwt.ttl.millis")); Map result = new HashMap<>(); result.put("userId", dto.getId()); result.put("username", dto.getName()); result.put("nickname", dto.getNickname()); result.put("firstLetterOfName", dto.getFirstLetterOfName()); result.put("avatarUrl", dto.getAvatarUrl()); result.put("token", jwt); // 删除缓存中的验证码 // CacheManagerFactory.getCacheManager().remove(CacheKeys.VERIFICATION_CODE_PREFIX + sessionId); // 删除登录失败次数缓存 CacheManagerFactory.getCacheManager().remove(CacheKeys.LOGIN_FAILURE_COUNT + sessionId); return ResponseEntity.ok(result); } @GetMapping("/verificationCode") public ResponseEntity getVerificationCode(FullHttpRequest request, HttpResponse response) { String verificationCode = UUID.randomUUID().toString().substring(0, 4); String sessionId = null; if((sessionId = getJSessionId(request)) == null) { // Session不存在 sessionId = UUID.randomUUID().toString().replaceAll("-", ""); response.getCookies().put(CacheKeys.JSESSIONID, sessionId); } CacheManagerFactory.getCacheManager().put(sessionId, (byte)0, SESSION_TIMEOUT); CacheManagerFactory.getCacheManager().put(CacheKeys.VERIFICATION_CODE_PREFIX + sessionId, verificationCode, VERIFICATION_CODE_TIMEOUT); return ResponseEntity.ok(verificationCode); } /** * 从cookie中得到Session Id * @param request * @return */ private String getJSessionId(FullHttpRequest request) { try { String cookieStr = request.headers().get("Cookie"); if(cookieStr == null || cookieStr.trim().isEmpty()) { return null; } Set cookies = ServerCookieDecoder.STRICT.decode(cookieStr); Iterator it = cookies.iterator(); while (it.hasNext()) { Cookie cookie = it.next(); if (cookie.name().equals(CacheKeys.JSESSIONID)) { if (CacheManagerFactory.getCacheManager().get(cookie.value()) != null) { return cookie.value(); } } } } catch (Exception e1) { return null; } return null; } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/controller/BaseController.java ================================================ package org.leo.im.http.controller; import org.leo.im.http.util.JwtUtils; import com.alibaba.fastjson.JSONObject; import io.jsonwebtoken.Claims; /** * 控制器基类 * * @author Leo * @date 2018/3/26 */ public class BaseController { /** * 从jwt中获取subject信息 * @param jwtUtils * @param jwt * @param key * @return */ protected String getSubjectFromJwt(String jwt, String key) { Claims claims = JwtUtils.parseJWT(jwt, System.getProperty("jwt.secret")); String subject = claims.getSubject(); if(key != null && !key.trim().equals("")) { JSONObject json = JSONObject.parseObject(subject); return json.getString(key); } else { return subject; } } /** * 验证jwt * @param jwt * @return */ protected boolean verifyJwt(String jwt) { try { JwtUtils.parseJWT(jwt, System.getProperty("jwt.secret")); return true; } catch(Exception e) { return false; } } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/controller/ChannelController.java ================================================ package org.leo.im.http.controller; import org.leo.im.api.dto.ChannelDTO; import org.leo.im.api.dto.ChannelMemberDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.api.service.ChannelService; import org.leo.im.common.data.Page; import org.leo.im.http.vo.ChannelVO; import org.leo.im.http.vo.UserChannelVO; import org.leo.im.service.support.ServiceProxy; import org.leo.im.util.BeanUtils; import org.leo.web.annotation.DeleteMapping; import org.leo.web.annotation.GetMapping; import org.leo.web.annotation.PatchMapping; import org.leo.web.annotation.PathVariable; import org.leo.web.annotation.PostMapping; import org.leo.web.annotation.PutMapping; import org.leo.web.annotation.RequestBody; import org.leo.web.annotation.RequestHeader; import org.leo.web.annotation.RequestMapping; import org.leo.web.annotation.RequestParam; import org.leo.web.annotation.RestController; import org.leo.web.rest.ResponseEntity; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import io.netty.handler.codec.http.FullHttpRequest; /** * 频道控制器 * * @author Leo * @date 2018/4/3 */ @RestController @RequestMapping("/channels") public final class ChannelController extends BaseController { @PostMapping("") public ResponseEntity createChannel(FullHttpRequest request, @RequestHeader("X-Token") String token, @RequestBody String body) { String userId = this.getSubjectFromJwt(token, "userId"); ChannelDTO dto = new ChannelDTO(); JSONObject json = JSONObject.parseObject(body); dto.setType(json.getString("type")); if ("G".equals(dto.getType())) { dto.setName(json.getString("name")); dto.setPurpose(json.getString("purpose")); JSONArray members = json.getJSONArray("members"); dto.setMemberCount(members.size()); for(int i = 0; i < members.size(); i++) { ChannelMemberDTO member = new ChannelMemberDTO(); JSONObject memberJson = members.getJSONObject(i); member.setId(memberJson.getString("id")); member.setNickname(memberJson.getString("nickname")); member.setAdmin(memberJson.getString("id").equals(userId)); dto.getMembers().add(member); } } if ("P".equals(dto.getType())) { dto.setFromUserId(userId); dto.setFromUsername(json.getString("fromUsername")); dto.setFromUserNickname(json.getString("fromUserNickname")); dto.setToUserId(json.getString("toUserId")); dto.setToUsername(json.getString("toUsername")); dto.setToUserNickname(json.getString("toUserNickname")); dto.setName(dto.getToUserNickname() != null && !dto.getToUserNickname().trim().isEmpty() ? dto.getToUserNickname() : dto.getToUsername()); dto.setMemberCount(2); ChannelMemberDTO member1 = new ChannelMemberDTO(); member1.setId(userId); member1.setAdmin(true); dto.getMembers().add(member1); ChannelMemberDTO member2 = new ChannelMemberDTO(); member2.setId(dto.getToUserId()); dto.getMembers().add(member2); } dto.setCreatorId(userId); ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); ChannelDTO returnDTO = serviceProxy.saveChannel(dto, json.getString("creatorNickname")); UserChannelVO vo = new UserChannelVO(); BeanUtils.copyProperties(returnDTO, vo); vo.setChannelId(returnDTO.getId()); vo.setChannelName(returnDTO.getName()); vo.setChannelDisplayName(getChannelDisplayName(returnDTO, userId)); vo.setChannelType(returnDTO.getType()); return ResponseEntity.created(vo); } @GetMapping("/{id}") public ResponseEntity getById(@PathVariable("id") String id) { ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); ChannelDTO dto = serviceProxy.getById(id); if(dto == null) { return ResponseEntity.notFound().build(); } ChannelVO vo = new ChannelVO(); BeanUtils.copyProperties(dto, vo); return ResponseEntity.ok(vo); } @GetMapping("/{channelId}/isAdmin") public ResponseEntity isAdmin(@PathVariable("channelId") String channelId, @RequestHeader("X-Token") String token) { String userId = this.getSubjectFromJwt(token, "userId"); ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); boolean isAdmin = serviceProxy.isAdmin(userId, channelId); return ResponseEntity.ok(isAdmin); } @PatchMapping("/{channelId}") public ResponseEntity updateChannel(@PathVariable("channelId") String channelId, @RequestBody String body) { JSONObject json = JSONObject.parseObject(body); ChannelService serviceProxy = null; if(json.containsKey("name")) { serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); int count = serviceProxy.updateName(channelId, json.getString("name")); return ResponseEntity.ok(count); } if(json.containsKey("purpose")) { serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); int count = serviceProxy.updatePurpose(channelId, json.getString("purpose")); return ResponseEntity.ok(count); } return ResponseEntity.internalServerError("not found allowed filed in body"); } @GetMapping("/{channelId}/members") public ResponseEntity> listMember(@PathVariable("channelId") String channelId, @RequestParam("username") String username, @RequestParam("limit") int limit, @RequestParam("offset") int offset) { ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); Page result = serviceProxy.listMember(channelId, username, limit, offset); return ResponseEntity.ok(result); } @PostMapping("/{channelId}/members") public ResponseEntity addMember(@PathVariable("channelId") String channelId, @RequestBody String body) { JSONObject data = JSONObject.parseObject(body); JSONArray jsonArray = data.getJSONArray("users"); String[] userIds = new String[jsonArray.size()]; String[] userNicknames = new String[jsonArray.size()]; for(int i = 0; i < userIds.length; i++) { userIds[i] = jsonArray.getJSONObject(i).getString("id"); userNicknames[i] = jsonArray.getJSONObject(i).getString("nickname"); } ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); int count = serviceProxy.addMember(channelId, userIds, userNicknames, data.getString("admin")); return ResponseEntity.created(count); } @DeleteMapping("/{channelId}/members") public ResponseEntity removeMember(@PathVariable("channelId") String channelId, @RequestBody String body) { ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); JSONObject data = JSONObject.parseObject(body); int count = serviceProxy.removeMember(channelId, data.getString("memberId"), data.getString("memberNickname"), data.getString("admin")); if(count > 0) { return ResponseEntity.noContent(count); } return ResponseEntity.notFound().build(); } @PutMapping("/{channelId}/admin") public ResponseEntity changeAdmin(@PathVariable("channelId") String channelId, @RequestBody String body) { JSONObject json = JSONObject.parseObject(body); ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); int count = serviceProxy.changeAdmin(channelId, json.getString("memberId"), json.getBooleanValue("isAdmin")); return ResponseEntity.created(count); } /** * 离开频道 * @param channelId * @param body * @return */ @DeleteMapping("/{channelId}/members/{memberId}") public ResponseEntity leaveChannel(@PathVariable("channelId") String channelId, @PathVariable("memberId") String memberId, @RequestBody String body) { JSONObject data = JSONObject.parseObject(body); ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); int count = serviceProxy.leaveChannel(channelId, memberId, data.getString("memberNickname")); return ResponseEntity.noContent(count); } @DeleteMapping("/{channelId}") public ResponseEntity removeChannel(@PathVariable("channelId") String channelId, @RequestHeader("X-Token") String token) { String userId = this.getSubjectFromJwt(token, "userId"); ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); int count = serviceProxy.removeChannel(channelId, userId); return ResponseEntity.noContent(count); } /** * 得到频道的显示名称 * @param dto * @param creatorId * @return */ private String getChannelDisplayName(ChannelDTO dto, String creatorId) { if(dto.getType().equals("G")) { return dto.getName(); } if(dto.getCreatorId().equals(creatorId)) { return dto.getToUserNickname(); } return dto.getFromUserNickname(); } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/controller/ExceptionController.java ================================================ package org.leo.im.http.controller; import org.leo.web.exception.ResourceNotFoundException; import org.leo.web.rest.HttpContextHolder; import org.leo.web.rest.HttpResponse; import org.leo.web.rest.HttpStatus; import org.leo.web.rest.controller.ExceptionHandler; /** * 异常处理器 * * @author Leo * @date 2018/3/30 */ public class ExceptionController implements ExceptionHandler { /** * 处理异常 * * @param e */ @Override public void doHandle(Exception e) { HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; if (e instanceof ResourceNotFoundException) { status = HttpStatus.NOT_FOUND; } String errorMessage = e.getCause() == null ? "" : e.getCause().getMessage(); if (errorMessage == null) { errorMessage = e.getMessage(); } HttpResponse response = HttpContextHolder.getResponse(); response.write(status, errorMessage); response.closeChannel(); } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/controller/MessageController.java ================================================ package org.leo.im.http.controller; import java.io.RandomAccessFile; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.UUID; import org.leo.im.api.dto.FileDTO; import org.leo.im.api.dto.MessageDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.api.service.MessageService; import org.leo.im.http.file.FileStorage; import org.leo.im.http.file.FileStorageFactory; import org.leo.im.http.vo.MessageVO; import org.leo.im.service.support.ServiceProxy; import org.leo.im.util.BeanUtils; import org.leo.web.annotation.DeleteMapping; import org.leo.web.annotation.GetMapping; import org.leo.web.annotation.PostMapping; import org.leo.web.annotation.RequestBody; import org.leo.web.annotation.RequestHeader; import org.leo.web.annotation.RequestMapping; import org.leo.web.annotation.RequestParam; import org.leo.web.annotation.RestController; import org.leo.web.annotation.UploadFile; import org.leo.web.annotation.UrlEncodedForm; import org.leo.web.multipart.MultipartFile; import org.leo.web.rest.ResponseEntity; import com.alibaba.fastjson.JSONObject; import io.netty.handler.codec.http.FullHttpRequest; /** * 消息控制器 * * @author Leo * @date 2018/5/16 */ @RestController() @RequestMapping("/messages") public final class MessageController extends BaseController { @GetMapping("") public ResponseEntity> listMessage(@RequestParam("channelId") String channelId, @RequestParam("maxCreateAt") long maxCreateAt, @RequestParam("limit") int limit) { MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService()); List dtoList = serviceProxy.listMessage(channelId, maxCreateAt, limit); List voList = new ArrayList<>(dtoList.size()); for(MessageDTO dto : dtoList) { MessageVO vo = new MessageVO(); BeanUtils.copyProperties(dto, vo); voList.add(vo); } return ResponseEntity.ok(voList); } @PostMapping("") public ResponseEntity saveMessage(FullHttpRequest request, @RequestHeader("X-Token") String token, @RequestBody String body) { String userId = this.getSubjectFromJwt(token, "userId"); JSONObject json = JSONObject.parseObject(body); MessageDTO dto = new MessageDTO(); dto.setSenderId(userId); if(json.containsKey("type")) { dto.setType(json.getString("type")); } dto.setChannelId(json.getString("channelId")); dto.setContent(json.getString("content")); dto.setCreateAt(new Date().getTime()); MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService()); MessageDTO returnDTO = serviceProxy.saveMessage(dto); if(returnDTO != null) { MessageVO vo = new MessageVO(); BeanUtils.copyProperties(returnDTO, vo); return ResponseEntity.created(vo); } return ResponseEntity.notFound().build(); } @PostMapping("/read") public ResponseEntity readMessage(@RequestHeader("X-Token") String token, @RequestBody String body) { String userId = this.getSubjectFromJwt(token, "userId"); JSONObject json = JSONObject.parseObject(body); MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService()); int count = serviceProxy.readMessage(json.getString("channelId"), userId, json.getShortValue("total")); return ResponseEntity.created(count); } @DeleteMapping("") public ResponseEntity removeMessage(@RequestHeader("X-Token") String token, @RequestBody String body) { String userId = this.getSubjectFromJwt(token, "userId"); MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService()); JSONObject json = JSONObject.parseObject(body); int count = serviceProxy.removeMessage(json.getLongValue("messageId"), userId, json.getString("channelId"), json.getString("toUserId")); if(count == 1) { return ResponseEntity.noContent(count); } return ResponseEntity.notFound().build(); } @PostMapping("/files") public ResponseEntity uploadFile(@UploadFile MultipartFile file, @RequestHeader("X-Token") String token, @UrlEncodedForm Map form) { String userId = this.getSubjectFromJwt(token, "userId"); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); String fileKey = sdf.format(new Date()) + "/" + UUID.randomUUID().toString().replace("-", ""); // 保存文件 FileStorage fs = FileStorageFactory.newInstance(); fs.save(fileKey, file.getFileName(), file.getFileData()); int width = Integer.parseInt(form.get("imageWidth")); int height = Integer.parseInt(form.get("imageHeight")); short thumbWidth = getThumbWidth(width); short thumbHeight = getThumbHeight(height); short[] realThumbSize = new short[] { 0, 0 }; // 如果是图片,保存略缩图 if(this.isImage(file.getFileType())) { realThumbSize = fs.saveThumb(fileKey + "/thumb", file.getFileName(), file.getFileData(), thumbWidth, thumbHeight); } // 保存文件信息到数据库 MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService()); FileDTO fileDTO = new FileDTO(); fileDTO.setName(file.getFileName()); fileDTO.setExtension(getFileExtension(file.getFileName())); fileDTO.setSize(Integer.parseInt(form.get("size"))); fileDTO.setMimeType(file.getFileType()); fileDTO.setWidth(width); fileDTO.setHeight(height); fileDTO.setThumbWidth(realThumbSize[0]); fileDTO.setThumbHeight(realThumbSize[1]); fileDTO.setPath(fileKey); String fileId = serviceProxy.saveFile(fileDTO); // 保存消息 MessageDTO messageDTO = new MessageDTO(); messageDTO.setSenderId(userId); messageDTO.setChannelId(form.get("channelId")); messageDTO.setCreateAt(new Date().getTime()); messageDTO.setFileId(fileId); MessageDTO returnDTO = serviceProxy.saveMessage(messageDTO); if(returnDTO != null) { MessageVO vo = new MessageVO(); BeanUtils.copyProperties(returnDTO, vo); return ResponseEntity.created(vo); } return ResponseEntity.notFound().build(); } @GetMapping("/files") public ResponseEntity getFile(@RequestParam("fileName") String fileName, @RequestParam("fullPath") String fullPath, @RequestParam("mimetype") String mimetype) { FileStorage fs = FileStorageFactory.newInstance(); RandomAccessFile raf = fs.read(fullPath); return ResponseEntity.ok(raf, mimetype, fileName); } /** * 判断是否为图片类型 * @param fileType * @return */ private boolean isImage(String fileType) { if("image/jpeg".equalsIgnoreCase(fileType)) { return true; } if("image/png".equalsIgnoreCase(fileType)) { return true; } return false; } /** * 得到缩略图宽度 * @param width * @return */ private short getThumbWidth(int width) { if(width <= 119) { return (short)width; } return 119; } /** * 得到缩略图宽度 * @param width * @return */ private short getThumbHeight(int height) { if(height <= 81) { return (short)height; } return 81; } /** * 得到文件扩展名 * @param fileName * @return */ private String getFileExtension(String fileName) { String[] nameSplit = fileName.split("[.]"); return nameSplit[nameSplit.length - 1]; } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/controller/UserChannelController.java ================================================ package org.leo.im.http.controller; import java.util.ArrayList; import java.util.List; import org.leo.im.api.dto.UserChannelDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.api.service.UserChannelService; import org.leo.im.http.vo.UserChannelVO; import org.leo.im.service.support.ServiceProxy; import org.leo.im.util.BeanUtils; import org.leo.web.annotation.GetMapping; import org.leo.web.annotation.PatchMapping; import org.leo.web.annotation.PathVariable; import org.leo.web.annotation.PostMapping; import org.leo.web.annotation.RequestBody; import org.leo.web.annotation.RequestHeader; import org.leo.web.annotation.RequestMapping; import org.leo.web.annotation.RequestParam; import org.leo.web.annotation.RestController; import org.leo.web.rest.ResponseEntity; @RestController @RequestMapping("/userChannels") public final class UserChannelController extends BaseController { @GetMapping("/{userId}") public ResponseEntity> listUserChannel(@PathVariable("userId") String userId, @RequestParam("limit") int limit) { UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService()); List dtoList = serviceProxy.listUserChannel(userId, null, limit); if(dtoList.isEmpty()) { return ResponseEntity.ok(new ArrayList()); } List voList = new ArrayList<>(dtoList.size()); for(UserChannelDTO dto : dtoList) { UserChannelVO vo = new UserChannelVO(); BeanUtils.copyProperties(dto, vo); voList.add(vo); } return ResponseEntity.ok(voList); } @GetMapping("") public ResponseEntity getUserChannel(@RequestParam("userId") String userId, @RequestParam("channelId") String channelId) { UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService()); UserChannelDTO dto = serviceProxy.get(userId, channelId); if(dto == null) { return ResponseEntity.notFound().build(); } UserChannelVO vo = new UserChannelVO(); BeanUtils.copyProperties(dto, vo); return ResponseEntity.ok(vo); } /** * 修改频道显示名称 * @param body * @return */ @PatchMapping("/{channelId}") public ResponseEntity updateDisplayName(@PathVariable("channelId") String channelId, @RequestBody String displayName, @RequestHeader("X-Token") String token) { String userId = this.getSubjectFromJwt(token, "userId"); UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService()); int count = serviceProxy.updateDisplayName(channelId, userId, displayName); return ResponseEntity.created(count); } /** * 隐藏频道 * @param channelId * @param token * @return */ @PostMapping("/{channelId}/hiding") public ResponseEntity hideChannel(@PathVariable("channelId") String channelId, @RequestHeader("X-Token") String token) { String userId = this.getSubjectFromJwt(token, "userId"); UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService()); int count = serviceProxy.hideChannel(userId, channelId); return ResponseEntity.created(count); } /** * 根据名称搜索用户频道 * @param userId * @param name * @return */ @GetMapping("/{userId}/search") public ResponseEntity> listUserChannelByName(@PathVariable("userId") String userId, @RequestParam("name") String name) { UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService()); List dtoList = serviceProxy.listByName(userId, name, null); if(dtoList.isEmpty()) { return ResponseEntity.ok(new ArrayList()); } List voList = new ArrayList<>(dtoList.size()); for(UserChannelDTO dto : dtoList) { UserChannelVO vo = new UserChannelVO(); BeanUtils.copyProperties(dto, vo); voList.add(vo); } return ResponseEntity.ok(voList); } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/controller/UserController.java ================================================ package org.leo.im.http.controller; import org.leo.im.api.service.UserService; import org.leo.im.common.data.Page; import org.leo.im.http.cache.CacheManagerFactory; import org.leo.im.http.constant.CacheKeys; import org.leo.im.http.file.AvatarStorage; import org.leo.im.http.file.AvatarStorageFactory; import java.io.RandomAccessFile; import java.util.Iterator; import java.util.Set; import org.leo.im.api.dto.UserDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.service.support.ServiceProxy; import org.leo.web.annotation.GetMapping; import org.leo.web.annotation.PatchMapping; import org.leo.web.annotation.PathVariable; import org.leo.web.annotation.PostMapping; import org.leo.web.annotation.PutMapping; import org.leo.web.annotation.RequestBody; import org.leo.web.annotation.RequestHeader; import org.leo.web.annotation.RequestMapping; import org.leo.web.annotation.RequestParam; import org.leo.web.annotation.RestController; import org.leo.web.annotation.UploadFile; import org.leo.web.multipart.MultipartFile; import org.leo.web.rest.HttpStatus; import org.leo.web.rest.ResponseEntity; import com.alibaba.fastjson.JSONObject; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.cookie.Cookie; import io.netty.handler.codec.http.cookie.ServerCookieDecoder; /** * 用户控制器 * * @author Leo * @date 2018/4/9 */ @RestController() @RequestMapping("/users") public final class UserController extends BaseController { @GetMapping("/{id}") public ResponseEntity getById(@PathVariable() String id) { UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); UserDTO dto = serviceProxy.getById(id); return ResponseEntity.ok(dto); } @GetMapping("/me") public ResponseEntity getCurrentUser(@RequestHeader("X-Token") String token) { String userId = this.getSubjectFromJwt(token, "userId"); UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); UserDTO dto = serviceProxy.getById(userId); return ResponseEntity.ok(dto); } @PostMapping("") public ResponseEntity register(FullHttpRequest request, @RequestBody String body) { // 检查验证码 JSONObject json = JSONObject.parseObject(body); String verificationCode = json.getString("verificationCode"); if(verificationCode == null || verificationCode.trim().isEmpty()) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build("验证码为空"); } String sessionId = getJSessionId(request); if(sessionId == null || sessionId.trim().isEmpty()) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build("无效的请求"); } Object verificationCodeInCache = CacheManagerFactory.getCacheManager().get(CacheKeys.VERIFICATION_CODE_PREFIX + sessionId); if(verificationCodeInCache == null || verificationCodeInCache.toString().trim().isEmpty()) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build("无效的验证码"); } if(!verificationCode.equals(verificationCodeInCache)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build("无效的验证码"); } // 注册用户 UserDTO dto = new UserDTO(); dto.setName(json.getString("name")); dto.setNickname(json.getString("nickname")); dto.setPassword(json.getString("password")); UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); serviceProxy.saveUser(dto); return ResponseEntity.created().build(); } @GetMapping("") public ResponseEntity> listUser(@RequestParam("name") String name, @RequestParam("limit") int limit, @RequestParam("offset") int offset) { UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); Page result = serviceProxy.listByNameOrNickname(name, limit, offset); return ResponseEntity.ok(result); } @PatchMapping("/{id}") public ResponseEntity patchUser(@PathVariable() String id, @RequestBody String body) { JSONObject json = JSONObject.parseObject(body); UserDTO dto = new UserDTO(); dto.setId(id); if(json.containsKey("nickname")) { dto.setNickname(json.getString("nickname")); } if(json.containsKey("password")) { dto.setPassword(json.getString("password")); } if(json.containsKey("locked")) { dto.setLocked(json.getBoolean("locked")); } if(json.containsKey("avatarUrl") && !json.getString("avatarUrl").trim().isEmpty()) { dto.setAvatarUrl(json.getString("avatarUrl")); } if(json.containsKey("lastPostAt")) { dto.setLastPostAt(json.getLong("lastPostAt")); } if(json.containsKey("onlineStatus")) { dto.setOnlineStatus(json.getString("onlineStatus")); } UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); UserDTO returnDTO = serviceProxy.updateUser(dto, false); return ResponseEntity.created(returnDTO); } @PostMapping("/me/avatar") public ResponseEntity uploadAvatar(@UploadFile MultipartFile avatar, @RequestHeader("X-Token") String token) { String userId = this.getSubjectFromJwt(token, "userId"); String imageType = "png"; if("image/jpeg".equalsIgnoreCase(avatar.getFileType())) { imageType = "jpg"; } // 生成两张图片,尺寸分别为 32x32、36x36、80x80 AvatarStorage as = AvatarStorageFactory.newInstance(); as.save(userId, imageType, avatar.getFileData(), 32, 32); as.save(userId, imageType, avatar.getFileData(), 36, 36); as.save(userId, imageType, avatar.getFileData(), 80, 80); return ResponseEntity.created("avatar." + imageType); } @GetMapping("/{id}/avatar") public ResponseEntity getMyAvatar(@PathVariable("id") String userId, @RequestParam("width") int width, @RequestParam("height") int height) { // 得到用户头像 UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); UserDTO dto = serviceProxy.getById(userId); if(dto != null) { String avatarUrl = dto.getAvatarUrl(); if(avatarUrl != null && !avatarUrl.trim().isEmpty()) { AvatarStorage as = AvatarStorageFactory.newInstance(); RandomAccessFile raf = as.read(userId, avatarUrl, width, height); String mimetype = avatarUrl.toLowerCase().trim().endsWith("png") ? "image/png" : "image/jpeg"; return ResponseEntity.ok(raf, mimetype); } return ResponseEntity.noContent().build(); } return ResponseEntity.notFound().build(); } @GetMapping("/nonChannelMembers") public ResponseEntity> listNonChannelMember(@RequestParam("channelId") String channelId, @RequestParam("username") String username, @RequestParam("limit") int limit, @RequestParam("offset") int offset) { UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); Page result = serviceProxy.listNonMembers(channelId, username, limit, offset); return ResponseEntity.ok(result); } @PutMapping("/{userId}/password") public ResponseEntity changePassword(@PathVariable("userId") String userId, @RequestHeader("X-Token") String token, @RequestBody String body) { String currentUserId = this.getSubjectFromJwt(token, "userId"); if(!userId.equals(currentUserId)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } JSONObject data = JSONObject.parseObject(body); UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); int count = serviceProxy.updatePassword(userId, data.getString("username"), data.getString("oldPassword"), data.getString("newPassword")); return ResponseEntity.ok(count); } /** * 从cookie中得到Session Id * @return */ private String getJSessionId(FullHttpRequest request) { try { String cookieStr = request.headers().get("Cookie"); if(cookieStr == null || cookieStr.trim().isEmpty()) { return null; } Set cookies = ServerCookieDecoder.STRICT.decode(cookieStr); Iterator it = cookies.iterator(); while (it.hasNext()) { Cookie cookie = it.next(); if (cookie.name().equals(CacheKeys.JSESSIONID)) { if (CacheManagerFactory.getCacheManager().get(cookie.value()) != null) { return cookie.value(); } } } } catch (Exception e1) { return null; } return null; } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/exception/NoSuchSettingException.java ================================================ package org.leo.im.http.exception; /** * 无此配置异常类 * * @author Leo * @date 2018/5/11 */ public final class NoSuchSettingException extends RuntimeException { private static final long serialVersionUID = 1911497055661761968L; public NoSuchSettingException() { } public NoSuchSettingException(String message) { super(message); } public NoSuchSettingException(String message, Throwable cause) { super(message, cause); } public NoSuchSettingException(Throwable cause) { super(cause); } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/file/AbstractLocalFileStorage.java ================================================ package org.leo.im.http.file; import java.io.FileNotFoundException; import java.io.RandomAccessFile; /** * 本地文件存储抽象类 * * @author Leo * @date 2018/5/12 */ abstract class AbstractLocalFileStorage { /** * 读取文件内容 * @param fileName * @return */ public RandomAccessFile readFile(String fileName) { try { return new RandomAccessFile(fileName, "r"); } catch (FileNotFoundException ignore) { return null; } } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/file/AvatarStorage.java ================================================ package org.leo.im.http.file; import java.io.RandomAccessFile; /** * 头像存储接口 * * @author Leo * @date 2018/5/11 */ public interface AvatarStorage { /** * 保存头像 * @param userId * @param fileType * @param data * @param width * @param height * @return */ boolean save(String userId, String fileType, byte[] data, int width, int height); /** * 读取头像内容 * @param userId * @param fileName * @param width * @param height * @return */ RandomAccessFile read(String userId, String fileName, int width, int height); } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/file/AvatarStorageFactory.java ================================================ package org.leo.im.http.file; /** * 头像存储工厂类 * * @author Leo * @date 2018/5/11 */ public final class AvatarStorageFactory { /** * 创建头像存储类的实例 * @return */ public static AvatarStorage newInstance() { if("local".equalsIgnoreCase(System.getProperty("file.settings.driver"))) { return new LocalAvatarStorage(); } return new LocalAvatarStorage(); } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/file/FileStorage.java ================================================ package org.leo.im.http.file; import java.io.RandomAccessFile; /** * 文件存储接口 * * @author Leo * @date 2018/5/11 */ public interface FileStorage { /** * 保存文件 * @param key * @param fileName * @param data * @return */ boolean save(String key, String fileName, byte[] data); /** * 保存预览图片 * @param key * @param fileName * @param data * @param width * @param height * @return */ short[] saveThumb(String key, String fileName, byte[] data, int width, int height); /** * 读取文件内容 * @param fileName * @return */ RandomAccessFile read(String fileName); } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/file/FileStorageFactory.java ================================================ package org.leo.im.http.file; /** * 文件存储工厂类 * @author Administrator * */ public final class FileStorageFactory { /** * 得到文件存储类的实例 * @return */ public static FileStorage newInstance() { if("local".equalsIgnoreCase(System.getProperty("file.settings.driver"))) { return new LocalFileStorage(); } return new LocalFileStorage(); } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/file/LocalAvatarStorage.java ================================================ package org.leo.im.http.file; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import org.leo.im.http.exception.NoSuchSettingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.coobird.thumbnailator.Thumbnails; /** * 本地存储头像实现类 * * @author Leo * @date 2018/5/11 */ class LocalAvatarStorage extends AbstractLocalFileStorage implements AvatarStorage { private static Logger logger = LoggerFactory.getLogger(LocalAvatarStorage.class); /** * 保存头像 * @param userId * @param fileType * @param data * @param width * @param height * @return */ @Override public boolean save(String userId, String fileType, byte[] data, int width, int height) { StringBuilder newFile = new StringBuilder(256); newFile.append(System.getProperty("file.settings.directory")); if(newFile.length() == 0) { throw new NoSuchSettingException("file.settings.directory"); } if(userId != null && !userId.trim().isEmpty()) { newFile.append("/").append(userId); } // 判断目录是否存在 File dir = new File(newFile.toString()); if(!dir.exists()) { dir.mkdir(); } newFile.append("/avatar").append(width).append("x").append(height).append(".").append(fileType); InputStream is = new ByteArrayInputStream(data); try { Thumbnails.of(is).size(width > 70 ? width : width * 2, height > 70 ? height : height * 2).toFile(newFile.toString()); } catch (IOException e) { logger.error(e.getMessage()); return false; } return true; } /** * 读取头像内容 * @param userId * @param fileName * @param width * @param height * @return */ @Override public RandomAccessFile read(String userId, String fileName, int width, int height) { StringBuilder file = new StringBuilder(256); file.append(System.getProperty("file.settings.directory")); if(file.length() == 0) { throw new NoSuchSettingException("file.settings.directory"); } if(userId != null && !userId.trim().isEmpty()) { file.append("/").append(userId); } String[] fileNameSplit = fileName.split("\\."); file.append("/avatar").append(width).append("x").append(height).append(".").append(fileNameSplit[fileNameSplit.length - 1]); try { return new RandomAccessFile(file.toString(), "r"); } catch (FileNotFoundException e) { return null; } } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/file/LocalFileStorage.java ================================================ package org.leo.im.http.file; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import javax.imageio.ImageIO; import org.leo.im.http.exception.NoSuchSettingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.coobird.thumbnailator.Thumbnails; /** * 本地文件存储实现类 * * @author Leo * @date 2018/5/11 */ class LocalFileStorage extends AbstractLocalFileStorage implements FileStorage { private static Logger logger = LoggerFactory.getLogger(LocalFileStorage.class); /** * 保存文件 * @param key * @param fileName * @param data */ @Override public boolean save(String key, String fileName, byte[] data) { StringBuilder newFile = new StringBuilder(256); newFile.append(System.getProperty("file.settings.directory")); if(newFile.length() == 0) { throw new NoSuchSettingException("file.settings.directory"); } if(key != null && !key.trim().isEmpty()) { newFile.append("/").append(key); } // 判断目录是否存在 File dir = new File(newFile.toString()); if(!dir.exists()) { dir.mkdirs(); } // 创建文件 newFile.append("/").append(fileName); File file = new File(newFile.toString()); OutputStream os = null; try { file.createNewFile(); os = new FileOutputStream(file); os.write(data); return true; } catch (IOException e) { logger.error(e.getMessage()); return false; } finally { if(os != null) { try { os.close(); } catch (IOException e) { logger.error(e.getMessage()); } } } } /** * 保存预览图片 * @param key * @param fileName * @param data * @param width * @param height * @return */ @Override public short[] saveThumb(String key, String fileName, byte[] data, int width, int height) { StringBuilder newFile = new StringBuilder(256); newFile.append(System.getProperty("file.settings.directory")); if(newFile.length() == 0) { throw new NoSuchSettingException("file.settings.directory"); } if(key != null && !key.trim().isEmpty()) { newFile.append("/").append(key); } // 判断目录是否存在 File dir = new File(newFile.toString()); if(!dir.exists()) { dir.mkdir(); } newFile.append("/").append(fileName); InputStream is = new ByteArrayInputStream(data); try { Thumbnails.of(is).size(width, height).toFile(newFile.toString()); File picture = new File(newFile.toString()); BufferedImage sourceImage = ImageIO.read(new FileInputStream(picture)); return new short[] { (short)sourceImage.getWidth(), (short)sourceImage.getHeight() }; } catch (IOException e) { logger.error(e.getMessage()); return null; } } @Override public RandomAccessFile read(String fileName) { StringBuilder file = new StringBuilder(256); file.append(System.getProperty("file.settings.directory")); if(file.length() == 0) { throw new NoSuchSettingException("file.settings.directory"); } file.append("/").append(fileName); try { return new RandomAccessFile(file.toString(), "r"); } catch (FileNotFoundException e) { return null; } } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/interceptor/AuthenticationInterceptor.java ================================================ package org.leo.im.http.interceptor; import org.leo.im.http.util.JwtUtils; import org.leo.web.rest.HttpResponse; import org.leo.web.rest.HttpStatus; import org.leo.web.rest.interceptor.Interceptor; import io.netty.handler.codec.http.FullHttpRequest; /** * 身份认证拦截器 * * @author Leo * @date 2018/4/3 */ public class AuthenticationInterceptor implements Interceptor { @Override public boolean preHandle(FullHttpRequest request, HttpResponse response) throws Exception { if (request.method().name().equalsIgnoreCase("OPTIONS")) { return true; } try { JwtUtils.parseJWT(request.headers().get("X-Token"), System.getProperty("jwt.secret")); return true; } catch (Exception e) { response.write(HttpStatus.UNAUTHORIZED, "Unauthorized"); return false; } } @Override public void postHandle(FullHttpRequest request, HttpResponse response) throws Exception { } @Override public void afterCompletion(FullHttpRequest request, HttpResponse response) { } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/interceptor/CorsInterceptor.java ================================================ package org.leo.im.http.interceptor; import org.leo.web.rest.HttpResponse; import org.leo.web.rest.interceptor.Interceptor; import io.netty.handler.codec.http.FullHttpRequest; /** * 跨域拦截器 * * @author Leo * @date 2018/4/2 */ public final class CorsInterceptor implements Interceptor { @Override public boolean preHandle(FullHttpRequest request, HttpResponse response) throws Exception { // 使用axios发送cookie,这里不能用*,需要使用Web前端地址,如:http://localhost:8080 // response.getHeaders().put("Access-Control-Allow-Origin", "*"); response.getHeaders().put("Access-Control-Allow-Origin", System.getProperty("http.origin")); response.getHeaders().put("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE, PATCH"); response.getHeaders().put("Access-Control-Max-Age", "3600"); response.getHeaders().put("Access-Control-Allow-Headers", "Content-Type,X-Token"); response.getHeaders().put("Access-Control-Allow-Credentials", "true"); return true; } @Override public void postHandle(FullHttpRequest request, HttpResponse response) throws Exception { } @Override public void afterCompletion(FullHttpRequest request, HttpResponse response) { } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/util/JwtUtils.java ================================================ package org.leo.im.http.util; import java.util.Base64; import java.util.Date; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; /** * Jwt工具类 * * @author Leo * @date 2018/3/28 */ public class JwtUtils { /** * 生成加密key * * @param secret * @return */ private static SecretKey generalKey(String secret) { byte[] encodedKey = Base64.getDecoder().decode(secret); SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } /** * 创建jwt * * @param subject * @param secret * @param ttlMillis * @return */ public static String createJWT(String subject, String secret, long ttlMillis) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); SecretKey key = generalKey(secret); JwtBuilder builder = Jwts.builder().setId("jwt").setIssuedAt(now).setSubject(subject) .signWith(signatureAlgorithm, key); if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } return builder.compact(); } /** * 解密jwt * * @param jwt * @param secret * @return * @throws Exception */ public static Claims parseJWT(String jwt, String secret) { SecretKey key = generalKey(secret); Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody(); return claims; } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/vo/ChannelListVO.java ================================================ package org.leo.im.http.vo; /** * 频道列表vo类 * * @author Leo * @date 2018/4/3 */ public final class ChannelListVO { private String id; private String name; private String displayName; private String otherSideOnlineStatus; private String type; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getOtherSideOnlineStatus() { return otherSideOnlineStatus; } public void setOtherSideOnlineStatus(String otherSideOnlineStatus) { this.otherSideOnlineStatus = otherSideOnlineStatus; } public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String toString() { return "ChannelListVO [id=" + id + ", name=" + name + ", displayName=" + displayName + ", otherSideOnlineStatus=" + otherSideOnlineStatus + ", type=" + type + "]"; } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/vo/ChannelVO.java ================================================ package org.leo.im.http.vo; import java.util.ArrayList; import java.util.List; import org.leo.im.api.dto.ChannelMemberDTO; /** * 频道vo类 * @author Leo * @date 2018/4/12 */ public final class ChannelVO { private String id; private String name; private String displayName; private String type; private int memberCount; private String otherSideId; private String otherSideOnlineStatus; private String creatorId; private List members = new ArrayList<>(128); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getType() { return type; } public void setType(String type) { this.type = type; } public int getMemberCount() { return memberCount; } public void setMemberCount(int memberCount) { this.memberCount = memberCount; } public String getOtherSideId() { return otherSideId; } public void setOtherSideId(String otherSideId) { this.otherSideId = otherSideId; } public String getOtherSideOnlineStatus() { return otherSideOnlineStatus; } public void setOtherSideOnlineStatus(String otherSideOnlineStatus) { this.otherSideOnlineStatus = otherSideOnlineStatus; } public String getCreatorId() { return creatorId; } public void setCreatorId(String creatorId) { this.creatorId = creatorId; } public List getMembers() { return members; } @Override public String toString() { return "ChannelVO [id=" + id + ", name=" + name + ", displayName=" + displayName + ", type=" + type + ", memberCount=" + memberCount + ", otherSideId=" + otherSideId + ", otherSideOnlineStatus=" + otherSideOnlineStatus + ", creatorId=" + creatorId + ", members=" + members + "]"; } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/vo/MessageVO.java ================================================ package org.leo.im.http.vo; /** * 消息vo类 * * @author Leo * @date 2018/5/16 */ public final class MessageVO { private long id; private String channelId; private long createAt; private String type; private String senderId; private String senderName; private String senderNickname; private String senderOnlineStatus; private String senderAvatarUrl; private String senderFirstLetterOfName; private String content; private String fileName; private String fileExtension; private int fileSize; private String fileMimeType; private String fileThumbPath; private String filePath; private int imageWidth; private int imageHeight; private short imageThumbWidth; private short imageThumbHeight; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getChannelId() { return channelId; } public void setChannelId(String channelId) { this.channelId = channelId; } public long getCreateAt() { return createAt; } public void setCreateAt(long createAt) { this.createAt = createAt; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSenderId() { return senderId; } public void setSenderId(String senderId) { this.senderId = senderId; } public String getSenderName() { return senderName; } public void setSenderName(String senderName) { this.senderName = senderName; } public String getSenderNickname() { return senderNickname; } public void setSenderNickname(String senderNickname) { this.senderNickname = senderNickname; } public String getSenderOnlineStatus() { return senderOnlineStatus; } public void setSenderOnlineStatus(String senderOnlineStatus) { this.senderOnlineStatus = senderOnlineStatus; } public String getSenderAvatarUrl() { return senderAvatarUrl; } public void setSenderAvatarUrl(String senderAvatarUrl) { this.senderAvatarUrl = senderAvatarUrl; } public String getSenderFirstLetterOfName() { return senderFirstLetterOfName; } public void setSenderFirstLetterOfName(String senderFirstLetterOfName) { this.senderFirstLetterOfName = senderFirstLetterOfName; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getFileExtension() { return fileExtension; } public void setFileExtension(String fileExtension) { this.fileExtension = fileExtension; } public int getFileSize() { return fileSize; } public void setFileSize(int fileSize) { this.fileSize = fileSize; } public String getFileMimeType() { return fileMimeType; } public void setFileMimeType(String fileMimeType) { this.fileMimeType = fileMimeType; } public String getFileThumbPath() { return fileThumbPath; } public void setFileThumbPath(String fileThumbPath) { this.fileThumbPath = fileThumbPath; } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } public int getImageWidth() { return imageWidth; } public void setImageWidth(int imageWidth) { this.imageWidth = imageWidth; } public int getImageHeight() { return imageHeight; } public void setImageHeight(int imageHeight) { this.imageHeight = imageHeight; } public short getImageThumbWidth() { return imageThumbWidth; } public void setImageThumbWidth(short imageThumbWidth) { this.imageThumbWidth = imageThumbWidth; } public short getImageThumbHeight() { return imageThumbHeight; } public void setImageThumbHeight(short imageThumbHeight) { this.imageThumbHeight = imageThumbHeight; } public String getSenderRealAvatarUrl() { if("http://".equalsIgnoreCase(this.senderAvatarUrl) || "https://".equalsIgnoreCase(this.senderAvatarUrl)) { return this.senderAvatarUrl; } if(this.senderAvatarUrl != null && !this.senderAvatarUrl.trim().isEmpty()) { return this.senderAvatarUrl; } return null; } @Override public String toString() { return "MessageVO [id=" + id + ", channelId=" + channelId + ", createAt=" + createAt + ", type=" + type + ", senderId=" + senderId + ", senderName=" + senderName + ", senderNickname=" + senderNickname + ", senderOnlineStatus=" + senderOnlineStatus + ", senderAvatarUrl=" + senderAvatarUrl + ", senderFirstLetterOfName=" + senderFirstLetterOfName + ", content=" + content + ", fileName=" + fileName + ", fileExtension=" + fileExtension + ", fileSize=" + fileSize + ", fileMimeType=" + fileMimeType + ", fileThumbPath=" + fileThumbPath + ", filePath=" + filePath + ", imageWidth=" + imageWidth + ", imageHeight=" + imageHeight + ", imageThumbWidth=" + imageThumbWidth + ", imageThumbHeight=" + imageThumbHeight + "]"; } } ================================================ FILE: leo-im-http/src/main/java/org/leo/im/http/vo/UserChannelVO.java ================================================ package org.leo.im.http.vo; /** * 用户频道vo类 * * @author Leo * @date 2018/4/20 */ public final class UserChannelVO { private String channelId; private String channelName; private String channelType; private String channelDisplayName; private String channelDescription; private String toUserId; private String toUserOnlineStatus; private short unreadMessageCount; private int memberCount; private String creatorId; public String getChannelId() { return channelId; } public void setChannelId(String channelId) { this.channelId = channelId; } public String getChannelName() { return channelName; } public void setChannelName(String channelName) { this.channelName = channelName; } public String getChannelType() { return channelType; } public void setChannelType(String channelType) { this.channelType = channelType; } public String getChannelDisplayName() { return channelDisplayName; } public void setChannelDisplayName(String channelDisplayName) { this.channelDisplayName = channelDisplayName; } public String getChannelDescription() { return channelDescription; } public void setChannelDescription(String channelDescription) { this.channelDescription = channelDescription; } public String getToUserId() { return toUserId; } public void setToUserId(String toUserId) { this.toUserId = toUserId; } public String getToUserOnlineStatus() { return toUserOnlineStatus; } public void setToUserOnlineStatus(String toUserOnlineStatus) { this.toUserOnlineStatus = toUserOnlineStatus; } public short getUnreadMessageCount() { return unreadMessageCount; } public void setUnreadMessageCount(short unreadMessageCount) { this.unreadMessageCount = unreadMessageCount; } public int getMemberCount() { return memberCount; } public void setMemberCount(int memberCount) { this.memberCount = memberCount; } public String getCreatorId() { return creatorId; } public void setCreatorId(String creatorId) { this.creatorId = creatorId; } @Override public String toString() { return "UserChannelVO [channelId=" + channelId + ", channelName=" + channelName + ", channelType=" + channelType + ", channelDisplayName=" + channelDisplayName + ", channelDescription=" + channelDescription + ", toUserId=" + toUserId + ", toUserOnlineStatus=" + toUserOnlineStatus + ", unreadMessageCount=" + unreadMessageCount + ", memberCount=" + memberCount + ", creatorId=" + creatorId + "]"; } } ================================================ FILE: leo-im-http/src/test/java/org/leo/im/http/AppTest.java ================================================ package org.leo.im.http; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-http/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: asm-6.1.jar thumbnailator-0.4.8.jar netty-all-4.1.24.Final .jar fastjson-1.2.47.jar netty-rest-server-1.0.jar leo-im-api-1.0.jar leo-im-api-provider-1.0.jar leo-im-service-1.0.jar leo-im-store-1.0. jar mysql-connector-java-8.0.11.jar protobuf-java-2.6.0.jar druid-1.1 .9.jar leo-im-model-1.0.jar leo-im-util-1.0.jar cglib-3.2.6.jar ant-1 .9.6.jar ant-launcher-1.9.6.jar jjwt-0.9.0.jar jackson-databind-2.8.9 .jar jackson-annotations-2.8.0.jar jackson-core-2.8.9.jar leo-im-noti fication-1.0.jar jedis-2.9.0.jar commons-pool2-2.4.2.jar leo-im-commo n-1.0.jar slf4j-api-1.7.25.jar logback-classic-1.2.3.jar logback-core -1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-http/target/classes/META-INF/maven/org.leo.im/leo-im-http/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:34 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-http m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-http artifactId=leo-im-http ================================================ FILE: leo-im-http/target/classes/META-INF/maven/org.leo.im/leo-im-http/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-http leo-im-http http://maven.apache.org UTF-8 org.ow2.asm asm 6.1 net.coobird thumbnailator 0.4.8 io.netty netty-all ${netty.version} com.alibaba fastjson ${fastjson.version} org.leo netty-rest-server 1.0 org.leo.im leo-im-api ${parent.version} org.leo.im leo-im-api-provider ${project.version} org.leo.im leo-im-util ${project.version} org.leo.im leo-im-notification ${project.version} org.leo.im leo-im-common ${project.version} ================================================ FILE: leo-im-http/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:21 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-http ================================================ FILE: leo-im-http/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-http/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\controller\BaseController.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\interceptor\AuthenticationInterceptor.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\controller\ChannelController.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\cache\CacheManagerFactory.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\HttpServer.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\interceptor\CorsInterceptor.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\vo\ChannelVO.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\cache\CacheManager.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\controller\AuthController.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\vo\UserChannelVO.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\exception\NoSuchSettingException.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\file\LocalAvatarStorage.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\cache\MapCacheManager.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\file\LocalFileStorage.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\controller\ExceptionController.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\file\AvatarStorage.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\cache\Cache.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\util\JwtUtils.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\vo\ChannelListVO.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\file\FileStorage.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\controller\MessageController.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\controller\UserChannelController.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\vo\MessageVO.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\file\AvatarStorageFactory.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\controller\UserController.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\file\AbstractLocalFileStorage.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\file\FileStorageFactory.java F:\Develop\open-source\leo-im-server\leo-im-http\src\main\java\org\leo\im\http\constant\CacheKeys.java ================================================ FILE: leo-im-http/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-http/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-http\src\test\java\org\leo\im\http\AppTest.java ================================================ FILE: leo-im-http/target/surefire-reports/TEST-org.leo.im.http.AppTest.xml ================================================ ================================================ FILE: leo-im-http/target/surefire-reports/org.leo.im.http.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.http.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.065 sec ================================================ FILE: leo-im-migration/.classpath ================================================ ================================================ FILE: leo-im-migration/.project ================================================ leo-im-migration org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-migration/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/main/resources=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-migration/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-migration/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-migration/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-migration leo-im-migration http://maven.apache.org UTF-8 org.flywaydb flyway-core ${flyway-core.version} ================================================ FILE: leo-im-migration/src/main/java/org/leo/im/migration/FlywayMigration.java ================================================ package org.leo.im.migration; import org.flywaydb.core.Flyway; public class FlywayMigration { /** * 数据库迁移 * @param url JDBC URL * @param user 数据库用户 * @param password 数据库口令 */ public void migrate(String url, String user, String password) { Flyway flyway = new Flyway(); flyway.setDataSource(url, user, password); flyway.migrate(); } } ================================================ FILE: leo-im-migration/src/main/resources/db/migration/V20180615___Init.sql ================================================ CREATE TABLE IF NOT EXISTS `im_channel` ( `id` char(32) NOT NULL, `name` varchar(64) NOT NULL, `type` char(1) NOT NULL, `purpose` varchar(256) DEFAULT NULL, `create_at` bigint(20) unsigned NOT NULL, `delete_at` bigint(20) unsigned NOT NULL DEFAULT '0', `last_post_at` bigint(20) unsigned NOT NULL DEFAULT '0', `member_count` mediumint(8) unsigned NOT NULL DEFAULT '0', `creator_id` char(32) NOT NULL DEFAULT '0', `from_user_id` char(32) DEFAULT '0', `to_user_id` char(32) DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_channel_member` ( `channel_id` char(32) NOT NULL, `user_id` char(32) NOT NULL, `is_admin` tinyint(3) unsigned NOT NULL, PRIMARY KEY (`channel_id`,`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_file` ( `id` char(32) NOT NULL, `name` varchar(128) NOT NULL, `extension` varchar(32) NOT NULL, `size` int(11) NOT NULL, `mime_typ` varchar(256) NOT NULL, `width` smallint(6) NOT NULL, `height` smallint(6) NOT NULL, `path` varchar(128) NOT NULL, `thumb_width` smallint(6) DEFAULT NULL, `thumb_height` smallint(6) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_hide_channel` ( `user_id` char(32) NOT NULL, `channel_id` char(32) NOT NULL, PRIMARY KEY (`user_id`,`channel_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_message` ( `id` bigint(20) unsigned NOT NULL, `channel_id` char(32) COLLATE utf8mb4_bin NOT NULL, `sender_id` char(32) COLLATE utf8mb4_bin NOT NULL, `create_at` bigint(20) NOT NULL, `type` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL, `content` varchar(3000) COLLATE utf8mb4_bin DEFAULT NULL, `delete_at` bigint(20) NOT NULL DEFAULT '0', `file_id` char(32) COLLATE utf8mb4_bin DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE IF NOT EXISTS `im_unread_message_count` ( `user_id` char(32) NOT NULL, `channel_id` char(32) NOT NULL, `total` smallint(5) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`user_id`,`channel_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_user` ( `id` char(32) NOT NULL, `name` varchar(32) NOT NULL, `name_first_letter` char(1) NOT NULL, `nickname` varchar(32) NOT NULL, `salt` varchar(64) NOT NULL, `password` varchar(64) NOT NULL, `locked` tinyint(4) NOT NULL DEFAULT '0', `avatar_url` varchar(512) DEFAULT NULL, `created_at` datetime NOT NULL, `last_post_at` bigint(20) DEFAULT NULL, `online_status` varchar(7) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_user_channel` ( `user_id` char(32) NOT NULL, `channel_id` char(32) NOT NULL, `display_name` varchar(64) NOT NULL, `to_user_id` char(32) DEFAULT NULL, PRIMARY KEY (`user_id`,`channel_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `im_user` (`id`, `name`, `name_first_letter`, `nickname`, `salt`, `password`, `locked`, `avatar_url`, `created_at`, `last_post_at`, `online_status`) VALUES ('00000000000000000000000000000000', 'leo', 'l', '系统用户', 'MOlssyhqweLKffidserewr==', 'FDFGHTY33456FDHG000FDEKKKLLLPP', 0, NULL, '2018-06-16 21:00:00', NULL, NULL); ================================================ FILE: leo-im-migration/src/test/java/org/leo/im/migration/AppTest.java ================================================ package org.leo.im.migration; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-migration/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: flyway-core-5.1.1.jar slf4j-api-1.7.25.jar logback-classic -1.2.3.jar logback-core-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-migration/target/classes/META-INF/maven/org.leo.im/leo-im-migration/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:09 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-migration m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-migration artifactId=leo-im-migration ================================================ FILE: leo-im-migration/target/classes/META-INF/maven/org.leo.im/leo-im-migration/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-migration leo-im-migration http://maven.apache.org UTF-8 org.flywaydb flyway-core ${flyway-core.version} ================================================ FILE: leo-im-migration/target/classes/db/migration/V20180615___Init.sql ================================================ CREATE TABLE IF NOT EXISTS `im_channel` ( `id` char(32) NOT NULL, `name` varchar(64) NOT NULL, `type` char(1) NOT NULL, `purpose` varchar(256) DEFAULT NULL, `create_at` bigint(20) unsigned NOT NULL, `delete_at` bigint(20) unsigned NOT NULL DEFAULT '0', `last_post_at` bigint(20) unsigned NOT NULL DEFAULT '0', `member_count` mediumint(8) unsigned NOT NULL DEFAULT '0', `creator_id` char(32) NOT NULL DEFAULT '0', `from_user_id` char(32) DEFAULT '0', `to_user_id` char(32) DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_channel_member` ( `channel_id` char(32) NOT NULL, `user_id` char(32) NOT NULL, `is_admin` tinyint(3) unsigned NOT NULL, PRIMARY KEY (`channel_id`,`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_file` ( `id` char(32) NOT NULL, `name` varchar(128) NOT NULL, `extension` varchar(32) NOT NULL, `size` int(11) NOT NULL, `mime_typ` varchar(256) NOT NULL, `width` smallint(6) NOT NULL, `height` smallint(6) NOT NULL, `path` varchar(128) NOT NULL, `thumb_width` smallint(6) DEFAULT NULL, `thumb_height` smallint(6) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_hide_channel` ( `user_id` char(32) NOT NULL, `channel_id` char(32) NOT NULL, PRIMARY KEY (`user_id`,`channel_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_message` ( `id` bigint(20) unsigned NOT NULL, `channel_id` char(32) COLLATE utf8mb4_bin NOT NULL, `sender_id` char(32) COLLATE utf8mb4_bin NOT NULL, `create_at` bigint(20) NOT NULL, `type` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL, `content` varchar(3000) COLLATE utf8mb4_bin DEFAULT NULL, `delete_at` bigint(20) NOT NULL DEFAULT '0', `file_id` char(32) COLLATE utf8mb4_bin DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE IF NOT EXISTS `im_unread_message_count` ( `user_id` char(32) NOT NULL, `channel_id` char(32) NOT NULL, `total` smallint(5) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`user_id`,`channel_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_user` ( `id` char(32) NOT NULL, `name` varchar(32) NOT NULL, `name_first_letter` char(1) NOT NULL, `nickname` varchar(32) NOT NULL, `salt` varchar(64) NOT NULL, `password` varchar(64) NOT NULL, `locked` tinyint(4) NOT NULL DEFAULT '0', `avatar_url` varchar(512) DEFAULT NULL, `created_at` datetime NOT NULL, `last_post_at` bigint(20) DEFAULT NULL, `online_status` varchar(7) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `im_user_channel` ( `user_id` char(32) NOT NULL, `channel_id` char(32) NOT NULL, `display_name` varchar(64) NOT NULL, `to_user_id` char(32) DEFAULT NULL, PRIMARY KEY (`user_id`,`channel_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `im_user` (`id`, `name`, `name_first_letter`, `nickname`, `salt`, `password`, `locked`, `avatar_url`, `created_at`, `last_post_at`, `online_status`) VALUES ('00000000000000000000000000000000', 'leo', 'l', '系统用户', 'MOlssyhqweLKffidserewr==', 'FDFGHTY33456FDHG000FDEKKKLLLPP', 0, NULL, '2018-06-16 21:00:00', NULL, NULL); ================================================ FILE: leo-im-migration/target/maven-archiver/pom.properties ================================================ #Created by Apache Maven 3.3.9 version=1.0 groupId=org.leo.im artifactId=leo-im-migration ================================================ FILE: leo-im-migration/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-migration/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-migration\src\main\java\org\leo\im\migration\FlywayMigration.java ================================================ FILE: leo-im-model/.classpath ================================================ ================================================ FILE: leo-im-model/.project ================================================ leo-im-model org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-model/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-model/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-model/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-model/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-model leo-im-model http://maven.apache.org ================================================ FILE: leo-im-model/src/main/java/org/leo/im/model/Channel.java ================================================ package org.leo.im.model; import java.util.ArrayList; import java.util.List; /** * 频道模型类 * * @author Leo * @date 2018/4/7 */ public final class Channel { private String id; private String name; private User from; private User to; /** * 频道类型 * G:群聊;P:私聊 */ private String type; private String purpose; private long createAt; private long deleteAt; private long lastPostAt; private int memberCount; private User creator; private List members = new ArrayList<>(128); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public User getFrom() { return from; } public void setFrom(User from) { this.from = from; } public User getTo() { return to; } public void setTo(User to) { this.to = to; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getPurpose() { return purpose; } public void setPurpose(String purpose) { this.purpose = purpose; } public long getCreateAt() { return createAt; } public void setCreateAt(long createAt) { this.createAt = createAt; } public long getDeleteAt() { return deleteAt; } public void setDeleteAt(long deleteAt) { this.deleteAt = deleteAt; } public long getLastPostAt() { return lastPostAt; } public void setLastPostAt(long lastPostAt) { this.lastPostAt = lastPostAt; } public int getMemberCount() { return memberCount; } public void setMemberCount(int memberCount) { this.memberCount = memberCount; } public User getCreator() { return creator; } public void setCreator(User creator) { this.creator = creator; } public List getMembers() { return members; } @Override public String toString() { return "Channel [id=" + id + ", name=" + name + ", from=" + from + ", to=" + to + ", type=" + type + ", purpose=" + purpose + ", createAt=" + createAt + ", deleteAt=" + deleteAt + ", lastPostAt=" + lastPostAt + ", memberCount=" + memberCount + ", creator=" + creator + ", members=" + members + "]"; } } ================================================ FILE: leo-im-model/src/main/java/org/leo/im/model/ChannelMember.java ================================================ package org.leo.im.model; /** * 频道成员实体类 * * @author Leo * @date 2018/4/11 */ public final class ChannelMember { private User user; private boolean admin; public User getUser() { return this.user; } public void setUser(User user) { this.user = user; } public boolean getAdmin() { return this.admin; } public void setAdmin(boolean admin) { this.admin = admin; } @Override public String toString() { return "ChannelMember [user=" + user + ", admin=" + admin + "]"; } } ================================================ FILE: leo-im-model/src/main/java/org/leo/im/model/File.java ================================================ package org.leo.im.model; /** * 文件类 * * @author Leo * @date 2018/6/13 */ public class File { private String id; private String name; private String extension; private int size; private String mimeType; private int width; private int height; private short thumbWidth; private short thumbHeight; private String path; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getExtension() { return extension; } public void setExtension(String extension) { this.extension = extension; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String getMimeType() { return mimeType; } public void setMimeType(String mimeType) { this.mimeType = mimeType; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public short getThumbWidth() { return thumbWidth; } public void setThumbWidth(short thumbWidth) { this.thumbWidth = thumbWidth; } public short getThumbHeight() { return thumbHeight; } public void setThumbHeight(short thumbHeight) { this.thumbHeight = thumbHeight; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } @Override public String toString() { return "File [id=" + id + ", name=" + name + ", extension=" + extension + ", size=" + size + ", mimeType=" + mimeType + ", width=" + width + ", height=" + height + ", thumbWidth=" + thumbWidth + ", thumbHeight=" + thumbHeight + ", path=" + path + "]"; } } ================================================ FILE: leo-im-model/src/main/java/org/leo/im/model/Message.java ================================================ package org.leo.im.model; /** * 消息类 * * @author Leo * @date 2018/4/21 */ public final class Message { private long id; private Channel channel; private User sender; private long createAt; /** * 消息类型(如:加某某加入群组、某某退出群组) */ private String type; private String content; private File file; public long getId() { return id; } public void setId(long id) { this.id = id; } public Channel getChannel() { return channel; } public void setChannel(Channel channel) { this.channel = channel; } public User getSender() { return sender; } public void setSender(User sender) { this.sender = sender; } public long getCreateAt() { return createAt; } public void setCreateAt(long createAt) { this.createAt = createAt; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public File getFile() { return file; } public void setFile(File file) { this.file = file; } @Override public String toString() { return "Message [id=" + id + ", channel=" + channel + ", sender=" + sender + ", createAt=" + createAt + ", type=" + type + ", content=" + content + ", file=" + file + "]"; } } ================================================ FILE: leo-im-model/src/main/java/org/leo/im/model/User.java ================================================ package org.leo.im.model; import java.util.Date; /** * 用户模型类 * * @author Leo * @date 2018/3/30 */ public final class User { private String id; private String name; private String firstLetterOfName; private String nickname; private String salt; private String password; private Boolean locked; private Date createdAt; private String avatarUr; private long lastPostAt; private String onlineStatus; public String getId() { return this.id; } public void setId(String id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getFirstLetterOfName() { return this.firstLetterOfName; } public void setFirstLetterOfName(String firstLetter) { this.firstLetterOfName = firstLetter; } public String getNickname() { return this.nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getSalt() { return this.salt; } public void setSalt(String salt) { this.salt = salt; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } public Boolean getLocked() { return this.locked; } public void setLocked(Boolean locked) { this.locked = locked; } public Date getCreatedAt() { return this.createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public long getLastPostAt() { return this.lastPostAt; } public void setLastPostAt(long lastPostAt) { this.lastPostAt = lastPostAt; } public String getAvatarUrl() { return this.avatarUr; } public void setAvatarUrl(String avatarUr) { this.avatarUr = avatarUr; } public String getOnlineStatus() { return this.onlineStatus; } public void setOnlineStatus(String onlineStatus) { this.onlineStatus = onlineStatus; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", firstLetterOfName=" + firstLetterOfName + ", nickname=" + nickname + ", salt=" + salt + ", password=" + password + ", locked=" + locked + ", createdAt=" + createdAt + ", avatarUr=" + avatarUr + ", onlineStatus=" + onlineStatus + "]"; } } ================================================ FILE: leo-im-model/src/main/java/org/leo/im/model/UserChannel.java ================================================ package org.leo.im.model; /** * 用户频道类 * * @author Leo * @date 2018/4/20 */ public final class UserChannel { private User user; private Channel channel; private String displayName; private User toUser; private short unreadMessageCount; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public Channel getChannel() { return channel; } public void setChannel(Channel channel) { this.channel = channel; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public User getToUser() { return toUser; } public void setToUser(User toUser) { this.toUser = toUser; } public short getUnreadMessageCount() { return unreadMessageCount; } public void setUnreadMessageCount(short unreadMessageCount) { this.unreadMessageCount = unreadMessageCount; } @Override public String toString() { return "UserChannel [user=" + user + ", channel=" + channel + ", displayName=" + displayName + ", toUser=" + toUser + ", unreadMessageCount=" + unreadMessageCount + "]"; } } ================================================ FILE: leo-im-model/src/test/java/org/leo/im/model/AppTest.java ================================================ package org.leo.im.model; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-model/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: slf4j-api-1.7.25.jar logback-classic-1.2.3.jar logback-cor e-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-model/target/classes/META-INF/maven/org.leo.im/leo-im-model/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:10 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-model m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-model artifactId=leo-im-model ================================================ FILE: leo-im-model/target/classes/META-INF/maven/org.leo.im/leo-im-model/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-model leo-im-model http://maven.apache.org ================================================ FILE: leo-im-model/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:10 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-model ================================================ FILE: leo-im-model/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-model/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-model\src\main\java\org\leo\im\model\ChannelMember.java F:\Develop\open-source\leo-im-server\leo-im-model\src\main\java\org\leo\im\model\User.java F:\Develop\open-source\leo-im-server\leo-im-model\src\main\java\org\leo\im\model\Channel.java F:\Develop\open-source\leo-im-server\leo-im-model\src\main\java\org\leo\im\model\UserChannel.java F:\Develop\open-source\leo-im-server\leo-im-model\src\main\java\org\leo\im\model\File.java F:\Develop\open-source\leo-im-server\leo-im-model\src\main\java\org\leo\im\model\Message.java ================================================ FILE: leo-im-model/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-model/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-model\src\test\java\org\leo\im\model\AppTest.java ================================================ FILE: leo-im-model/target/surefire-reports/TEST-org.leo.im.model.AppTest.xml ================================================ ================================================ FILE: leo-im-model/target/surefire-reports/org.leo.im.model.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.model.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.076 sec ================================================ FILE: leo-im-notification/.classpath ================================================ ================================================ FILE: leo-im-notification/.project ================================================ leo-im-notification org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-notification/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-notification/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-notification/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-notification/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-notification leo-im-notification http://maven.apache.org UTF-8 redis.clients jedis ${jedis.version} com.alibaba fastjson ${fastjson.version} ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/ActionNames.java ================================================ package org.leo.im.notification; /** * 动作名称常量类 * * @author Leo * @date 2018/3/29 */ public class ActionNames { /** * 在线状态改变 */ public static final String ONLINE_STATUS_CHANGED = "ONLINE_STATUS_CHANGED"; /** * 昵称改变 */ public static final String NICKNAME_CHANGED = "NICKNAME_CHANGED"; /** * 头像改变 */ public static final String AVATAR_CHANGED = "AVATAR_CHANGED"; /** * 收到新消息 */ public static final String NEW_MESSAGE = "NEW_MESSAGE"; /** * 读取消息 */ public static final String READ_MESSAGE = "READ_MESSAGE"; /** * 创建频道 */ public static final String CREATE_CHANNEL = "CREATE_CHANNEL"; /** * 频道被删除 */ public static final String CHANNEL_REMOVED = "CHANNEL_REMOVED"; /** * 加入频道 */ public static final String JOIN_CHANNEL = "JOIN_CHANNEL"; /** * 离开频道 */ public static final String LEAVE_CHANNEL = "LEAVE_CHANNEL"; /** * 被移除频道 */ public static final String REMOVE_FROM_CHANNEL = "REMOVE_FROM_CHANNEL"; /** * 消息被删除事件 */ public static final String MESSAGE_REMOVED = "MESSAGE_REMOVED"; /** * 频道名称改变事件 */ public static final String CHANNEL_NAME_CHANGED = "CHANNEL_NAME_CHANGED"; /** * 频道成员数量变更通知 */ public static final String MEMBERS_COUNT_CHANGED = "MEMBERS_COUNT_CHANGED"; } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/PublishKeys.java ================================================ package org.leo.im.notification; /** * 发布key常量类 * * @author Leo * @date 2018/3/29 */ public class PublishKeys { /** * 私聊消息频道 */ public static final String PRIVATE_MESSAGE_CHANNEL; /** * 群聊消息频道 */ public static final String GROUP_MESSAGE_CHANNEL; /** * 系统消息频道 */ public static final String SYSTEM_MESSAGE_CHANNEL; static { PRIVATE_MESSAGE_CHANNEL = System.getProperty("channel.private.message") == null ? "im-channel" : System.getProperty("channel.private.message"); GROUP_MESSAGE_CHANNEL = System.getProperty("channel.group.message") == null ? "im-channel" : System.getProperty("channel.group.message"); SYSTEM_MESSAGE_CHANNEL = System.getProperty("channel.system.message") == null ? "im-channel" : System.getProperty("channel.system.message"); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/Publisher.java ================================================ package org.leo.im.notification; /** * 发布者接口 * * @author Leo * @date 2018/3/28 */ public interface Publisher { /** * 发布消息 * @param channel * @param message */ void publish(String channel, String message); /** * 订阅频道 * @param subscriber * @param channels */ void subscribe(Subscriber subscriber, String... channels); } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/PublisherFactory.java ================================================ package org.leo.im.notification; import org.leo.im.notification.Publisher; import org.leo.im.notification.QueuePublisher; /** * 发布者工厂类 * * @author Leo * @date 2018/3/29 */ public class PublisherFactory { /** * 创建发布者的实例 * * @return */ public static Publisher createPublisher() { switch (System.getProperty("notification.type")) { case "queue": return QueuePublisher.getInstance(); default: return QueuePublisher.getInstance(); } } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/QueuePublisher.java ================================================ package org.leo.im.notification; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 队列发布者实现类 * * @author Leo * @date 2018/3/30 */ public class QueuePublisher implements Publisher { private static final int QUEUE_CAPACITY; private static final Logger logger = LoggerFactory.getLogger(QueuePublisher.class); static { Integer queueCapacity = Integer.getInteger("notification.capacity"); QUEUE_CAPACITY = (queueCapacity == null ? 10240 : queueCapacity); } /** * 包含频道的哈希表 */ private Map> channels = new ConcurrentHashMap<>(2); private QueuePublisher() { } private static class InstanceHolder { private static QueuePublisher instance = new QueuePublisher(); } public static QueuePublisher getInstance() { return InstanceHolder.instance; } /** * 发布消息 * * @param channel * @param message */ @Override public void publish(String channel, String message) { BlockingQueue queue = channels.get(message); if (queue == null) { queue = new LinkedBlockingQueue(QUEUE_CAPACITY); BlockingQueue returnQueue = this.channels.putIfAbsent(channel, queue); if (returnQueue != null) { queue = returnQueue; } } try { queue.put(message); } catch (InterruptedException e) { logger.error(e.getMessage()); } } /** * 订阅频道 * * @param subscriber * @param subscribeChannels */ @Override public void subscribe(Subscriber subscriber, String... subscribeChannels) { for (String channel : subscribeChannels) { BlockingQueue queue = new LinkedBlockingQueue<>(QUEUE_CAPACITY); BlockingQueue returnQueue = this.channels.putIfAbsent(channel, queue); final BlockingQueue subscribeQueue = returnQueue != null ? returnQueue : queue; Thread thread = new Thread(() -> { for (;;) { final String message = takeFromQueue(subscribeQueue); if (message != null) { ThreadPoolHolder.getThreadPool().execute(() -> { subscriber.onMessage(channel, message); }); } } }); thread.setName("SubscriberThread-" + channel); thread.start(); } } /** * 从队列中获取数据 * * @param subscribeQueue * @return */ private String takeFromQueue(BlockingQueue subscribeQueue) { try { return subscribeQueue.take(); } catch (InterruptedException e) { logger.error(e.getMessage()); return null; } } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/Subscriber.java ================================================ package org.leo.im.notification; /** * 订阅者接口 * * @author Leo * @date 2018/3/28 */ public interface Subscriber { /** * 接收数据 * @param channel * @param message */ void onMessage(String channel, String message); } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/ThreadPoolHolder.java ================================================ package org.leo.im.notification; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * 线程池持有者 * * @author Leo * @date 2018/3/30 */ public class ThreadPoolHolder { private static final int THREAD_POOL_THREADS; private static final int THREAD_POOL_QUEUES; static { Integer threads = Integer.getInteger("thread.pool.threads"); Integer queues = Integer.getInteger("thread.pool.queues"); THREAD_POOL_THREADS = threads == null ? Runtime.getRuntime().availableProcessors() * 2 : threads; THREAD_POOL_QUEUES = queues == null ? 512 : queues; } // 业务线程池 private final static ExecutorService THREAD_POOL = new ThreadPoolExecutor(THREAD_POOL_THREADS, THREAD_POOL_THREADS, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(THREAD_POOL_QUEUES), new ThreadFactory() { private AtomicInteger threadIndex = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { return new Thread(r, "handlerThread_" + this.threadIndex.incrementAndGet()); } }, new ThreadPoolExecutor.DiscardPolicy()); /** * 得到线程池 * * @return */ public static ExecutorService getThreadPool() { return THREAD_POOL; } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/AvatarChangedEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 头像改变事件 * * @author Leo * @date 2018/5/14 */ public class AvatarChangedEvent implements NotificationEvent { private String userId; private String avatar; public AvatarChangedEvent(String userId, String avatar) { this.userId = userId; this.avatar = avatar; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.AVATAR_CHANGED); message.put("userId", userId); message.put("avatar", avatar); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/ChannelCreatedEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 创建频道事件处理器 * * @author Leo * @date 2018/5/27 */ public class ChannelCreatedEvent implements NotificationEvent { private String channelId; private String userIds; public ChannelCreatedEvent(String channelId, String userIds) { this.channelId = channelId; this.userIds = userIds; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.CREATE_CHANNEL); message.put("channelId", this.channelId); message.put("userIds", this.userIds); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/ChannelNameChangedEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 频道名称改变事件 * * @author Leo * @date 2018/6/8 */ public class ChannelNameChangedEvent implements NotificationEvent { private String channelId; private String channelName; public ChannelNameChangedEvent(String channelId, String channelName) { this.channelId = channelId; this.channelName = channelName; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.CHANNEL_NAME_CHANGED); message.put("channelId", this.channelId); message.put("channelName", this.channelName); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/ChannelRemovedEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 频道被删除事件 * * @author Leo * @date 2018/6/12 */ public class ChannelRemovedEvent implements NotificationEvent { private String channelId; public ChannelRemovedEvent(String channelId) { this.channelId = channelId; } @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.CHANNEL_REMOVED); message.put("channelId", this.channelId); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/JoinChannelEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; /** * 加入频道事件 * * @author Leo * @date 2018/5/28 */ public class JoinChannelEvent implements NotificationEvent { private String channelId; private String[] userIds; private String[] userNicknames; private String admin; private boolean sendBroadcast; public JoinChannelEvent(String channelId, String[] userIds, String[] userNicknames, String admin, boolean sendBroadcast) { this.channelId = channelId; this.userIds = userIds; this.userNicknames = userNicknames; this.admin = admin; this.sendBroadcast = sendBroadcast; } @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.JOIN_CHANNEL); message.put("channelId", this.channelId); message.put("channelType", "G"); message.put("admin", this.admin); message.put("sendBroadcast", this.sendBroadcast); JSONArray userIdArray = new JSONArray(); if(this.userIds != null) { for(String userId : this.userIds) { JSONObject userIdJSON = new JSONObject(); userIdJSON.put("id", userId); userIdArray.add(userIdJSON); } } message.put("userIds", userIdArray); JSONArray userNicknameArray = new JSONArray(); if(this.userNicknames != null) { for(String nickname : this.userNicknames) { JSONObject nicknameJSON = new JSONObject(); nicknameJSON.put("nickname", nickname); userNicknameArray.add(nicknameJSON); } } message.put("userNicknames", userNicknameArray); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/LeaveChannelEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 离开频道事件 * * @author Leo * @date 2018/6/11 */ public class LeaveChannelEvent implements NotificationEvent { private String channelId; private String userId; private String userNickname; private boolean sendBroadcast; public LeaveChannelEvent(String channelId, String userId, String userNickname, boolean sendBroadcast) { this.channelId = channelId; this.userId = userId; this.userNickname = userNickname; this.sendBroadcast = sendBroadcast; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.LEAVE_CHANNEL); message.put("channelId", this.channelId); message.put("channelType", "G"); message.put("sendBroadcast", this.sendBroadcast); message.put("userId", this.userId); message.put("userNickname", this.userNickname); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/MembersCountChangedEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 成员数量变更事件 * * @author Leo * @date 2018/6/11 */ public class MembersCountChangedEvent implements NotificationEvent { private String channelId; private int count; public MembersCountChangedEvent(String channelId, int count) { this.channelId = channelId; this.count = count; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.MEMBERS_COUNT_CHANGED); message.put("channelId", this.channelId); message.put("count", this.count); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/MessageRemovedEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 消息被删除事件 * @author Leo * @date 2018/6/5 */ public class MessageRemovedEvent implements NotificationEvent { private long messageId; private String senderId; private String channelId; private String toUserId; public MessageRemovedEvent(long messageId, String senderId, String channelId, String toUserId) { this.messageId = messageId; this.senderId = senderId; this.channelId = channelId; this.toUserId = toUserId; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.MESSAGE_REMOVED); message.put("messageId", this.messageId); message.put("senderId", this.senderId); message.put("channelId", this.channelId); message.put("toUserId", toUserId); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/NewMessageEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 收到新消息事件 * * @author Leo * @date 2018/5/16 */ public class NewMessageEvent implements NotificationEvent { private JSONObject message; public NewMessageEvent(JSONObject message) { this.message = message; } /** * 触发事件 */ @Override public void trigger() { message.put("action", ActionNames.NEW_MESSAGE); // 群发、私聊、系统消息 if(message.containsKey("type")) { PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); return; } if(message.containsKey("userIds")) { PublisherFactory.createPublisher().publish(PublishKeys.PRIVATE_MESSAGE_CHANNEL, message.toJSONString()); return; } PublisherFactory.createPublisher().publish(PublishKeys.GROUP_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/NicknameChangedEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 昵称改变事件 * * @author Leo * @date 2018/5/14 */ public class NicknameChangedEvent implements NotificationEvent { private String userId; private String nickname; public NicknameChangedEvent(String userId, String nickname) { this.userId = userId; this.nickname = nickname; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.NICKNAME_CHANGED); message.put("userId", userId); message.put("nickname", nickname); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/NotificationEvent.java ================================================ package org.leo.im.notification.event; /** * 通知事件接口 * * @author Leo * @date 2018/5/10 */ public interface NotificationEvent { /** * 触发事件 */ void trigger(); } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/OnlineStatusChangedEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 在线状态改变事件 * * @author Le * @date 2018/5/10 */ public class OnlineStatusChangedEvent implements NotificationEvent { private String userId; private String onlineStatus; public OnlineStatusChangedEvent(String userId, String onlineStatus) { this.userId = userId; this.onlineStatus = onlineStatus; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.ONLINE_STATUS_CHANGED); message.put("userId", userId); message.put("onlineStatus", onlineStatus); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/ReadMessageEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONObject; /** * 读取消息事件 * * @author Leo * @date 2018/5/22 */ public class ReadMessageEvent implements NotificationEvent { private String userId; private String channelId; private short total; private boolean readAll; public ReadMessageEvent(String userId, String channelId, short total, boolean readAll) { this.userId = userId; this.channelId = channelId; this.total = total; this.readAll = readAll; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.READ_MESSAGE); message.put("userId", userId); message.put("channelId", channelId); message.put("total", total); message.put("readAll", readAll); PublisherFactory.createPublisher().publish(PublishKeys.PRIVATE_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/main/java/org/leo/im/notification/event/RemoveFromChannelEvent.java ================================================ package org.leo.im.notification.event; import org.leo.im.notification.ActionNames; import org.leo.im.notification.PublishKeys; import org.leo.im.notification.PublisherFactory; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; /** * 从频道被删除事件 * * @author Leo * @date 2018/6/11 */ public class RemoveFromChannelEvent implements NotificationEvent { private String channelId; private String[] userIds; private String[] userNicknames; private String admin; private boolean sendBroadcast; public RemoveFromChannelEvent(String channelId, String[] userIds, String[] userNicknames, String admin, boolean sendBroadcast) { this.channelId = channelId; this.userIds = userIds; this.userNicknames = userNicknames; this.admin = admin; this.sendBroadcast = sendBroadcast; } /** * 触发事件 */ @Override public void trigger() { JSONObject message = new JSONObject(); message.put("action", ActionNames.REMOVE_FROM_CHANNEL); message.put("channelId", this.channelId); message.put("channelType", "G"); message.put("admin", this.admin); message.put("sendBroadcast", this.sendBroadcast); JSONArray userIdArray = new JSONArray(); if(this.userIds != null) { for(String userId : this.userIds) { JSONObject userIdJSON = new JSONObject(); userIdJSON.put("id", userId); userIdArray.add(userIdJSON); } } message.put("userIds", userIdArray); JSONArray userNicknameArray = new JSONArray(); if(this.userNicknames != null) { for(String nickname : this.userNicknames) { JSONObject nicknameJSON = new JSONObject(); nicknameJSON.put("nickname", nickname); userNicknameArray.add(nicknameJSON); } } message.put("userNicknames", userNicknameArray); PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString()); } } ================================================ FILE: leo-im-notification/src/test/java/org/leo/im/notification/AppTest.java ================================================ package org.leo.im.notification; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-notification/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: jedis-2.9.0.jar commons-pool2-2.4.2.jar fastjson-1.2.47.ja r slf4j-api-1.7.25.jar logback-classic-1.2.3.jar logback-core-1.2.3.j ar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-notification/target/classes/META-INF/maven/org.leo.im/leo-im-notification/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:11 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-notification m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-notification artifactId=leo-im-notification ================================================ FILE: leo-im-notification/target/classes/META-INF/maven/org.leo.im/leo-im-notification/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-notification leo-im-notification http://maven.apache.org UTF-8 redis.clients jedis ${jedis.version} com.alibaba fastjson ${fastjson.version} ================================================ FILE: leo-im-notification/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:16 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-notification ================================================ FILE: leo-im-notification/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ org\leo\im\notification\QueuePublisher$1.class ================================================ FILE: leo-im-notification/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\ReadMessageEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\QueuePublisher.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\NicknameChangedEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\JoinChannelEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\NewMessageEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\PublishKeys.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\LeaveChannelEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\ChannelNameChangedEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\PublisherFactory.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\Publisher.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\MessageRemovedEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\ChannelCreatedEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\MembersCountChangedEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\OnlineStatusChangedEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\ChannelRemovedEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\NotificationEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\ActionNames.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\ThreadPoolHolder.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\AvatarChangedEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\event\RemoveFromChannelEvent.java F:\Develop\open-source\leo-im-server\leo-im-notification\src\main\java\org\leo\im\notification\Subscriber.java ================================================ FILE: leo-im-notification/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-notification/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-notification\src\test\java\org\leo\im\notification\AppTest.java ================================================ FILE: leo-im-notification/target/surefire-reports/TEST-org.leo.im.notification.AppTest.xml ================================================ ================================================ FILE: leo-im-notification/target/surefire-reports/org.leo.im.notification.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.notification.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.084 sec ================================================ FILE: leo-im-service/.classpath ================================================ ================================================ FILE: leo-im-service/.project ================================================ leo-im-service org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-service/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-service/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-service/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-service/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-service leo-im-service http://maven.apache.org org.leo.im leo-im-api ${parent.version} org.leo.im leo-im-store ${parent.version} org.leo.im leo-im-util ${project.version} org.leo.im leo-im-notification ${project.version} com.alibaba fastjson ${fastjson.version} ================================================ FILE: leo-im-service/src/main/java/org/leo/im/service/ChannelServiceImpl.java ================================================ package org.leo.im.service; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.leo.im.api.dto.ChannelDTO; import org.leo.im.api.dto.ChannelListDTO; import org.leo.im.api.dto.ChannelMemberDTO; import org.leo.im.api.service.ChannelService; import org.leo.im.common.data.Page; import org.leo.im.model.Channel; import org.leo.im.model.ChannelMember; import org.leo.im.model.User; import org.leo.im.model.UserChannel; import org.leo.im.notification.event.ChannelCreatedEvent; import org.leo.im.notification.event.ChannelNameChangedEvent; import org.leo.im.notification.event.ChannelRemovedEvent; import org.leo.im.notification.event.JoinChannelEvent; import org.leo.im.notification.event.MembersCountChangedEvent; import org.leo.im.notification.event.NotificationEvent; import org.leo.im.notification.event.RemoveFromChannelEvent; import org.leo.im.store.dao.ChannelDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.factory.DAOFactory; import org.leo.im.util.BeanUtils; /** * 频道服务实现类 * * @author Leo * @date 2018/4/3 */ public final class ChannelServiceImpl implements ChannelService { /** * 得到频道列表 * * @param parameters * @param limit * @return */ @Override public List listChannel(Map parameters, int limit) { List list = DAOFactory.createChannelDAO().list(parameters, limit); List dtoList = new ArrayList<>(list.size()); for (Channel channel : list) { ChannelListDTO dto = new ChannelListDTO(); BeanUtils.copyProperties(channel, dto); dtoList.add(dto); } return dtoList; } /** * 得到群组频道列表 * * @param parameters * @param limit * @return */ @Override public List listGroupChannel(Map parameters, int limit) { List list = DAOFactory.createChannelDAO().listGroupChannel(parameters, new String[] { "O", "P" }, limit); List dtoList = new ArrayList<>(list.size()); for (Channel channel : list) { ChannelListDTO dto = new ChannelListDTO(); BeanUtils.copyProperties(channel, dto); dtoList.add(dto); } return dtoList; } /** * 添加频道 * * @param dto * @param creatorNickname * @return */ @Override public ChannelDTO saveChannel(ChannelDTO dto, String creatorNickname) { ChannelDAO channelDAO = DAOFactory.createChannelDAO(); Channel channel = new Channel(); BeanUtils.copyProperties(dto, channel); User creator = new User(); creator.setId(dto.getCreatorId()); channel.setCreator(creator); if (dto.getFromUserId() != null && !dto.getFromUserId().trim().isEmpty()) { User fromUser = new User(); fromUser.setId(dto.getFromUserId()); fromUser.setName(dto.getFromUsername()); fromUser.setNickname(dto.getFromUserNickname()); channel.setFrom(fromUser); } if (dto.getToUserId() != null && !dto.getToUserId().trim().isEmpty()) { User toUser = new User(); toUser.setId(dto.getToUserId()); toUser.setName(dto.getToUsername()); toUser.setNickname(dto.getToUserNickname()); channel.setTo(toUser); } Channel returnChannel = channelDAO.save(channel); boolean channelExists = false; if (returnChannel == null) { // channel已存在,从数据库中查询已存在的channel。 returnChannel = channelDAO.getByFromAndTo(dto.getFromUserId(), dto.getToUserId()); channelExists = true; } if (returnChannel == null) { throw new DAOException("Channel not exists"); } if(channelExists && dto.getType().equals("P")) { // 删除隐藏频道 DAOFactory.createHideChannelDAO().remove(dto.getCreatorId(), returnChannel.getId()); } if (!channelExists) { // 得到对方用户(ToUser)的在线状态 if(dto.getType().equals("P")) { User toUser = DAOFactory.createUserDAO().getById(dto.getToUserId()); if(toUser != null) { returnChannel.getTo().setOnlineStatus(toUser.getOnlineStatus()); } } // 保存频道成员 List members = this.getMembers(dto); DAOFactory.createChannelMemberDAO().save(returnChannel.getId(), members); // 保存用户频道信息 List userChannels = this.getUserChannels(channel, members); DAOFactory.createUserChannelDAO().batchSave(userChannels); // 保存未读消息数量 if (dto.getType().equals("P")) { // 私聊 String[] userIds = new String[] { dto.getFromUserId(), dto.getToUserId() }; DAOFactory.createUnreadMessageCountDAO().batchSave(userIds, returnChannel.getId(), (short)0); } else { // 群聊 String[] userIds = new String[dto.getMemberCount()]; for(int i = 0; i < dto.getMemberCount(); i++) { userIds[i] = dto.getMembers().get(i).getId(); } DAOFactory.createUnreadMessageCountDAO().batchSave(userIds, returnChannel.getId(), (short)0); } this.publishChannelCreatedEvent(returnChannel, members); this.publishJoinChannelEvent(returnChannel, members, creatorNickname, false); } return getDTOFromChannel(returnChannel); } /** * 根据id得到频道信息 * * @param id * @return */ @Override public ChannelDTO getById(String id) { Channel channel = DAOFactory.createChannelDAO().getById(id); if (channel == null) { return null; } ChannelDTO dto = new ChannelDTO(); BeanUtils.copyProperties(channel, dto); dto.setCreatorId(channel.getCreator().getId()); return dto; } /** * 得到用户是否为频道的管理员 * @param userId * @param channelId * @return */ @Override public boolean isAdmin(String userId, String channelId) { return DAOFactory.createChannelMemberDAO().isAdmin(userId, channelId); } /** * 更新频道名称 * @param channelId * @param name * @return */ @Override public int updateName(String channelId, String name) { int count = DAOFactory.createChannelDAO().updateStringField(channelId, "name", name); if(count > 0) { this.publishNameChangedEvent(channelId, name); } return count; } /** * 更新频道用途 * @param channelId * @param purpose * @return */ @Override public int updatePurpose(String channelId, String purpose) { return DAOFactory.createChannelDAO().updateStringField(channelId, "purpose", purpose); } /** * 添加频道成员 * @param channelId * @param userIds * @param userNicknames * @param admin * @return */ @Override public int addMember(String channelId, String[] userIds, String[] userNicknames, String admin) { List members = new ArrayList<>(userIds.length); for(int i = 0; i < userIds.length; i++) { ChannelMember member = new ChannelMember(); User user = new User(); user.setId(userIds[i]); user.setNickname(userNicknames[i]); member.setUser(user); member.setAdmin(false); members.add(member); } int count = DAOFactory.createChannelMemberDAO().save(channelId, members); // 更新组成员数量 if(count > 0) { ChannelDAO dao = DAOFactory.createChannelDAO(); dao.increaseMemberCount(channelId, count); Channel channel = dao.getById(channelId); if(channel != null) { List ucs = new ArrayList<>(userIds.length); for(String userId : userIds) { UserChannel uc = new UserChannel(); uc.setChannel(channel); User user = new User(); user.setId(userId); uc.setUser(user); uc.setDisplayName(channel.getName()); ucs.add(uc); } DAOFactory.createUserChannelDAO().batchSave(ucs); DAOFactory.createUnreadMessageCountDAO().batchSave(userIds, channelId, (short)0); this.publishJoinChannelEvent(channel, members, admin, true); } new MembersCountChangedEvent(channelId, count).trigger(); } return count; } /** * 移除组成员 * @param channelId * @param memberId * @param memberNickname * @param admin * @return */ @Override public int removeMember(String channelId, String memberId, String memberNickname, String admin) { int count = DAOFactory.createChannelMemberDAO().removeMember(channelId, memberId); if(count > 0) { DAOFactory.createChannelDAO().increaseMemberCount(channelId, -1); DAOFactory.createUserChannelDAO().remove(memberId, channelId); DAOFactory.createUnreadMessageCountDAO().remove(channelId, memberId); new MembersCountChangedEvent(channelId, -1).trigger(); NotificationEvent event = new RemoveFromChannelEvent(channelId, new String[] { memberId }, new String[] { memberNickname }, admin, true); event.trigger(); } return count; } /** * 得到成员列表 * @param channelId * @param username * @param limit * @param offset * @return */ @Override public Page listMember(String channelId, String username, int limit, int offset) { Page members = DAOFactory.createChannelMemberDAO().listMember(channelId, username, limit, offset); List dtos = new ArrayList<>(members.getRows().size()); for(ChannelMember member : members.getRows()) { ChannelMemberDTO dto = new ChannelMemberDTO(); dto.setId(member.getUser().getId()); dto.setNickname(member.getUser().getNickname()); dto.setAdmin(member.getAdmin()); dtos.add(dto); } return new Page(members.getTotal(), dtos); } /** * 变更频道管理员 * @param channelId * @param memberId * @param isAdmin * @return */ @Override public int changeAdmin(String channelId, String memberId, boolean isAdmin) { return DAOFactory.createChannelMemberDAO().changeAdmin(channelId, memberId, isAdmin); } /** * 离开频道 * @param channelId * @param memberId * @param memberNickname * @return */ @Override public int leaveChannel(String channelId, String memberId, String memberNickname) { int count = DAOFactory.createChannelMemberDAO().removeMember(channelId, memberId); if(count > 0) { DAOFactory.createChannelDAO().increaseMemberCount(channelId, -1); DAOFactory.createUserChannelDAO().remove(memberId, channelId); DAOFactory.createUnreadMessageCountDAO().remove(channelId, memberId); new MembersCountChangedEvent(channelId, -1).trigger(); } return count; } /** * 删除频道 * @param channelId * @param adminId */ @Override public int removeChannel(String channelId, String adminId) { // 判断删除群组的管理员是否是群组创建人 ChannelDAO dao = DAOFactory.createChannelDAO(); Channel channel = dao.getById(channelId); if(channel != null) { if(!channel.getCreator().getId().equals(adminId)) { return 0; } int count = dao.remove(channelId); if(count > 0) { NotificationEvent event = new ChannelRemovedEvent(channelId); event.trigger(); } return count; } return 0; } /** * 从Channel得到ChannelDTO * @param channel * @return */ private ChannelDTO getDTOFromChannel(Channel channel) { ChannelDTO dto = new ChannelDTO(); BeanUtils.copyProperties(channel, dto); dto.setCreatorId(channel.getCreator().getId()); dto.setFromUserId(channel.getFrom() == null ? null : channel.getFrom().getId()); dto.setFromUsername(channel.getFrom() == null ? null : channel.getFrom().getName()); dto.setFromUserNickname(channel.getFrom() == null ? null : channel.getFrom().getNickname()); dto.setToUserId(channel.getTo() == null ? null : channel.getTo().getId()); dto.setToUsername(channel.getTo() == null ? null : channel.getTo().getName()); dto.setToUserNickname(channel.getTo() == null ? null : channel.getTo().getNickname()); dto.setType(channel.getType()); if(dto.getType().equals("P")) { dto.setToUserOnlineStatus(channel.getTo().getOnlineStatus()); } return dto; } /** * 得到频道成员 * * @param dto * @return */ private List getMembers(ChannelDTO dto) { List members = new ArrayList<>(dto.getMemberCount()); for (ChannelMemberDTO memberDto : dto.getMembers()) { ChannelMember member = new ChannelMember(); User user = new User(); user.setId(memberDto.getId()); user.setNickname(memberDto.getNickname()); member.setUser(user); member.setAdmin(memberDto.getAdmin()); members.add(member); } return members; } /** * 得到用户频道 * * @param channel * @param members * @return */ private List getUserChannels(Channel channel, List members) { List userChannelList = null; if (channel.getType().equals("P")) { // 私聊 userChannelList = new ArrayList<>(2); UserChannel userChannel1 = new UserChannel(); userChannel1.setUser(channel.getFrom()); userChannel1.setChannel(channel); userChannel1.setDisplayName( channel.getTo().getNickname() != null && !channel.getTo().getNickname().trim().isEmpty() ? channel.getTo().getNickname() : channel.getTo().getName()); userChannel1.setToUser(channel.getTo()); userChannelList.add(userChannel1); UserChannel userChannel2 = new UserChannel(); userChannel2.setUser(channel.getTo()); userChannel2.setChannel(channel); userChannel2.setDisplayName( channel.getFrom().getNickname() != null && !channel.getFrom().getNickname().trim().isEmpty() ? channel.getFrom().getNickname() : channel.getFrom().getName()); userChannel2.setToUser(channel.getFrom()); userChannelList.add(userChannel2); return userChannelList; } // 群聊 userChannelList = new ArrayList<>(members.size()); for (ChannelMember member : members) { UserChannel userChannel = new UserChannel(); userChannel.setUser(member.getUser()); userChannel.setChannel(channel); userChannel.setDisplayName(channel.getName()); userChannelList.add(userChannel); } return userChannelList; } /** * 发布频道创建事件 * @param channel * @param members */ private void publishChannelCreatedEvent(Channel channel, List members) { StringBuilder userIds = null; if(channel.getType().equals("P")) { // 私聊 userIds = new StringBuilder(66); userIds.append(channel.getFrom().getId()).append(",").append(channel.getTo().getId()).append(","); } else { userIds = new StringBuilder(33 * channel.getMemberCount()); for(ChannelMember member : members) { userIds.append(member.getUser().getId()).append(","); } } userIds.setLength(userIds.length() - 1); NotificationEvent event = new ChannelCreatedEvent(channel.getId(), userIds.toString()); event.trigger(); } /** * 发布加入频道事件 * @param channel * @param members * @param admin * @param sendBroadcast */ private void publishJoinChannelEvent(Channel channel, List members, String admin, boolean sendBroadcast) { String[] userIds = null; String[] userNicknames = null; if(channel.getType().equals("G")) { userIds = new String[members.size()]; userNicknames = new String[members.size()]; for(int i = 0; i < members.size(); i++) { userIds[i] = members.get(i).getUser().getId(); userNicknames[i] = members.get(i).getUser().getNickname(); } } else { userIds = new String[] { channel.getTo().getId() }; } NotificationEvent event = new JoinChannelEvent(channel.getId(), userIds, userNicknames, admin, sendBroadcast); event.trigger(); } /** * 发布频道名称改变事件 * @param channelId * @param channelName */ private void publishNameChangedEvent(String channelId, String channelName) { NotificationEvent event = new ChannelNameChangedEvent(channelId, channelName); event.trigger(); } } ================================================ FILE: leo-im-service/src/main/java/org/leo/im/service/MessageServiceImpl.java ================================================ package org.leo.im.service; import java.util.ArrayList; import java.util.List; import org.leo.im.api.dto.FileDTO; import org.leo.im.api.dto.MessageDTO; import org.leo.im.api.service.MessageService; import org.leo.im.model.Channel; import org.leo.im.model.File; import org.leo.im.model.Message; import org.leo.im.model.User; import org.leo.im.notification.event.MessageRemovedEvent; import org.leo.im.notification.event.NewMessageEvent; import org.leo.im.notification.event.NotificationEvent; import org.leo.im.notification.event.ReadMessageEvent; import org.leo.im.store.dao.UnreadMessageCountDAO; import org.leo.im.store.factory.DAOFactory; import org.leo.im.util.BeanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSONObject; /** * 消息服务实现类 * * @author Leo * @date 2018/5/16 */ public final class MessageServiceImpl implements MessageService { private static final Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class); /** * 得到消息列表 * @param channelId * @param maxCreateAt * @param limit * @return */ @Override public List listMessage(String channelId, long maxCreateAt, int limit) { List list = DAOFactory.createMessageDAO().listMessage(channelId, maxCreateAt, limit); List dtoList = new ArrayList<>(list.size()); for(Message message : list) { MessageDTO dto = new MessageDTO(); BeanUtils.copyProperties(message, dto); dto.setSenderId(message.getSender().getId()); dto.setSenderName(message.getSender().getName()); dto.setSenderNickname(message.getSender().getNickname()); dto.setSenderAvatarUrl(message.getSender().getAvatarUrl()); dto.setSenderOnlineStatus(message.getSender().getOnlineStatus()); dto.setSenderFirstLetterOfName(message.getSender().getFirstLetterOfName()); if(message.getFile() != null) { dto.setFileId(message.getFile().getId()); dto.setFileName(message.getFile().getName()); dto.setFileExtension(message.getFile().getExtension()); dto.setFileSize(message.getFile().getSize()); dto.setFileMimeType(message.getFile().getMimeType()); dto.setImageWidth(message.getFile().getWidth()); dto.setImageHeight(message.getFile().getHeight()); dto.setImageThumbWidth(message.getFile().getThumbWidth()); dto.setImageThumbHeight(message.getFile().getThumbHeight()); dto.setFilePath(message.getFile().getPath()); } dtoList.add(dto); } return dtoList; } /** * 根据id得到消息 * @param id * @return */ @Override public MessageDTO getById(long id) { Message message = DAOFactory.createMessageDAO().getById(id); if(message != null) { MessageDTO dto = new MessageDTO(); BeanUtils.copyProperties(message, dto); dto.setChannelId(message.getChannel().getId()); dto.setSenderId(message.getSender().getId()); dto.setSenderName(message.getSender().getName()); dto.setSenderNickname(message.getSender().getNickname()); dto.setSenderAvatarUrl(message.getSender().getAvatarUrl()); dto.setSenderOnlineStatus(message.getSender().getOnlineStatus()); dto.setSenderFirstLetterOfName(message.getSender().getFirstLetterOfName()); if(message.getFile() != null) { dto.setFileId(message.getFile().getId()); dto.setFileName(message.getFile().getName()); dto.setFileExtension(message.getFile().getExtension()); dto.setFileSize(message.getFile().getSize()); dto.setFileMimeType(message.getFile().getMimeType()); dto.setImageWidth(message.getFile().getWidth()); dto.setImageHeight(message.getFile().getHeight()); dto.setImageThumbWidth(message.getFile().getThumbWidth()); dto.setImageThumbHeight(message.getFile().getThumbHeight()); dto.setFilePath(message.getFile().getPath()); } return dto; } return null; } /** * 添加消息 * @param dto * @return */ @Override public MessageDTO saveMessage(MessageDTO dto) { Message message = new Message(); BeanUtils.copyProperties(dto, message); Channel channel = new Channel(); channel.setId(dto.getChannelId()); message.setChannel(channel); User sender = new User(); sender.setId(dto.getSenderId()); message.setSender(sender); if(dto.getFileId() != null && !dto.getFileId().trim().isEmpty()) { File file = new File(); file.setId(dto.getFileId().trim()); message.setFile(file); } long id = DAOFactory.createMessageDAO().save(message); if(id > 0) { // 更新频道的最后发送消息时间 DAOFactory.createChannelDAO().updateLastPostAt(dto.getChannelId(), dto.getCreateAt()); MessageDTO resultDTO = this.getById(id); if(resultDTO != null) { // 更新用户的未读消息数量 Channel channelModel = DAOFactory.createChannelDAO().getById(dto.getChannelId()); if(channelModel == null) { logger.warn("The channel is not found, channel id: " + dto.getChannelId()); } else { if(channelModel.getType().equals("P")) { // 私聊 String userId = dto.getSenderId().equals(channelModel.getFrom().getId()) ? channelModel.getTo().getId() : channelModel.getFrom().getId(); DAOFactory.createUnreadMessageCountDAO().increase(userId, dto.getChannelId(), (short)1); // 删除对方的隐藏频道 DAOFactory.createHideChannelDAO().remove(userId, dto.getChannelId()); } else { // 群聊 DAOFactory.createUnreadMessageCountDAO().increaseGroupChannel(dto.getChannelId(), new String[]{ dto.getSenderId() }, (short)1); } this.publishNewMessageEvent(resultDTO, channelModel); } } return resultDTO; } return null; } /** * 批量添加消息 * @param dtos * @return */ @Override public int saveMessage(List dtos) { List messages = new ArrayList<>(dtos.size()); for(MessageDTO dto : dtos) { Message message = new Message(); BeanUtils.copyProperties(dto, message); Channel channel = new Channel(); channel.setId(dto.getChannelId()); message.setChannel(channel); User sender = new User(); sender.setId(dto.getSenderId()); message.setSender(sender); messages.add(message); } int total = DAOFactory.createMessageDAO().save(messages); DAOFactory.createChannelDAO().updateLastPostAt(dtos.get(0).getChannelId(), dtos.get(0).getCreateAt()); // 更新用户的未读消息数量 if (dtos.get(0).getChannelType().equals("G")) { DAOFactory.createUnreadMessageCountDAO().increaseGroupChannel(dtos.get(0).getChannelId(), null, (short)(total > 99 ? 99 : total)); } Channel channelModel = DAOFactory.createChannelDAO().getById(dtos.get(0).getChannelId()); for(MessageDTO dto : dtos) { this.publishNewMessageEvent(dto, channelModel); } return total; } /** * 读取消息 * @param channelId * @param userId * @param total * @return */ @Override public int readMessage(String channelId, String userId, short total) { UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO(); int count = dao.decrease(userId, channelId, total); boolean readAll = false; if(count == 0) { count = dao.update(userId, channelId, (short)0); readAll = true; } NotificationEvent event = new ReadMessageEvent(userId, channelId, total, readAll); event.trigger(); return count; } /** * 删除消息 * @param messageId * @param userId * @return */ /** * 删除消息 * @param messageId * @param senderId * @param channelId * @param toUserId * @return */ @Override public int removeMessage(long messageId, String senderId, String channelId, String toUserId) { int count = DAOFactory.createMessageDAO().remove(messageId, senderId); NotificationEvent event = new MessageRemovedEvent(messageId, senderId, channelId, toUserId); event.trigger(); return count; } /** * 添加文件 * @param dto * @return */ @Override public String saveFile(FileDTO dto) { File file = new File(); BeanUtils.copyProperties(dto, file); return DAOFactory.createFileDAO().save(file); } /** * 发布新消息 * @param dto * @param channel */ private void publishNewMessageEvent(MessageDTO dto, Channel channel) { // 判断是私聊还是群聊 JSONObject message = (JSONObject)JSONObject.toJSON(dto); if(dto.getType() != null && !dto.getType().isEmpty()) { message.put("type", dto.getType()); } if(channel.getType().equals("P")) { // 私聊 message.put("userIds", dto.getSenderId().equals(channel.getFrom().getId()) ? channel.getTo().getId() : channel.getFrom().getId()); } else { message.put("groupIds", channel.getId()); } NotificationEvent event = new NewMessageEvent(message); event.trigger(); } } ================================================ FILE: leo-im-service/src/main/java/org/leo/im/service/UnreadMessageCountServiceImpl.java ================================================ package org.leo.im.service; import org.leo.im.api.service.UnreadMessageCountService; import org.leo.im.store.dao.UnreadMessageCountDAO; import org.leo.im.store.factory.DAOFactory; /** * 未读消息数量 * @author Administrator * */ public class UnreadMessageCountServiceImpl implements UnreadMessageCountService { /** * 批量添加未读消息数量 * @param userIds * @param channelId * @param total * @return */ @Override public int batchSaveUnreadMessageCount(String[] userIds, String channelId, short total) { UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO(); return dao.batchSave(userIds, channelId, total); } /** * 更新未读消息数量 * @param userId * @param channelId * @param quantity * @return */ @Override public int updateUnreadMessageCount(String userId, String channelId, short total) { UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO(); return dao.update(userId, channelId, total); } /** * 批量更新未读消息数量 * @param userIds * @param channelId * @param quantity * @return */ @Override public int batchUpdateUnreadMessageCount(String[] userIds, String channelId, short total) { UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO(); return dao.batchUpdate(userIds, channelId, total); } /** * 批量增加未读消息数量 * @param userIds * @param channelId * @param quantity * @return */ @Override public int batchIncreaseUnreadMessageCount(String[] userIds, String channelId, short quantity) { UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO(); return dao.batchIncrease(userIds, channelId, quantity); } /** * 增加未读消息数量 * @param userId * @param channelId * @param quantity * @return */ @Override public int increaseUnreadMessageCount(String userId, String channelId, short quantity) { UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO(); return dao.increase(userId, channelId, quantity); } } ================================================ FILE: leo-im-service/src/main/java/org/leo/im/service/UserChannelServiceImpl.java ================================================ package org.leo.im.service; import java.util.ArrayList; import java.util.List; import org.leo.im.api.dto.UserChannelDTO; import org.leo.im.api.service.UserChannelService; import org.leo.im.model.UserChannel; import org.leo.im.store.factory.DAOFactory; /** * 用户频道服务实现类 * * @author Lining * @date 2018/4/20 */ public final class UserChannelServiceImpl implements UserChannelService { /** * 得到用户频道列表 * @param userId * @param type * @param limit * @return */ @Override public List listUserChannel(String userId, String type, int limit) { List list = DAOFactory.createUserChannelDAO().listByUserId(userId, type, limit); List dtoList = new ArrayList<>(list.size()); for(UserChannel userChannel : list) { UserChannelDTO dto = new UserChannelDTO(); dto.setChannelId(userChannel.getChannel().getId()); dto.setChannelName(userChannel.getChannel().getName()); dto.setChannelType(userChannel.getChannel().getType()); dto.setChannelDisplayName(userChannel.getDisplayName()); dto.setCreatorId(userChannel.getChannel().getCreator().getId()); if(dto.getChannelType().equals("P")) { dto.setToUserId(userChannel.getToUser().getId()); dto.setToUserOnlineStatus(userChannel.getToUser().getOnlineStatus()); } dto.setUnreadMessageCount(userChannel.getUnreadMessageCount()); dtoList.add(dto); } return dtoList; } /** * 得到用户频道 * @param userId * @param channelId * @return */ @Override public UserChannelDTO get(String userId, String channelId) { UserChannel userChannel = DAOFactory.createUserChannelDAO().get(userId, channelId); if(userChannel == null) { return null; } UserChannelDTO dto = new UserChannelDTO(); dto.setChannelId(userChannel.getChannel().getId()); dto.setChannelName(userChannel.getChannel().getName()); dto.setChannelType(userChannel.getChannel().getType()); dto.setChannelDisplayName(userChannel.getDisplayName()); dto.setMemberCount(userChannel.getChannel().getMemberCount()); dto.setCreatorId(userChannel.getChannel().getCreator().getId()); if(dto.getChannelType().equals("P")) { dto.setToUserId(userChannel.getToUser().getId()); dto.setToUserOnlineStatus(userChannel.getToUser().getOnlineStatus()); } dto.setUnreadMessageCount(userChannel.getUnreadMessageCount()); return dto; } /** * 更新用户频道 * @param channelId * @param userId * @param displayName * @return */ @Override public int updateDisplayName(String channelId, String userId, String displayName) { return DAOFactory.createUserChannelDAO().updateDisplayName(channelId, userId, displayName); } /** * 隐藏频道 * @param userId * @param channelId * @return */ @Override public int hideChannel(String userId, String channelId) { return DAOFactory.createHideChannelDAO().save(userId, channelId); } /** * 根据名称得到用户频道列表 * @param userId * @param name * @param type * @return */ @Override public List listByName(String userId, String name, String type) { List list = DAOFactory.createUserChannelDAO().listByName(userId, name, type); List dtoList = new ArrayList<>(list.size()); for(UserChannel userChannel : list) { UserChannelDTO dto = new UserChannelDTO(); dto.setChannelId(userChannel.getChannel().getId()); dto.setChannelName(userChannel.getChannel().getName()); dto.setChannelType(userChannel.getChannel().getType()); dto.setChannelDisplayName(userChannel.getDisplayName()); dto.setCreatorId(userChannel.getChannel().getCreator().getId()); if(dto.getChannelType().equals("P")) { dto.setToUserId(userChannel.getToUser().getId()); dto.setToUserOnlineStatus(userChannel.getToUser().getOnlineStatus()); } dto.setUnreadMessageCount(userChannel.getUnreadMessageCount()); dtoList.add(dto); } return dtoList; } } ================================================ FILE: leo-im-service/src/main/java/org/leo/im/service/UserServiceImpl.java ================================================ package org.leo.im.service; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.leo.im.api.exception.ServiceException; import org.leo.im.api.service.UserService; import org.leo.im.common.data.Page; import org.leo.im.api.dto.UserDTO; import org.leo.im.model.User; import org.leo.im.notification.event.AvatarChangedEvent; import org.leo.im.notification.event.NicknameChangedEvent; import org.leo.im.notification.event.NotificationEvent; import org.leo.im.notification.event.OnlineStatusChangedEvent; import org.leo.im.service.util.PasswordUtils; import org.leo.im.store.dao.UserDAO; import org.leo.im.store.factory.DAOFactory; import org.leo.im.util.BeanUtils; /** * 用户服务实现类 * * @author Leo * @date 2018/4/9 */ public final class UserServiceImpl implements UserService { /** * 验证用户登录 * @param name * @param password * @return */ @Override public UserDTO verifyLogin(String name, String password) { User user = DAOFactory.createUserDAO().getByName(name); if(user == null) { return null; } String md5Password = null; try { md5Password = PasswordUtils.getMd5Password(password, user.getSalt()); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new ServiceException(e); } if(md5Password != null && md5Password.equals(user.getPassword())) { UserDTO dto = new UserDTO(); BeanUtils.copyProperties(user, dto); return dto; } return null; } /** * 根据id得到用户 * @param id * @return */ @Override public UserDTO getById(String id) { UserDTO dto = new UserDTO(); User user = DAOFactory.createUserDAO().getById(id); if(user != null) { BeanUtils.copyProperties(user, dto); } return dto; } /** * 添加用户 * @param dto * @return */ @Override public String saveUser(UserDTO dto) { UserDAO dao = DAOFactory.createUserDAO(); if(dao.usernameExists(null, dto.getName())) { throw new ServiceException("用户 " + dto.getName() + " 已存在"); } User user = new User(); BeanUtils.copyProperties(dto, user); String salt = PasswordUtils.generateSalt(); user.setSalt(salt); String md5Password = null; try { md5Password = PasswordUtils.getMd5Password(user.getPassword(), salt); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new ServiceException(e); } user.setPassword(md5Password); return dao.save(user); } /** * 根据名称或昵称分页查询用户 * @param name * @param limit * @param offset * @return */ @Override public Page listByNameOrNickname(String name, int limit, int offset) { Page userResult = DAOFactory.createUserDAO().listByNameOrNickname(name, limit, offset); List dtoList = new ArrayList<>(userResult.getRows().size()); for(User user : userResult.getRows()) { UserDTO dto = new UserDTO(); BeanUtils.copyProperties(user, dto); dtoList.add(dto); } return new Page(userResult.getTotal(), dtoList); } /** * 更新用户 * @param dto * @param updateNullValueField * @return */ @Override public UserDTO updateUser(UserDTO dto, boolean updateNullValueField) { User user = new User(); BeanUtils.copyProperties(dto, user); if(dto.getPassword() != null && !dto.getPassword().trim().isEmpty()) { String salt = PasswordUtils.generateSalt(); user.setSalt(salt); String md5Password = null; try { md5Password = PasswordUtils.getMd5Password(user.getPassword(), salt); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new ServiceException(e); } user.setPassword(md5Password); } UserDAO dao = DAOFactory.createUserDAO(); if(dao.update(user, updateNullValueField) == 1) { publishEvent(dto); User returnUser = dao.getById(dto.getId()); UserDTO returnDTO = new UserDTO(); BeanUtils.copyProperties(returnUser, returnDTO); return returnDTO; } return null; } /** * * @param channelId * @param username * @param limit * @param offset * @return */ public Page listNonMembers(String channelId, String username, int limit, int offset) { Page pagedUser = DAOFactory.createUserDAO().listNonMembers(channelId, username, limit, offset); if(pagedUser.getTotal() == 0) { return new Page(0, new ArrayList(0)); } List list = new ArrayList<>(pagedUser.getRows().size()); for(User user : pagedUser.getRows()) { UserDTO dto = new UserDTO(); dto.setId(user.getId()); dto.setName(user.getName()); dto.setNickname(user.getNickname()); dto.setFirstLetterOfName(user.getFirstLetterOfName()); dto.setAvatarUrl(user.getAvatarUrl()); list.add(dto); } return new Page(pagedUser.getTotal(), list); } /** * 批量下线用户 * @param userIds */ @Override public int batchOffline(Set userIds) { return DAOFactory.createUserDAO().offline(userIds); } /** * 修改用户口令 * @param userId * @param username * @param oldPassword * @param newPassword * @return */ @Override public int updatePassword(String userId, String username, String oldPassword, String newPassword) { if(this.verifyLogin(username, oldPassword) != null) { User user = new User(); // 更新口令 String salt = PasswordUtils.generateSalt(); try { String newMd5Password = PasswordUtils.getMd5Password(newPassword, salt); user.setId(userId); user.setSalt(salt); user.setPassword(newMd5Password); return DAOFactory.createUserDAO().update(user, false); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { return 0; } } return 0; } /** * 发布消息 * @param dto */ private void publishEvent(UserDTO dto) { if(dto.getOnlineStatus() != null && !dto.getOnlineStatus().trim().isEmpty()) { NotificationEvent event = new OnlineStatusChangedEvent(dto.getId(), dto.getOnlineStatus()); event.trigger(); } if(dto.getNickname() != null && !dto.getNickname().trim().isEmpty()) { NotificationEvent event = new NicknameChangedEvent(dto.getId(), dto.getNickname()); event.trigger(); } if(dto.getAvatarUrl() != null && !dto.getAvatarUrl().trim().isEmpty()) { NotificationEvent event = new AvatarChangedEvent(dto.getId(), dto.getAvatarUrl()); event.trigger(); } } } ================================================ FILE: leo-im-service/src/main/java/org/leo/im/service/support/CacheableHolder.java ================================================ package org.leo.im.service.support; /** * 缓存注解持有者 * * @author Leo * @date 2018/3/19 */ public class CacheableHolder { private static final ThreadLocal THREADLOCAL = new ThreadLocal(); /** * 设置是否启用缓存注解 * @param cacheable */ public static void setCacheable(boolean cacheable) { THREADLOCAL.set(cacheable); } /** * 得到是否启用缓存注解 * @return */ public static boolean getCacheable() { Boolean cacheable = THREADLOCAL.get(); return cacheable == null ? false : cacheable; } /** * 删除是否启用缓存注解 */ public static void remove() { THREADLOCAL.remove(); } } ================================================ FILE: leo-im-service/src/main/java/org/leo/im/service/support/ServiceProxy.java ================================================ package org.leo.im.service.support; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.sql.Connection; import org.leo.im.api.annotation.Cacheable; import org.leo.im.api.annotation.Transactional; import org.leo.im.store.connection.impl.PoolConnectionFactory; /** * 服务代理类 * * @author Leo * @date 2018/3/23 */ public final class ServiceProxy implements InvocationHandler { /** * 代理目标 */ private Object target; private ServiceProxy(Object target) { this.target = target; } /** * 通过Class来生成动态代理对象Proxy * * @param target * @param connectionString * 数据库连接字符串 * @return */ @SuppressWarnings("unchecked") public static T newProxyInstance(Object target) { return (T) java.lang.reflect.Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new ServiceProxy(target)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 得到数据库连接 Connection conn = PoolConnectionFactory.getInstance().getConnection(); // 判断方法是否启用了缓存注解 CacheableHolder.setCacheable(method.getAnnotation(Cacheable.class) != null); // 是否启用了事务注解 if (method.getAnnotation(Transactional.class) == null) { // 执行业务逻辑 try { return method.invoke(this.target, args); } finally { PoolConnectionFactory.getInstance().closeConnection(); CacheableHolder.remove(); } } conn.setAutoCommit(false); try { Object obj = method.invoke(this.target, args); conn.commit(); return obj; } catch (Throwable t) { conn.rollback(); throw t; } finally { PoolConnectionFactory.getInstance().closeConnection(); CacheableHolder.remove(); } } } ================================================ FILE: leo-im-service/src/main/java/org/leo/im/service/util/PasswordUtils.java ================================================ package org.leo.im.service.util; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; /** * 密码工具类 * * @author Leo * @date 2018/3/20 */ public final class PasswordUtils { /** * 盐的长度 */ private static final int SALT_LENGTH = 16; /** * 生成盐 * * @return */ public static String generateSalt() { byte[] salt = new byte[SALT_LENGTH]; SecureRandom sr = new SecureRandom(); sr.nextBytes(salt); return Base64.getEncoder().encodeToString(salt); } /** * 得到md5加密口令 * * @param password * @param salt * @return * @throws NoSuchAlgorithmException * @throws UnsupportedEncodingException */ public static String getMd5Password(String password, String salt) throws NoSuchAlgorithmException, UnsupportedEncodingException { MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] bs = md5.digest((password + "|" + salt + "|").getBytes()); return byteToHexString(bs); } /** * 将数组转换成16进制字符串 * @param salt * @return */ private static String byteToHexString(byte[] salt) { StringBuffer hexString = new StringBuffer(); for (int i = 0; i < salt.length; i++) { String hex = Integer.toHexString(salt[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } hexString.append(hex.toUpperCase()); } return hexString.toString(); } } ================================================ FILE: leo-im-service/src/test/java/org/leo/im/service/AppTest.java ================================================ package org.leo.im.service; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-service/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: leo-im-api-1.0.jar leo-im-common-1.0.jar leo-im-store-1.0. jar mysql-connector-java-8.0.11.jar protobuf-java-2.6.0.jar druid-1.1 .9.jar leo-im-model-1.0.jar leo-im-util-1.0.jar cglib-3.2.6.jar asm-6 .0.jar ant-1.9.6.jar ant-launcher-1.9.6.jar jjwt-0.9.0.jar jackson-da tabind-2.8.9.jar jackson-annotations-2.8.0.jar jackson-core-2.8.9.jar leo-im-notification-1.0.jar jedis-2.9.0.jar commons-pool2-2.4.2.jar fastjson-1.2.47.jar slf4j-api-1.7.25.jar logback-classic-1.2.3.jar lo gback-core-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-service/target/classes/META-INF/maven/org.leo.im/leo-im-service/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:16 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-service m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-service artifactId=leo-im-service ================================================ FILE: leo-im-service/target/classes/META-INF/maven/org.leo.im/leo-im-service/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-service leo-im-service http://maven.apache.org org.leo.im leo-im-api ${parent.version} org.leo.im leo-im-store ${parent.version} org.leo.im leo-im-util ${project.version} org.leo.im leo-im-notification ${project.version} com.alibaba fastjson ${fastjson.version} ================================================ FILE: leo-im-service/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:18 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-service ================================================ FILE: leo-im-service/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-service/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-service\src\main\java\org\leo\im\service\ChannelServiceImpl.java F:\Develop\open-source\leo-im-server\leo-im-service\src\main\java\org\leo\im\service\UnreadMessageCountServiceImpl.java F:\Develop\open-source\leo-im-server\leo-im-service\src\main\java\org\leo\im\service\UserServiceImpl.java F:\Develop\open-source\leo-im-server\leo-im-service\src\main\java\org\leo\im\service\support\ServiceProxy.java F:\Develop\open-source\leo-im-server\leo-im-service\src\main\java\org\leo\im\service\support\CacheableHolder.java F:\Develop\open-source\leo-im-server\leo-im-service\src\main\java\org\leo\im\service\util\PasswordUtils.java F:\Develop\open-source\leo-im-server\leo-im-service\src\main\java\org\leo\im\service\MessageServiceImpl.java F:\Develop\open-source\leo-im-server\leo-im-service\src\main\java\org\leo\im\service\UserChannelServiceImpl.java ================================================ FILE: leo-im-service/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-service/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-service\src\test\java\org\leo\im\service\AppTest.java ================================================ FILE: leo-im-service/target/surefire-reports/TEST-org.leo.im.service.AppTest.xml ================================================ ================================================ FILE: leo-im-service/target/surefire-reports/org.leo.im.service.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.service.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.088 sec ================================================ FILE: leo-im-socket/.classpath ================================================ ================================================ FILE: leo-im-socket/.project ================================================ leo-im-socket org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-socket/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-socket/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-socket/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-socket/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-socket leo-im-socket http://maven.apache.org io.netty netty-all ${netty.version} org.leo.im leo-im-util ${project.version} org.leo.im leo-im-api ${project.version} org.leo.im leo-im-api-provider ${project.version} org.leo.im leo-im-notification ${project.version} ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/ChannelIdSet.java ================================================ package org.leo.im.socket; import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Channel Id 集合类 * 使用读写锁保证一个用户多个连接保存和删除的同步性 * * @author Leo * @date 2018/3/29 */ public class ChannelIdSet { private ReadWriteLock rwLock = new ReentrantReadWriteLock(); private Set channelIds = new HashSet<>(); /** * 得到列表长度 * * @return */ public int size() { try { this.rwLock.writeLock().lock(); return this.channelIds.size(); } finally { this.rwLock.writeLock().unlock(); } } /** * 得到包含Channel Id的集合 * * @return */ public Set getSet() { try { rwLock.readLock().lock(); return this.channelIds; } finally { rwLock.readLock().unlock(); } } /** * 添加Channel Id * * @param channelId * @return */ public boolean add(String channelId) { try { rwLock.writeLock().lock(); return this.channelIds.add(channelId); } finally { rwLock.writeLock().unlock(); } } /** * 删除Channel Id * * @param channelId * @return */ public boolean remove(String channelId) { try { rwLock.writeLock().lock(); return this.channelIds.remove(channelId); } finally { rwLock.writeLock().unlock(); } } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/ChannelsHolder.java ================================================ package org.leo.im.socket; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import io.netty.channel.Channel; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.util.concurrent.ImmediateEventExecutor; /** * 通道持有者类 * * @author Leo * @date 2018/3/29 */ public class ChannelsHolder { private static final Map CHANNELS = new ConcurrentHashMap<>(1024); /** * ChannelGroup * group会自动监测里面的channel,当channel断开时,会主动踢出该channel,永远保留当前可用的channel列表 。 */ private static final Map CHANNEL_GROUPS = new ConcurrentHashMap<>(100); /** * 用户和Channel对应关系哈希Map */ private static final Map USER_CHANNEL_IDS = new ConcurrentHashMap<>(1024); /** * Channel和用户对应关系哈希Map */ private static final Map CHANNEL_USER_ID = new ConcurrentHashMap<>(1024); /** * Channel和IM频道的的对应关系哈希Map */ // private static final Map CHANNEL_IM_CHANNELS = new ConcurrentHashMap<>(1024); public static Map getChannelGroups() { return CHANNEL_GROUPS; } /** * 添加Channel * @param userId * @param channel */ public static void addChannel(String userId, Channel channel) { if(CHANNEL_GROUPS.containsKey("all")) { CHANNEL_GROUPS.get("all").add(channel); } String channelId = channel.id().asShortText(); CHANNELS.putIfAbsent(channelId, channel); ChannelIdSet channels = new ChannelIdSet(); channels.add(channelId); ChannelIdSet returnSet = USER_CHANNEL_IDS.putIfAbsent(userId, channels); if(returnSet != null) { returnSet.add(channelId); } CHANNEL_USER_ID.putIfAbsent(channelId, userId); } /** * 删除Channel * @param channelId */ public static void removeChannel(String channelId) { CHANNELS.remove(channelId); String currentUserId = CHANNEL_USER_ID.get(channelId); if(currentUserId != null) { ChannelIdSet userIds = USER_CHANNEL_IDS.get(currentUserId); if(userIds != null) { if(userIds.remove(channelId)) { if(userIds.size() == 0) { USER_CHANNEL_IDS.remove(currentUserId); } } } } CHANNEL_USER_ID.remove(channelId); } /** * 根据频道id得到用户id * @param channelId * @return */ public static String getUserIdByChannelId(String channelId) { return CHANNEL_USER_ID.get(channelId); } /** * 根据用户id得到channel集合 * @param userId * @return */ public static ChannelIdSet getChannelsByUserId(String userId) { return USER_CHANNEL_IDS.get(userId); } /** * 根据channel id得到channel * @param channelId * @return */ public static Channel getChannelById(String channelId) { return CHANNELS.get(channelId); } /** * 将channel添加到GroupChannel中 * @param groupId * @param channel */ public static void addChannelToGroup(String groupId, Channel channel) { DefaultChannelGroup channelGroup = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE); ChannelGroup returnChannelGroup = CHANNEL_GROUPS.putIfAbsent(groupId, channelGroup); if(returnChannelGroup == null) { // 不存在该ChannelGroup,第一次添加。 channelGroup.add(channel); return; } // ChannelGroup已经存在 returnChannelGroup.add(channel); } /** * 添加IM Channel Id * @param channelId * @param IMChannelId */ // public static void addIMChannel(String channelId, String IMChannelId) { // CHANNEL_IM_CHANNELS.put(channelId, IMChannelId); // } /** * 移除IM Channel Id * @param channelId */ // public static void removeIMChannel(String channelId) { // CHANNEL_IM_CHANNELS.remove(channelId); // } /** * 得到所用用户id * @return */ public static Set getUserIds() { return USER_CHANNEL_IDS.keySet(); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/SocketChannel.java ================================================ package org.leo.im.socket; import io.netty.channel.Channel; /** * Socket 通道类 * * @author Leo * @date 2018/3/29 */ public class SocketChannel { private String userId; private Channel channel; @Override public String toString() { return "SocketChannel [userId=" + userId + ", channel=" + channel + "]"; } public SocketChannel(Channel channel) { this.channel = channel; } public String getUserId() { return this.userId; } public void setUserId(String userId) { this.userId = userId; } public Channel getChannel() { return this.channel; } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/WebSocketChannelInitializer.java ================================================ package org.leo.im.socket; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import org.leo.im.socket.handler.TextWebSocketFrameHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.EventExecutorGroup; import io.netty.util.concurrent.RejectedExecutionHandlers; /** * WebSocket 通道初始化类 * * @author Leo * @date 2018/3/29 */ public class WebSocketChannelInitializer extends ChannelInitializer { /** * 业务线程池线程数 */ private static int eventExecutorGroupThreads = 0; /** * 业务线程池队列长度 */ private static int eventExecutorGroupQueues = 0; static { eventExecutorGroupThreads = Integer.getInteger("websocket.executor.threads", 0); if(eventExecutorGroupThreads == 0) { eventExecutorGroupThreads = Runtime.getRuntime().availableProcessors(); } eventExecutorGroupQueues = Integer.getInteger("websocket.executor.queues", 0); if(eventExecutorGroupQueues == 0) { eventExecutorGroupQueues = 512; } } /** * 业务线程组 */ private static final EventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup( eventExecutorGroupThreads, new ThreadFactory() { private AtomicInteger threadIndex = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { return new Thread(r, "WebSocketRequestHandlerThread_" + this.threadIndex.incrementAndGet()); } }, eventExecutorGroupQueues, RejectedExecutionHandlers.reject()); @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // WebSocket协议本身是基于HTTP协议的,所以要使用HTTP解编码器 pipeline.addLast(new HttpServerCodec()); // 以块的方式来写的处理器 pipeline.addLast(new ChunkedWriteHandler()); // Netty是基于分段请求的,HttpObjectAggregator的作用是将请求分段再聚合,参数是聚合字节的最大长度 pipeline.addLast(new HttpObjectAggregator(8192)); // 文本消息处理器 pipeline.addLast(eventExecutorGroup, new TextWebSocketFrameHandler()); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/WebSocketServer.java ================================================ package org.leo.im.socket; import java.util.HashSet; import java.util.Set; import org.leo.im.notification.PublisherFactory; import org.leo.im.socket.subscription.SubscriberFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.util.concurrent.ImmediateEventExecutor; /** * WebSocket Server * * @author Leo * @date 2018/4/3 */ public class WebSocketServer { private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class); /** * 监听端口号 */ private int port; /** * Boss线程数 */ private int bossThreads = 1; /** * Worker线程数 */ private int workerThreads = 2; public WebSocketServer(int port) { this.port = port; } public int getBossThreads() { return this.bossThreads; } public void setBossThreads(int bossThreads) { this.bossThreads = bossThreads; } public int getWorkerThreads() { return this.workerThreads; } public void setWorkerThreads(int workerThreads) { this.workerThreads = workerThreads; } /** * 启动服务 * @throws InterruptedException */ public void start() throws InterruptedException { // 参考:https://www.jianshu.com/p/9a97e667cf84 http://www.importnew.com/21561.html http://wiki.jikexueyuan.com/project/netty-4-user-guide/implement-websocket-chat-function.html /* * ChannelOption.SO_BACKLOG对应的是TCP/IP协议listen函数中的backlog参数, * 函数listen(int socketfd,int backlog)用来初始化服务端可连接队列,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接, * 多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小 */ // BossGroup处理nio的Accept事件(TCP连接) NioEventLoopGroup bossGroup = new NioEventLoopGroup(this.bossThreads); // Worker处理nio的Read和Write事件(通道的I/O事件) NioEventLoopGroup workerGroup = new NioEventLoopGroup(this.workerThreads); try { // handler在初始化时就会执行,而childHandler会在客户端成功connect后才执行。 ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .handler(new LoggingHandler(LogLevel.DEBUG)) .childHandler(new WebSocketChannelInitializer()); ChannelFuture f = bootstrap.bind(port).sync(); logger.info("The netty websocket server is now ready to accept requests on port {}", this.port); // 初始化群组 // 考虑到群组可能较多,一次性加载占用资源较大,所以在每个用户登录后,将其加载的群组注册到服务中。 // initChannelGroup(); ChannelsHolder.getChannelGroups().put("all", new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE)); // 订阅消息频道,每个队列对应一个线程,如果规模不大,建议一个队列即可。 Set channelSet = new HashSet<>(3); channelSet.add(System.getProperty("channel.private.message")); channelSet.add(System.getProperty("channel.group.message")); channelSet.add(System.getProperty("channel.system.message")); Object[] objs = channelSet.toArray(); String[] channels = new String[objs.length]; for(int i = 0; i < objs.length; i++) { channels[i] = objs[i].toString(); } PublisherFactory.createPublisher().subscribe(SubscriberFactory.createSubscriber(), channels); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } /** * 初始化Netty ChannelGroup */ // private void initChannelGroup() { // // 得到所有群组类型的channel // Map parameters = new HashMap<>(1); // parameters.put("deleteAt", 0); // ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService()); // List dtoList = serviceProxy.listGroupChannel(parameters, 0); // for(ChannelListDTO dto : dtoList) { // ChannelsHolder.getChannelGroups().put(dto.getId(), new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE)); // } // ChannelsHolder.getChannelGroups().put("all", new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE)); // } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/exception/MessageHandleException.java ================================================ package org.leo.im.socket.exception; /** * 消息处理异常类 * * @author Leo * @date 2018/3/29 */ public final class MessageHandleException extends RuntimeException { private static final long serialVersionUID = -2512674268771543792L; public MessageHandleException() { } public MessageHandleException(String message) { super(message); } public MessageHandleException(String message, Throwable cause) { super(message, cause); } public MessageHandleException(Throwable cause) { super(cause); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/handler/TextWebSocketFrameHandler.java ================================================ package org.leo.im.socket.handler; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.leo.im.api.dto.UserDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.api.service.UserService; import org.leo.im.notification.event.NotificationEvent; import org.leo.im.notification.event.OnlineStatusChangedEvent; import org.leo.im.service.support.ServiceProxy; import org.leo.im.socket.ChannelsHolder; import org.leo.im.util.JwtUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import io.jsonwebtoken.Claims; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; import io.netty.util.CharsetUtil; /** * Http 处理器 * * @author Leo * @date 2018/3/29 */ public final class TextWebSocketFrameHandler extends SimpleChannelInboundHandler { /** * ChannelInboundHandlerAdapter的channelRead方法处理完消息后不会自动释放消息, * 若想自动释放收到的消息,可以使用SimpleChannelInboundHandler。 */ private WebSocketServerHandshaker handshaker; private static final Logger logger = LoggerFactory.getLogger(TextWebSocketFrameHandler.class); @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { // 处理HTTP请求 handleHttpRequest(ctx, (FullHttpRequest) msg); return; } if (msg instanceof WebSocketFrame) { // 处理WebSocket请求 handleWebSocketFrame(ctx, (WebSocketFrame) msg); } // 手动释放消息(SimpleChannelInboundHandler会自动释放) // ReferenceCountUtil.release(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { super.handlerRemoved(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { this.clientOffline(ctx); super.channelInactive(ctx); } /** * 处理HTTP请求 * * @param ctx * @param request */ private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) { // WebSocket访问,处理握手升级。 if (request.headers().get("Connection").equals("Upgrade")) { // Handshake WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("", null, true); handshaker = wsFactory.newHandshaker(request); if (handshaker == null) { // 无法处理的WebSocket版本 WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); return; } // 验证token String token = getRequestParameter(request, "token"); if (token == null) { FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.UNAUTHORIZED, Unpooled.copiedBuffer("Token not found in request url", CharsetUtil.UTF_8)); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); logger.error("Token not found in request url"); return; } JSONObject json = null; try { Claims claims = JwtUtils.parseJWT(token, System.getProperty("jwt.secret")); String subject = claims.getSubject(); json = JSONObject.parseObject(subject); } catch (Exception e) { FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.UNAUTHORIZED, Unpooled.copiedBuffer("Token is not available", CharsetUtil.UTF_8)); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); logger.error("Token is not available"); return; } // 向客户端发送WebSocket握手,完成握手。 final String userId = json.getString("userId"); ChannelFuture channelFuture = handshaker.handshake(ctx.channel(), request); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { future.channel().close(); return; } // 加入到ChannelHolders中 ChannelsHolder.addChannel(userId, future.channel()); } }); return; } // 普通的HTTP访问 logger.warn("无效的访问: {}", request.uri()); } /** * 处理WebSocket请求 * * @param ctx * @param frame */ private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { if (frame instanceof CloseWebSocketFrame) { handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); ctx.close(); return; } // 没有使用WebSocketServerProtocolHandler,所以不会接收到PingWebSocketFrame。 // if (frame instanceof PingWebSocketFrame) { // ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain())); // return; // } if (!(frame instanceof TextWebSocketFrame)) { throw new UnsupportedOperationException( String.format("%s frame types not supported", frame.getClass().getName())); } String request = ((TextWebSocketFrame) frame).text(); logger.debug("收到客户端发送的数据:" + request); // 回复心跳 if (request.length() == 0) { ctx.writeAndFlush(new TextWebSocketFrame("")); return; } this.handleMessage(ctx.channel(), request); } /** * 得到Http请求的Query String * * @param req * @param name * @return */ private static String getRequestParameter(FullHttpRequest req, String name) { QueryStringDecoder decoder = new QueryStringDecoder(req.uri()); Map> parameters = decoder.parameters(); Set>> entrySet = parameters.entrySet(); for (Entry> entry : entrySet) { if (entry.getKey().equalsIgnoreCase(name)) { return entry.getValue().get(0); } } return null; } /** * 处理消息 * @param channel * @param message */ private void handleMessage(Channel channel, String message) { JSONObject json = null; try { json = JSONObject.parseObject(message); } catch(JSONException e) { logger.error(e.getMessage()); return; } if(!json.containsKey("action")) { logger.warn("can not find action."); return; } switch(json.getString("action").toUpperCase()) { case "BIND_GROUP_CHANNEL": bindChannelToGroupChannel(json, channel); break; case "REMOVE_CHANNEL_FROM_GROUP": removeChannelFromGroupChannel(json, channel); break; } } /** * 绑定channel到ChannelGroup中 * @param message * @param channel */ private void bindChannelToGroupChannel(JSONObject message, Channel channel) { String[] groupIds = message.getString("groupIds").split(","); for(int i = 0; i < groupIds.length; i++) { if(groupIds[i] != null && !groupIds[i].trim().isEmpty()) { ChannelsHolder.addChannelToGroup(groupIds[i], channel); } } } /** * 从GroupChannel中删除channel * @param message * @param channel */ private void removeChannelFromGroupChannel(JSONObject message, Channel channel) { String imChannelId = message.getString("channelId"); ChannelGroup cg = ChannelsHolder.getChannelGroups().get(imChannelId); if(cg != null) { cg.remove(channel); } } /** * 客户端下线 * @param ctx */ private void clientOffline(ChannelHandlerContext ctx) { String channelId = ctx.channel().id().asShortText(); ctx.close(); String userId = ChannelsHolder.getUserIdByChannelId(channelId); ChannelsHolder.removeChannel(channelId); // 更新用户在线状态 if(userId != null) { UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); UserDTO dto = new UserDTO(); dto.setId(userId); dto.setOnlineStatus("offline"); if(serviceProxy.updateUser(dto, false) != null) { NotificationEvent event = new OnlineStatusChangedEvent(dto.getId(), dto.getOnlineStatus()); event.trigger(); } } } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/QueueSubscriber.java ================================================ package org.leo.im.socket.subscription; import org.leo.im.notification.Subscriber; import org.leo.im.socket.exception.MessageHandleException; import org.leo.im.socket.subscription.handler.MessageHandler; import org.leo.im.socket.subscription.handler.MessageHandlerFactory; import com.alibaba.fastjson.JSONObject; /** * 队列订阅器实现类 * * @author Leo * @date 2018/3/29 */ public class QueueSubscriber implements Subscriber { /** * 接收数据 * @param channel * @param message */ @Override public void onMessage(String channel, String message) { if(message == null || message.trim().isEmpty()) { throw new MessageHandleException("Message is empty"); } JSONObject json = null; try { json = JSONObject.parseObject(message); } catch(Exception e) { throw new MessageHandleException("Message is not a valid json format, " + message); } MessageHandler handler = MessageHandlerFactory.createMessageHandler(json.getString("action")); if(handler == null) { throw new MessageHandleException("Invalid action, " + json.getString("action")); } handler.doHandle(channel, json.toJSONString()); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/SubscriberFactory.java ================================================ package org.leo.im.socket.subscription; import org.leo.im.notification.Subscriber; /** * 订阅器工厂类 * * @author Leo * @date 2018/3/29 */ public class SubscriberFactory { /** * 创建订阅器 * * @return */ public static Subscriber createSubscriber() { switch (System.getProperty("notification.type")) { case "queue": return new QueueSubscriber(); default: return new QueueSubscriber(); } } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/AbstractMessageHandler.java ================================================ package org.leo.im.socket.subscription.handler; import org.leo.im.socket.ChannelIdSet; import org.leo.im.socket.ChannelsHolder; import io.netty.channel.Channel; import io.netty.channel.group.ChannelGroup; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; /** * 消息处理抽象类 * * @author Leo * @date 2018/5/15 */ public class AbstractMessageHandler { private String[] groups = null; private String[] users = null; protected void setGroups(String... groups) { this.groups = groups; } protected void setUsers(String... users) { this.users = users; } protected String[] getUsers() { return this.users; } /** * 处理消息 * @param message */ public void handleMessage(String message) { if (this.users != null) { for (String user : users) { if (user != null && !user.trim().isEmpty()) { ChannelIdSet chIdSet = ChannelsHolder.getChannelsByUserId(user); if (chIdSet == null) { continue; } for (String channelId : chIdSet.getSet()) { Channel ch = ChannelsHolder.getChannelById(channelId); if (ch != null) { ch.writeAndFlush(new TextWebSocketFrame(message)); } } } } } if (this.groups != null) { for (String group : groups) { if (group != null && !group.trim().isEmpty()) { ChannelGroup cg = ChannelsHolder.getChannelGroups().get(group); if(cg != null) { cg.writeAndFlush(new TextWebSocketFrame(message)); } } } } } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/AvatarChangedHandler.java ================================================ package org.leo.im.socket.subscription.handler; /** * 头像改变消息处理器 * @author Administrator * */ public class AvatarChangedHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * * @param channel * @param message */ @Override public void doHandle(String channel, String message) { super.setGroups("all"); super.handleMessage(message); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/ChannelCreatedHandler.java ================================================ package org.leo.im.socket.subscription.handler; import java.util.Set; import org.leo.im.socket.ChannelIdSet; import org.leo.im.socket.ChannelsHolder; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import io.netty.channel.Channel; /** * 频道创建消息处理器 * * @author Leo * @date 2018/5/28 */ public class ChannelCreatedHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 */ @Override public void doHandle(String channel, String message) { JSONObject json = JSON.parseObject(message); if (!json.containsKey("userIds")) { return; } String channelId = json.getString("channelId"); String[] userIds = json.getString("userIds").split(","); for (String userId : userIds) { ChannelIdSet channelIdSet = ChannelsHolder.getChannelsByUserId(userId); if (channelIdSet != null) { Set imChannelIds = channelIdSet.getSet(); for (String imChannelId : imChannelIds) { Channel imChannel = ChannelsHolder.getChannelById(imChannelId); if(imChannel == null) { continue; } ChannelsHolder.addChannelToGroup(channelId, imChannel); } } } super.setUsers(json.getString("userIds").split(",")); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/ChannelNameChangedHandler.java ================================================ package org.leo.im.socket.subscription.handler; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; /** * 频道名称改变处理器 * * @author Leo * @date 2018/6/8 */ public class ChannelNameChangedHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * @param channel * @param message */ @Override public void doHandle(String channel, String message) { JSONObject json = JSON.parseObject(message); super.setGroups(json.getString("channelId")); super.handleMessage(message); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/ChannelRemovedHandler.java ================================================ package org.leo.im.socket.subscription.handler; import com.alibaba.fastjson.JSONObject; /** * 频道被删除处理器 * * @author Leo * @date 2018/6/12 */ public class ChannelRemovedHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * * @param channel * @param message */ @Override public void doHandle(String channel, String message) { JSONObject json = JSONObject.parseObject(message); super.setGroups(json.getString("channelId")); super.handleMessage(message); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/JoinChannelHandler.java ================================================ package org.leo.im.socket.subscription.handler; import java.util.ArrayList; import java.util.List; import org.leo.im.api.dto.MessageDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.api.service.MessageService; import org.leo.im.service.support.ServiceProxy; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; /** * 加入频道消息处理器 * * @author Leo * @date 2018/5/28 */ public class JoinChannelHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * @param channel * @param message */ @Override public void doHandle(String channel, String message) { JSONObject json = JSON.parseObject(message); JSONArray userIdArray = json.getJSONArray("userIds"); String[] userIds = new String[userIdArray.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = userIdArray.getJSONObject(i).getString("id"); } super.setUsers(userIds); JSONObject sendMessage = new JSONObject(); sendMessage.put("action", json.getString("action")); sendMessage.put("channelId", json.getString("channelId")); super.handleMessage(sendMessage.toJSONString()); if (json.getString("channelType").equals("G")) { // 发送系统消息 JSONArray userNicknames = json.getJSONArray("userNicknames"); List dtos = new ArrayList<>(userNicknames.size()); long createAt = new java.util.Date().getTime(); for (int i = 0; i < userNicknames.size(); i++) { MessageDTO dto = new MessageDTO(); dto.setId(System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000)); dto.setChannelType("G"); dto.setType("system_add_to_channel"); dto.setContent(userNicknames.getJSONObject(i).getString("nickname") + " 被 " + json.getString("admin") + " 加入到该频道"); dto.setChannelId(json.getString("channelId")); dto.setSenderFirstLetterOfName("L"); dto.setSenderNickname("系统用户"); dto.setSenderId("00000000000000000000000000000000"); dto.setCreateAt(createAt); dtos.add(dto); } MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService()); serviceProxy.saveMessage(dtos); } } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/LeaveChannelHandler.java ================================================ package org.leo.im.socket.subscription.handler; import org.leo.im.api.dto.MessageDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.api.service.MessageService; import org.leo.im.service.support.ServiceProxy; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; /** * 离开频道处理器 * * @author Leo * @date 2018/6/12 */ public class LeaveChannelHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * * @param channel * @param message */ @Override public void doHandle(String channel, String message) { JSONObject data = JSON.parseObject(message); super.setGroups(data.getString("channelId")); JSONObject sendMessage = new JSONObject(); sendMessage.put("action", data.getString("action")); sendMessage.put("channelId", data.getString("channelId")); sendMessage.put("userId", data.getString("userId")); sendMessage.put("userNickname", data.getString("userNickname")); super.handleMessage(sendMessage.toJSONString()); if (data.getString("channelType").equals("G")) { // 发送系统消息 long createAt = new java.util.Date().getTime(); MessageDTO dto = new MessageDTO(); dto.setId(System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000)); dto.setChannelType("G"); dto.setType("system_add_to_channel"); dto.setContent(data.getString("userNickname") + " 离开该频道"); dto.setChannelId(data.getString("channelId")); dto.setSenderFirstLetterOfName("L"); dto.setSenderNickname("系统用户"); dto.setSenderId("00000000000000000000000000000000"); dto.setCreateAt(createAt); MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService()); serviceProxy.saveMessage(dto); } } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/MembersCountChangedHandler.java ================================================ package org.leo.im.socket.subscription.handler; import com.alibaba.fastjson.JSONObject; /** * 成员数量变更处理器 * * @author Leo * @date 2018/6/11 */ public class MembersCountChangedHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * @param channel * @param message */ @Override public void doHandle(String channel, String message) { JSONObject json = JSONObject.parseObject(message); super.setGroups(json.getString("channelId")); super.handleMessage(message); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/MessageHandler.java ================================================ package org.leo.im.socket.subscription.handler; /** * 消息处理器接口 * * @author Leo * @date 2018/3/29 */ public interface MessageHandler { /** * 处理消息 * @param channel * @param message */ void doHandle(String channel, String message); } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/MessageHandlerFactory.java ================================================ package org.leo.im.socket.subscription.handler; import org.leo.im.notification.ActionNames; /** * 消息处理器工厂类 * * @author Leo * @date 2018/3/29 */ public class MessageHandlerFactory { /** * 创建消息处理器 * * @param messageType * @return */ public static MessageHandler createMessageHandler(String messageType) { switch (messageType) { case ActionNames.ONLINE_STATUS_CHANGED: return new OnlineStatusChangedHandler(); case ActionNames.NICKNAME_CHANGED: return new NicknameChangedHandler(); case ActionNames.AVATAR_CHANGED: return new AvatarChangedHandler(); case ActionNames.NEW_MESSAGE: return new NewMessageHandler(); case ActionNames.READ_MESSAGE: return new ReadMessageHandler(); case ActionNames.CREATE_CHANNEL: return new ChannelCreatedHandler(); case ActionNames.JOIN_CHANNEL: return new JoinChannelHandler(); case ActionNames.MESSAGE_REMOVED: return new RemoveMessageHandler(); case ActionNames.CHANNEL_NAME_CHANGED: return new ChannelNameChangedHandler(); case ActionNames.MEMBERS_COUNT_CHANGED: return new MembersCountChangedHandler(); case ActionNames.REMOVE_FROM_CHANNEL: return new RemoveFromChannelHandler(); case ActionNames.LEAVE_CHANNEL: return new LeaveChannelHandler(); case ActionNames.CHANNEL_REMOVED: return new ChannelRemovedHandler(); } return null; } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/NewMessageHandler.java ================================================ package org.leo.im.socket.subscription.handler; import com.alibaba.fastjson.JSONObject; /** * 新消息处理器 * * @author Leo * @date 2018/5/16 */ public class NewMessageHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * * @param channel * @param message */ @Override public void doHandle(String channel, String message) { JSONObject json = JSONObject.parseObject(message); if(json.containsKey("groupIds")) { String[] groupIds = json.getString("groupIds").split(","); super.setGroups(groupIds); } if(json.containsKey("userIds")) { String[] userIds = json.getString("userIds").split(","); super.setUsers(userIds); } json.remove("groupIds"); json.remove("userIds"); super.handleMessage(json.toJSONString()); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/NicknameChangedHandler.java ================================================ package org.leo.im.socket.subscription.handler; /** * 昵称改变消息处理器 * * @author Leo * @date 2018/5/15 */ public class NicknameChangedHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * * @param channel * @param message */ @Override public void doHandle(String channel, String message) { super.setGroups("all"); super.handleMessage(message); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/OnlineStatusChangedHandler.java ================================================ package org.leo.im.socket.subscription.handler; /** * 用户在线状态改变动作处理器 * * @author Leo * @date 2018/3/29 */ public class OnlineStatusChangedHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * * @param channel * @param message */ @Override public void doHandle(String channel, String message) { super.setGroups("all"); super.handleMessage(message); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/ReadMessageHandler.java ================================================ package org.leo.im.socket.subscription.handler; import com.alibaba.fastjson.JSONObject; /** * 读取消息处理器 * * @author Leo * @date 2018/5/22 */ public class ReadMessageHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * * @param channel * @param message */ @Override public void doHandle(String channel, String message) { JSONObject json = JSONObject.parseObject(message); super.setUsers(json.getString("userId")); super.handleMessage(message); } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/RemoveFromChannelHandler.java ================================================ package org.leo.im.socket.subscription.handler; import java.util.ArrayList; import java.util.List; import org.leo.im.api.dto.MessageDTO; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.api.service.MessageService; import org.leo.im.service.support.ServiceProxy; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; /** * 从频道被移除处理器 * * @author Leo * @date 2018/6/12 */ public class RemoveFromChannelHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 * @param channel * @param message */ @Override public void doHandle(String channel, String message) { JSONObject json = JSON.parseObject(message); JSONArray userIdArray = json.getJSONArray("userIds"); String[] userIds = new String[userIdArray.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = userIdArray.getJSONObject(i).getString("id"); } super.setUsers(userIds); JSONObject sendMessage = new JSONObject(); sendMessage.put("action", json.getString("action")); sendMessage.put("channelId", json.getString("channelId")); super.handleMessage(sendMessage.toJSONString()); if (json.getString("channelType").equals("G")) { // 发送系统消息 JSONArray userNicknames = json.getJSONArray("userNicknames"); List dtos = new ArrayList<>(userNicknames.size()); long createAt = new java.util.Date().getTime(); for (int i = 0; i < userNicknames.size(); i++) { MessageDTO dto = new MessageDTO(); dto.setId(System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000)); dto.setChannelType("G"); dto.setType("system_remove_from_channel"); dto.setContent(userNicknames.getJSONObject(i).getString("nickname") + " 被 " + json.getString("admin") + " 移出该频道"); dto.setChannelId(json.getString("channelId")); dto.setSenderFirstLetterOfName("L"); dto.setSenderNickname("系统用户"); dto.setSenderId("00000000000000000000000000000000"); dto.setCreateAt(createAt); dtos.add(dto); } MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService()); serviceProxy.saveMessage(dtos); } } } ================================================ FILE: leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/RemoveMessageHandler.java ================================================ package org.leo.im.socket.subscription.handler; import org.leo.im.notification.ActionNames; import com.alibaba.fastjson.JSONObject; /** * 删除消息处理器 * * @author Leo * @date 2018/6/5 */ public class RemoveMessageHandler extends AbstractMessageHandler implements MessageHandler { /** * 处理消息 */ @Override public void doHandle(String channel, String message) { JSONObject json = JSONObject.parseObject(message); if(json.containsKey("toUserId") && json.getString("toUserId") != null) { super.setUsers(json.getString("toUserId")); } else { super.setGroups(json.getString("channelId")); } JSONObject sendMessage = new JSONObject(); sendMessage.put("action", ActionNames.MESSAGE_REMOVED); sendMessage.put("messageId", json.getLongValue("messageId")); sendMessage.put("senderId", json.getString("senderId")); super.handleMessage(sendMessage.toJSONString()); } } ================================================ FILE: leo-im-socket/src/test/java/org/leo/im/socket/AppTest.java ================================================ package org.leo.im.socket; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-socket/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: netty-all-4.1.24.Final.jar leo-im-util-1.0.jar cglib-3.2.6 .jar asm-6.0.jar ant-1.9.6.jar ant-launcher-1.9.6.jar jjwt-0.9.0.jar jackson-databind-2.8.9.jar jackson-annotations-2.8.0.jar jackson-core -2.8.9.jar leo-im-api-1.0.jar leo-im-common-1.0.jar leo-im-api-provid er-1.0.jar leo-im-service-1.0.jar leo-im-store-1.0.jar mysql-connecto r-java-8.0.11.jar protobuf-java-2.6.0.jar druid-1.1.9.jar leo-im-mode l-1.0.jar leo-im-notification-1.0.jar jedis-2.9.0.jar commons-pool2-2 .4.2.jar fastjson-1.2.47.jar slf4j-api-1.7.25.jar logback-classic-1.2 .3.jar logback-core-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-socket/target/classes/META-INF/maven/org.leo.im/leo-im-socket/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:19 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-socket m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-socket artifactId=leo-im-socket ================================================ FILE: leo-im-socket/target/classes/META-INF/maven/org.leo.im/leo-im-socket/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-socket leo-im-socket http://maven.apache.org io.netty netty-all ${netty.version} org.leo.im leo-im-util ${project.version} org.leo.im leo-im-api ${project.version} org.leo.im leo-im-api-provider ${project.version} org.leo.im leo-im-notification ${project.version} ================================================ FILE: leo-im-socket/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:23 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-socket ================================================ FILE: leo-im-socket/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-socket/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\exception\MessageHandleException.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\AbstractMessageHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\ChannelNameChangedHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\NicknameChangedHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\RemoveFromChannelHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\SocketChannel.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\MessageHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\ReadMessageHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\ChannelRemovedHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\ChannelIdSet.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\WebSocketServer.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\AvatarChangedHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\ChannelCreatedHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\SubscriberFactory.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\WebSocketChannelInitializer.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\RemoveMessageHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\QueueSubscriber.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\LeaveChannelHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\JoinChannelHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\OnlineStatusChangedHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\ChannelsHolder.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\MessageHandlerFactory.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\handler\TextWebSocketFrameHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\NewMessageHandler.java F:\Develop\open-source\leo-im-server\leo-im-socket\src\main\java\org\leo\im\socket\subscription\handler\MembersCountChangedHandler.java ================================================ FILE: leo-im-socket/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-socket/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-socket\src\test\java\org\leo\im\socket\AppTest.java ================================================ FILE: leo-im-socket/target/surefire-reports/TEST-org.leo.im.socket.AppTest.xml ================================================ ================================================ FILE: leo-im-socket/target/surefire-reports/org.leo.im.socket.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.socket.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.076 sec ================================================ FILE: leo-im-starter/.classpath ================================================ ================================================ FILE: leo-im-starter/.project ================================================ leo-im-starter org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-starter/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-starter/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-starter/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-starter/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-starter leo-im-starter http://maven.apache.org org.leo.im leo-im-http ${parent.version} org.leo.im leo-im-socket ${parent.version} org.leo.im leo-im-migration ${parent.version} ================================================ FILE: leo-im-starter/src/main/conf/app.conf ================================================ #服务器类别 #可选项有http,websocket server.type=http,websocket #Web前端地址 http.origin=http://localhost:3006 #Http服务器监听端口 http.port=2006 #Http服务器boss线程数与worker线程数 #建议boss为1,worker为2,可根据服务器配置自行调整 http.boss.threads=1 http.worker.threads=2 #Http业务线程池与业务队列设置 #Http Executor线程处理的Http请求较多,建议将线程数设置为2以上。 #threads如果设置为0,则使用默认值:CPU核心数*2,如果queues设置为0,则使用默认值1024 http.executor.threads=2 http.executor.queues=256 #Http最大内容长度(默认 为10M) http.max.content.length=50 #WebSocket监听端口 websocket.port=2007 #WebSocket服务器boss线程数与worker线程数 #建议boss为1,worker为1,可根据服务器配置自行调整 websocket.boss.threads=1 websocket.worker.threads=1 #WebSocket业务线程池与业务队列设置 #WebSocket的Executor线程主要处理用户的登录逻辑,建议在并发量不大的场景下,将线程数设置为1即可。 #threads如果设置为0,则使用默认值:CPU核心数,如果queues设置为0,则使用默认值512 websocket.executor.threads=1 websocket.executor.queues=256 #线程池设置 #threads如果设置为0,则使用默认值:CPU核心数*2,如果queues设置为0,则使用默认值512 thread.pool.threads=2 thread.pool.queues=256 #数据库连接池配置(私有部署需要配置) db.pool.url=jdbc:mysql://localhost:3306/leo-im1?serverTimezone=GMT%2B8&useSSL=false db.pool.username=root db.pool.password=root db.pool.maxActive=10 db.pool.initialSize=1 db.pool.maxWait=60000 db.pool.minIdle=1 db.pool.testOnBorrow=false db.pool.testOnReturn=false #dao类型 dao.type=jdbc #JWT秘钥 jwt.secret=ILoveLeoIM #JWT有效期(毫秒),默认15天 jwt.ttl.millis=1296000000 #缓存设置 #org.leo.im.http.cache包中有基于Java Map的缓存实现,如果有其他缓存实现,请实现CacheManager接口,并修改CacheManagerFactory类,并修改配置。 cache.type=map #通知处理器类型 #可选类型还有queue、redis,如果是集群部署或Http与WebSocket独立部署,需要选择redis方式。 notification.type=queue #通知缓存长度 notification.capacity=1024 #通知处理器redis配置 notification.redis.host=192.168.0.10 notification.redis.port=6379 notification.redis.timeout=5000 notification.redis.password=password #最大连接数 notification.redis.pool.maxTotal=100 #最大空闲连接数 notification.redis.pool.maxIdle=5 #获取连接的最大等待时间 notification.redis.pool.maxWait=10000 #获取的连接是否需要校验 notification.redis.pool.testOnBorrow=0 #redis db index notification.redis.db.index=0 #消息频道设置 #将私聊、群聊、系统消息放到不同的队列中处理,可提高处理速度,但是每个队列对应一个处理线程,建议根据实际负载量与服务器配置做合理设置。 channel.private.message=im-message channel.group.message=im-message channel.system.message=im-message #文件存储类型(local为本地存储,也可以使用对象存储服务,如minio,需实现org.leo.im.http.file.FileStorage接口) #file.settings.driver=local #文件保存路径 file.settings.directory=f:/develop/leo-files ================================================ FILE: leo-im-starter/src/main/java/org/leo/im/starter/App.java ================================================ package org.leo.im.starter; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Set; import org.leo.im.api.provider.ServiceFactory; import org.leo.im.api.service.UserService; import org.leo.im.http.HttpServer; import org.leo.im.migration.FlywayMigration; import org.leo.im.service.support.ServiceProxy; import org.leo.im.socket.ChannelsHolder; import org.leo.im.socket.WebSocketServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Leo IM Starter * * @author Leo * @date 2018/3/30 */ public class App { private final static Logger logger = LoggerFactory.getLogger(App.class); public static void main(String[] args) { // 加载配置文件 String confHome = System.getProperty("conf.home"); if(confHome == null || confHome.trim().isEmpty()) { logger.error("请在启动参数中设置配置文件所在路径, -Dconf.home"); return; } // 加载配置文件 if(confHome.endsWith("/") || confHome.endsWith("\\")) { confHome = confHome.substring(0, confHome.length() - 1); } try { loadConfigFile(confHome); } catch(IOException e) { logger.error("加载配置文件失败,{}", e.getMessage()); return; } //初始化数据库 FlywayMigration dbMigration = new FlywayMigration(); dbMigration.migrate(System.getProperty("db.pool.url"), System.getProperty("db.pool.username"), System.getProperty("db.pool.password")); String serverTypeConfig = System.getProperty("server.type"); if(canStartServer(serverTypeConfig, "http")) { // 启动Http服务 Thread httpServerThread = new Thread(() -> { try { new HttpServer().start(); } catch (InterruptedException e) { logger.error("启动Http Server失败,{}", e.getMessage()); } }); httpServerThread.setName("httpServerThread"); httpServerThread.start(); } if(canStartServer(serverTypeConfig, "websocket")) { Runtime.getRuntime().addShutdownHook(new Thread(() -> { logger.info("Clean resources..."); try { cleanResources(); logger.info("Clean resources finished"); } catch(Exception e) { logger.error(e.getMessage()); } })); WebSocketServer wsServer = new WebSocketServer(Integer.getInteger("websocket.port")); wsServer.setBossThreads(Integer.getInteger("websocket.boss.threads")); wsServer.setWorkerThreads(Integer.getInteger("websocket.worker.threads")); // 启动WebSocket服务 Thread websocketServerThread = new Thread(() -> { try { wsServer.start(); } catch (InterruptedException e) { logger.error("启动WebSocket Server失败,{}", e.getMessage()); } }); websocketServerThread.setName("websocketServerThread"); websocketServerThread.start(); } } /** * 加载配置文件 * @param confHome * @throws IOException */ private static void loadConfigFile(String confHome) throws IOException { InputStream is = new FileInputStream(confHome + "/app.conf"); System.getProperties().load(is); } /** * 是否可以启动指定类型的服务器 * @param serverTypeConfig * @param targerServer * @return */ private static boolean canStartServer(String serverTypeConfig, String targerServer) { String[] serverTypes = serverTypeConfig.split(","); for(String serverType : serverTypes) { if(serverType.equalsIgnoreCase(targerServer)) { return true; } } return false; } /** * 清理资源 */ private static void cleanResources() { Set userIds = ChannelsHolder.getUserIds(); if(userIds != null && userIds.size() > 0) { UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService()); serviceProxy.batchOffline(userIds); } } } ================================================ FILE: leo-im-starter/src/test/java/org/leo/im/starter/AppTest.java ================================================ package org.leo.im.starter; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-starter/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: leo-im-http-1.0.jar asm-6.1.jar thumbnailator-0.4.8.jar ne tty-all-4.1.24.Final.jar fastjson-1.2.47.jar netty-rest-server-1.0.ja r leo-im-api-1.0.jar leo-im-api-provider-1.0.jar leo-im-service-1.0.j ar leo-im-store-1.0.jar mysql-connector-java-8.0.11.jar protobuf-java -2.6.0.jar druid-1.1.9.jar leo-im-model-1.0.jar leo-im-util-1.0.jar c glib-3.2.6.jar ant-1.9.6.jar ant-launcher-1.9.6.jar jjwt-0.9.0.jar ja ckson-databind-2.8.9.jar jackson-annotations-2.8.0.jar jackson-core-2 .8.9.jar leo-im-notification-1.0.jar jedis-2.9.0.jar commons-pool2-2. 4.2.jar leo-im-common-1.0.jar leo-im-socket-1.0.jar leo-im-migration- 1.0.jar flyway-core-5.1.1.jar slf4j-api-1.7.25.jar logback-classic-1. 2.3.jar logback-core-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-starter/target/classes/META-INF/maven/org.leo.im/leo-im-starter/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:35 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-starter m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-starter artifactId=leo-im-starter ================================================ FILE: leo-im-starter/target/classes/META-INF/maven/org.leo.im/leo-im-starter/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-starter leo-im-starter http://maven.apache.org org.leo.im leo-im-http ${parent.version} org.leo.im leo-im-socket ${parent.version} org.leo.im leo-im-migration ${parent.version} ================================================ FILE: leo-im-starter/target/leo-im-starter-1.0/bin/run.bat ================================================ @REM app launcher script @REM @REM Environment: @REM JAVA_HOME - location of a JDK home dir (optional if java on path) @setlocal enabledelayedexpansion @echo off cd %~dp0 cd ../ if "%JAVA_OPS%" == "" set JAVA_OPS=-Dfile.encoding=utf-8 -Dio.netty.noUnsafe=true -server -Xmx128m -Xms128m -Xss256k java %JAVA_OPS% -Dconf.home=%cd%\conf\ -jar %cd%\lib\leo-im-starter-1.0.jar ================================================ FILE: leo-im-starter/target/leo-im-starter-1.0/bin/run.sh ================================================ #!/bin/sh ### ------------------------------- ### ### leo-im-server launcher script ### ### ------------------------------- ### cd `dirname $0` cd ../ if [ -z "$JAVA_OPS" ]; then JAVA_OPS="-Dfile.encoding=utf-8 -Dio.netty.noUnsafe=true -Xms128M -Xmx128M -Xss256K" fi java $JAVA_OPS -Dconf.home=$(pwd)/conf/ -jar $(pwd)/lib/leo-im-starter-1.0.jar ================================================ FILE: leo-im-starter/target/leo-im-starter-1.0/conf/app.conf ================================================ #服务器类别 #可选项有http,websocket server.type=http,websocket #Web前端地址 http.origin=http://localhost:3006 #Http服务器监听端口 http.port=2006 #Http服务器boss线程数与worker线程数 #建议boss为1,worker为2,可根据服务器配置自行调整 http.boss.threads=1 http.worker.threads=2 #Http业务线程池与业务队列设置 #Http Executor线程处理的Http请求较多,建议将线程数设置为2以上。 #threads如果设置为0,则使用默认值:CPU核心数*2,如果queues设置为0,则使用默认值1024 http.executor.threads=2 http.executor.queues=256 #Http最大内容长度(默认 为10M) http.max.content.length=50 #WebSocket监听端口 websocket.port=2007 #WebSocket服务器boss线程数与worker线程数 #建议boss为1,worker为1,可根据服务器配置自行调整 websocket.boss.threads=1 websocket.worker.threads=1 #WebSocket业务线程池与业务队列设置 #WebSocket的Executor线程主要处理用户的登录逻辑,建议在并发量不大的场景下,将线程数设置为1即可。 #threads如果设置为0,则使用默认值:CPU核心数,如果queues设置为0,则使用默认值512 websocket.executor.threads=1 websocket.executor.queues=256 #线程池设置 #threads如果设置为0,则使用默认值:CPU核心数*2,如果queues设置为0,则使用默认值512 thread.pool.threads=2 thread.pool.queues=256 #数据库连接池配置(私有部署需要配置) db.pool.url=jdbc:mysql://localhost:3306/leo-im1?serverTimezone=GMT%2B8&useSSL=false db.pool.username=root db.pool.password=root db.pool.maxActive=10 db.pool.initialSize=1 db.pool.maxWait=60000 db.pool.minIdle=1 db.pool.testOnBorrow=false db.pool.testOnReturn=false #dao类型 dao.type=jdbc #JWT秘钥 jwt.secret=ILoveLeoIM #JWT有效期(毫秒),默认15天 jwt.ttl.millis=1296000000 #缓存设置 #org.leo.im.http.cache包中有基于Java Map的缓存实现,如果有其他缓存实现,请实现CacheManager接口,并修改CacheManagerFactory类,并修改配置。 cache.type=map #通知处理器类型 #可选类型还有queue、redis,如果是集群部署或Http与WebSocket独立部署,需要选择redis方式。 notification.type=queue #通知缓存长度 notification.capacity=1024 #通知处理器redis配置 notification.redis.host=192.168.0.28 notification.redis.port=6379 notification.redis.timeout=5000 notification.redis.password=justep #最大连接数 notification.redis.pool.maxTotal=100 #最大空闲连接数 notification.redis.pool.maxIdle=5 #获取连接的最大等待时间 notification.redis.pool.maxWait=10000 #获取的连接是否需要校验 notification.redis.pool.testOnBorrow=0 #redis db index notification.redis.db.index=0 #消息频道设置 #将私聊、群聊、系统消息放到不同的队列中处理,可提高处理速度,但是每个队列对应一个处理线程,建议根据实际负载量与服务器配置做合理设置。 channel.private.message=im-message channel.group.message=im-message channel.system.message=im-message #文件存储类型(local为本地存储,也可以使用对象存储服务,如minio,需实现org.leo.im.http.file.FileStorage接口) #file.settings.driver=local #文件保存路径 file.settings.directory=f:/develop/leo-files ================================================ FILE: leo-im-starter/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:25 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-starter ================================================ FILE: leo-im-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-starter\src\main\java\org\leo\im\starter\App.java ================================================ FILE: leo-im-starter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-starter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-starter\src\test\java\org\leo\im\starter\AppTest.java ================================================ FILE: leo-im-starter/target/surefire-reports/TEST-org.leo.im.starter.AppTest.xml ================================================ ================================================ FILE: leo-im-starter/target/surefire-reports/org.leo.im.starter.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.starter.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.057 sec ================================================ FILE: leo-im-store/.classpath ================================================ ================================================ FILE: leo-im-store/.project ================================================ leo-im-store org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-store/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-store/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-store/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-store/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-store leo-im-store http://maven.apache.org mysql mysql-connector-java ${mysql-connector-java.version} com.alibaba druid ${druid.version} org.leo.im leo-im-model ${parent.version} org.leo.im leo-im-common ${parent.version} ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/connection/ConnectionFactory.java ================================================ package org.leo.im.store.connection; import java.sql.Connection; /** * 数据库连接工厂接口 * * @author Leo * @date 2018/3/19 */ public interface ConnectionFactory { /** * 数据库连接ThreadLocal */ ThreadLocal threadDbConnection = new ThreadLocal(); /** * 得到数据库连接 * @return */ Connection getConnection(); /** * 得到数据库连接 * @param connectionString 连接字符串,开发或测试环境下需要传入连接字符串,生产环境下无需传入。 * @return */ Connection getConnection(String connectionString); /** * 关闭数据库连接 */ void closeConnection(); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/connection/ConnectionProvider.java ================================================ package org.leo.im.store.connection; import java.sql.Connection; /** * 数据库连接提供者类 * * @author Leo * @date 2018/3/19 */ public final class ConnectionProvider { /** * 得到数据库连接 * @return */ public static Connection getConnection() { return ConnectionFactory.threadDbConnection.get(); } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/connection/impl/PoolConnectionFactory.java ================================================ package org.leo.im.store.connection.impl; import java.sql.Connection; import java.sql.SQLException; import org.leo.im.store.connection.ConnectionFactory; import org.leo.im.store.datasource.ConnectionPool; import org.leo.im.store.datasource.impl.DruidConnectionPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 数据库连接池连接工厂类 * * @author Leo * @date 2018/3/19 */ public final class PoolConnectionFactory implements ConnectionFactory { private final static Logger logger = LoggerFactory.getLogger(PoolConnectionFactory.class); /** * 私有构造函数,避免外部创建实例。 */ private PoolConnectionFactory() { } private static class InstanceHolder { private static PoolConnectionFactory instance = new PoolConnectionFactory(); } /** * 得到单例实例 * @return */ public static PoolConnectionFactory getInstance() { return InstanceHolder.instance; } /** * 得到数据库连接 * @return */ @Override public Connection getConnection() { return getConnection(null); } /** * 得到数据库连接 * @param connectionString 连接字符串,开发或测试环境下需要传入连接字符串,生产环境下无需传入。 * @return */ @Override public Connection getConnection(String connectionString) { Connection conn = threadDbConnection.get(); if(conn == null) { conn = getConnectionPool().getConnection(); threadDbConnection.set(conn); } return conn; } /** * 关闭数据库连接 */ @Override public void closeConnection() { Connection conn = threadDbConnection.get(); if(conn != null) { try { conn.close(); } catch (SQLException e) { logger.error(e.getMessage()); } threadDbConnection.remove(); } } /** * 得到连接池实例 * @return */ private ConnectionPool getConnectionPool() { return DruidConnectionPool.getInstance(); } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/BaseDAO.java ================================================ package org.leo.im.store.dao; import java.sql.PreparedStatement; import java.sql.SQLException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.UUID; import org.leo.im.store.exception.DAOException; import org.leo.im.store.support.Parameter; /** * dao基类 * * @author Leo * @date 2018/3/30 */ public interface BaseDAO { String CONNECTION_NOT_FOUND_EXCEPTION = "Connection Not Found"; /** * 创建id * * @return */ default String createId() { return UUID.randomUUID().toString().replace("-", ""); } /** * 设置PreparedStatement的参数 * * @param stmt * @param parameters * @throws SQLException * @throws NumberFormatException */ default void setPreparedStatmentParameters(PreparedStatement stmt, List parameters) throws NumberFormatException, SQLException { if (parameters != null) { for (int i = 0; i < parameters.size(); i++) { switch (parameters.get(i).getDataType()) { case SHORT: stmt.setShort(i + 1, Short.parseShort(parameters.get(i).getValue().toString())); break; case INT: stmt.setInt(i + 1, Integer.parseInt(parameters.get(i).getValue().toString())); break; case LONG: stmt.setLong(i + 1, Long.parseLong(parameters.get(i).getValue().toString())); break; case FLOAT: stmt.setFloat(i + 1, Float.parseFloat(parameters.get(i).getValue().toString())); break; case DOUBLE: stmt.setDouble(i + 1, Double.parseDouble(parameters.get(i).getValue().toString())); break; case STRING: stmt.setString(i + 1, parameters.get(i).getValue().toString()); break; case DATE: SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd"); try { Date formattedDate = dateFormatter.parse(parameters.get(i).getValue().toString()); stmt.setDate(i + 1, new java.sql.Date(formattedDate.getTime())); } catch (ParseException e) { throw new DAOException(e); } break; case DATETIME: Date date = (Date) parameters.get(i).getValue(); stmt.setDate(i + 1, new java.sql.Date(date.getTime())); break; case TIMESTAMP: Date dt = (Date) parameters.get(i).getValue(); SimpleDateFormat timestampFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); stmt.setTimestamp(i + 1, java.sql.Timestamp.valueOf(timestampFormatter.format(dt))); break; case BOOLEAN: stmt.setBoolean(i + 1, Boolean.parseBoolean(parameters.get(i).getValue().toString())); break; case OBJECT: stmt.setObject(i + 1, parameters.get(i)); break; default: stmt.setObject(i + 1, parameters.get(i)); } } } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/ChannelDAO.java ================================================ package org.leo.im.store.dao; import java.util.List; import java.util.Map; import org.leo.im.model.Channel; /** * 频道dao接口 * * @author Leo * @date 2018/4/10 */ public interface ChannelDAO { /** * 得到频道列表 * @param parameters * @param limit * @return */ List list(Map parameters, int limit); /** * 得到群组频道 * @param parameters * @param types * @param limit * @return */ List listGroupChannel(Map parameters, String[] types, int limit); /** * 添加频道 * @param channel * @return */ Channel save(Channel channel); /** * 根据from和to得到频道 * @param fromUserId * @param toUserId * @return */ Channel getByFromAndTo(String fromUserId, String toUserId); /** * 根据频道id查找频道信息 * @param id * @return */ Channel getById(String id); /** * 更新最后发送消息的时间 * @param id * @param lastPostAt * @return */ int updateLastPostAt(String id, long lastPostAt); /** * 增加成员数量 * @param id * @param count * @return */ int increaseMemberCount(String id, int count); /** * 更新字符字段 * @param id * @param field * @param value * @return */ int updateStringField(String id, String field, String value); /** * 更新整型字段 * @param id * @param field * @param value * @return */ int updateIntegerField(String id, String field, int value); /** * 删除频道 * @param id * @return */ int remove(String id); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/ChannelMemberDAO.java ================================================ package org.leo.im.store.dao; import java.util.List; import org.leo.im.common.data.Page; import org.leo.im.model.ChannelMember; /** * 频道成员dao接口 * * @author Leo * @date 2018/4/10 */ public interface ChannelMemberDAO { /** * 添加成员 * @param channelId * @param members * @return */ int save(String channelId, List members); /** * 得到成员列表 * @param channelId * @param username * @param limit * @param offset * @return */ Page listMember(String channelId, String username, int limit, int offset); /** * 得到用户是否为频道管理员 * @param userId * @param channelId * @return */ boolean isAdmin(String userId, String channelId); /** * 移除成员 * @param channelId * @param memberId * @return */ int removeMember(String channelId, String memberId); /** * 修改管理员 * @param channelId * @param memberId * @param isAdmin * @return */ int changeAdmin(String channelId, String memberId, boolean isAdmin); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/FileDAO.java ================================================ package org.leo.im.store.dao; import org.leo.im.model.File; /** * 文件dao接口 * * @author Leo * @date 2018/6/13 */ public interface FileDAO { /** * 添加文件 * @param file * @return */ String save(File file); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/HideChannelDAO.java ================================================ package org.leo.im.store.dao; /** * 隐藏频道dao接口 * * @author Leo * @date 2018/6/13 */ public interface HideChannelDAO { /** * 添加隐藏频道 * @param userId * @param channelId * @return */ int save(String userId, String channelId); /** * 删除隐藏频道 * @param userId * @param channelId * @return */ int remove(String userId, String channelId); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/MessageDAO.java ================================================ package org.leo.im.store.dao; import java.util.List; import org.leo.im.model.Message; /** * 消息dao接口 * * @author Leo * @date 2018/4/21 */ public interface MessageDAO { /** * 得到消息列表 * @param channelId * @param maxCreateAt * @param limit * @return */ List listMessage(String channelId, long maxCreateAt, int limit); /** * 根据id得到消息 * @param id * @return */ Message getById(long id); /** * 添加消息 * @param message * @return */ long save(Message message); /** * 批量添加消息 * @param messages * @return */ int save(List messages); /** * 删除消息 * @param messageId * @param userId * @return */ int remove(long messageId, String userId); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/UnreadMessageCountDAO.java ================================================ package org.leo.im.store.dao; /** * 未读消息数量dao接口 * * @author Leo * @date 2018/4/26 */ public interface UnreadMessageCountDAO { /** * 添加未读消息 * @param userId * @param channelId * @param total * @return */ int save(String userId, String channelId, short total); /** * 批量添加未读消息 * @param userIds * @param channelId * @param total * @return */ int batchSave(String[] userIds, String channelId, short total); /** * 更新未读消息数量 * @param userId * @param channelId * @param total * @return */ int update(String userId, String channelId, short total); /** * 批量更新未读消息数量 * @param userIds * @param channelId * @param total * @return */ int batchUpdate(String[] userIds, String channelId, short total); /** * 增加未读消息数 * @param userId * @param channelId * @param total * @return */ int increase(String userId, String channelId, short total); /** * 批量增加未读消息数 * @param userIds * @param channelId * @param total * @return */ int batchIncrease(String[] userIds, String channelId, short total); /** * 增加群组频道的未读消息数 * @param channelId * @param exceptiveUserIds * @param total * @return */ int increaseGroupChannel(String channelId, String[] exceptiveUserIds, short total); /** * 减少未读消息数量 * @param userId * @param channelId * @param total * @return */ int decrease(String userId, String channelId, short total); /** * 删除未读消息数量 * @param channelId * @param userId * @return */ int remove(String channelId, String userId); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/UserChannelDAO.java ================================================ package org.leo.im.store.dao; import java.util.List; import org.leo.im.model.UserChannel; /** * 用户频道dao接口 * * @author Leo * @date 2018/4/20 */ public interface UserChannelDAO { /** * 根据用户id得到用户频道列表 * @param userId * @param type * @param limit * @return */ List listByUserId(String userId, String type, int limit); /** * 添加用户频道 * @param userChannel * @return */ int save(UserChannel userChannel); /** * 批量添加用户频道 * @param userChannels * @return */ int batchSave(List userChannels); /** * 得到用户频道 * @param userId * @param channelId * @return */ UserChannel get(String userId, String channelId); /** * 更新用户频道显示名 * @param channelId * @param userId * @param displayName * @return */ int updateDisplayName(String channelId, String userId, String displayName); /** * 删除用户频道 * @param userId * @param channelId * @return */ int remove(String userId, String channelId); /** * 根据名称得到用户频道列表 * @param userId * @param name * @param type * @return */ List listByName(String userId, String name, String type); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/UserDAO.java ================================================ package org.leo.im.store.dao; import java.util.Set; import org.leo.im.common.data.Page; import org.leo.im.model.User; /** * 用户管理dao接口 * * @author Leo * @date 2018/4/8 */ public interface UserDAO { /** * 添加用户 * @param user * @return 用户id */ String save(User user); /** * 根据用户名得到用户 * @param name * @return */ User getByName(String name); /** * 根据id得到用户 * @param id * @return */ User getById(String id); /** * 根据名称或昵称得到用户列表 * @param name * @param limit * @param offset * @return */ Page listByNameOrNickname(String name, int limit, int offset); /** * 更新用户 * @param user * @return */ int update(User user); /** * 更新用户 * @param user * @param updateNullValueField * @return */ int update(User user, boolean updateNullValueField); /** * 判断用户名是否已经存在 * @param userId * @param username * @return */ boolean usernameExists(String userId, String username); /** * 分页查询非组成员 * @param channelId * @param username * @param limit * @param offset * @return */ Page listNonMembers(String channelId, String username, int limit, int offset); /** * 批量设置用户下线 * @param userIds * @return */ int offline(Set userIds); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcChannelDAOImpl.java ================================================ package org.leo.im.store.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.leo.im.model.Channel; import org.leo.im.model.User; import org.leo.im.store.connection.ConnectionProvider; import org.leo.im.store.dao.BaseDAO; import org.leo.im.store.dao.ChannelDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.support.Parameter; import org.leo.im.store.support.ParameterDataTypeEnum; import org.leo.im.store.support.SqlBuildResult; import org.leo.im.store.util.DbUtils; /** * Channel DAO JDBC实现类 * * @author Leo * @date 2018/4/10 */ public final class JdbcChannelDAOImpl implements BaseDAO, ChannelDAO { private static final String CHANNEL_TABLE = "im_channel"; private static final String USER_TABLE = "im_user"; /** * 得到频道列表 * @param parameters * @param limit * @return */ @Override public List list(Map parameters, int limit) { return this.listChannel(parameters, null, limit); } /** * 得到群组频道 * @param parameters * @param types * @param limit * @return */ @Override public List listGroupChannel(Map parameters, String[] types, int limit) { StringBuilder sql = new StringBuilder(32); if(types != null && types.length > 0) { for(String type : types) { sql.append("type='").append(type).append("' OR "); } } if(sql.length() > 0) { sql.setLength(sql.length() - 4); return this.listChannel(parameters, "(" + sql.toString() + ")", limit); } return this.listChannel(parameters, null, limit); } /** * 添加频道 * * @param channel * @return */ @Override public Channel save(Channel channel) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } channel.setId(this.createId()); SqlBuildResult sql = "G".equalsIgnoreCase(channel.getType()) ? this.buildInsertGroupChannelSql(channel) : this.buildInsertPrivateChannelSql(channel); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.getSql()); this.setPreparedStatmentParameters(stmt, sql.getParameters()); if(stmt.executeUpdate() > 0) { return channel; } return null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 根据from和to得到频道 * @param fromUserId * @param toUserId * @return */ @Override public Channel getByFromAndTo(String fromUserId, String toUserId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("SELECT c.id,c.creator_id,c.from_user_id,c.to_user_id,c.type,"); sql.append("u1.name AS from_user_name,u1.nickname AS from_user_nickname,"); sql.append("u2.name AS to_user_name,u2.nickname AS to_user_nickname,u2.online_status AS to_user_online_status "); sql.append("FROM ").append(CHANNEL_TABLE).append(" c INNER JOIN ").append(USER_TABLE).append(" u1 ON c.from_user_id=u1.id "); sql.append("INNER JOIN ").append(USER_TABLE).append(" u2 ON c.to_user_id=u2.id "); sql.append("WHERE (c.from_user_id=? AND c.to_user_id=?) OR (c.from_user_id=? AND c.to_user_id=?)"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, fromUserId); stmt.setString(2, toUserId); stmt.setString(3, toUserId); stmt.setString(4, fromUserId); rs = stmt.executeQuery(); while(rs.next()) { Channel channel = new Channel(); channel.setId(rs.getString("id")); channel.setType(rs.getString("type")); User creator = new User(); creator.setId(rs.getString("creator_id")); channel.setCreator(creator); User fromUser = new User(); fromUser.setId(rs.getString("from_user_id")); fromUser.setName(rs.getString("from_user_name")); fromUser.setNickname(rs.getString("from_user_nickname")); channel.setFrom(fromUser); User toUser = new User(); toUser.setId(rs.getString("to_user_id")); toUser.setName(rs.getString("to_user_name")); toUser.setNickname(rs.getString("to_user_nickname")); toUser.setOnlineStatus(rs.getString("to_user_online_status")); channel.setTo(toUser); return channel; } return null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); DbUtils.closeResultSet(rs); } } /** * 根据频道id查找频道信息 * @param id * @return */ @Override public Channel getById(String id) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("SELECT ch.id,ch.name,ch.type,ch.purpose,ch.member_count,ch.creator_id,ch.from_user_id,ch.to_user_id,"); sql.append("u1.name AS from_user_name,u1.nickname AS from_user_nickname,u1.online_status AS from_user_online_status,"); sql.append("u2.name AS to_user_name,u2.nickname AS to_user_nickname,u2.online_status AS to_user_online_status FROM "); sql.append(CHANNEL_TABLE).append(" ch LEFT JOIN im_user u1 ON ch.from_user_id=u1.id "); sql.append("LEFT JOIN im_user u2 ON ch.to_user_id=u2.id"); sql.append(" WHERE ch.id=?"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, id); rs = stmt.executeQuery(); while(rs.next()) { Channel channel = new Channel(); channel.setId(id); channel.setName(rs.getString("name")); channel.setType(rs.getString("type")); channel.setPurpose(rs.getString("purpose")); channel.setMemberCount(rs.getInt("member_count")); User creator = new User(); creator.setId(rs.getString("creator_id")); channel.setCreator(creator); if(rs.getString("from_user_id") != null) { User fromUser = new User(); fromUser.setId(rs.getString("from_user_id")); fromUser.setName(rs.getString("from_user_name")); fromUser.setNickname(rs.getString("from_user_nickname")); fromUser.setOnlineStatus(rs.getString("from_user_online_status")); channel.setFrom(fromUser); } if(rs.getString("to_user_id") != null) { User toUser = new User(); toUser.setId(rs.getString("to_user_id")); toUser.setName(rs.getString("to_user_name")); toUser.setNickname(rs.getString("to_user_nickname")); toUser.setOnlineStatus(rs.getString("to_user_online_status")); channel.setTo(toUser); } return channel; } return null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); DbUtils.closeResultSet(rs); } } /** * 更新最后发送消息的时间 * @param id * @param lastPostAt * @return */ public int updateLastPostAt(String id, long lastPostAt) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("UPDATE ").append(CHANNEL_TABLE).append(" SET last_post_at=? WHERE id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setLong(1, lastPostAt); stmt.setString(2, id); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 增加成员数量 * @param id * @param count * @return */ public int increaseMemberCount(String id, int count) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("UPDATE ").append(CHANNEL_TABLE).append(" SET member_count=member_count+? WHERE id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setInt(1, count); stmt.setString(2, id); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 更新字符字段 * @param id * @param field * @param value * @return */ @Override public int updateStringField(String id, String field, String value) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("UPDATE ").append(CHANNEL_TABLE).append(" SET "); sql.append(field).append("=? WHERE id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, value); stmt.setString(2, id); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 更新整型字段 * @param id * @param field * @param value * @return */ @Override public int updateIntegerField(String id, String field, int value) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("UPDATE ").append(CHANNEL_TABLE).append(" SET "); sql.append(field).append("=? WHERE id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setInt(1, value); stmt.setString(2, id); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 删除频道 * @param id * @return */ @Override public int remove(String id) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); long deleteAt = new java.util.Date().getTime(); sql.append("UPDATE ").append(CHANNEL_TABLE).append(" SET delete_at=? WHERE id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setLong(1, deleteAt); stmt.setString(2, id); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 得到频道列表 * @param parameters * @param otherCondition * @param limit * @return */ private List listChannel(Map parameters, String otherCondition, int limit) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } SqlBuildResult sql = this.buildQuerySql(parameters, otherCondition, limit); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.getSql()); this.setPreparedStatmentParameters(stmt, sql.getParameters()); rs = stmt.executeQuery(); List list = new ArrayList(64); while (rs.next()) { Channel channel = new Channel(); channel.setId(rs.getString("id")); channel.setName(rs.getString("name")); channel.setType(rs.getString("type")); if(rs.getString("from_user_id") != null) { User from = new User(); from.setId(rs.getString("from_user_id")); channel.setFrom(from); } if(rs.getString("to_user_id") != null) { User to = new User(); to.setId(rs.getString("to_user_id")); channel.setTo(to); } list.add(channel); } return list; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 构建查询语句 * @param parameters * @param otherCondition * @param limit * @return */ private SqlBuildResult buildQuerySql(Map parameters, String otherCondition, int limit) { StringBuilder sql = new StringBuilder(256); StringBuilder whereSql = new StringBuilder(128); sql.append("SELECT id,name,type,from_user_id,to_user_id FROM ").append(CHANNEL_TABLE); List parameterList = new ArrayList<>(64); if(!parameters.isEmpty()) { Set> entrySet = parameters.entrySet(); for(Entry entry : entrySet) { Parameter param = getParameter(entry.getKey(), entry.getValue()); if(param != null) { parameterList.add(param); whereSql.append(param.getName()).append("=? AND "); } } } if(whereSql.length() > 0) { whereSql.setLength(whereSql.length() - 5); sql.append(" WHERE ").append(whereSql.toString()); } if(otherCondition != null && !otherCondition.trim().isEmpty()) { sql.append(whereSql.length() > 0 ? " AND " : " WHERE "); sql.append(otherCondition); } sql.append(" ORDER BY last_post_at"); if(limit > 0) { sql.append(" LIMIT ").append(limit); } return new SqlBuildResult(sql.toString(), parameterList); } /** * 得到查询参数 * @param name * @param value * @return */ private Parameter getParameter(String name, Object value) { if(name.equals("deleteAt")) { return new Parameter("delete_at", ParameterDataTypeEnum.LONG, Long.parseLong(value.toString())); } if(name.equals("creatorId")) { return new Parameter("creator_id", ParameterDataTypeEnum.STRING, value.toString()); } if(name.equals("type")) { return new Parameter("type", ParameterDataTypeEnum.STRING, value.toString()); } return null; } /** * 构建插入群聊频道语句 * @param channel * @return */ private SqlBuildResult buildInsertGroupChannelSql(Channel channel) { StringBuilder sql = new StringBuilder(256); StringBuilder fields = new StringBuilder(128); StringBuilder values = new StringBuilder(32); List parameters = new ArrayList<>(16); sql.append("INSERT INTO ").append(CHANNEL_TABLE); fields.append("id,name,type,create_at,member_count,creator_id"); values.append("?,?,?,?,?,?"); parameters.add(new Parameter("id", ParameterDataTypeEnum.STRING, channel.getId())); parameters.add(new Parameter("name", ParameterDataTypeEnum.STRING, channel.getName())); parameters.add(new Parameter("type", ParameterDataTypeEnum.STRING, channel.getType())); parameters.add(new Parameter("create_at", ParameterDataTypeEnum.LONG, System.currentTimeMillis())); parameters.add(new Parameter("member_count", ParameterDataTypeEnum.INT, channel.getMemberCount())); parameters.add(new Parameter("creator_id", ParameterDataTypeEnum.STRING, channel.getCreator().getId())); if(channel.getPurpose() != null && !channel.getPurpose().trim().isEmpty()) { fields.append(",purpose"); values.append(",?"); parameters.add(new Parameter("purpose", ParameterDataTypeEnum.STRING, channel.getPurpose().trim())); } if(channel.getFrom() != null && channel.getFrom().getId() != null && !channel.getFrom().getId().trim().isEmpty()) { fields.append(",from_user_id"); values.append(",?"); parameters.add(new Parameter("from_user_id", ParameterDataTypeEnum.STRING, channel.getFrom().getId().trim())); } if(channel.getTo() != null && channel.getTo().getId() != null && !channel.getTo().getId().trim().isEmpty()) { fields.append(",to_user_id"); values.append(",?"); parameters.add(new Parameter("to_user_id", ParameterDataTypeEnum.STRING, channel.getTo().getId().trim())); } sql.append("(").append(fields.toString()).append(")").append(" VALUES(").append(values.toString()).append(")"); return new SqlBuildResult(sql.toString(), parameters); } /** * 构建插入私聊频道语句 * @param channel * @return */ private SqlBuildResult buildInsertPrivateChannelSql(Channel channel) { StringBuilder sql = new StringBuilder(256); StringBuilder fields = new StringBuilder(128); StringBuilder values = new StringBuilder(32); List parameters = new ArrayList<>(16); sql.append("INSERT INTO ").append(CHANNEL_TABLE); fields.append("id,name,type,create_at,member_count,creator_id,from_user_id,to_user_id"); values.append(" SELECT ?,?,?,?,?,?,?,?"); parameters.add(new Parameter("id", ParameterDataTypeEnum.STRING, channel.getId())); parameters.add(new Parameter("name", ParameterDataTypeEnum.STRING, channel.getName())); parameters.add(new Parameter("type", ParameterDataTypeEnum.STRING, channel.getType())); parameters.add(new Parameter("create_at", ParameterDataTypeEnum.LONG, System.currentTimeMillis())); parameters.add(new Parameter("member_count", ParameterDataTypeEnum.INT, channel.getMembers().size())); parameters.add(new Parameter("creator_id", ParameterDataTypeEnum.STRING, channel.getCreator().getId())); parameters.add(new Parameter("from_user_id", ParameterDataTypeEnum.STRING, channel.getFrom().getId().trim())); parameters.add(new Parameter("to_user_id", ParameterDataTypeEnum.STRING, channel.getTo().getId().trim())); if(channel.getPurpose() != null && !channel.getPurpose().trim().isEmpty()) { fields.append(",purpose"); values.append(",?"); parameters.add(new Parameter("purpose", ParameterDataTypeEnum.STRING, channel.getPurpose().trim())); } values.append(" FROM dual ").append(" WHERE NOT EXISTS(SELECT id FROM ").append(CHANNEL_TABLE); values.append(" WHERE (from_user_id=? AND to_user_id=?) OR (from_user_id=? AND to_user_id=?))"); parameters.add(new Parameter("from_user1", ParameterDataTypeEnum.STRING, channel.getFrom().getId().trim())); parameters.add(new Parameter("to_user1", ParameterDataTypeEnum.STRING, channel.getTo().getId().trim())); parameters.add(new Parameter("from_user2", ParameterDataTypeEnum.STRING, channel.getTo().getId().trim())); parameters.add(new Parameter("to_user2", ParameterDataTypeEnum.STRING, channel.getFrom().getId().trim())); sql.append("(").append(fields.toString()).append(")").append(values.toString()); return new SqlBuildResult(sql.toString(), parameters); } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcChannelMemberDAOImpl.java ================================================ package org.leo.im.store.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.leo.im.common.data.Page; import org.leo.im.model.ChannelMember; import org.leo.im.model.User; import org.leo.im.store.connection.ConnectionProvider; import org.leo.im.store.dao.BaseDAO; import org.leo.im.store.dao.ChannelMemberDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.util.DbUtils; /** * 频道成员dao jdbc实现类 * * @author Leo * @date 2018/4/11 */ public class JdbcChannelMemberDAOImpl implements BaseDAO, ChannelMemberDAO { private static final String CHANNEL_MEMBER_TABLE = "im_channel_member"; private static final String USER_TABLE = "im_user"; /** * 添加成员 * * @param channelId * @param members * @return */ @Override public int save(String channelId, List members) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("INSERT INTO ").append(CHANNEL_MEMBER_TABLE).append(" VALUES(?,?,?)"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); for (ChannelMember member : members) { stmt.setString(1, channelId); stmt.setString(2, member.getUser().getId()); stmt.setShort(3, member.getAdmin() ? (short)1 : 0); stmt.addBatch(); } return stmt.executeBatch().length; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 得到成员列表 * * @param channelId * @param username * @param limit * @param offset * @return */ @Override public Page listMember(String channelId, String username, int limit, int offset) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder countSql = new StringBuilder(256); StringBuilder querySql = new StringBuilder(256); countSql.append("SELECT COUNT(*) FROM ").append(CHANNEL_MEMBER_TABLE).append(" cm INNER JOIN "); countSql.append(USER_TABLE).append(" u ON cm.user_id=u.id WHERE cm.channel_id=?"); if(username != null && !username.trim().equals("")) { countSql.append(" AND (u.name LIKE ? OR u.nickname LIKE ?)"); } querySql.append("SELECT u.id,u.name,u.name_first_letter,u.nickname,u.avatar_url,cm.is_admin FROM "); querySql.append(CHANNEL_MEMBER_TABLE).append(" cm INNER JOIN ").append(USER_TABLE).append(" u "); querySql.append("ON cm.user_id=u.id WHERE cm.channel_id=?"); if(username != null && !username.trim().equals("")) { querySql.append(" AND (u.name LIKE ? OR u.nickname LIKE ?)"); } PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(countSql.toString()); stmt.setString(1, channelId); if(username != null && !username.trim().equals("")) { stmt.setString(2, "%" + username + "%"); stmt.setString(3, "%" + username + "%"); } rs = stmt.executeQuery(); int total = 0; if (rs.next()) { total = rs.getInt(1); } List rows = new ArrayList<>(limit); if (total == 0) { return new Page(0, rows); } stmt.close(); stmt = conn.prepareStatement(querySql.toString()); stmt.setString(1, channelId); if(username != null && !username.trim().equals("")) { stmt.setString(2, "%" + username + "%"); stmt.setString(3, "%" + username + "%"); } rs.close(); rs = stmt.executeQuery(); while (rs.next()) { ChannelMember member = new ChannelMember(); User user = new User(); user.setId(rs.getString("id")); user.setName(rs.getString("name")); user.setFirstLetterOfName(rs.getString("name_first_letter")); user.setNickname(rs.getString("nickname")); user.setAvatarUrl(rs.getString("avatar_url")); member.setUser(user); member.setAdmin(rs.getBoolean("is_admin")); rows.add(member); } return new Page(total, rows); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 得到用户是否为频道管理员 * @param userId * @param channelId * @return */ @Override public boolean isAdmin(String userId, String channelId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("SELECT is_admin FROM ").append(CHANNEL_MEMBER_TABLE); sql.append(" WHERE user_id=? AND channel_id=?"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, userId); stmt.setString(2, channelId); rs = stmt.executeQuery(); while(rs.next()) { return rs.getShort("is_admin") == 1; } return false; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 移除成员 * @param channelId * @param memberId * @return */ public int removeMember(String channelId, String memberId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(128); sql.append("DELETE FROM ").append(CHANNEL_MEMBER_TABLE).append(" WHERE channel_id=? AND user_id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, channelId); stmt.setString(2, memberId); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 修改管理员 * @param channelId * @param memberId * @param isAdmin * @return */ @Override public int changeAdmin(String channelId, String memberId, boolean isAdmin) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("UPDATE ").append(CHANNEL_MEMBER_TABLE).append(" SET is_admin=? WHERE channel_id=? AND user_id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setShort(1, (short)(isAdmin ? 1 : 0)); stmt.setString(2, channelId); stmt.setString(3, memberId); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcFileDAOImpl.java ================================================ package org.leo.im.store.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import org.leo.im.model.File; import org.leo.im.store.connection.ConnectionProvider; import org.leo.im.store.dao.BaseDAO; import org.leo.im.store.dao.FileDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.util.DbUtils; /** * 文件dao jdbc实现类 * * @author Leo * @date 2018/6/13 */ public class JdbcFileDAOImpl implements BaseDAO, FileDAO { private final static String FILE_TABLE = "im_file"; /** * 添加文件 * @param file * @return */ @Override public String save(File file) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("INSERT INTO ").append(FILE_TABLE).append("(id,name,extension,size,mime_typ,width,height,path,thumb_width,thumb_height) "); sql.append("VALUES(?,?,?,?,?,?,?,?,?,?)"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); String fileId = createId(); stmt.setString(1, fileId); stmt.setString(2, file.getName()); stmt.setString(3, file.getExtension()); stmt.setInt(4, file.getSize()); stmt.setString(5,file.getMimeType()); stmt.setInt(6, file.getWidth()); stmt.setInt(7, file.getHeight()); stmt.setString(8, file.getPath()); stmt.setShort(9, file.getThumbWidth()); stmt.setShort(10, file.getThumbHeight()); if(stmt.executeUpdate() > 0) { return fileId; } return null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcHideChannelDAOImpl.java ================================================ package org.leo.im.store.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import org.leo.im.store.connection.ConnectionProvider; import org.leo.im.store.dao.BaseDAO; import org.leo.im.store.dao.HideChannelDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.util.DbUtils; public class JdbcHideChannelDAOImpl implements BaseDAO, HideChannelDAO { private static final String HIDE_CHANNEL_TABLE = "im_hide_channel"; /** * 添加隐藏频道 * @param userId * @param channelId */ @Override public int save(String userId, String channelId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(64); sql.append("INSERT INTO ").append(HIDE_CHANNEL_TABLE).append("(user_id,channel_id) VALUES(?,?)"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, userId); stmt.setString(2, channelId); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 删除隐藏频道 * @param userId * @param channelId */ @Override public int remove(String userId, String channelId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(64); sql.append("DELETE FROM ").append(HIDE_CHANNEL_TABLE).append(" WHERE user_id=? AND channel_id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, userId); stmt.setString(2, channelId); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcMessageDAOImpl.java ================================================ package org.leo.im.store.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.leo.im.model.Channel; import org.leo.im.model.File; import org.leo.im.model.Message; import org.leo.im.model.User; import org.leo.im.store.connection.ConnectionProvider; import org.leo.im.store.dao.BaseDAO; import org.leo.im.store.dao.MessageDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.support.BatchSqlBuildResult; import org.leo.im.store.support.Parameter; import org.leo.im.store.support.ParameterDataTypeEnum; import org.leo.im.store.support.SqlBuildResult; import org.leo.im.store.util.DbUtils; /** * 消息dao jdbc实现类 * * @author Leo * @date 2018/4/25 */ public final class JdbcMessageDAOImpl implements BaseDAO, MessageDAO { private static final String MESSAGE_TABLE = "im_message"; private static final String USER_TABLE = "im_user"; private static final String FILE_TABLE = "im_file"; /** * 得到消息列表 * * @param channelId * @param maxCreateTime * @param limit * @return */ @Override public List listMessage(String channelId, long maxCreateAt, int limit) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("SELECT m.id,m.channel_id,m.sender_id,m.create_at,m.type,m.content,m.file_id,"); sql.append("u.name,u.nickname,u.online_status,u.avatar_url,u.name_first_letter,"); sql.append("f.name AS file_name,f.extension,f.size,f.mime_typ,f.width,f.height,f.path,f.thumb_width,f.thumb_height "); sql.append("FROM ").append(MESSAGE_TABLE).append(" m INNER JOIN ").append(USER_TABLE).append(" u "); sql.append("ON m.sender_id=u.id LEFT JOIN ").append(FILE_TABLE).append(" f ON m.file_id=f.id "); sql.append("WHERE m.channel_id=? AND delete_at=0"); if (maxCreateAt > 0) { sql.append(" AND m.create_at 0) { parameterIndex++; stmt.setLong(parameterIndex, maxCreateAt); } parameterIndex++; stmt.setInt(parameterIndex, limit); rs = stmt.executeQuery(); return this.getMessageList(rs, limit); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 根据id得到消息 * * @param id * @return */ @Override public Message getById(long id) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("SELECT m.id,m.channel_id,m.sender_id,m.create_at,m.type,m.content,m.file_id,"); sql.append("u.name,u.nickname,u.online_status,u.avatar_url,name_first_letter,"); sql.append("f.name AS file_name,f.extension,f.size,f.mime_typ,f.width,f.height,f.path,f.thumb_width,f.thumb_height "); sql.append("FROM ").append(MESSAGE_TABLE).append(" m INNER JOIN ").append(USER_TABLE).append(" u "); sql.append("ON m.sender_id=u.id LEFT JOIN ").append(FILE_TABLE).append(" f ON m.file_id=f.id "); sql.append("WHERE m.id=?"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setLong(1, id); rs = stmt.executeQuery(); List list = this.getMessageList(rs, 1); return list.size() > 0 ? list.get(0) : null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 添加消息 * * @param message * @return */ @Override public long save(Message message) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } long id = System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000); message.setId(id); SqlBuildResult sql = this.buildInsertSql(message); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.getSql()); this.setPreparedStatmentParameters(stmt, sql.getParameters()); return stmt.executeUpdate() > 0 ? id : 0; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 批量添加消息 * @param messages * @return */ @Override public int save(List messages) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } for(Message message : messages) { long id = System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000); message.setId(id); } BatchSqlBuildResult sql = this.buildBatchInsertSql(messages); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.getSql()); for(List ps : sql.getParameters()) { this.setPreparedStatmentParameters(stmt, ps); stmt.addBatch(); } return stmt.executeBatch().length; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 删除消息 * @param messageId * @param userId * @return */ @Override public int remove(long messageId, String userId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("UPDATE ").append(MESSAGE_TABLE).append(" SET delete_at=? WHERE id=? AND sender_id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setLong(1, new java.util.Date().getTime()); stmt.setLong(2, messageId); stmt.setString(3, userId); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 构建insert 语句 * * @param message * @return */ private SqlBuildResult buildInsertSql(Message message) { StringBuilder sql = new StringBuilder(256); StringBuilder fields = new StringBuilder(128); StringBuilder values = new StringBuilder(16); List ps = new ArrayList<>(8); fields.append("id,channel_id,sender_id,create_at"); values.append("?,?,?,?"); ps.add(new Parameter("id", ParameterDataTypeEnum.LONG, message.getId())); ps.add(new Parameter("channel_id", ParameterDataTypeEnum.STRING, message.getChannel().getId())); ps.add(new Parameter("sender_id", ParameterDataTypeEnum.STRING, message.getSender().getId())); ps.add(new Parameter("create_at", ParameterDataTypeEnum.LONG, message.getCreateAt())); if (message.getType() != null && !message.getType().trim().isEmpty()) { fields.append(",type"); values.append(",?"); ps.add(new Parameter("type", ParameterDataTypeEnum.STRING, message.getType())); } if (message.getContent() != null && !message.getContent().trim().isEmpty()) { fields.append(",content"); values.append(",?"); ps.add(new Parameter("content", ParameterDataTypeEnum.STRING, message.getContent())); } if (message.getFile() != null) { fields.append(",file_id"); values.append(",?"); ps.add(new Parameter("file_id", ParameterDataTypeEnum.STRING, message.getFile().getId())); } sql.append("INSERT INTO ").append(MESSAGE_TABLE).append("(").append(fields.toString()).append(") VALUES("); sql.append(values.toString()).append(")"); return new SqlBuildResult(sql.toString(), ps); } /** * 构建批量insert语句 * @param messages * @return */ private BatchSqlBuildResult buildBatchInsertSql(List messages) { StringBuilder sql = new StringBuilder(256); StringBuilder fields = new StringBuilder(128); StringBuilder values = new StringBuilder(16); List> pss = new ArrayList<>(messages.size()); fields.append("id,channel_id,sender_id,create_at"); values.append("?,?,?,?"); if (messages.get(0).getType() != null && !messages.get(0).getType().trim().isEmpty()) { fields.append(",type"); values.append(",?"); } if (messages.get(0).getContent() != null && !messages.get(0).getContent().trim().isEmpty()) { fields.append(",content"); values.append(",?"); } if (messages.get(0).getFile() != null) { fields.append(",file_id"); values.append(",?"); } sql.append("INSERT INTO ").append(MESSAGE_TABLE).append("(").append(fields.toString()).append(") VALUES("); sql.append(values.toString()).append(")"); for(Message message : messages) { List ps = new ArrayList<>(8); ps.add(new Parameter("id", ParameterDataTypeEnum.LONG, message.getId())); ps.add(new Parameter("channel_id", ParameterDataTypeEnum.STRING, message.getChannel().getId())); ps.add(new Parameter("sender_id", ParameterDataTypeEnum.STRING, message.getSender().getId())); ps.add(new Parameter("create_at", ParameterDataTypeEnum.LONG, message.getCreateAt())); if (message.getType() != null && !message.getType().trim().isEmpty()) { ps.add(new Parameter("type", ParameterDataTypeEnum.STRING, message.getType())); } if (message.getContent() != null && !message.getContent().trim().isEmpty()) { ps.add(new Parameter("content", ParameterDataTypeEnum.STRING, message.getContent())); } if (message.getFile() != null) { ps.add(new Parameter("file_id", ParameterDataTypeEnum.STRING, message.getFile().getId())); } pss.add(ps); } return new BatchSqlBuildResult(sql.toString(), pss); } /** * 得到消息列表 * * @param rs * @param count * @return * @throws SQLException */ private List getMessageList(ResultSet rs, int count) throws SQLException { List list = new ArrayList<>(count); while (rs.next()) { Message message = new Message(); message.setId(rs.getLong("id")); message.setCreateAt(rs.getLong("create_at")); message.setType(rs.getString("type")); message.setContent(rs.getString("content")); if(rs.getString("file_id") != null) { File file = new File(); file.setId(rs.getString("file_id")); file.setName(rs.getString("file_name")); file.setExtension(rs.getString("extension")); file.setSize(rs.getInt("size")); file.setMimeType(rs.getString("mime_typ")); file.setWidth(rs.getInt("width")); file.setHeight(rs.getInt("height")); file.setPath(rs.getString("path")); file.setThumbWidth(rs.getShort("thumb_width")); file.setThumbHeight(rs.getShort("thumb_height")); message.setFile(file); } Channel channel = new Channel(); channel.setId(rs.getString("channel_id")); message.setChannel(channel); User sender = new User(); sender.setId(rs.getString("sender_id")); sender.setName(rs.getString("name")); sender.setNickname(rs.getString("nickname")); sender.setOnlineStatus(rs.getString("online_status")); sender.setAvatarUrl(rs.getString("avatar_url")); sender.setFirstLetterOfName(rs.getString("name_first_letter")); message.setSender(sender); list.add(message); } return list; } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcUnreadMessageCountDAOImpl.java ================================================ package org.leo.im.store.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.leo.im.store.connection.ConnectionProvider; import org.leo.im.store.dao.BaseDAO; import org.leo.im.store.dao.UnreadMessageCountDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.support.Parameter; import org.leo.im.store.support.ParameterDataTypeEnum; import org.leo.im.store.util.DbUtils; /** * 未读消息数量dao jdbc实现类 * * @author Leo * @date 2018/4/26 */ public final class JdbcUnreadMessageCountDAOImpl implements BaseDAO, UnreadMessageCountDAO { private static final String UNREAD_MESSAGE_COUNT_TABLE = "im_unread_message_count"; /** * 添加未读消息 * @param userId * @param channelId * @param total * @return */ @Override public int save(String userId, String channelId, short total) { return this.batchSave(new String[] { userId }, channelId, total); } /** * 批量添加未读消息 * @param userIds * @param channelId * @param total * @return */ @Override public int batchSave(String[] userIds, String channelId, short total) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } String sql = "INSERT INTO " + UNREAD_MESSAGE_COUNT_TABLE + "(user_id,channel_id,total) VALUES(?,?,?)"; PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql); for(String userId : userIds) { stmt.setString(1, userId); stmt.setString(2, channelId); stmt.setShort(3, total); stmt.addBatch(); } return stmt.executeBatch().length; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 更新未读消息数量 * @param userId * @param channelId * @param total * @return */ @Override public int update(String userId, String channelId, short total) { return batchUpdate(new String[]{ userId }, channelId, total); } /** * 批量更新未读消息数量 * @param userIds * @param channelId * @param total * @return */ @Override public int batchUpdate(String[] userIds, String channelId, short total) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } String sql = "UPDATE " + UNREAD_MESSAGE_COUNT_TABLE + " SET total=? WHERE user_id=? AND channel_id=? AND total<99"; PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql); for(String userId : userIds) { stmt.setShort(1, total); stmt.setString(2, userId); stmt.setString(3, channelId); stmt.addBatch(); } return stmt.executeBatch().length; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 增加未读消息数 * @param userId * @param channelId * @param total * @return */ @Override public int increase(String userId, String channelId, short total) { return this.batchIncrease(new String[] { userId }, channelId, total); } /** * 批量增加未读消息数 * @param userIds * @param channelId * @param total * @return */ @Override public int batchIncrease(String[] userIds, String channelId, short total) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } String sql = "UPDATE " + UNREAD_MESSAGE_COUNT_TABLE + " SET total=total+? WHERE user_id=? AND channel_id=? AND total<99"; PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql); for(String userId : userIds) { stmt.setShort(1, total); stmt.setString(2, userId); stmt.setString(3, channelId); stmt.addBatch(); } return stmt.executeBatch().length; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 增加群组频道的未读消息数 * @param channelId * @param exceptiveUserIds * @param total * @return */ @Override public int increaseGroupChannel(String channelId, String[] exceptiveUserIds, short total) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); List ps = new ArrayList<>(8); sql.append("UPDATE ").append(UNREAD_MESSAGE_COUNT_TABLE).append(" SET total=total+? WHERE channel_id=? AND total<99"); ps.add(new Parameter("total", ParameterDataTypeEnum.SHORT, total)); ps.add(new Parameter("channel_id", ParameterDataTypeEnum.STRING, channelId)); StringBuilder notInBuilder = new StringBuilder(32); if(exceptiveUserIds != null && exceptiveUserIds.length > 0) { for(int i = 0; i < exceptiveUserIds.length; i++) { notInBuilder.append(notInBuilder.length() > 0 ? "," : "").append("?"); ps.add(new Parameter("exceptive_user_id" + i, ParameterDataTypeEnum.STRING, exceptiveUserIds[i])); } sql.append(" AND user_id NOT IN(").append(notInBuilder.toString()).append(")"); } PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); this.setPreparedStatmentParameters(stmt, ps); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 减少未读消息数量 * @param userId * @param channelId * @param total * @return */ public int decrease(String userId, String channelId, short total) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("UPDATE ").append(UNREAD_MESSAGE_COUNT_TABLE).append(" SET total=total-? "); sql.append("WHERE channel_id=? AND user_id=? AND total>=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, channelId); stmt.setString(2, userId); stmt.setShort(3, total); stmt.setShort(4, total); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 删除未读消息数量 * @param channelId * @param userId * @return */ @Override public int remove(String channelId, String userId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(128); sql.append("DELETE FROM ").append(UNREAD_MESSAGE_COUNT_TABLE).append(" WHERE channel_id=? AND user_id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, channelId); stmt.setString(2, userId); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcUserChannelDAOImpl.java ================================================ package org.leo.im.store.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.leo.im.model.Channel; import org.leo.im.model.User; import org.leo.im.model.UserChannel; import org.leo.im.store.connection.ConnectionProvider; import org.leo.im.store.dao.BaseDAO; import org.leo.im.store.dao.UserChannelDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.support.Parameter; import org.leo.im.store.support.ParameterDataTypeEnum; import org.leo.im.store.util.DbUtils; /** * 用户频道dao jdbc实现类 * * @author Leo * @date 2018/4/20 */ public final class JdbcUserChannelDAOImpl implements BaseDAO, UserChannelDAO { private static final String USER_CHANNEL_TABLE = "im_user_channel"; private static final String CHANNEL_TABLE = "im_channel"; private static final String USER_TABLE = "im_user"; private static final String UNREAD_MESSAGE_COUNT_TABLE = "im_unread_message_count"; private static final String HIDE_CHANNEL_TABLE = "im_hide_channel"; /** * 根据用户id得到用户频道列表 * @param userId * @param type * @param limit * @return */ @Override public List listByUserId(String userId, String type, int limit) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("SELECT c.id,c.name,c.type,c.creator_id,uc.display_name,uc.to_user_id,u.online_status,umc.total "); sql.append("FROM ").append(CHANNEL_TABLE).append(" c INNER JOIN ").append(USER_CHANNEL_TABLE); sql.append(" uc ON c.id=uc.channel_id LEFT JOIN ").append(USER_TABLE); sql.append(" u ON uc.to_user_id=u.id INNER JOIN ").append(UNREAD_MESSAGE_COUNT_TABLE).append(" umc "); sql.append("ON umc.user_id=uc.user_id AND c.id=umc.channel_id "); sql.append("WHERE c.delete_at=0 AND uc.user_id=? "); sql.append("AND NOT EXISTS(SELECT channel_id FROM ").append(HIDE_CHANNEL_TABLE).append(" WHERE user_id=uc.user_id AND channel_id=c.id) "); if(type != null && !type.trim().isEmpty()) { sql.append(" AND c.type=? "); } sql.append("ORDER BY c.last_post_at DESC LIMIT ?"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, userId); if(type != null && !type.trim().isEmpty()) { stmt.setString(2, type); stmt.setInt(3, limit); } else { stmt.setInt(2, limit); } rs = stmt.executeQuery(); List list = new ArrayList<>(64); while(rs.next()) { UserChannel userChannel = new UserChannel(); Channel channel = new Channel(); channel.setId(rs.getString("id")); channel.setName(rs.getString("name")); channel.setType(rs.getString("type")); User creator = new User(); creator.setId(rs.getString("creator_id")); channel.setCreator(creator); userChannel.setChannel(channel); userChannel.setDisplayName(rs.getString("display_name")); User toUser = new User(); toUser.setId(rs.getString("to_user_id")); toUser.setOnlineStatus(rs.getString("online_status")); userChannel.setToUser(toUser); userChannel.setUnreadMessageCount(rs.getShort("total")); list.add(userChannel); } return list; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 添加用户频道 * @param userChannel * @return */ @Override public int save(UserChannel userChannel) { List list = new ArrayList<>(1); list.add(userChannel); return batchSave(list); } /** * 批量添加用户频道 * @param userChannels * @return */ @Override public int batchSave(List userChannels) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } String sql = "INSERT INTO " + USER_CHANNEL_TABLE + "(user_id,channel_id,display_name,to_user_id) " + "VALUES(?,?,?,?)"; PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql); for(UserChannel userChannel : userChannels) { stmt.setString(1, userChannel.getUser().getId()); stmt.setString(2, userChannel.getChannel().getId()); stmt.setString(3, userChannel.getDisplayName()); stmt.setString(4, userChannel.getToUser() == null ? null : userChannel.getToUser().getId()); stmt.addBatch(); } return stmt.executeBatch().length; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 得到用户频道 * @param userId * @param channelId * @return */ @Override public UserChannel get(String userId, String channelId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("SELECT c.id,c.name,c.type,c.purpose,c.member_count,uc.to_user_id,c.creator_id,uc.display_name,u.online_status,umc.total "); sql.append("FROM ").append(CHANNEL_TABLE).append(" c INNER JOIN ").append(USER_CHANNEL_TABLE).append(" uc "); sql.append("ON c.id=uc.channel_id LEFT JOIN ").append(USER_TABLE).append(" u ON uc.to_user_id=u.id "); sql.append("INNER JOIN ").append(UNREAD_MESSAGE_COUNT_TABLE).append(" umc ON umc.user_id=uc.user_id AND umc.channel_id=c.id "); sql.append("WHERE uc.user_id=? AND uc.channel_id=?"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, userId); stmt.setString(2, channelId); rs = stmt.executeQuery(); while(rs.next()) { UserChannel userChannel = new UserChannel(); Channel channel = new Channel(); channel.setId(rs.getString("id")); channel.setName(rs.getString("name")); channel.setType(rs.getString("type")); channel.setPurpose(rs.getString("purpose")); channel.setMemberCount(rs.getInt("member_count")); User creator = new User(); creator.setId(rs.getString("creator_id")); channel.setCreator(creator); userChannel.setChannel(channel); userChannel.setDisplayName(rs.getString("display_name")); User toUser = new User(); toUser.setId(rs.getString("to_user_id")); toUser.setOnlineStatus(rs.getString("online_status")); userChannel.setToUser(toUser); userChannel.setUnreadMessageCount(rs.getShort("total")); return userChannel; } return null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 更新用户频道显示名 * @param channelId * @param userId * @param displayName * @return */ @Override public int updateDisplayName(String channelId, String userId, String displayName) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); List ps = new ArrayList<>(4); sql.append("UPDATE ").append(USER_CHANNEL_TABLE).append(" SET display_name=? WHERE channel_id=? AND user_id=?"); ps.add(new Parameter("display_name", ParameterDataTypeEnum.STRING, displayName)); ps.add(new Parameter("channel_id", ParameterDataTypeEnum.STRING, channelId)); ps.add(new Parameter("user_id", ParameterDataTypeEnum.STRING, userId)); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); this.setPreparedStatmentParameters(stmt, ps); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 删除用户频道 * @param userId * @param channelId * @return */ @Override public int remove(String userId, String channelId) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(128); sql.append("DELETE FROM ").append(USER_CHANNEL_TABLE).append(" WHERE user_id=? AND channel_id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, userId); stmt.setString(2, channelId); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 根据名称得到用户频道列表 * @param userId * @param name * @param type * @return */ public List listByName(String userId, String name, String type) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(256); sql.append("SELECT c.id,c.name,c.type,c.creator_id,uc.display_name,uc.to_user_id,u.online_status,umc.total "); sql.append("FROM ").append(CHANNEL_TABLE).append(" c INNER JOIN ").append(USER_CHANNEL_TABLE); sql.append(" uc ON c.id=uc.channel_id LEFT JOIN ").append(USER_TABLE); sql.append(" u ON uc.to_user_id=u.id INNER JOIN ").append(UNREAD_MESSAGE_COUNT_TABLE).append(" umc "); sql.append("ON umc.user_id=uc.user_id AND c.id=umc.channel_id "); sql.append("WHERE c.delete_at=0 AND uc.user_id=? AND (c.name LIKE ? OR uc.display_name LIKE ?)"); if(type != null && !type.trim().isEmpty()) { sql.append(" AND c.type=?"); } sql.append(" ORDER BY c.last_post_at DESC"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, userId); stmt.setString(2, "%" + name + "%"); stmt.setString(3, "%" + name + "%"); if(type != null && !type.trim().isEmpty()) { stmt.setString(4, type); } rs = stmt.executeQuery(); List list = new ArrayList<>(64); while(rs.next()) { UserChannel userChannel = new UserChannel(); Channel channel = new Channel(); channel.setId(rs.getString("id")); channel.setName(rs.getString("name")); channel.setType(rs.getString("type")); User creator = new User(); creator.setId(rs.getString("creator_id")); channel.setCreator(creator); userChannel.setChannel(channel); userChannel.setDisplayName(rs.getString("display_name")); User toUser = new User(); toUser.setId(rs.getString("to_user_id")); toUser.setOnlineStatus(rs.getString("online_status")); userChannel.setToUser(toUser); userChannel.setUnreadMessageCount(rs.getShort("total")); list.add(userChannel); } return list; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcUserDAOImpl.java ================================================ package org.leo.im.store.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; import org.leo.im.common.data.Page; import org.leo.im.model.User; import org.leo.im.store.connection.ConnectionProvider; import org.leo.im.store.dao.BaseDAO; import org.leo.im.store.dao.UserDAO; import org.leo.im.store.exception.DAOException; import org.leo.im.store.support.Parameter; import org.leo.im.store.support.ParameterDataTypeEnum; import org.leo.im.store.support.SqlBuildResult; import org.leo.im.store.util.DbUtils; import org.leo.im.store.util.FirstLetterUtil; /** * 用户dao jdbc实现类 * * @author Leo * @date 2018/4/10 */ public class JdbcUserDAOImpl implements BaseDAO, UserDAO { private final static String USER_TABLE = "im_user"; private final static String CHANNEL_MEMBER_TABLE = "im_channel_member"; /** * 添加用户 * * @param user * @return 用户id */ @Override public String save(User user) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } SqlBuildResult sql = this.buildInsertSql(user); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.getSql()); this.setPreparedStatmentParameters(stmt, sql.getParameters()); if (stmt.executeUpdate() > 0) { return user.getId(); } return null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 根据用户名得到用户 * * @param name * @return */ @Override public User getByName(String name) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } String sql = "SELECT id,nickname,salt,password,name_first_letter,avatar_url FROM " + USER_TABLE + " WHERE name=? AND locked=0"; PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql); stmt.setString(1, name); rs = stmt.executeQuery(); while (rs.next()) { User user = new User(); user.setId(rs.getString("id")); user.setName(name); user.setNickname(rs.getString("nickname")); user.setSalt(rs.getString("salt")); user.setPassword(rs.getString("password")); user.setFirstLetterOfName(rs.getString("name_first_letter")); user.setAvatarUrl(rs.getString("avatar_url")); return user; } return null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 根据id得到用户 * * @param id * @return */ @Override public User getById(String id) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } String sql = "SELECT name,name_first_letter,nickname,avatar_url,online_status FROM " + USER_TABLE + " WHERE id=? AND locked=0"; PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql); stmt.setString(1, id); rs = stmt.executeQuery(); while (rs.next()) { User user = new User(); user.setId(id); user.setName(rs.getString("name")); user.setFirstLetterOfName(rs.getString("name_first_letter")); user.setNickname(rs.getString("nickname")); user.setAvatarUrl(rs.getString("avatar_url")); user.setOnlineStatus(rs.getString("online_status")); return user; } return null; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 根据名称或昵称得到用户列表 * @param name * @param limit * @param offset * @return */ @Override public Page listByNameOrNickname(String name, int limit, int offset) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } SqlBuildResult sql = this.buildPagingQueryResult(name, limit, offset); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.getCountSql()); this.setPreparedStatmentParameters(stmt, sql.getParameters()); rs = stmt.executeQuery(); int total = 0; if(rs.next()) { total = rs.getInt(1); } List rows = new ArrayList<>(limit); if(total == 0) { return new Page(total, rows); } stmt.close(); stmt = conn.prepareStatement(sql.getSql()); this.setPreparedStatmentParameters(stmt, sql.getParameters()); rs.close(); rs = stmt.executeQuery(); while (rs.next()) { User user = new User(); user.setId(rs.getString("id")); user.setName(rs.getString("name")); user.setFirstLetterOfName(rs.getString("name_first_letter")); user.setNickname(rs.getString("nickname")); user.setAvatarUrl(rs.getString("avatar_url")); rows.add(user); } return new Page(total, rows); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 更新用户 * @param user * @return */ @Override public int update(User user) { return update(user, true); } /** * 更新用户 * @param user * @param updateNullValueField * @return */ @Override public int update(User user, boolean updateNullValueField) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } SqlBuildResult sql = this.buildUpdateSql(user, updateNullValueField); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.getSql()); this.setPreparedStatmentParameters(stmt, sql.getParameters()); return stmt.executeUpdate(); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 判断用户名是否已经存在 * @param userId * @param username * @return */ @Override public boolean usernameExists(String userId, String username) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(128); sql.append("SELECT id FROM ").append(USER_TABLE).append(" WHERE name=?"); if(userId != null && !userId.trim().isEmpty()) { sql.append(" AND id<>?"); } sql.append(" LIMIT 1"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, username); if(userId != null && !userId.trim().isEmpty()) { stmt.setString(2, userId); } rs = stmt.executeQuery(); if(rs != null) { while(rs.next()) { return true; } } return false; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 分页查询非组成员 * @param channelId * @param username * @param limit * @param offset * @return */ @Override public Page listNonMembers(String channelId, String username, int limit, int offset) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder countSql = new StringBuilder(256); countSql.append("SELECT COUNT(*) FROM ").append(USER_TABLE).append(" u "); countSql.append("WHERE NOT EXISTS(SELECT user_id FROM ").append(CHANNEL_MEMBER_TABLE).append(" WHERE user_id=u.id AND channel_id=?)"); countSql.append(" AND u.id<>'00000000000000000000000000000000'"); if(username != null && !username.trim().equals("")) { countSql.append(" AND (u.name LIKE ? OR u.nickname LIKE ?)"); } StringBuilder querySql = new StringBuilder(256); querySql.append("SELECT u.id,u.name,u.name_first_letter,u.nickname,u.avatar_url "); querySql.append("FROM im_user u "); querySql.append("WHERE NOT EXISTS(SELECT user_id FROM ").append(CHANNEL_MEMBER_TABLE).append(" WHERE user_id=u.id AND channel_id=?)"); querySql.append(" AND u.id<>'00000000000000000000000000000000'"); if(username != null && !username.trim().equals("")) { querySql.append(" AND (u.name LIKE ? OR u.nickname LIKE ?)"); } querySql.append(" LIMIT ? OFFSET ?"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement(countSql.toString()); stmt.setString(1, channelId); if(username != null && !username.trim().equals("")) { stmt.setString(2, "%" + username + "%"); stmt.setString(3, "%" + username + "%"); } rs = stmt.executeQuery(); int total = 0; if (rs.next()) { total = rs.getInt(1); } List rows = new ArrayList<>(limit); if (total == 0) { return new Page(0, rows); } stmt.close(); stmt = conn.prepareStatement(querySql.toString()); int position = 1; stmt.setString(position, channelId); if(username != null && !username.trim().equals("")) { stmt.setString(2, "%" + username + "%"); stmt.setString(3, "%" + username + "%"); position += 2; } position++; stmt.setInt(position, limit); position++; stmt.setInt(position, offset); rs.close(); rs = stmt.executeQuery(); while (rs.next()) { User user = new User(); user.setId(rs.getString("id")); user.setName(rs.getString("name")); user.setFirstLetterOfName(rs.getString("name_first_letter")); user.setNickname(rs.getString("nickname")); user.setAvatarUrl(rs.getString("avatar_url")); rows.add(user); } return new Page(total, rows); } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeResultSet(rs); DbUtils.closeStatement(stmt); } } /** * 批量设置用户下线 * @param userIds * @return */ @Override public int offline(Set userIds) { Connection conn = ConnectionProvider.getConnection(); if (conn == null) { throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION); } StringBuilder sql = new StringBuilder(128); sql.append("UPDATE ").append(USER_TABLE).append(" SET online_status=? WHERE id=?"); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(sql.toString()); for(String userId : userIds) { stmt.setString(1, "offline"); stmt.setString(2, userId); stmt.addBatch(); } return stmt.executeBatch().length; } catch (SQLException e) { throw new DAOException(e); } finally { DbUtils.closeStatement(stmt); } } /** * 构建insert语句 * * @param user * @return */ private SqlBuildResult buildInsertSql(User user) { String id = this.createId(); user.setId(id); StringBuilder sql = new StringBuilder(256); StringBuilder values = new StringBuilder(64); sql.append("INSERT INTO ").append(USER_TABLE).append("(id,name,salt,password,created_at,name_first_letter"); values.append("?,?,?,?,?,?"); List ps = new ArrayList(); ps.add(new Parameter("id", ParameterDataTypeEnum.STRING, id)); ps.add(new Parameter("name", ParameterDataTypeEnum.STRING, user.getName())); ps.add(new Parameter("salt", ParameterDataTypeEnum.STRING, user.getSalt())); ps.add(new Parameter("password", ParameterDataTypeEnum.STRING, user.getPassword())); ps.add(new Parameter("created_at", ParameterDataTypeEnum.DATETIME, new Date())); String firstLetterOfName = FirstLetterUtil.getFirstLetter(user.getNickname() != null && !user.getNickname().trim().isEmpty() ? user.getNickname().trim() : user.getName().trim()); ps.add(new Parameter("name_first_letter", ParameterDataTypeEnum.STRING, firstLetterOfName)); if (user.getNickname() != null && !user.getNickname().trim().isEmpty()) { sql.append(",nickname"); values.append(",?"); ps.add(new Parameter("nickname", ParameterDataTypeEnum.STRING, user.getNickname().trim())); } sql.append(") VALUES(").append(values.toString()).append(")"); return new SqlBuildResult(sql.toString(), ps); } /** * 构建分页查询语句 * @param name * @param limit * @param offset * @return */ private SqlBuildResult buildPagingQueryResult(String name, int limit, int offset) { StringBuilder countSql = new StringBuilder(256); StringBuilder querySql = new StringBuilder(256); StringBuilder whereSql = new StringBuilder(64); List ps = new ArrayList<>(3); countSql.append("SELECT COUNT(*) FROM ").append(USER_TABLE); querySql.append("SELECT id,name,name_first_letter,nickname,avatar_url FROM ").append(USER_TABLE); whereSql.append(" id<>'00000000000000000000000000000000'"); if(name != null && !name.trim().isEmpty()) { whereSql.append(" AND (name LIKE ? OR nickname LIKE ?)"); ps.add(new Parameter("name", ParameterDataTypeEnum.STRING, "%" + name + "%")); ps.add(new Parameter("nickname", ParameterDataTypeEnum.STRING, "%" + name + "%")); } if(whereSql.length() > 0) { countSql.append(" WHERE ").append(whereSql.toString()); querySql.append(" WHERE ").append(whereSql.toString()); } if(limit > 0) { querySql.append(" LIMIT ").append(limit); } if(offset >= 0) { querySql.append(" OFFSET ").append(offset); } return new SqlBuildResult(countSql.toString(), querySql.toString(), ps); } /** * 构建更新语句 * @param user * @param updateNullValueField * @return */ private SqlBuildResult buildUpdateSql(User user, boolean updateNullValueField) { StringBuilder sql = new StringBuilder(256); List ps = new ArrayList<>(16); sql.append("UPDATE ").append(USER_TABLE).append(" SET "); boolean hasUpdateField = false; if((user.getName() != null && !user.getName().trim().isEmpty()) || updateNullValueField) { sql.append("name=?"); ps.add(new Parameter("name", ParameterDataTypeEnum.STRING, user.getName())); hasUpdateField = true; } if((user.getNickname() != null && !user.getNickname().trim().isEmpty()) || updateNullValueField) { sql.append(hasUpdateField ? "," : "").append("nickname=?"); ps.add(new Parameter("nickname", ParameterDataTypeEnum.STRING, user.getNickname())); hasUpdateField = true; } if(ps.size() > 0) { String firstLetterOfName = FirstLetterUtil.getFirstLetter(user.getNickname() != null && !user.getNickname().trim().isEmpty() ? user.getNickname().trim() : user.getName().trim()); sql.append(hasUpdateField ? "," : "").append("name_first_letter=?"); ps.add(new Parameter("name_first_letter", ParameterDataTypeEnum.STRING, firstLetterOfName)); hasUpdateField = true; } if((user.getSalt() != null && !user.getSalt().trim().isEmpty()) || updateNullValueField) { sql.append(hasUpdateField ? "," : "").append("salt=?"); ps.add(new Parameter("salt", ParameterDataTypeEnum.STRING, user.getSalt())); hasUpdateField = true; } if((user.getPassword() != null && !user.getPassword().trim().isEmpty()) || updateNullValueField) { sql.append(hasUpdateField ? "," : "").append("password=?"); ps.add(new Parameter("password", ParameterDataTypeEnum.STRING, user.getPassword())); hasUpdateField = true; } if(user.getLocked() != null || updateNullValueField) { sql.append(hasUpdateField ? "," : "").append("locked=?"); hasUpdateField = true; } if((user.getAvatarUrl() != null && !user.getAvatarUrl().trim().isEmpty()) || updateNullValueField) { sql.append(hasUpdateField ? "," : "").append("avatar_url=?"); ps.add(new Parameter("avatar_url", ParameterDataTypeEnum.STRING, user.getAvatarUrl())); hasUpdateField = true; } if(user.getLastPostAt() > 0) { sql.append(hasUpdateField ? "," : "").append("last_post_at=?"); ps.add(new Parameter("last_post_at", ParameterDataTypeEnum.LONG, user.getLastPostAt())); hasUpdateField = true; } if((user.getOnlineStatus() != null && !user.getOnlineStatus().trim().isEmpty()) || updateNullValueField) { sql.append(hasUpdateField ? "," : "").append("online_status=?"); ps.add(new Parameter("online_status", ParameterDataTypeEnum.STRING, user.getOnlineStatus())); } sql.append(" WHERE id=?"); ps.add(new Parameter("id", ParameterDataTypeEnum.STRING, user.getId())); return new SqlBuildResult(sql.toString(), ps); } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/datasource/ConnectionPool.java ================================================ package org.leo.im.store.datasource; import java.sql.Connection; /** * 数据库连接池接口 * * @author Leo * @date 2018/3/19 */ public interface ConnectionPool { /** * 得到数据库连接 * @return */ Connection getConnection(); } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/datasource/impl/DruidConnectionPool.java ================================================ package org.leo.im.store.datasource.impl; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import org.leo.im.store.datasource.ConnectionPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; /** * druid数据库连接池实现类 * * @author Leo * @date 2018/3/19 */ public class DruidConnectionPool implements ConnectionPool { private final static Logger logger = LoggerFactory.getLogger(DruidConnectionPool.class); // Druid数据源 private static DruidDataSource dataSource = null; /** * 私有构造函数,避免外部创建实例。 */ private DruidConnectionPool() { } private static class InstanceHolder { static { Properties druidProps = new Properties(); Properties sysProps = System.getProperties(); sysProps.forEach((key, value) -> { if(key.toString().startsWith("db.pool.")) { druidProps.put(key.toString().replace("db.pool.", ""), value); } }); try { dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(druidProps); } catch (Exception e) { logger.error(e.getMessage()); } } private static DruidConnectionPool instance = new DruidConnectionPool(); } /** * 得到单例实例 * * @return */ public static DruidConnectionPool getInstance() { return InstanceHolder.instance; } /** * 得到数据库连接 * * @return */ @Override public Connection getConnection() { try { return dataSource == null ? null : dataSource.getConnection(); } catch (SQLException e) { return null; } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/exception/DAOException.java ================================================ package org.leo.im.store.exception; /** * dao异常类 * * @author Leo * @date 2018/3/19 */ public final class DAOException extends RuntimeException { private static final long serialVersionUID = 4070326969626499752L; public DAOException() { } public DAOException(String message) { super(message); } public DAOException(String message, Throwable cause) { super(message, cause); } public DAOException(Throwable cause) { super(cause); } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/factory/DAOFactory.java ================================================ package org.leo.im.store.factory; import org.leo.im.store.dao.ChannelDAO; import org.leo.im.store.dao.ChannelMemberDAO; import org.leo.im.store.dao.FileDAO; import org.leo.im.store.dao.HideChannelDAO; import org.leo.im.store.dao.MessageDAO; import org.leo.im.store.dao.UnreadMessageCountDAO; import org.leo.im.store.dao.UserChannelDAO; import org.leo.im.store.dao.UserDAO; import org.leo.im.store.dao.impl.JdbcChannelDAOImpl; import org.leo.im.store.dao.impl.JdbcChannelMemberDAOImpl; import org.leo.im.store.dao.impl.JdbcFileDAOImpl; import org.leo.im.store.dao.impl.JdbcHideChannelDAOImpl; import org.leo.im.store.dao.impl.JdbcMessageDAOImpl; import org.leo.im.store.dao.impl.JdbcUnreadMessageCountDAOImpl; import org.leo.im.store.dao.impl.JdbcUserChannelDAOImpl; import org.leo.im.store.dao.impl.JdbcUserDAOImpl; /** * dao工厂类 * * @author Leo * @date 2018/3/30 */ public final class DAOFactory { /** * 创建UserDAO的实例 * @return */ public static UserDAO createUserDAO() { switch (System.getProperty("dao.type")) { case "jdbc": return new JdbcUserDAOImpl(); default: return new JdbcUserDAOImpl(); } } /** * 创建ChannelDAO的实例 * @return */ public static ChannelDAO createChannelDAO() { switch (System.getProperty("dao.type")) { case "jdbc": return new JdbcChannelDAOImpl(); default: return new JdbcChannelDAOImpl(); } } /** * 创建ChannelMemberDAO实例 * @return */ public static ChannelMemberDAO createChannelMemberDAO() { switch (System.getProperty("dao.type")) { case "jdbc": return new JdbcChannelMemberDAOImpl(); default: return new JdbcChannelMemberDAOImpl(); } } /** * 创建UserChannelDAO的实例 * @return */ public static UserChannelDAO createUserChannelDAO() { switch (System.getProperty("dao.type")) { case "jdbc": return new JdbcUserChannelDAOImpl(); default: return new JdbcUserChannelDAOImpl(); } } /** * 创建UnreadMessageCountDAO的实例 * @return */ public static UnreadMessageCountDAO createUnreadMessageCountDAO() { switch (System.getProperty("dao.type")) { case "jdbc": return new JdbcUnreadMessageCountDAOImpl(); default: return new JdbcUnreadMessageCountDAOImpl(); } } /** * 创建MessageDAO的实例 * @return */ public static MessageDAO createMessageDAO() { switch (System.getProperty("dao.type")) { case "jdbc": return new JdbcMessageDAOImpl(); default: return new JdbcMessageDAOImpl(); } } /** * 创建HideChannelDAO的实例 * @return */ public static HideChannelDAO createHideChannelDAO() { switch (System.getProperty("dao.type")) { case "jdbc": return new JdbcHideChannelDAOImpl(); default: return new JdbcHideChannelDAOImpl(); } } /** * 创建FileDAO的实例 * @return */ public static FileDAO createFileDAO() { switch (System.getProperty("dao.type")) { case "jdbc": return new JdbcFileDAOImpl(); default: return new JdbcFileDAOImpl(); } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/support/BatchSqlBuildResult.java ================================================ package org.leo.im.store.support; import java.util.List; /** * 批量SQL构建结果类 * * @author Leo * @date 2018/5/29 */ public final class BatchSqlBuildResult { private String sql; private List> parameters; public BatchSqlBuildResult(String sql, List> parameters) { this.sql = sql; this.parameters = parameters; } public String getSql() { return this.sql; } public List> getParameters() { return this.parameters; } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/support/Parameter.java ================================================ package org.leo.im.store.support; /** * 数据库参数类 * * @author Leo * @date 2018/3/19 */ public final class Parameter { private String name; private ParameterDataTypeEnum dataType; private Object value; public Parameter(String name, ParameterDataTypeEnum dataType, Object value) { this.name = name; this.dataType = dataType; this.value = value; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public ParameterDataTypeEnum getDataType() { return this.dataType; } public void setDataType(ParameterDataTypeEnum dataType) { this.dataType = dataType; } public Object getValue() { return this.value; } public void setValue(Object value) { this.value = value; } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/support/ParameterDataTypeEnum.java ================================================ package org.leo.im.store.support; /** * 参数数据类型枚举 * * @author Leo * @date 2018/3/19 */ public enum ParameterDataTypeEnum { SHORT, INT, LONG, FLOAT, DOUBLE, STRING, DATE, DATETIME, TIMESTAMP, BOOLEAN, OBJECT } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/support/SqlBuildResult.java ================================================ package org.leo.im.store.support; import java.util.List; /** * SQL构建结果类 * * @author Leo * @date 2018/3/20 */ public final class SqlBuildResult { private String countSql; private String sql; private List parameters; public SqlBuildResult(String sql, List parameters) { this.sql = sql; this.parameters = parameters; } public SqlBuildResult(String countSql, String sql, List parameters) { this.countSql = countSql; this.sql = sql; this.parameters = parameters; } public String getCountSql() { return this.countSql; } public String getSql() { return this.sql; } public List getParameters() { return this.parameters; } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/util/DbUtils.java ================================================ package org.leo.im.store.util; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 数据库工具类 * * @author Leo * @date 2018/3/19 */ public class DbUtils { private final static Logger logger = LoggerFactory.getLogger(DbUtils.class); /** * 关闭ResultSet * * @param rs */ public static void closeResultSet(ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { logger.error(e.getMessage()); } } } /** * 关闭Statement * * @param stmt */ public static void closeStatement(Statement stmt) { if (stmt != null) { try { stmt.close(); } catch (SQLException e) { logger.error(e.getMessage()); } } } } ================================================ FILE: leo-im-store/src/main/java/org/leo/im/store/util/FirstLetterUtil.java ================================================ package org.leo.im.store.util; /** * 去字符串首字母工具类 * * @author Leo * @date 2018/3/30 */ public class FirstLetterUtil { private static int BEGIN = 45217; private static int END = 63486; // 按照声母表示,这个表是在GB2312中的出现的第一个汉字,也就是说“啊”是代表首字母a的第一个汉字。 // i, u, v都不做声母, 自定规则跟随前面的字母 private static char[] chartable = { '啊', '芭', '擦', '搭', '蛾', '发', '噶', '哈', '哈', '击', '喀', '垃', '妈', '拿', '哦', '啪', '期', '然', '撒', '塌', '塌', '塌', '挖', '昔', '压', '匝', }; // 二十六个字母区间对应二十七个端点 // GB2312码汉字区间十进制表示 private static int[] table = new int[27]; // 对应首字母区间表 private static char[] initialtable = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'h', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 't', 't', 'w', 'x', 'y', 'z', }; // 初始化 static { for (int i = 0; i < 26; i++) { table[i] = gbValue(chartable[i]);// 得到GB2312码的首字母区间端点表,十进制。 } table[26] = END;// 区间表结尾 } /** * 根据一个包含汉字的字符串返回一个汉字拼音首字母的字符串 最重要的一个方法,思路如下:一个个字符读入、判断、输出 */ // public static String getFirstLetter(String sourceStr) { // String result = ""; // String str = sourceStr.toLowerCase(); // int StrLength = str.length(); // int i; // try { // for (i = 0; i < StrLength; i++) { // result += Char2Initial(str.charAt(i)); // } // } catch (Exception e) { // result = ""; // } // return result; // } /** * 根据一个包含汉字的字符串返回一个汉字拼音首字母的字符串 最重要的一个方法,思路如下:一个个字符读入、判断、输出 * * @param source * @return */ public static String getFirstLetter(String source) { try { return String.valueOf(char2Initial(source.charAt(0))); } catch (Exception e) { return ""; } } /** * 输入字符,得到他的声母,英文字母返回对应的大写字母,其他非简体汉字返回 '0' * * @param ch * @return */ private static char char2Initial(char ch) { // 对英文字母的处理:小写字母转换为大写,大写的直接返回 if (ch >= 'a' && ch <= 'z') { return ch; } if (ch >= 'A' && ch <= 'Z') { return ch; } // 对非英文字母的处理:转化为首字母,然后判断是否在码表范围内, // 若不是,则直接返回。 // 若是,则在码表内的进行判断。 int gb = gbValue(ch);// 汉字转换首字母 if ((gb < BEGIN) || (gb > END))// 在码表区间之前,直接返回 { return ch; } int i; for (i = 0; i < 26; i++) {// 判断匹配码表区间,匹配到就break,判断区间形如“[,)” if ((gb >= table[i]) && (gb < table[i + 1])) { break; } } if (gb == END) {// 补上GB2312区间最右端 i = 25; } return initialtable[i]; // 在码表区间中,返回首字母 } /** * 取出汉字的编码 cn 汉字 * * @param ch * @return */ private static int gbValue(char ch) { // 将一个汉字(GB2312)转换为十进制表示。 String str = new String(); str += ch; try { byte[] bytes = str.getBytes("GB2312"); if (bytes.length < 2) { return 0; } return (bytes[0] << 8 & 0xff00) + (bytes[1] & 0xff); } catch (Exception e) { return 0; } } } ================================================ FILE: leo-im-store/src/test/java/org/leo/im/store/AppTest.java ================================================ package org.leo.im.store; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-store/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: mysql-connector-java-8.0.11.jar protobuf-java-2.6.0.jar dr uid-1.1.9.jar leo-im-model-1.0.jar leo-im-common-1.0.jar slf4j-api-1. 7.25.jar logback-classic-1.2.3.jar logback-core-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-store/target/classes/META-INF/maven/org.leo.im/leo-im-store/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:13 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-store m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-store artifactId=leo-im-store ================================================ FILE: leo-im-store/target/classes/META-INF/maven/org.leo.im/leo-im-store/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-store leo-im-store http://maven.apache.org mysql mysql-connector-java ${mysql-connector-java.version} com.alibaba druid ${druid.version} org.leo.im leo-im-model ${parent.version} org.leo.im leo-im-common ${parent.version} ================================================ FILE: leo-im-store/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:13 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-store ================================================ FILE: leo-im-store/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ org\leo\im\store\dao\BaseDAO$1.class org\leo\im\store\connection\impl\PoolConnectionFactory$1.class org\leo\im\store\datasource\impl\DruidConnectionPool$1.class ================================================ FILE: leo-im-store/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\util\FirstLetterUtil.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\support\Parameter.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\ChannelMemberDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\FileDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\UserDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\support\BatchSqlBuildResult.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\UserChannelDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\impl\JdbcFileDAOImpl.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\connection\ConnectionFactory.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\impl\JdbcUserChannelDAOImpl.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\impl\JdbcUserDAOImpl.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\connection\impl\PoolConnectionFactory.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\support\ParameterDataTypeEnum.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\connection\ConnectionProvider.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\ChannelDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\MessageDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\exception\DAOException.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\HideChannelDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\impl\JdbcChannelDAOImpl.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\impl\JdbcChannelMemberDAOImpl.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\impl\JdbcMessageDAOImpl.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\datasource\impl\DruidConnectionPool.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\impl\JdbcUnreadMessageCountDAOImpl.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\util\DbUtils.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\UnreadMessageCountDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\datasource\ConnectionPool.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\BaseDAO.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\dao\impl\JdbcHideChannelDAOImpl.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\factory\DAOFactory.java F:\Develop\open-source\leo-im-server\leo-im-store\src\main\java\org\leo\im\store\support\SqlBuildResult.java ================================================ FILE: leo-im-store/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-store/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-store\src\test\java\org\leo\im\store\AppTest.java ================================================ FILE: leo-im-store/target/surefire-reports/TEST-org.leo.im.store.AppTest.xml ================================================ ================================================ FILE: leo-im-store/target/surefire-reports/org.leo.im.store.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.store.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.093 sec ================================================ FILE: leo-im-util/.classpath ================================================ ================================================ FILE: leo-im-util/.project ================================================ leo-im-util org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature ================================================ FILE: leo-im-util/.settings/org.eclipse.core.resources.prefs ================================================ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 ================================================ FILE: leo-im-util/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 ================================================ FILE: leo-im-util/.settings/org.eclipse.m2e.core.prefs ================================================ activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 ================================================ FILE: leo-im-util/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-util leo-im-util http://maven.apache.org UTF-8 cglib cglib ${cglib.version} io.jsonwebtoken jjwt ${jjwt.version} ================================================ FILE: leo-im-util/src/main/java/org/leo/im/util/BeanUtils.java ================================================ package org.leo.im.util; import net.sf.cglib.beans.BeanCopier; /** * Java Bean 工具类 * * @author Leo * @date 2018/3/20 */ public final class BeanUtils { /** * 拷贝属性 * @param source * @param target */ public static void copyProperties(Object source, Object target) { BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false); copier.copy(source, target, null); } } ================================================ FILE: leo-im-util/src/main/java/org/leo/im/util/JwtUtils.java ================================================ package org.leo.im.util; import java.util.Base64; import java.util.Date; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; /** * Jwt工具类 * * @author Leo * @date 2018/3/28 */ public class JwtUtils { /** * 生成加密key * * @param secret * @return */ private static SecretKey generalKey(String secret) { byte[] encodedKey = Base64.getDecoder().decode(secret); SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } /** * 创建jwt * * @param subject * @param secret * @param ttlMillis * @return */ public static String createJWT(String subject, String secret, long ttlMillis) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); SecretKey key = generalKey(secret); JwtBuilder builder = Jwts.builder().setId("jwt").setIssuedAt(now).setSubject(subject) .signWith(signatureAlgorithm, key); if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } return builder.compact(); } /** * 解密jwt * * @param jwt * @param secret * @return * @throws Exception */ public static Claims parseJWT(String jwt, String secret) { SecretKey key = generalKey(secret); Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody(); return claims; } } ================================================ FILE: leo-im-util/src/test/java/org/leo/im/util/AppTest.java ================================================ package org.leo.im.util; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: leo-im-util/target/classes/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Built-By: Administrator Class-Path: cglib-3.2.6.jar asm-6.0.jar ant-1.9.6.jar ant-launcher-1.9 .6.jar jjwt-0.9.0.jar jackson-databind-2.8.9.jar jackson-annotations- 2.8.0.jar jackson-core-2.8.9.jar slf4j-api-1.7.25.jar logback-classic -1.2.3.jar logback-core-1.2.3.jar Build-Jdk: 1.8.0_131 Created-By: Maven Integration for Eclipse Main-Class: org.leo.im.starter.App ================================================ FILE: leo-im-util/target/classes/META-INF/maven/org.leo.im/leo-im-util/pom.properties ================================================ #Generated by Maven Integration for Eclipse #Tue Jun 19 09:17:15 CST 2018 version=1.0 groupId=org.leo.im m2e.projectName=leo-im-util m2e.projectLocation=F\:\\Develop\\open-source\\leo-im-server\\leo-im-util artifactId=leo-im-util ================================================ FILE: leo-im-util/target/classes/META-INF/maven/org.leo.im/leo-im-util/pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 leo-im-util leo-im-util http://maven.apache.org UTF-8 cglib cglib ${cglib.version} io.jsonwebtoken jjwt ${jjwt.version} ================================================ FILE: leo-im-util/target/maven-archiver/pom.properties ================================================ #Generated by Maven #Tue Jun 12 16:14:14 CST 2018 version=1.0 groupId=org.leo.im artifactId=leo-im-util ================================================ FILE: leo-im-util/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ ================================================ FILE: leo-im-util/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-util\src\main\java\org\leo\im\util\BeanUtils.java F:\Develop\open-source\leo-im-server\leo-im-util\src\main\java\org\leo\im\util\JwtUtils.java ================================================ FILE: leo-im-util/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst ================================================ ================================================ FILE: leo-im-util/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst ================================================ F:\Develop\open-source\leo-im-server\leo-im-util\src\test\java\org\leo\im\util\AppTest.java ================================================ FILE: leo-im-util/target/surefire-reports/TEST-org.leo.im.util.AppTest.xml ================================================ ================================================ FILE: leo-im-util/target/surefire-reports/org.leo.im.util.AppTest.txt ================================================ ------------------------------------------------------------------------------- Test set: org.leo.im.util.AppTest ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.094 sec ================================================ FILE: pom.xml ================================================ 4.0.0 org.leo.im leo-im 1.0 pom leo-im-server http://maven.apache.org UTF-8 1.8 3.7.0 3.1.0 3.1.0 4.1.24.Final 0.9.0 1.2.47 3.2.6 8.0.11 1.1.9 2.9.0 5.1.1 org.slf4j slf4j-api 1.7.25 ch.qos.logback logback-classic 1.2.3 junit junit 4.12 test org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} conf/** true org.leo.im.starter.App org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} ${java.version} UTF-8 org.apache.maven.plugins maven-assembly-plugin ${maven-assembly-plugin.version} make-assembly package single false org.leo.im.starter.App assemble/package.xml leo-im-api leo-im-model leo-im-store leo-im-starter leo-im-service leo-im-http leo-im-util leo-im-socket leo-im-notification leo-im-api-provider leo-im-common leo-im-migration