├── .gitignore ├── .idea ├── .gitignore ├── checkstyle-idea.xml ├── compiler.xml ├── encodings.xml ├── jarRepositories.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── webrtc-meetings.iml ├── README.md ├── meeting.png ├── meeting ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── io │ │ └── springstudent │ │ └── meeting │ │ ├── Application.java │ │ ├── account │ │ ├── bean │ │ │ └── BaseAccount.java │ │ ├── controller │ │ │ └── AccountController.java │ │ ├── dao │ │ │ ├── AccountDao.java │ │ │ └── impl │ │ │ │ └── AccountDaoImpl.java │ │ ├── pojo │ │ │ └── Account.java │ │ └── service │ │ │ ├── AccountService.java │ │ │ └── impl │ │ │ └── AccountServiceImpl.java │ │ ├── chat │ │ ├── bean │ │ │ └── ChatInfo.java │ │ ├── controller │ │ │ └── ChatController.java │ │ ├── dao │ │ │ ├── ChatDao.java │ │ │ └── ChatDaoImpl.java │ │ ├── pojo │ │ │ └── Chat.java │ │ └── service │ │ │ ├── ChatService.java │ │ │ └── ChatServiceImpl.java │ │ ├── common │ │ ├── bean │ │ │ ├── LoginResult.java │ │ │ └── RoomEvent.java │ │ ├── config │ │ │ ├── DataSourceConfig.java │ │ │ ├── RedisConfig.java │ │ │ ├── ShiroConfig.java │ │ │ ├── SwaggerConfig.java │ │ │ └── ThreadPoolConfig.java │ │ ├── controller │ │ │ └── LoginController.java │ │ ├── exception │ │ │ ├── ApplicationException.java │ │ │ ├── DataNotFoundException.java │ │ │ ├── ParamInvalidException.java │ │ │ └── ResultException.java │ │ ├── express │ │ │ └── ruler │ │ │ │ ├── And.java │ │ │ │ ├── Chain.java │ │ │ │ ├── Condition.java │ │ │ │ ├── Content.java │ │ │ │ ├── None.java │ │ │ │ └── Or.java │ │ ├── handler │ │ │ ├── GlobalExceptionHandler.java │ │ │ └── ResponseBodyHandler.java │ │ ├── http │ │ │ ├── CorsAndXssFilter.java │ │ │ ├── ResponseResult.java │ │ │ ├── UserAuthenticationFilter.java │ │ │ └── XssRequestWrapper.java │ │ ├── service │ │ │ ├── LoginService.java │ │ │ └── impl │ │ │ │ └── LoginServiceImpl.java │ │ ├── shiro │ │ │ ├── HeaderWebSessionManager.java │ │ │ ├── ShiroVariable.java │ │ │ ├── UserPrincipal.java │ │ │ └── UserRealm.java │ │ ├── signal │ │ │ ├── ISignalListener.java │ │ │ ├── SignalListener.java │ │ │ ├── SignalServerConfig.java │ │ │ └── SignalServerStarter.java │ │ └── util │ │ │ ├── EmptyUtils.java │ │ │ ├── HttpRequestUtils.java │ │ │ ├── SpringUtil.java │ │ │ └── SubjectUtils.java │ │ └── room │ │ ├── bean │ │ └── RoomInfo.java │ │ ├── controller │ │ └── RoomController.java │ │ ├── dao │ │ ├── RoomBoardDao.java │ │ ├── RoomBoardDaoImpl.java │ │ ├── RoomBoardTxtDao.java │ │ ├── RoomBoardTxtDaoImpl.java │ │ ├── RoomDao.java │ │ ├── RoomDaoImpl.java │ │ ├── RoomHistoryDao.java │ │ └── RoomHistoryDaoImpl.java │ │ ├── pojo │ │ ├── Room.java │ │ ├── RoomBoard.java │ │ ├── RoomBoardTxt.java │ │ └── RoomHistory.java │ │ └── service │ │ ├── RoomService.java │ │ └── RoomServiceImpl.java │ └── resources │ ├── application.properties │ ├── log4j2.xml │ └── meeting.sql ├── meeting4u ├── .gitignore ├── .npmrc ├── babel.config.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── public │ ├── favicon.ico │ ├── index.html │ ├── photo.html │ ├── record.html │ └── record2.html ├── src │ ├── App.vue │ ├── assets │ │ ├── logo.png │ │ └── meeting.png │ ├── components │ │ └── play.vue │ ├── main.js │ ├── router │ │ └── index.js │ └── views │ │ ├── historypage.vue │ │ ├── home.vue │ │ ├── homepage.vue │ │ └── meetingpage.vue └── vue.config.js ├── meetingdetail.png ├── meetinghistory.png └── meetingstart.png /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | disconf.properties 12 | 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | # Package Files # 21 | *.jar 22 | *.war 23 | *.ear 24 | 25 | # Emqx Files # 26 | *.lck 27 | 28 | *.classpath 29 | !/.project 30 | .project 31 | *.settings 32 | target/ 33 | .idea/ 34 | .DS_Store 35 | .idea 36 | overlays/ 37 | .gradle/ 38 | build/ 39 | bin/ 40 | *.iml 41 | *.log 42 | *.log.gz 43 | out/ 44 | MANIFEST.MF 45 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /../../../../:\intellij\webrtc-meetings\.idea/dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/webrtc-meetings.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 基于srs+socketio+webrtc实现的会议系统 2 | 注:本项目是个人学习项目,可能存在着各种Bug,如果您有意愿修复,欢迎提PR. 3 | 4 | ### 启动服务端meeting 5 | 1.将meeting.sql导入至自己的数据库 6 | 7 | 2.修改application.properties配置中mysql和redis的配置 8 | 9 | 3.运行Application.java 10 | 11 | ### 启动srs 12 | 参考 13 | 14 | ### 启动客户端meeting4u 15 | 1.修改meeting4u/src/main.js如下配置 16 | 17 | ``` 18 | Vue.prototype.$srsServerAPIURL = 'http://172.16.1.72:1985/'; 19 | Vue.prototype.$srsServerRTCURL = 'webrtc://172.16.1.72:8080/live/'; 20 | Vue.prototype.$meetingServerURL = "http://172.16.1.72:9898/meeting/" 21 | Vue.prototype.$meetingWebsocketURL = "http://172.16.1.72:9999/" 22 | ``` 23 | 24 | 2.进入meeting4u目录执行npm install 25 | 26 | 3.执行npm run serve启动客户端 27 | 28 | ### 浏览器访问meeting4u地址 29 | 1.由于浏览器安全限制必须采用https访问系统才可以获取到系统摄像头和录音设备,如果没有https证书条件,建议使用访问系统 30 | 31 | 2.系统测试账号如下 32 | ``` 33 | zhouning 111111 34 | laoning 111111 35 | xiaoning 111111 36 | ``` 37 | 38 | ### 系统部分截图 39 | ![NiceTool](meetingstart.png) 40 | ![NiceTool](meeting.png) 41 | ![NiceTool](meetinghistory.png) 42 | ![NiceTool](meetingdetail.png) 43 | 44 | -------------------------------------------------------------------------------- /meeting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringStudent/webrtc-meetings/4e5c2786fc9b186892430397533371bcd809a914/meeting.png -------------------------------------------------------------------------------- /meeting/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | disconf.properties 12 | 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | # Package Files # 21 | *.jar 22 | *.war 23 | *.ear 24 | 25 | *.classpath 26 | !/.project 27 | .project 28 | *.settings 29 | target/ 30 | .idea/ 31 | .DS_Store 32 | .idea 33 | overlays/ 34 | .gradle/ 35 | build/ 36 | bin/ 37 | *.iml 38 | *.log 39 | *.log.gz 40 | out/ 41 | MANIFEST.MF 42 | -------------------------------------------------------------------------------- /meeting/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | io.springstudent.meeting 8 | meeting 9 | 1.0.0 10 | 南苑會議系統 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.5.3 16 | 17 | 18 | 19 | UTF-8 20 | 1.8 21 | 3.0.0 22 | 3.7 23 | 1.2.28 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-log4j2 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-data-jdbc 41 | 42 | 43 | mysql 44 | mysql-connector-java 45 | 46 | 47 | com.alibaba 48 | druid 49 | 1.1.23 50 | 51 | 52 | io.github.springstudent 53 | GyJdbc 54 | 1.4.1.RELEASE 55 | 56 | 57 | 58 | org.projectlombok 59 | lombok 60 | true 61 | 62 | 63 | 64 | com.alibaba 65 | fastjson 66 | ${fastjson.version} 67 | 68 | 69 | 70 | io.springfox 71 | springfox-boot-starter 72 | ${swagger.version} 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-starter-aop 77 | 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-starter-data-redis 82 | 83 | 84 | redis.clients 85 | jedis 86 | 87 | 88 | 89 | cn.hutool 90 | hutool-all 91 | 5.4.1 92 | 93 | 94 | 95 | org.apache.shiro 96 | shiro-spring-boot-starter 97 | 1.7.1 98 | 99 | 100 | org.apache.commons 101 | commons-lang3 102 | ${common.lang3.version} 103 | 104 | 105 | org.crazycake 106 | shiro-redis 107 | 2.6.0 108 | 109 | 110 | org.apache.shiro 111 | shiro-core 112 | 113 | 114 | 115 | 116 | 117 | com.google.guava 118 | guava 119 | 30.1-jre 120 | 121 | 122 | 123 | io.springfox 124 | springfox-boot-starter 125 | 3.0.0 126 | 127 | 128 | 129 | com.corundumstudio.socketio 130 | netty-socketio 131 | 1.7.17 132 | 133 | 134 | 135 | 136 | 137 | 138 | org.springframework.boot 139 | spring-boot-maven-plugin 140 | 141 | 142 | 143 | org.projectlombok 144 | lombok 145 | 146 | 147 | 148 | 149 | 150 | org.apache.maven.plugins 151 | maven-resources-plugin 152 | 3.1.0 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/Application.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/account/bean/BaseAccount.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.account.bean; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class BaseAccount { 8 | @ApiModelProperty("用户名") 9 | private String username; 10 | @ApiModelProperty("真实名称") 11 | private String realname; 12 | @ApiModelProperty("密码") 13 | private String password; 14 | } 15 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/account/controller/AccountController.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.account.controller; 2 | 3 | import io.springstudent.meeting.account.bean.BaseAccount; 4 | import io.springstudent.meeting.account.service.AccountService; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.web.bind.annotation.*; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | 10 | import javax.annotation.Resource; 11 | 12 | /** 13 | * @author zhouning 14 | * @date 2023/02/27 17:41 15 | */ 16 | @RestController 17 | @RequestMapping("/account") 18 | public class AccountController { 19 | 20 | private static final Logger logger = LogManager.getLogger(AccountController.class); 21 | 22 | @Resource 23 | private AccountService accountService; 24 | 25 | @PostMapping("/add") 26 | @ApiOperation(value = "添加账号", notes = "添加账号") 27 | public void add(@RequestBody BaseAccount baseAccount) throws Exception { 28 | try { 29 | accountService.add(baseAccount); 30 | } catch (Exception e) { 31 | logger.error("add error,baseAccount={}", baseAccount, e); 32 | throw e; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/account/dao/AccountDao.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.account.dao; 2 | 3 | 4 | import com.gysoft.jdbc.dao.EntityDao; 5 | import io.springstudent.meeting.account.pojo.Account; 6 | 7 | public interface AccountDao extends EntityDao { 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/account/dao/impl/AccountDaoImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.account.dao.impl; 2 | 3 | import com.gysoft.jdbc.dao.EntityDaoImpl; 4 | import io.springstudent.meeting.account.dao.AccountDao; 5 | import io.springstudent.meeting.account.pojo.Account; 6 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 7 | import org.springframework.jdbc.core.JdbcTemplate; 8 | import org.springframework.stereotype.Repository; 9 | 10 | import javax.annotation.Resource; 11 | 12 | @Repository 13 | public class AccountDaoImpl extends EntityDaoImplimplements AccountDao { 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/account/pojo/Account.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.account.pojo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class Account { 13 | 14 | private String id; 15 | 16 | private String pwd; 17 | 18 | private String realname; 19 | 20 | private String username; 21 | 22 | private String salt; 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/account/service/AccountService.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.account.service; 2 | 3 | import io.springstudent.meeting.account.bean.BaseAccount; 4 | import io.springstudent.meeting.account.pojo.Account; 5 | 6 | public interface AccountService { 7 | 8 | Account findByUsername(String username) throws Exception; 9 | 10 | void add(BaseAccount baseAccount) throws Exception; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/account/service/impl/AccountServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.account.service.impl; 2 | 3 | import cn.hutool.core.util.IdUtil; 4 | import com.gysoft.jdbc.bean.Criteria; 5 | import io.springstudent.meeting.account.bean.BaseAccount; 6 | import io.springstudent.meeting.account.dao.AccountDao; 7 | import io.springstudent.meeting.account.pojo.Account; 8 | import io.springstudent.meeting.account.service.AccountService; 9 | import io.springstudent.meeting.common.exception.ParamInvalidException; 10 | import io.springstudent.meeting.common.util.SubjectUtils; 11 | 12 | import org.apache.shiro.crypto.hash.Md5Hash; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import javax.annotation.Resource; 17 | 18 | @Service 19 | public class AccountServiceImpl implements AccountService { 20 | 21 | @Resource 22 | private AccountDao accountDao; 23 | 24 | @Override 25 | public Account findByUsername(String username) throws Exception { 26 | return accountDao.queryOne(new Criteria().where("username",username)); 27 | } 28 | 29 | @Override 30 | @Transactional(rollbackFor = Exception.class) 31 | public void add(BaseAccount baseAccount) throws Exception { 32 | String salt = IdUtil.fastSimpleUUID().substring(0, 6); 33 | Md5Hash md5Hash = new Md5Hash(baseAccount.getPassword(), salt, 1024); 34 | accountDao.save(Account.builder().username(baseAccount.getUsername()).realname(baseAccount.getRealname()) 35 | .pwd(md5Hash.toHex()).salt(salt).build()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/chat/bean/ChatInfo.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.chat.bean; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author zhouning 7 | * @date 2023/04/25 16:17 8 | */ 9 | @Data 10 | public class ChatInfo { 11 | private Integer type; 12 | private String content; 13 | private String sender; 14 | private String sendTime; 15 | private String roomCode; 16 | } 17 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/chat/controller/ChatController.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.chat.controller; 2 | 3 | import io.springstudent.meeting.chat.bean.ChatInfo; 4 | import io.springstudent.meeting.chat.service.ChatService; 5 | import io.springstudent.meeting.room.controller.RoomController; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.annotation.Resource; 13 | import java.util.List; 14 | 15 | /** 16 | * @author zhouning 17 | * @date 2023/04/25 16:15 18 | */ 19 | @RestController 20 | @RequestMapping("/chat") 21 | public class ChatController { 22 | 23 | private static final Logger logger = LogManager.getLogger(RoomController.class); 24 | 25 | @Resource 26 | private ChatService chatService; 27 | 28 | @GetMapping("/chatInfos") 29 | public List chatInfos(String roomCode)throws Exception{ 30 | try { 31 | return chatService.chatInfos(roomCode); 32 | }catch (Exception e){ 33 | logger.error("chatInfos error,roomCode={}",roomCode,e); 34 | throw e; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/chat/dao/ChatDao.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.chat.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDao; 4 | import io.springstudent.meeting.chat.pojo.Chat; 5 | 6 | /** 7 | * @author zhouning 8 | * @date 2023/04/19 13:09 9 | */ 10 | public interface ChatDao extends EntityDao { 11 | } 12 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/chat/dao/ChatDaoImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.chat.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDaoImpl; 4 | import io.springstudent.meeting.chat.pojo.Chat; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * @author zhouning 9 | * @date 2023/04/19 13:10 10 | */ 11 | @Repository 12 | public class ChatDaoImpl extends EntityDaoImpl implements ChatDao { 13 | } 14 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/chat/pojo/Chat.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.chat.pojo; 2 | 3 | import com.gysoft.jdbc.annotation.Table; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author zhouning 11 | * @date 2023/04/19 13:08 12 | */ 13 | @Data 14 | @Table(name = "chat") 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class Chat { 19 | 20 | private Integer id; 21 | 22 | private Integer type; 23 | 24 | private String content; 25 | 26 | private String sender; 27 | 28 | private Long sendTime; 29 | 30 | private String roomCode; 31 | } 32 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/chat/service/ChatService.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.chat.service; 2 | 3 | import io.springstudent.meeting.chat.bean.ChatInfo; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author zhouning 9 | * @date 2023/04/25 16:15 10 | */ 11 | public interface ChatService { 12 | 13 | List chatInfos(String roomCode)throws Exception; 14 | } 15 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/chat/service/ChatServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.chat.service; 2 | 3 | import cn.hutool.core.bean.BeanUtil; 4 | import com.gysoft.jdbc.bean.Criteria; 5 | import com.gysoft.jdbc.bean.Sort; 6 | import io.springstudent.meeting.chat.bean.ChatInfo; 7 | import io.springstudent.meeting.chat.dao.ChatDao; 8 | import org.apache.commons.lang.time.DateFormatUtils; 9 | import org.springframework.stereotype.Service; 10 | 11 | import javax.annotation.Resource; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * @author zhouning 17 | * @date 2023/04/25 16:15 18 | */ 19 | @Service 20 | public class ChatServiceImpl implements ChatService { 21 | 22 | @Resource 23 | private ChatDao chatDao; 24 | 25 | @Override 26 | public List chatInfos(String roomCode) throws Exception { 27 | return chatDao.queryWithCriteria(new Criteria().where("roomCode",roomCode).orderBy(new Sort("sendTime","ASC"))) 28 | .stream().map(chat -> { 29 | ChatInfo chatInfo = BeanUtil.copyProperties(chat,ChatInfo.class); 30 | chatInfo.setSendTime(DateFormatUtils.format(chat.getSendTime(),"yyyy-MM-dd HH:mm:ss")); 31 | return chatInfo; 32 | }).collect(Collectors.toList()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/bean/LoginResult.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.bean; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class LoginResult { 7 | 8 | private String username; 9 | 10 | private String realname; 11 | 12 | private String token; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/bean/RoomEvent.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.bean; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author zhouning 7 | * @date 2023/04/21 10:01 8 | */ 9 | @Data 10 | public class RoomEvent { 11 | 12 | private String eventName; 13 | 14 | private String roomCode; 15 | 16 | private String roomName; 17 | 18 | private String username; 19 | } 20 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/config/DataSourceConfig.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.config; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import lombok.Data; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Primary; 9 | 10 | import javax.sql.DataSource; 11 | import java.sql.SQLException; 12 | 13 | @Data 14 | @Configuration 15 | @ConfigurationProperties(prefix = "spring.datasource") 16 | public class DataSourceConfig { 17 | 18 | private String type; 19 | private String driverClassName; 20 | private String url; 21 | private String username; 22 | private String password; 23 | 24 | @Bean 25 | @ConfigurationProperties("spring.datasource.druid") 26 | public DruidConfig getDruidConfig() { 27 | return new DruidConfig(); 28 | } 29 | 30 | @Data 31 | public static class DruidConfig { 32 | private String filters; 33 | private String connectionProperties; 34 | } 35 | 36 | @Bean 37 | @Primary 38 | public DataSource druidDataSource() throws SQLException { 39 | DruidDataSource dataSource = new DruidDataSource(); 40 | DruidConfig druidConfig; 41 | dataSource.setDbType(type); 42 | dataSource.setDriverClassName(driverClassName); 43 | dataSource.setUrl(url); 44 | dataSource.setUsername(username); 45 | dataSource.setPassword(password); 46 | druidConfig = getDruidConfig(); 47 | dataSource.setFilters(druidConfig.filters); 48 | dataSource.setConnectionProperties(druidConfig.connectionProperties); 49 | return dataSource; 50 | 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.config; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import io.springstudent.meeting.common.shiro.ShiroVariable; 5 | import io.springstudent.meeting.common.util.EmptyUtils; 6 | import org.crazycake.shiro.RedisManager; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.data.redis.connection.RedisConnectionFactory; 11 | import org.springframework.data.redis.connection.RedisStandaloneConfiguration; 12 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 13 | import org.springframework.data.redis.core.RedisTemplate; 14 | import org.springframework.data.redis.serializer.StringRedisSerializer; 15 | 16 | @Configuration 17 | public class RedisConfig { 18 | @Value(value = "${spring.redis.host}") 19 | private String host; 20 | @Value(value = "${spring.redis.port}") 21 | private int port; 22 | @Value(value = "${spring.redis.password}") 23 | private String password; 24 | @Value(value = "${spring.redis.database0}") 25 | private int database0; 26 | 27 | //配置工厂 28 | public RedisConnectionFactory connectionFactory(int dbIndex) { 29 | RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host, port); 30 | if (StrUtil.isNotEmpty(password)) { 31 | redisStandaloneConfiguration.setPassword(password); 32 | } 33 | redisStandaloneConfiguration.setDatabase(dbIndex); 34 | JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); 35 | jedisConnectionFactory.afterPropertiesSet(); 36 | return jedisConnectionFactory; 37 | } 38 | 39 | @Bean("redisTemplate") 40 | public RedisTemplate redisTemplate() { 41 | RedisTemplate redisTemplate = new RedisTemplate<>(); 42 | redisTemplate.setDefaultSerializer(new StringRedisSerializer()); 43 | redisTemplate.setConnectionFactory(connectionFactory(database0)); 44 | return redisTemplate; 45 | } 46 | 47 | @Bean("shiroRedisManager") 48 | public RedisManager shiroRedisManager() { 49 | RedisManager redisManager = new RedisManager(); 50 | redisManager.setHost(host); 51 | redisManager.setPort(port); 52 | redisManager.setDatabase(database0); 53 | if(EmptyUtils.isNotEmpty(password)){ 54 | redisManager.setPassword(password); 55 | } 56 | redisManager.setExpire(ShiroVariable.SESSION_TIMEOUT); 57 | return redisManager; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/config/ShiroConfig.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.config; 2 | 3 | import io.springstudent.meeting.common.shiro.HeaderWebSessionManager; 4 | import io.springstudent.meeting.common.http.CorsAndXssFilter; 5 | import io.springstudent.meeting.common.http.UserAuthenticationFilter; 6 | import io.springstudent.meeting.common.shiro.UserRealm; 7 | import org.apache.shiro.mgt.DefaultSecurityManager; 8 | import org.apache.shiro.session.mgt.SessionManager; 9 | import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 10 | import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 11 | import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 12 | import org.springframework.beans.factory.annotation.Qualifier; 13 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.core.Ordered; 17 | import org.crazycake.shiro.RedisCacheManager; 18 | import org.crazycake.shiro.RedisManager; 19 | import org.crazycake.shiro.RedisSessionDAO; 20 | 21 | import javax.annotation.Resource; 22 | import javax.servlet.Filter; 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | 29 | @Configuration 30 | public class ShiroConfig { 31 | 32 | 33 | @Resource 34 | private RedisManager redisManager; 35 | 36 | /** 37 | * 自定义认证规则 38 | */ 39 | @Bean 40 | public UserRealm userRealm() { 41 | return new UserRealm(); 42 | } 43 | 44 | public RedisCacheManager cacheManager() { 45 | RedisCacheManager redisCacheManager = new RedisCacheManager(); 46 | redisCacheManager.setRedisManager(redisManager); 47 | return redisCacheManager; 48 | } 49 | 50 | @Bean 51 | public RedisSessionDAO redisSessionDAO() { 52 | RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); 53 | redisSessionDAO.setRedisManager(redisManager); 54 | redisSessionDAO.setKeyPrefix(redisSessionDAO.getKeyPrefix() + "business:"); 55 | return redisSessionDAO; 56 | } 57 | 58 | /** 59 | * 自定义session管理器,解决前后端分离跨域和app无法携带cookie的问题 60 | */ 61 | @Bean 62 | public SessionManager sessionManager() { 63 | HeaderWebSessionManager headerWebSessionManager = new HeaderWebSessionManager(); 64 | headerWebSessionManager.setSessionIdUrlRewritingEnabled(false); 65 | headerWebSessionManager.setSessionDAO(redisSessionDAO()); 66 | return headerWebSessionManager; 67 | } 68 | 69 | @Bean 70 | public DefaultWebSecurityManager securityManager() { 71 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 72 | securityManager.setRealm(userRealm()); 73 | securityManager.setCacheManager(cacheManager()); 74 | securityManager.setSessionManager(sessionManager()); 75 | return securityManager; 76 | } 77 | 78 | 79 | /** 80 | * 授权认证uri配置,认证规则如下 81 | * anon: 无需认证就可以访问 82 | * authc: 必须认证才能访问 83 | * user: 必须拥有记住我功能才能用 84 | * perms: 拥有对某个资源的权限才能访问 85 | * role: 拥有某个角色权限才能访问 86 | */ 87 | @Bean 88 | public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { 89 | ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); 90 | Map filters = new HashMap<>(); 91 | filters.put("authc", new UserAuthenticationFilter()); 92 | factoryBean.setFilters(filters); 93 | factoryBean.setSecurityManager(securityManager); 94 | Map map = new HashMap<>(); 95 | //允许未登录访问 96 | map.put("/login", "anon"); 97 | map.put("/logout", "anon"); 98 | map.put("/account/**", "anon"); 99 | map.put("/v3/api-docs", "anon"); 100 | map.put("/swagger-resources/**", "anon"); 101 | map.put("/swagger-ui/**", "anon"); 102 | //必须登录后才可访问 103 | map.put("/**", "authc"); 104 | factoryBean.setFilterChainDefinitionMap(map); 105 | return factoryBean; 106 | } 107 | 108 | /** 109 | * 开启Shiro注解模式,可通过@RequiresRoles注解实现接口调用权限校验 110 | */ 111 | @Bean 112 | public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultSecurityManager securityManager) { 113 | AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); 114 | authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); 115 | return authorizationAttributeSourceAdvisor; 116 | } 117 | 118 | @Bean 119 | public FilterRegistrationBean corsAndXssFilter() { 120 | FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); 121 | filterRegistrationBean.setFilter(new CorsAndXssFilter()); 122 | List urls = new ArrayList<>(); 123 | urls.add("/*"); 124 | filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); 125 | filterRegistrationBean.setUrlPatterns(urls); 126 | return filterRegistrationBean; 127 | } 128 | 129 | 130 | } 131 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.config; 2 | 3 | 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.builders.RequestParameterBuilder; 10 | import springfox.documentation.service.*; 11 | import springfox.documentation.spi.DocumentationType; 12 | import springfox.documentation.spi.service.contexts.SecurityContext; 13 | import springfox.documentation.spring.web.plugins.Docket; 14 | import springfox.documentation.swagger.web.DocExpansion; 15 | import springfox.documentation.swagger.web.ModelRendering; 16 | import springfox.documentation.swagger.web.OperationsSorter; 17 | import springfox.documentation.swagger.web.UiConfiguration; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | @Configuration 24 | public class SwaggerConfig { 25 | 26 | @Bean 27 | UiConfiguration uiConfig() { 28 | return new UiConfiguration(true, false, 1, 1, 29 | ModelRendering.MODEL, true, DocExpansion.NONE, true, null, 30 | OperationsSorter.ALPHA, true, false, null, 31 | UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, "", "http://127.0.0.1:9876/meeting"); 32 | } 33 | 34 | @Bean 35 | public Docket createDefaultDocker() { 36 | return new Docket(DocumentationType.OAS_30).apiInfo(apiInfo("1.0.0")) 37 | .select() 38 | .apis(RequestHandlerSelectors.basePackage("io.springstudent.meeting")) 39 | .paths(PathSelectors.any()) 40 | .build() 41 | .globalRequestParameters(requestParameterInfo()) 42 | .securitySchemes(securitySchemes()) 43 | .securityContexts(securityContexts()); 44 | } 45 | 46 | private ApiInfo apiInfo(String version) { 47 | return new ApiInfoBuilder() 48 | .title("会议系统") 49 | .description("接口文档") 50 | .contact(new Contact("", "", "")) 51 | .version(version) 52 | .build(); 53 | } 54 | 55 | private List requestParameterInfo() { 56 | return Arrays.asList(new RequestParameterBuilder().name("Token").description("鉴权认证").in(ParameterType.HEADER).build()); 57 | } 58 | 59 | private List securitySchemes() { 60 | List securitySchemes = new ArrayList<>(); 61 | securitySchemes.add(new ApiKey("Token", "Token", "header")); 62 | return securitySchemes; 63 | } 64 | 65 | private List securityContexts() { 66 | List securityContexts = new ArrayList<>(); 67 | securityContexts.add(SecurityContext.builder() 68 | .securityReferences(defaultAuth()).build()); 69 | return securityContexts; 70 | } 71 | 72 | private List defaultAuth() { 73 | AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); 74 | AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; 75 | authorizationScopes[0] = authorizationScope; 76 | List securityReferences = new ArrayList<>(); 77 | securityReferences.add(new SecurityReference("Token", authorizationScopes)); 78 | return securityReferences; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/config/ThreadPoolConfig.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 6 | 7 | 8 | @Configuration 9 | public class ThreadPoolConfig { 10 | 11 | @Bean("uploadModelExecutor") 12 | public ThreadPoolTaskExecutor uploadModelExecutor() { 13 | ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); 14 | threadPoolTaskExecutor.setMaxPoolSize(5); 15 | threadPoolTaskExecutor.setCorePoolSize(5); 16 | threadPoolTaskExecutor.setThreadNamePrefix("upload-model-executor-"); 17 | threadPoolTaskExecutor.initialize(); 18 | return threadPoolTaskExecutor; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.controller; 2 | 3 | import io.springstudent.meeting.common.shiro.UserPrincipal; 4 | import io.springstudent.meeting.common.util.SubjectUtils; 5 | import io.springstudent.meeting.common.bean.LoginResult; 6 | import io.swagger.annotations.ApiOperation; 7 | import io.springstudent.meeting.common.service.LoginService; 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import javax.annotation.Resource; 15 | 16 | @RestController 17 | public class LoginController { 18 | 19 | private static final Logger logger = LogManager.getLogger(LoginController.class); 20 | 21 | @Resource 22 | private LoginService loginService; 23 | 24 | /** 25 | * 登录接口 26 | */ 27 | @PostMapping("/login") 28 | @ApiOperation(value = "登录", notes = "登录") 29 | public LoginResult login(@RequestParam String username, @RequestParam String password) throws Exception { 30 | try { 31 | return loginService.login(username, password); 32 | } catch (Exception e) { 33 | logger.error("login error,username={},password={}", username, password, e); 34 | throw e; 35 | } 36 | } 37 | 38 | /** 39 | * 登出接口 40 | */ 41 | @PostMapping("/logout") 42 | @ApiOperation(value = "登出", notes = "登出") 43 | public void logout() throws Exception { 44 | try { 45 | loginService.logout(); 46 | } catch (Exception e) { 47 | logger.error("logout error", e); 48 | throw e; 49 | } 50 | } 51 | 52 | /** 53 | * 获取当前登录用户 54 | */ 55 | @PostMapping("/currentUser") 56 | public UserPrincipal currentUser() { 57 | return SubjectUtils.loginUser(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/exception/ApplicationException.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.exception; 2 | 3 | public class ApplicationException extends Exception { 4 | public ApplicationException() { 5 | } 6 | 7 | public ApplicationException(String message) { 8 | super(message); 9 | } 10 | 11 | public ApplicationException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public ApplicationException(Throwable cause) { 16 | super(cause); 17 | } 18 | 19 | public ApplicationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 20 | super(message, cause, enableSuppression, writableStackTrace); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/exception/DataNotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.exception; 2 | 3 | public class DataNotFoundException extends ApplicationException{ 4 | public DataNotFoundException() { 5 | super(); 6 | } 7 | 8 | public DataNotFoundException(String message) { 9 | super(message); 10 | } 11 | 12 | public DataNotFoundException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public DataNotFoundException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | public DataNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 21 | super(message, cause, enableSuppression, writableStackTrace); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/exception/ParamInvalidException.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.exception; 2 | 3 | public class ParamInvalidException extends ApplicationException{ 4 | public ParamInvalidException() { 5 | super(); 6 | } 7 | 8 | public ParamInvalidException(String message) { 9 | super(message); 10 | } 11 | 12 | public ParamInvalidException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public ParamInvalidException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | public ParamInvalidException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 21 | super(message, cause, enableSuppression, writableStackTrace); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/exception/ResultException.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.exception; 2 | 3 | public class ResultException extends ApplicationException { 4 | public ResultException() { 5 | super(); 6 | } 7 | 8 | public ResultException(String message) { 9 | super(message); 10 | } 11 | 12 | public ResultException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public ResultException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | public ResultException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 21 | super(message, cause, enableSuppression, writableStackTrace); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/express/ruler/And.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.express.ruler; 2 | 3 | /** 4 | * @author zhouning 5 | * @date 2023/04/07 13:40 6 | */ 7 | public class And extends Condition { 8 | public And(String element) { 9 | super(element); 10 | } 11 | 12 | @Override 13 | public String type() { 14 | return "And"; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/express/ruler/Chain.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.express.ruler; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | /** 9 | * @author zhouning 10 | * @date 2023/04/07 13:24 11 | */ 12 | @Data 13 | public class Chain { 14 | 15 | private Entry head; 16 | 17 | private Entry tail; 18 | 19 | public static final Pattern CONDITION_PATTERN = Pattern.compile("[&,|]"); 20 | 21 | public Chain(String str) { 22 | final Matcher matcher = CONDITION_PATTERN.matcher(str); 23 | int start = 0; 24 | String type = null; 25 | while (matcher.find()) { 26 | String separator = matcher.group(); 27 | String content = str.substring(start, matcher.start()); 28 | start = matcher.end(); 29 | 30 | buildChain(type, content); 31 | type = separator; 32 | } 33 | buildChain(type, str.substring(start)); 34 | } 35 | 36 | private void buildChain(String separator, String content) { 37 | Entry entry = new Entry(); 38 | if (head == null) { 39 | entry.setCondition(new None(content)); 40 | head = entry; 41 | tail = entry; 42 | } else { 43 | tail.next = entry; 44 | tail = entry; 45 | } 46 | if ("&".equals(separator)) { 47 | entry.setCondition(new And(content)); 48 | } else if ("|".equals(separator)) { 49 | entry.setCondition(new Or(content)); 50 | } 51 | } 52 | 53 | public boolean loop(Content content) { 54 | Entry next = head; 55 | boolean result = content.compare(head.condition); 56 | while (next.getNext() != null) { 57 | next = next.getNext(); 58 | if (next.getCondition().type().equals("Or")) { 59 | result = result || content.compare(next.condition); 60 | } else if (next.getCondition().type().equals("And")) { 61 | result = result && content.compare(next.condition); 62 | } 63 | } 64 | return result; 65 | } 66 | 67 | @Data 68 | private static class Entry { 69 | private Entry next; 70 | private Condition condition; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/express/ruler/Condition.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.express.ruler; 2 | 3 | /** 4 | * @author zhouning 5 | * @date 2023/04/07 13:25 6 | */ 7 | public abstract class Condition { 8 | protected String element; 9 | 10 | public Condition(String element) { 11 | this.element = element; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return "Condition{" + 17 | "element='" + element + '\'' + 18 | '}'; 19 | } 20 | 21 | abstract String type(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/express/ruler/Content.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.express.ruler; 2 | 3 | import lombok.Data; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | /** 9 | * @author zhouning 10 | * @date 2023/04/07 13:25 11 | */ 12 | @Data 13 | public class Content { 14 | 15 | private List elements; 16 | 17 | public Content(Object... eles) { 18 | elements = Arrays.stream(eles).collect(Collectors.toList()); 19 | } 20 | 21 | public boolean compare(Condition condition){ 22 | return this.elements.contains(condition.element); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/express/ruler/None.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.express.ruler; 2 | 3 | /** 4 | * @author zhouning 5 | * @date 2023/04/07 15:42 6 | */ 7 | public class None extends Condition{ 8 | public None(String element) { 9 | super(element); 10 | } 11 | 12 | @Override 13 | String type() { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/express/ruler/Or.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.express.ruler; 2 | 3 | /** 4 | * @author zhouning 5 | * @date 2023/04/07 13:40 6 | */ 7 | public class Or extends Condition { 8 | public Or(String element) { 9 | super(element); 10 | } 11 | 12 | @Override 13 | public String type() { 14 | return "Or"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/handler/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.handler; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import io.springstudent.meeting.common.exception.DataNotFoundException; 5 | import io.springstudent.meeting.common.exception.ParamInvalidException; 6 | import io.springstudent.meeting.common.exception.ResultException; 7 | import io.springstudent.meeting.common.http.ResponseResult; 8 | import org.apache.shiro.authc.IncorrectCredentialsException; 9 | import org.apache.shiro.authz.UnauthenticatedException; 10 | import org.apache.shiro.authz.UnauthorizedException; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.web.bind.annotation.ControllerAdvice; 13 | import org.springframework.web.bind.annotation.ExceptionHandler; 14 | import org.springframework.web.bind.annotation.ResponseBody; 15 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | 19 | @ControllerAdvice 20 | public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { 21 | 22 | @ExceptionHandler(value = Exception.class) 23 | @ResponseBody 24 | public ResponseResult exceptionHandler(HttpServletRequest req, Exception e) { 25 | if (StrUtil.isNotEmpty(e.getMessage()) && e.getMessage().indexOf("提示") != -1) { 26 | return ResponseResult.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); 27 | } 28 | return ResponseResult.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务内部错误"); 29 | } 30 | 31 | 32 | @ExceptionHandler(IncorrectCredentialsException.class) 33 | @ResponseBody 34 | public ResponseResult UnAuthorizedExceptionHandler(IncorrectCredentialsException e) { 35 | return ResponseResult.fail(HttpStatus.UNAUTHORIZED.value(), "账号或密码不正确"); 36 | } 37 | 38 | @ExceptionHandler(UnauthorizedException.class) 39 | @ResponseBody 40 | public ResponseResult UnauthorizedException(UnauthorizedException e) { 41 | return ResponseResult.fail(HttpStatus.UNAUTHORIZED.value(), "用户登录未被授权"); 42 | } 43 | 44 | @ExceptionHandler(UnauthenticatedException.class) 45 | @ResponseBody 46 | public ResponseResult UnauthenticatedException(UnauthenticatedException e) { 47 | return ResponseResult.fail(HttpStatus.UNAUTHORIZED.value(), "用户未登录"); 48 | } 49 | 50 | @ExceptionHandler(value = ParamInvalidException.class) 51 | @ResponseBody 52 | public ResponseResult paramInvalidExceptionHandler(HttpServletRequest req, ParamInvalidException e) throws Exception { 53 | return ResponseResult.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage()); 54 | } 55 | 56 | @ExceptionHandler(value = DataNotFoundException.class) 57 | @ResponseBody 58 | public ResponseResult dataNotFoundExceptionHandler(HttpServletRequest req, Exception e) throws Exception { 59 | return ResponseResult.fail(HttpStatus.UNPROCESSABLE_ENTITY.value(), e.getMessage()); 60 | } 61 | 62 | @ExceptionHandler(value = ResultException.class) 63 | @ResponseBody 64 | public ResponseResult resultExceptionHandler(HttpServletRequest req, Exception e) throws Exception { 65 | return ResponseResult.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); 66 | } 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/handler/ResponseBodyHandler.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.handler; 2 | 3 | 4 | import io.springstudent.meeting.common.http.ResponseResult; 5 | import org.springframework.core.MethodParameter; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.http.converter.HttpMessageConverter; 8 | import org.springframework.http.converter.StringHttpMessageConverter; 9 | import org.springframework.http.server.ServerHttpRequest; 10 | import org.springframework.http.server.ServerHttpResponse; 11 | import org.springframework.web.bind.annotation.ControllerAdvice; 12 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 13 | 14 | @ControllerAdvice(basePackages = "io.springstudent.meeting") 15 | public class ResponseBodyHandler implements ResponseBodyAdvice { 16 | 17 | @Override 18 | public boolean supports(MethodParameter returnType, Class> converterType) { 19 | return true; 20 | } 21 | 22 | @Override 23 | public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, 24 | Class> selectedConverterType, ServerHttpRequest request, 25 | ServerHttpResponse response) { 26 | if (null == body && !selectedConverterType.equals(StringHttpMessageConverter.class)) { 27 | return ResponseResult.success(); 28 | } 29 | if (selectedConverterType.equals(StringHttpMessageConverter.class)) { 30 | return ResponseResult.buildSuccessResultStr(body); 31 | } 32 | if(body.getClass().equals(ResponseResult.class)||body.getClass().equals(byte[].class)) { 33 | return body; 34 | } 35 | return ResponseResult.success(body); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/http/CorsAndXssFilter.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.http; 2 | 3 | import org.springframework.web.filter.OncePerRequestFilter; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | public class CorsAndXssFilter extends OncePerRequestFilter { 12 | 13 | @Override 14 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 15 | //跨域处理 16 | response.setContentType("application/json;charset=UTF-8"); 17 | response.addHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); 18 | response.addHeader("Access-Control-Allow-Methods", "*"); 19 | response.addHeader("Access-Control-Allow-Credentials", "true"); 20 | response.setHeader("Access-Control-Allow-Headers", "content-type,Cookie, Token, Random, Signature, Timestamp, Content-Length, Content-Disposition, filename"); 21 | response.setHeader("Access-Control-Max-Age", "3600"); 22 | if(request.getRequestURI().indexOf("socket.io")!=-1){ 23 | filterChain.doFilter(request, response); 24 | }else{ 25 | filterChain.doFilter(new XssRequestWrapper(request), response); 26 | } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/http/ResponseResult.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.http; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class ResponseResult { 9 | 10 | private int code; 11 | private String msg; 12 | private T result; 13 | 14 | private static final Integer HTTP_OK = 200; 15 | 16 | private static final Integer HTTP_INTERNAL_SERVER_ERROR = 500; 17 | 18 | private static final Integer HTTP_REQUEST_TIMEOUT = 408; 19 | 20 | public static ResponseResult success() { 21 | return new ResponseResultBuilder().code(HTTP_OK).msg("success").build(); 22 | } 23 | 24 | public static ResponseResult success(T result) { 25 | return new ResponseResultBuilder().code(HTTP_OK).msg("success").result(result).build(); 26 | } 27 | 28 | public static ResponseResult success(T result, String msg) { 29 | return new ResponseResultBuilder().code(HTTP_OK).msg(msg).result(result).build(); 30 | } 31 | 32 | public static ResponseResult fail() { 33 | return new ResponseResultBuilder().code(HTTP_INTERNAL_SERVER_ERROR).build(); 34 | } 35 | 36 | public static ResponseResult fail(Exception e) { 37 | return new ResponseResultBuilder().code(HTTP_INTERNAL_SERVER_ERROR).msg(e.getMessage().toString()).build(); 38 | } 39 | 40 | public static ResponseResult fail(String msg) { 41 | return new ResponseResultBuilder().code(HTTP_INTERNAL_SERVER_ERROR).msg(msg).build(); 42 | } 43 | 44 | public static ResponseResult fail(Integer code, String msg) { 45 | return new ResponseResultBuilder().code(code).msg(msg).build(); 46 | } 47 | 48 | public static String buildSuccessResultStr(Object result) { 49 | if (null != result && result instanceof String) { 50 | String res = (String) result; 51 | String replaceAllStr = res.replaceAll("\"", "\\\\\""); 52 | return "{\"msg\": \"success\",\"code\": " + HTTP_OK + ",\"result\": \"" + replaceAllStr.replaceAll("\"", "\\\"") + "\"}"; 53 | } 54 | if (null == result) { 55 | return "{\"msg\": \"success\",\"code\": " + HTTP_OK + ",\"result\": " + result + "}"; 56 | } 57 | return "{\"msg\": \"success\",\"code\": " + HTTP_OK + ",\"result\": \"" + result.toString().replaceAll("\"", "\\\"") + "\"}"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/http/UserAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.http; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; 6 | import org.springframework.http.HttpStatus; 7 | 8 | import javax.servlet.ServletRequest; 9 | import javax.servlet.ServletResponse; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | 14 | public class UserAuthenticationFilter extends FormAuthenticationFilter { 15 | 16 | private static final Logger logger = LogManager.getLogger(UserAuthenticationFilter.class); 17 | 18 | @Override 19 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { 20 | if (isLoginRequest(request, response)) { 21 | if (isLoginSubmission(request, response)) { 22 | if (logger.isTraceEnabled()) { 23 | logger.trace("Login submission detected. Attempting to execute login."); 24 | } 25 | return executeLogin(request, response); 26 | } else { 27 | if (logger.isTraceEnabled()) { 28 | logger.trace("Login page view."); 29 | } 30 | //allow them to see the login page ;) 31 | return true; 32 | } 33 | } else { 34 | saveRequest(request); 35 | HttpServletRequest req = (HttpServletRequest) request; 36 | HttpServletResponse resp = (HttpServletResponse) response; 37 | resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin")); 38 | resp.setHeader("Access-Control-Allow-Methods", "*"); 39 | resp.setHeader("Access-Control-Allow-Credentials", "true"); 40 | resp.setHeader("Access-Control-Allow-Headers", "content-type,Cookie, Token, Random, Signature, Timestamp, Content-Length, Content-Disposition, filename"); 41 | resp.setHeader("Access-Control-Max-Age", "3600"); 42 | resp.setContentType("application/json; charset=utf-8"); 43 | Object result = null; 44 | resp.getWriter().write("{\"msg\": \"用户未登录\",\"code\": " + HttpStatus.UNAUTHORIZED.value() + ",\"result\": " + result + "}"); 45 | return false; 46 | } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/http/XssRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.http; 2 | 3 | import io.springstudent.meeting.common.util.HttpRequestUtils; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletRequestWrapper; 7 | import java.util.LinkedHashMap; 8 | import java.util.Map; 9 | 10 | public class XssRequestWrapper extends HttpServletRequestWrapper { 11 | 12 | public XssRequestWrapper(HttpServletRequest request) { 13 | super(request); 14 | } 15 | 16 | @Override 17 | public Map getParameterMap() { 18 | Map map = new LinkedHashMap<>(); 19 | Map parameters = super.getParameterMap(); 20 | for (Map.Entry entry : parameters.entrySet()) { 21 | String[] values = entry.getValue(); 22 | for (int i = 0; i < values.length; i++) { 23 | values[i] = HttpRequestUtils.clean(values[i]); 24 | } 25 | map.put(entry.getKey(), values); 26 | } 27 | return map; 28 | } 29 | 30 | @Override 31 | public String[] getParameterValues(String name) { 32 | String[] values = super.getParameterValues(name); 33 | if (values == null) { 34 | return null; 35 | } 36 | int count = values.length; 37 | String[] encodedValues = new String[count]; 38 | for (int i = 0; i < count; i++) { 39 | encodedValues[i] = HttpRequestUtils.clean(values[i]); 40 | } 41 | return encodedValues; 42 | } 43 | 44 | @Override 45 | public String getParameter(String name) { 46 | String value = super.getParameter(name); 47 | if (value == null) { 48 | return null; 49 | } 50 | return HttpRequestUtils.clean(value); 51 | } 52 | 53 | @Override 54 | public Object getAttribute(String name) { 55 | Object value = super.getAttribute(name); 56 | if (value instanceof String) { 57 | HttpRequestUtils.clean((String) value); 58 | } 59 | return value; 60 | } 61 | 62 | @Override 63 | public String getHeader(String name) { 64 | String value = super.getHeader(name); 65 | if (value == null) { 66 | return null; 67 | } 68 | return HttpRequestUtils.clean(value); 69 | } 70 | 71 | @Override 72 | public String getQueryString() { 73 | String value = super.getQueryString(); 74 | if (value == null) { 75 | return null; 76 | } 77 | return HttpRequestUtils.clean(value); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/service/LoginService.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.service; 2 | 3 | import io.springstudent.meeting.common.bean.LoginResult; 4 | 5 | 6 | public interface LoginService { 7 | 8 | LoginResult login(String username, String password) throws Exception; 9 | 10 | void logout()throws Exception; 11 | } 12 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/service/impl/LoginServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.service.impl; 2 | 3 | import io.springstudent.meeting.common.bean.LoginResult; 4 | import io.springstudent.meeting.common.service.LoginService; 5 | import io.springstudent.meeting.common.shiro.UserPrincipal; 6 | import io.springstudent.meeting.common.util.SubjectUtils; 7 | import org.apache.shiro.SecurityUtils; 8 | import org.apache.shiro.authc.UsernamePasswordToken; 9 | import org.apache.shiro.session.Session; 10 | import org.apache.shiro.session.mgt.eis.SessionDAO; 11 | import org.apache.shiro.subject.SimplePrincipalCollection; 12 | import org.apache.shiro.subject.Subject; 13 | import org.apache.shiro.subject.support.DefaultSubjectContext; 14 | import org.springframework.stereotype.Service; 15 | 16 | import javax.annotation.Resource; 17 | import java.util.Collection; 18 | 19 | 20 | @Service 21 | public class LoginServiceImpl implements LoginService { 22 | 23 | @Resource 24 | private SessionDAO sessionDAO; 25 | 26 | @Override 27 | public LoginResult login(String username, String password) throws Exception { 28 | Subject subject = SecurityUtils.getSubject(); 29 | UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password); 30 | subject.login(usernamePasswordToken); 31 | String token = String.valueOf(subject.getSession().getId()); 32 | UserPrincipal userPrincipal = SubjectUtils.loginUser(); 33 | //同一用户多次登录互踢 34 | kickSameUser(username, token); 35 | LoginResult loginResult = new LoginResult(); 36 | loginResult.setUsername(userPrincipal.getUsername()); 37 | loginResult.setRealname(userPrincipal.getRealname()); 38 | loginResult.setToken(token); 39 | return loginResult; 40 | } 41 | 42 | private void kickSameUser(String loginUser, String token) throws Exception { 43 | Collection sessions = sessionDAO.getActiveSessions(); 44 | for (Session session : sessions) { 45 | if (session != null && session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) != null) { 46 | SimplePrincipalCollection principalCollection = (SimplePrincipalCollection) session 47 | .getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); 48 | UserPrincipal userPrincipal = (UserPrincipal) principalCollection.getPrimaryPrincipal(); 49 | //同一个用户已经登录过一次,直接踢出 50 | if (userPrincipal.getUsername().equals(loginUser) && !token.equals(String.valueOf(session.getId()))) { 51 | sessionDAO.delete(session); 52 | if (session != null) { 53 | session.stop(); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | @Override 61 | public void logout() throws Exception { 62 | if (SubjectUtils.loginUser() != null) { 63 | SecurityUtils.getSubject().logout(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/shiro/HeaderWebSessionManager.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.shiro; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; 5 | import org.apache.shiro.web.servlet.ShiroHttpServletRequest; 6 | import org.apache.shiro.web.util.WebUtils; 7 | 8 | import javax.servlet.ServletRequest; 9 | import javax.servlet.ServletResponse; 10 | import java.io.Serializable; 11 | 12 | public class HeaderWebSessionManager extends DefaultWebSessionManager implements ShiroVariable { 13 | 14 | 15 | public HeaderWebSessionManager() { 16 | super(); 17 | setGlobalSessionTimeout(SESSION_TIMEOUT * 1000); 18 | } 19 | 20 | /** 21 | * 解决shiro的session跨域共享和app端无法携带cookie的问题 22 | */ 23 | @Override 24 | protected Serializable getSessionId(ServletRequest request, ServletResponse response) { 25 | //首先从http请求头Token中获取sessionId,获取不到在从cookie获取 26 | String sessionId = WebUtils.toHttp(request).getHeader(TOKEN); 27 | if (StrUtil.isEmpty(sessionId)) { 28 | return super.getSessionId(request, response); 29 | } else { 30 | //防止shiro重定向在url中拼接jsessionId字符串,导致请求400 31 | request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, this.isSessionIdUrlRewritingEnabled()); 32 | request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "Stateless"); 33 | request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId); 34 | request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); 35 | return sessionId; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/shiro/ShiroVariable.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.shiro; 2 | 3 | public interface ShiroVariable { 4 | 5 | String TOKEN = "Token"; 6 | 7 | int SESSION_TIMEOUT = 30*60; 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/shiro/UserPrincipal.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.shiro; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class UserPrincipal implements Serializable { 9 | 10 | private String username; 11 | 12 | private String realname; 13 | } 14 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/shiro/UserRealm.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.shiro; 2 | 3 | import io.springstudent.meeting.account.pojo.Account; 4 | import io.springstudent.meeting.account.service.AccountService; 5 | import org.apache.shiro.authc.*; 6 | import org.apache.shiro.authc.credential.CredentialsMatcher; 7 | import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 8 | import org.apache.shiro.authz.AuthorizationInfo; 9 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 10 | import org.apache.shiro.realm.AuthorizingRealm; 11 | import org.apache.shiro.subject.PrincipalCollection; 12 | import org.apache.shiro.util.ByteSource; 13 | 14 | import javax.annotation.Resource; 15 | 16 | public class UserRealm extends AuthorizingRealm { 17 | 18 | @Resource 19 | private AccountService accountService; 20 | 21 | /** 22 | * 权限处理,暂时用不到 23 | */ 24 | @Override 25 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { 26 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 27 | return info; 28 | } 29 | 30 | /** 31 | * 设置密码认证方式,这里选择md5+salt 32 | */ 33 | @Override 34 | public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { 35 | HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); 36 | hashedCredentialsMatcher.setHashAlgorithmName("MD5"); 37 | hashedCredentialsMatcher.setHashIterations(1024); 38 | super.setCredentialsMatcher(hashedCredentialsMatcher); 39 | } 40 | 41 | /** 42 | * 用户认证 43 | */ 44 | @Override 45 | protected AuthenticationInfo doGetAuthenticationInfo( 46 | AuthenticationToken authToken) throws AuthenticationException { 47 | UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authToken; 48 | String username = usernamePasswordToken.getUsername(); 49 | Account account = null; 50 | try { 51 | account = accountService.findByUsername(username); 52 | } catch (Exception e) { 53 | } 54 | if (account == null) { 55 | throw new IncorrectCredentialsException(); 56 | } 57 | UserPrincipal userPrincipal = new UserPrincipal(); 58 | userPrincipal.setUsername(username); 59 | userPrincipal.setRealname(account.getRealname()); 60 | return new SimpleAuthenticationInfo(userPrincipal, account.getPwd(), ByteSource.Util.bytes(account.getSalt()), getName()); 61 | } 62 | 63 | } 64 | 65 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/signal/ISignalListener.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.signal; 2 | 3 | import com.corundumstudio.socketio.SocketIOClient; 4 | import io.springstudent.meeting.chat.bean.ChatInfo; 5 | import io.springstudent.meeting.common.bean.RoomEvent; 6 | 7 | /** 8 | * @author zhouning 9 | * @date 2023/04/21 8:57 10 | */ 11 | public interface ISignalListener { 12 | 13 | void onConnect(SocketIOClient client) throws Exception; 14 | 15 | void onDisconnect(SocketIOClient client) throws Exception; 16 | 17 | void roomUser(SocketIOClient client) throws Exception; 18 | 19 | void sendMsg(SocketIOClient client, String msg) throws Exception; 20 | 21 | void meetingEvent(SocketIOClient client, RoomEvent roomEvent) throws Exception; 22 | 23 | void drawLine(SocketIOClient client, int nowX, int nowY, int x, int y, String color, int drawSize) throws Exception; 24 | 25 | void drawText(SocketIOClient client,String id,String txt, int x, int y) throws Exception; 26 | 27 | void drawSqr(SocketIOClient client, int x, int y, int width, int height)throws Exception; 28 | 29 | void reDrawText(SocketIOClient client,String id,String txt, int x, int y,int width,int height) throws Exception; 30 | 31 | void storeBoard(SocketIOClient client, String boardData) throws Exception; 32 | 33 | void clearBoard(SocketIOClient client) throws Exception; 34 | 35 | void getBoard(SocketIOClient client)throws Exception; 36 | 37 | void getChat(SocketIOClient client)throws Exception; 38 | } 39 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/signal/SignalListener.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.signal; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import com.corundumstudio.socketio.SocketIOClient; 5 | import com.corundumstudio.socketio.SocketIOServer; 6 | import com.corundumstudio.socketio.annotation.OnConnect; 7 | import com.corundumstudio.socketio.annotation.OnDisconnect; 8 | import com.corundumstudio.socketio.annotation.OnEvent; 9 | import com.gysoft.jdbc.bean.Criteria; 10 | import com.gysoft.jdbc.bean.SQL; 11 | import io.springstudent.meeting.chat.dao.ChatDao; 12 | import io.springstudent.meeting.chat.pojo.Chat; 13 | import io.springstudent.meeting.chat.service.ChatService; 14 | import io.springstudent.meeting.common.bean.RoomEvent; 15 | import io.springstudent.meeting.common.util.EmptyUtils; 16 | import io.springstudent.meeting.common.util.SpringUtil; 17 | import io.springstudent.meeting.room.dao.RoomBoardDao; 18 | import io.springstudent.meeting.room.dao.RoomBoardTxtDao; 19 | import io.springstudent.meeting.room.dao.RoomDao; 20 | import io.springstudent.meeting.room.dao.RoomHistoryDao; 21 | import io.springstudent.meeting.room.pojo.Room; 22 | import io.springstudent.meeting.room.pojo.RoomBoard; 23 | import io.springstudent.meeting.room.pojo.RoomBoardTxt; 24 | import io.springstudent.meeting.room.pojo.RoomHistory; 25 | import org.springframework.stereotype.Component; 26 | import org.springframework.transaction.annotation.Transactional; 27 | 28 | import javax.annotation.Resource; 29 | import java.util.ArrayList; 30 | import java.util.Date; 31 | import java.util.List; 32 | import java.util.function.Consumer; 33 | import java.util.stream.Collectors; 34 | 35 | 36 | /** 37 | * @author zhouning 38 | * @date 2023/04/18 13:39 39 | */ 40 | @Component 41 | @Transactional(rollbackFor = Exception.class) 42 | public class SignalListener implements ISignalListener { 43 | 44 | @Resource 45 | private SocketIOServer socketIOServer; 46 | @Resource 47 | private ChatDao chatDao; 48 | @Resource 49 | private RoomHistoryDao roomHistoryDao; 50 | @Resource 51 | private RoomDao roomDao; 52 | @Resource 53 | private RoomBoardDao roomBoardDao; 54 | @Resource 55 | private RoomBoardTxtDao roomBoardTxtDao; 56 | 57 | @Override 58 | @OnConnect 59 | public void onConnect(SocketIOClient client) throws Exception { 60 | String roomCode = roomCode(client); 61 | String username = username(client); 62 | String msg = username + "加入会议室"; 63 | Long now = new Date().getTime(); 64 | client.joinRoom(roomCode); 65 | socketIOServer.getRoomOperations(roomCode).sendEvent("joinRoom", username, msg); 66 | roomHistoryDao.save(RoomHistory.builder().username(username).roomCode(roomCode).action("join").createTime(now).build()); 67 | chatDao.save(Chat.builder().roomCode(roomCode).type(1).sender(username).sendTime(now).content(msg).build()); 68 | if (socketIOServer.getRoomOperations(roomCode).getClients().size() == 1) { 69 | roomDao.updateWithSql(new SQL().update(Room.class).set("endTime", null).where("roomCode", roomCode)); 70 | } 71 | } 72 | 73 | @Override 74 | @OnDisconnect 75 | public void onDisconnect(SocketIOClient client) throws Exception { 76 | String roomCode = roomCode(client); 77 | String username = username(client); 78 | String msg = username + "离开会议室"; 79 | Long now = new Date().getTime(); 80 | client.leaveRoom(roomCode); 81 | socketIOServer.getRoomOperations(roomCode).sendEvent("leaveRoom", username, msg); 82 | roomHistoryDao.save(RoomHistory.builder().username(username(client)).roomCode(roomCode).action("leave").createTime(now).build()); 83 | chatDao.save(Chat.builder().roomCode(roomCode).type(1).sender(username).sendTime(now).content(msg).build()); 84 | if (EmptyUtils.isEmpty(socketIOServer.getRoomOperations(roomCode).getClients())) { 85 | roomDao.updateWithSql(new SQL().update(Room.class).set("endTime", new Date().getTime()).where("roomCode", roomCode)); 86 | } 87 | } 88 | 89 | @Override 90 | @OnEvent("roomUser") 91 | public void roomUser(SocketIOClient client) throws Exception { 92 | String roomCode = roomCode(client); 93 | List roomUsers = new ArrayList<>(); 94 | socketIOServer.getRoomOperations(roomCode).getClients().forEach(new Consumer() { 95 | @Override 96 | public void accept(SocketIOClient client) { 97 | roomUsers.add(username(client)); 98 | } 99 | }); 100 | System.out.println(roomUsers); 101 | client.sendEvent("roomUser", roomUsers); 102 | } 103 | 104 | @Override 105 | @OnEvent("sendMsg") 106 | public void sendMsg(SocketIOClient client, String msg) throws Exception { 107 | String roomCode = roomCode(client); 108 | String username = username(client); 109 | Long now = new Date().getTime(); 110 | socketIOServer.getRoomOperations(roomCode(client)).sendEvent("receiveMsg", username, DateUtil.format(new Date(), "HH:mm:ss"), msg); 111 | chatDao.save(Chat.builder().roomCode(roomCode).type(0).sender(username).sendTime(now).content(msg).build()); 112 | } 113 | 114 | @Override 115 | @OnEvent("meetingEvent") 116 | public void meetingEvent(SocketIOClient client, RoomEvent roomEvent) throws Exception { 117 | String roomCode = roomCode(client); 118 | String username = username(client); 119 | Long now = new Date().getTime(); 120 | String eventName = roomEvent.getEventName(); 121 | if (eventName.equals("editName")) { 122 | String msg = username + "修改会议室名称为" + roomEvent.getRoomName(); 123 | socketIOServer.getRoomOperations(roomCode(client)).sendEvent("meetingEvent_" + roomEvent.getEventName(), roomEvent.getRoomName(), msg); 124 | chatDao.save(Chat.builder().roomCode(roomCode).type(1).sender(username).sendTime(now).content(msg).build()); 125 | } 126 | } 127 | 128 | @Override 129 | @OnEvent("drawLine") 130 | public void drawLine(SocketIOClient client, int nowX, int nowY, int x, int y, String color, int drawSize) throws Exception { 131 | String roomCode = roomCode(client); 132 | socketIOServer.getRoomOperations(roomCode).sendEvent("drawLine", client, nowX, nowY, x, y, color, drawSize); 133 | } 134 | 135 | @Override 136 | @OnEvent("drawText") 137 | public void drawText(SocketIOClient client, String id, String txt, int x, int y) throws Exception { 138 | String roomCode = roomCode(client); 139 | socketIOServer.getRoomOperations(roomCode).sendEvent("drawText", client, txt, x, y); 140 | roomBoardTxtDao.save(RoomBoardTxt.builder().roomCode(roomCode).id(id).txt(txt).x(x).y(y).build()); 141 | } 142 | 143 | @Override 144 | @OnEvent("drawSqr") 145 | public void drawSqr(SocketIOClient client, int x, int y, int width, int height) throws Exception { 146 | String roomCode = roomCode(client); 147 | socketIOServer.getRoomOperations(roomCode).sendEvent("drawSqr", client, x, y, width,height); 148 | } 149 | 150 | @Override 151 | @OnEvent("reDrawText") 152 | public void reDrawText(SocketIOClient client, String id, String txt, int x, int y, int width, int height) throws Exception { 153 | String roomCode = roomCode(client); 154 | roomBoardTxtDao.updateWithSql(new SQL().update(RoomBoardTxt.class).set("txt", txt).set("x", x).set("y", y).where("id", id)); 155 | socketIOServer.getRoomOperations(roomCode).sendEvent("reDrawText", client, txt, x, y, width, height); 156 | } 157 | 158 | @Override 159 | @OnEvent("storeBoard") 160 | public void storeBoard(SocketIOClient client, String boardData) throws Exception { 161 | String roomCode = roomCode(client); 162 | roomBoardDao.updateWithSql(new SQL().update(RoomBoard.class).set("boardData", boardData).where("roomCode", roomCode)); 163 | } 164 | 165 | @Override 166 | @OnEvent("clearBoard") 167 | public void clearBoard(SocketIOClient client) throws Exception { 168 | String roomCode = roomCode(client); 169 | roomBoardDao.updateWithSql(new SQL().update(RoomBoard.class).set("boardData", null).where("roomCode", roomCode)); 170 | roomBoardTxtDao.deleteWithCriteria(new Criteria().where("roomCode",roomCode)); 171 | socketIOServer.getRoomOperations(roomCode).sendEvent("clearBoard", client); 172 | } 173 | 174 | @Override 175 | @OnEvent("getBoard") 176 | public void getBoard(SocketIOClient client) throws Exception { 177 | String roomCode = roomCode(client); 178 | RoomBoard roomBoard = roomBoardDao.queryOne(new Criteria().where("roomCode", roomCode)); 179 | if (roomBoard != null) { 180 | client.sendEvent("getBoard", roomBoard.getBoardData()); 181 | } 182 | } 183 | 184 | @Override 185 | @OnEvent("getChat") 186 | public void getChat(SocketIOClient client) throws Exception { 187 | client.sendEvent("getChat", SpringUtil.getBean(ChatService.class).chatInfos(roomCode(client))); 188 | } 189 | 190 | private String roomCode(SocketIOClient client) { 191 | return client.getHandshakeData().getSingleUrlParam("roomCode"); 192 | } 193 | 194 | private String username(SocketIOClient client) { 195 | return client.getHandshakeData().getSingleUrlParam("username"); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/signal/SignalServerConfig.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.signal; 2 | 3 | import com.corundumstudio.socketio.SocketIOServer; 4 | import com.corundumstudio.socketio.Transport; 5 | import com.corundumstudio.socketio.annotation.SpringAnnotationScanner; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | /** 11 | * @author zhouning 12 | * @date 2023/04/18 13:37 13 | */ 14 | @Configuration 15 | public class SignalServerConfig { 16 | @Value("${socketio.port}") 17 | private Integer socketioPort; 18 | 19 | @Bean 20 | public SocketIOServer socketIOServer() { 21 | com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); 22 | config.setPort(socketioPort); 23 | config.setMaxFramePayloadLength(1024 * 1024); 24 | config.setMaxHttpContentLength(1024 * 1024); 25 | config.setAllowCustomRequests(true); 26 | config.setWorkerThreads(100); 27 | config.setTransports(Transport.POLLING, Transport.WEBSOCKET); 28 | config.setAuthorizationListener(data -> { 29 | data.getHttpHeaders().get("authroization"); 30 | return true; 31 | }); 32 | SocketIOServer server = new SocketIOServer(config); 33 | return server; 34 | } 35 | 36 | @Bean 37 | public SpringAnnotationScanner springAnnotationScanner() { 38 | return new SpringAnnotationScanner(socketIOServer()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/signal/SignalServerStarter.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.signal; 2 | 3 | import com.corundumstudio.socketio.SocketIOServer; 4 | import org.springframework.beans.factory.DisposableBean; 5 | import org.springframework.beans.factory.InitializingBean; 6 | import org.springframework.boot.CommandLineRunner; 7 | import org.springframework.stereotype.Component; 8 | 9 | import javax.annotation.Resource; 10 | 11 | /** 12 | * @author zhouning 13 | * @date 2023/04/19 8:56 14 | */ 15 | @Component 16 | public class SignalServerStarter implements CommandLineRunner { 17 | 18 | @Resource 19 | private SocketIOServer socketIOServer; 20 | 21 | @Override 22 | public void run(String... args) throws Exception { 23 | socketIOServer.start(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/util/EmptyUtils.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.util; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | import java.util.function.Supplier; 6 | 7 | public class EmptyUtils { 8 | 9 | public static final String EMPTY_STR=""; 10 | 11 | public static boolean isBlank(String source){ 12 | return source==null || source.trim().length()==0; 13 | } 14 | public static boolean isNotBlank(String source){ 15 | return !isBlank(source); 16 | } 17 | public static boolean isEmpty(String source){ 18 | return source==null || source.length()==0; 19 | } 20 | public static boolean isNotEmpty(String source){ 21 | return !isEmpty(source); 22 | } 23 | public static boolean isEmpty(Collection list){ 24 | return list==null || list.size()==0; 25 | } 26 | public static boolean isNotEmpty(Collection list){ 27 | return !isEmpty(list); 28 | } 29 | public static boolean isEmpty(Map map){ 30 | return map==null || map.size()==0; 31 | } 32 | public static boolean isNotEmpty(Map map){ 33 | return !isEmpty(map); 34 | } 35 | public static boolean isEmpty(T[] arrays){ 36 | return arrays==null || arrays.length==0; 37 | } 38 | public static boolean isNotEmpty(T[] arrays){ 39 | return !isEmpty(arrays); 40 | } 41 | public static boolean isEmpty(Supplier supplier){ 42 | Object object = supplier.get(); 43 | if(object == null){ 44 | return true; 45 | }else if(object instanceof String){ 46 | return isEmpty((String) object); 47 | }else if(object instanceof Collection){ 48 | return isEmpty((Collection) object); 49 | }else if (object instanceof Map){ 50 | return isEmpty((Map) object); 51 | }else{ 52 | return false; 53 | } 54 | } 55 | public static boolean isNotEmpty(Supplier supplier){ 56 | Object object = supplier.get(); 57 | if(object == null){ 58 | return false; 59 | }else if(object instanceof String){ 60 | return isNotEmpty((String) object); 61 | }else if(object instanceof Collection){ 62 | return isNotEmpty((Collection) object); 63 | }else if (object instanceof Map){ 64 | return isNotEmpty((Map) object); 65 | }else{ 66 | return true; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/util/HttpRequestUtils.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.web.util.HtmlUtils; 5 | public class HttpRequestUtils { 6 | 7 | /** 8 | * 对value进行Xss过滤处理 9 | */ 10 | public static String clean(String value) { 11 | if (StringUtils.isBlank(value) || value.startsWith("http:") || value.startsWith("https:")) { 12 | return value; 13 | } 14 | String convertValue = convert(value); 15 | //漏掉 " 16 | char[] values = convertValue.toCharArray(); 17 | int length = values.length; 18 | String newValue = ""; 19 | for (int i = 0; i < length; i++) { 20 | char ch = values[i]; 21 | if ('&' == ch || '"' == ch || '·' == ch || '€' == ch || '£' == ch) { 22 | newValue += String.valueOf(ch); 23 | } else if ('−' == ch) { 24 | newValue += String.valueOf('-'); 25 | } else { 26 | newValue += HtmlUtils.htmlEscape(String.valueOf(ch)); 27 | } 28 | } 29 | if (!newValue.equals(value)) { 30 | System.out.println(value + " is unsafe,be cleaned:" + newValue); 31 | } 32 | return newValue; 33 | } 34 | 35 | public static String convert(String value) { 36 | value = value.replaceAll("<", "<"); 37 | value = value.replaceAll("--", "--"); 38 | value = value.replaceAll(">", ">"); 39 | value = value.replaceAll("'", "' "); 40 | value = value.replaceAll("…", "..."); 41 | value = value.replaceAll("·", "\\·"); 42 | value = value.replaceAll("¥", "¥"); 43 | value = value.replaceAll("“", """); 44 | value = value.replaceAll("”", """); 45 | value = value.replaceAll("——", "--"); 46 | value = value.replaceAll("—", "-"); 47 | value = value.replaceAll("‘", "' "); 48 | value = value.replaceAll("’", "' "); 49 | value = value.replaceAll("=", "="); 50 | return value; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/util/SpringUtil.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 5 | import org.springframework.context.ApplicationContext; 6 | import org.springframework.context.ApplicationContextAware; 7 | import org.springframework.stereotype.Component; 8 | 9 | //加入component注解,让spring扫描到该bean 10 | @Component 11 | public class SpringUtil implements ApplicationContextAware { 12 | private static ApplicationContext applicationContext = null; 13 | 14 | public SpringUtil() { 15 | } 16 | 17 | public void setApplicationContext(ApplicationContext arg0) throws BeansException { 18 | if (applicationContext == null) { 19 | applicationContext = arg0; 20 | } 21 | 22 | } 23 | 24 | public static ApplicationContext getApplicationContext() { 25 | return applicationContext; 26 | } 27 | 28 | public static void setAppCtx(ApplicationContext webAppCtx) { 29 | if (webAppCtx != null) { 30 | applicationContext = webAppCtx; 31 | } 32 | } 33 | 34 | /** 35 | * 拿到ApplicationContext对象实例后就可以手动获取Bean的注入实例对象 36 | */ 37 | public static T getBean(Class clazz) { 38 | return getApplicationContext().getBean(clazz); 39 | } 40 | 41 | public static T getBean(String name, Class clazz) throws ClassNotFoundException { 42 | return getApplicationContext().getBean(name, clazz); 43 | } 44 | 45 | public static final Object getBean(String beanName) { 46 | return getApplicationContext().getBean(beanName); 47 | } 48 | 49 | public static final Object getBean(String beanName, String className) throws ClassNotFoundException { 50 | Class clz = Class.forName(className); 51 | return getApplicationContext().getBean(beanName, clz.getClass()); 52 | } 53 | 54 | public static boolean containsBean(String name) { 55 | return getApplicationContext().containsBean(name); 56 | } 57 | 58 | public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { 59 | return getApplicationContext().isSingleton(name); 60 | } 61 | 62 | public static Class getType(String name) throws NoSuchBeanDefinitionException { 63 | return getApplicationContext().getType(name); 64 | } 65 | 66 | public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { 67 | return getApplicationContext().getAliases(name); 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/common/util/SubjectUtils.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.common.util; 2 | 3 | import io.springstudent.meeting.common.shiro.UserPrincipal; 4 | import org.apache.shiro.SecurityUtils; 5 | 6 | public class SubjectUtils { 7 | 8 | /** 9 | * 获取登录用户名 10 | */ 11 | public static String loginUserName() { 12 | UserPrincipal userPrincipal = loginUser(); 13 | return userPrincipal != null ? userPrincipal.getUsername() : null; 14 | } 15 | 16 | /** 17 | * 登录用户信息 18 | */ 19 | public static UserPrincipal loginUser() { 20 | return (UserPrincipal) SecurityUtils.getSubject().getPrincipal(); 21 | } 22 | 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/bean/RoomInfo.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.bean; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author zhouning 9 | * @date 2023/04/20 13:23 10 | */ 11 | @Data 12 | public class RoomInfo { 13 | 14 | private String id; 15 | 16 | private String roomCode; 17 | 18 | private String roomName; 19 | 20 | private Long startTime; 21 | 22 | private Long endTime; 23 | 24 | private String createUser; 25 | 26 | private List joinUser; 27 | 28 | private String boardData; 29 | } 30 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/controller/RoomController.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.controller; 2 | 3 | import com.gysoft.jdbc.bean.Page; 4 | import com.gysoft.jdbc.bean.PageResult; 5 | import io.springstudent.meeting.room.bean.RoomInfo; 6 | import io.springstudent.meeting.room.pojo.RoomBoardTxt; 7 | import io.springstudent.meeting.room.service.RoomService; 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import javax.annotation.Resource; 13 | import java.util.List; 14 | 15 | /** 16 | * @author zhouning 17 | * @date 2023/04/20 13:21 18 | */ 19 | @RestController 20 | @RequestMapping("/room") 21 | public class RoomController { 22 | 23 | private static final Logger logger = LogManager.getLogger(RoomController.class); 24 | 25 | @Resource 26 | private RoomService roomService; 27 | 28 | @PostMapping("/createRoom") 29 | public String createRoom() throws Exception { 30 | try { 31 | return roomService.createRoom(); 32 | } catch (Exception e) { 33 | logger.error("createRoom error", e); 34 | throw e; 35 | } 36 | } 37 | 38 | @PostMapping("/updateRoomName") 39 | public void updateRoomName(String roomCode, String roomName) throws Exception { 40 | try { 41 | roomService.updateRoomName(roomCode, roomName); 42 | } catch (Exception e) { 43 | logger.error("updateRoomName error,roomCode={},roomName", roomCode, roomName); 44 | throw e; 45 | } 46 | } 47 | 48 | @GetMapping("/queryRoom") 49 | public RoomInfo queryRoom(@RequestParam String roomCode) throws Exception { 50 | try { 51 | return roomService.queryRoom(roomCode); 52 | } catch (Exception e) { 53 | logger.error("queryRoom error,roomCode={}", roomCode, e); 54 | throw e; 55 | } 56 | } 57 | 58 | @PostMapping("/pageRoom") 59 | public PageResult pageRoom(@RequestBody Page page)throws Exception{ 60 | try { 61 | return roomService.pageRoom(page); 62 | }catch (Exception e){ 63 | logger.error("pageRoom error,page={}",page,e); 64 | throw e; 65 | } 66 | } 67 | 68 | @GetMapping("/listRoomBoardTxt") 69 | public List listRoomBoardTxt(@RequestParam String roomCode)throws Exception{ 70 | try { 71 | return roomService.listRoomBoardTxt(roomCode); 72 | }catch (Exception e){ 73 | logger.error("listRoomBoardTxt error,roomCode={}",roomCode,e); 74 | throw e; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/dao/RoomBoardDao.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDao; 4 | import io.springstudent.meeting.room.pojo.RoomBoard; 5 | 6 | /** 7 | * @author zhouning 8 | * @date 2023/04/24 9:19 9 | */ 10 | public interface RoomBoardDao extends EntityDao { 11 | } 12 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/dao/RoomBoardDaoImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDaoImpl; 4 | import io.springstudent.meeting.room.pojo.RoomBoard; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * @author zhouning 9 | * @date 2023/04/24 9:19 10 | */ 11 | @Repository 12 | public class RoomBoardDaoImpl extends EntityDaoImpl implements RoomBoardDao{ 13 | } 14 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/dao/RoomBoardTxtDao.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDao; 4 | import io.springstudent.meeting.room.pojo.RoomBoardTxt; 5 | 6 | /** 7 | * @author zhouning 8 | * @date 2023/11/18 14:18 9 | */ 10 | public interface RoomBoardTxtDao extends EntityDao { 11 | } 12 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/dao/RoomBoardTxtDaoImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDaoImpl; 4 | import io.springstudent.meeting.room.pojo.RoomBoardTxt; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * @author zhouning 9 | * @date 2023/11/18 14:18 10 | */ 11 | @Repository 12 | public class RoomBoardTxtDaoImpl extends EntityDaoImpl implements RoomBoardTxtDao{ 13 | } 14 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/dao/RoomDao.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDao; 4 | import io.springstudent.meeting.room.pojo.Room; 5 | 6 | /** 7 | * @author zhouning 8 | * @date 2023/04/19 13:12 9 | */ 10 | public interface RoomDao extends EntityDao { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/dao/RoomDaoImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDaoImpl; 4 | import io.springstudent.meeting.room.pojo.Room; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * @author zhouning 9 | * @date 2023/04/19 13:12 10 | */ 11 | @Repository 12 | public class RoomDaoImpl extends EntityDaoImpl implements RoomDao{ 13 | } 14 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/dao/RoomHistoryDao.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDao; 4 | import io.springstudent.meeting.room.pojo.RoomHistory; 5 | 6 | /** 7 | * @author zhouning 8 | * @date 2023/04/19 13:13 9 | */ 10 | 11 | public interface RoomHistoryDao extends EntityDao { 12 | } 13 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/dao/RoomHistoryDaoImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.dao; 2 | 3 | import com.gysoft.jdbc.dao.EntityDaoImpl; 4 | import io.springstudent.meeting.room.pojo.RoomHistory; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * @author zhouning 9 | * @date 2023/04/19 13:14 10 | */ 11 | @Repository 12 | public class RoomHistoryDaoImpl extends EntityDaoImpl implements RoomHistoryDao { 13 | } 14 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/pojo/Room.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.pojo; 2 | 3 | import com.gysoft.jdbc.annotation.Table; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author zhouning 11 | * @date 2023/04/19 13:11 12 | */ 13 | @Data 14 | @Table(name = "room") 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class Room { 19 | 20 | private Integer id; 21 | 22 | private String roomCode; 23 | 24 | private String roomName; 25 | 26 | private Long startTime; 27 | 28 | private Long endTime; 29 | 30 | private String createUser; 31 | } 32 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/pojo/RoomBoard.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.pojo; 2 | 3 | import com.gysoft.jdbc.annotation.Table; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author zhouning 11 | * @date 2023/04/24 9:19 12 | */ 13 | @Data 14 | @Table(name = "room_board") 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class RoomBoard { 19 | 20 | private Integer id; 21 | 22 | private String roomCode; 23 | 24 | private String boardData; 25 | } 26 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/pojo/RoomBoardTxt.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.pojo; 2 | 3 | import com.gysoft.jdbc.annotation.Table; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author zhouning 11 | * @date 2023/11/18 14:16 12 | */ 13 | @Data 14 | @Table(name = "room_board_txt") 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class RoomBoardTxt { 19 | 20 | private String id; 21 | 22 | private String roomCode; 23 | 24 | private String txt; 25 | 26 | private Integer x; 27 | 28 | private Integer y; 29 | } 30 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/pojo/RoomHistory.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.pojo; 2 | 3 | import com.gysoft.jdbc.annotation.Table; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author zhouning 11 | * @date 2023/04/19 13:13 12 | */ 13 | @Data 14 | @Table(name = "room_history") 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class RoomHistory { 19 | 20 | private Integer id; 21 | 22 | private String roomCode; 23 | 24 | private String action; 25 | 26 | private String username; 27 | 28 | private Long createTime; 29 | } 30 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/service/RoomService.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.service; 2 | 3 | import com.gysoft.jdbc.bean.Page; 4 | import com.gysoft.jdbc.bean.PageResult; 5 | import io.springstudent.meeting.room.bean.RoomInfo; 6 | import io.springstudent.meeting.room.pojo.RoomBoardTxt; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author zhouning 12 | * @date 2023/04/20 13:16 13 | */ 14 | public interface RoomService { 15 | 16 | String createRoom()throws Exception; 17 | 18 | void updateRoomName(String roomCode,String roomName)throws Exception; 19 | 20 | RoomInfo queryRoom(String roomCode)throws Exception; 21 | 22 | PageResult pageRoom(Page page)throws Exception; 23 | 24 | List listRoomBoardTxt(String roomCode)throws Exception; 25 | } 26 | -------------------------------------------------------------------------------- /meeting/src/main/java/io/springstudent/meeting/room/service/RoomServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.springstudent.meeting.room.service; 2 | 3 | import com.gysoft.jdbc.bean.*; 4 | import io.springstudent.meeting.common.util.EmptyUtils; 5 | import io.springstudent.meeting.common.util.SubjectUtils; 6 | import io.springstudent.meeting.room.bean.RoomInfo; 7 | import io.springstudent.meeting.room.dao.RoomBoardDao; 8 | import io.springstudent.meeting.room.dao.RoomBoardTxtDao; 9 | import io.springstudent.meeting.room.dao.RoomDao; 10 | import io.springstudent.meeting.room.dao.RoomHistoryDao; 11 | import io.springstudent.meeting.room.pojo.Room; 12 | import io.springstudent.meeting.room.pojo.RoomBoard; 13 | import io.springstudent.meeting.room.pojo.RoomBoardTxt; 14 | import io.springstudent.meeting.room.pojo.RoomHistory; 15 | import org.apache.commons.lang3.RandomStringUtils; 16 | import org.springframework.stereotype.Service; 17 | import org.springframework.transaction.annotation.Transactional; 18 | 19 | import javax.annotation.Resource; 20 | import java.util.*; 21 | import java.util.function.Consumer; 22 | import java.util.function.Function; 23 | import java.util.stream.Collectors; 24 | 25 | /** 26 | * @author zhouning 27 | * @date 2023/04/20 13:16 28 | */ 29 | @Service 30 | public class RoomServiceImpl implements RoomService { 31 | 32 | @Resource 33 | private RoomDao roomDao; 34 | 35 | @Resource 36 | private RoomBoardDao roomBoardDao; 37 | 38 | @Resource 39 | private RoomHistoryDao roomHistoryDao; 40 | 41 | @Resource 42 | private RoomBoardTxtDao roomBoardTxtDao; 43 | 44 | @Override 45 | @Transactional(rollbackFor = Exception.class) 46 | public String createRoom() throws Exception { 47 | Long now = new Date().getTime(); 48 | String roomCode = RandomStringUtils.randomAlphanumeric(9); 49 | roomDao.save(Room.builder() 50 | .roomCode(roomCode) 51 | .roomName("会议室" + roomCode) 52 | .startTime(now) 53 | .createUser(SubjectUtils.loginUserName()) 54 | .build()); 55 | roomHistoryDao.save(RoomHistory.builder().username(SubjectUtils.loginUserName()).roomCode(roomCode).action("join").createTime(now).build()); 56 | roomBoardDao.save(RoomBoard.builder().roomCode(roomCode).boardData(null).build()); 57 | return roomCode; 58 | } 59 | 60 | @Override 61 | @Transactional(rollbackFor = Exception.class) 62 | public void updateRoomName(String roomCode, String roomName) throws Exception { 63 | roomDao.updateWithSql(new SQL().update(Room.class).set("roomName", roomName).where("roomCode", roomCode)); 64 | } 65 | 66 | @Override 67 | public RoomInfo queryRoom(String roomCode) throws Exception { 68 | RoomInfo roomInfo = roomDao.queryWithSql(RoomInfo.class, new SQL().select("id,roomCode,roomName,startTime,endTime,createUser").from(Room.class).where("roomCode", roomCode)).queryOne(); 69 | if(roomInfo!=null){ 70 | roomInfo.setJoinUser(roomHistoryDao.queryWithSql(String.class, new SQL().select("distinct(username)").from(RoomHistory.class).where("roomCode", roomCode)).queryForList()); 71 | roomInfo.setBoardData(roomBoardDao.queryWithSql(String.class,new SQL().select("boardData").from(RoomBoard.class).where("roomCode", roomCode)).queryObject()); 72 | } 73 | return roomInfo; 74 | } 75 | 76 | @Override 77 | public PageResult pageRoom(Page page) throws Exception { 78 | SQL sql = new SQL().select("id,roomCode,roomName,startTime,endTime,createUser").from(Room.class).orderBy(new Sort("startTime", "desc")); 79 | PageResult pageResult = roomDao.queryWithSql(RoomInfo.class, sql).pageQuery(page); 80 | if (EmptyUtils.isNotEmpty(pageResult.getList())) { 81 | Map> roomUserMap = roomHistoryDao.queryWithCriteria(new Criteria().in("roomCode",pageResult.getList().stream().map(RoomInfo::getRoomCode).collect(Collectors.toList()))).stream() 82 | .collect(Collectors.groupingBy(RoomHistory::getRoomCode, Collectors.mapping(RoomHistory::getUsername, Collectors.toSet()))); 83 | pageResult.getList().forEach(roomInfo -> roomInfo.setJoinUser(new ArrayList<>(roomUserMap.get(roomInfo.getRoomCode())))); 84 | } 85 | return pageResult; 86 | } 87 | 88 | @Override 89 | public List listRoomBoardTxt(String roomCode) throws Exception { 90 | return roomBoardTxtDao.queryWithCriteria(new Criteria().where("roomCode",roomCode)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /meeting/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=meeting 2 | server.servlet.context-path=/meeting 3 | server.port=9898 4 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 5 | spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 6 | spring.datasource.url=jdbc:mysql://127.0.0.1:3306/meeting?setUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false 7 | spring.datasource.username=root 8 | spring.datasource.password=root 9 | 10 | spring.redis.host=172.16.2.253 11 | spring.redis.port=6379 12 | spring.redis.database0=11 13 | spring.redis.timeout=6000 14 | spring.redis.password=Cy@2$4%5Sk 15 | spring.redis.jedis.pool.max-active=1000 16 | spring.redis.jedis.pool.max-wait=-1ms 17 | spring.redis.jedis.pool.max-idle=8 18 | spring.redis.jedis.pool.min-idle=1 19 | 20 | socketio.port=9899 -------------------------------------------------------------------------------- /meeting/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /meeting4u/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /meeting4u/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | -------------------------------------------------------------------------------- /meeting4u/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /meeting4u/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /meeting4u/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meeting4u", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@fortawesome/fontawesome-svg-core": "^6.4.0", 12 | "@fortawesome/free-brands-svg-icons": "^6.4.0", 13 | "@fortawesome/free-regular-svg-icons": "^6.4.0", 14 | "@fortawesome/free-solid-svg-icons": "^6.4.0", 15 | "@fortawesome/vue-fontawesome": "latest-2", 16 | "axios": "^1.3.5", 17 | "core-js": "^3.8.3", 18 | "element-ui": "^2.15.13", 19 | "socket.io-client": "^2.1.1", 20 | "vue": "^2.6.14", 21 | "vue-router": "^3.5.1", 22 | "vuex": "^3.4.0" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.12.16", 26 | "@babel/eslint-parser": "^7.12.16", 27 | "@vue/cli-plugin-babel": "~5.0.0", 28 | "@vue/cli-plugin-eslint": "~5.0.0", 29 | "@vue/cli-plugin-router": "~5.0.0", 30 | "@vue/cli-service": "~5.0.0", 31 | "vue-template-compiler": "^2.6.14" 32 | }, 33 | "eslintConfig": { 34 | "root": true, 35 | "env": { 36 | "node": true 37 | }, 38 | "extends": [ 39 | "plugin:vue/essential" 40 | ], 41 | "parserOptions": { 42 | "parser": "@babel/eslint-parser" 43 | }, 44 | "rules": {} 45 | }, 46 | "browserslist": [ 47 | "> 1%", 48 | "last 2 versions", 49 | "not dead" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /meeting4u/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringStudent/webrtc-meetings/4e5c2786fc9b186892430397533371bcd809a914/meeting4u/public/favicon.ico -------------------------------------------------------------------------------- /meeting4u/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /meeting4u/public/photo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | webrtc拍照 6 | 13 | 14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 | 22 | 80 | 81 | -------------------------------------------------------------------------------- /meeting4u/public/record.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 在线录制屏幕视频 10 | 11 | 12 | 13 | 29 | 30 | 31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 | 开始录制 39 | 结束录制 40 |
41 |

tips:

42 |

1、选择分享整个屏幕时,如果想要系统声音请勾选左下角分享系统中的音频

43 |

2、选择分享窗口时,系统声音无法录制

44 |
45 |
46 | 47 | 53 |

请下载录制视频,取消则清空录制视频

54 |

55 | 保存视频名字: 56 | 57 |

58 | 59 | 取 消 60 | 确 定 61 | 62 |
63 |
64 | 65 | 139 | 140 | -------------------------------------------------------------------------------- /meeting4u/public/record2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | webRTC实现纯前端录音功能 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /meeting4u/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 29 | -------------------------------------------------------------------------------- /meeting4u/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringStudent/webrtc-meetings/4e5c2786fc9b186892430397533371bcd809a914/meeting4u/src/assets/logo.png -------------------------------------------------------------------------------- /meeting4u/src/assets/meeting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringStudent/webrtc-meetings/4e5c2786fc9b186892430397533371bcd809a914/meeting4u/src/assets/meeting.png -------------------------------------------------------------------------------- /meeting4u/src/components/play.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 40 | 67 | -------------------------------------------------------------------------------- /meeting4u/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import ElementUI from 'element-ui'; 5 | import 'element-ui/lib/theme-chalk/index.css'; 6 | import axios from 'axios' 7 | import {library} from '@fortawesome/fontawesome-svg-core' 8 | 9 | import {fas} from '@fortawesome/free-solid-svg-icons' 10 | import { far } from '@fortawesome/free-regular-svg-icons' 11 | 12 | import {FontAwesomeIcon} 13 | from '@fortawesome/vue-fontawesome' 14 | 15 | library.add(fas,far) 16 | Vue.component('font-awesome-icon', FontAwesomeIcon) 17 | 18 | const service = axios.create({ 19 | timeout: 5000 20 | }) 21 | 22 | service.interceptors.request.use((config) => { 23 | if (config.url.indexOf("/meeting") == -1) { 24 | return config; 25 | } 26 | if (sessionStorage.getItem("token") != undefined) { 27 | config.headers.Token = sessionStorage.getItem("token"); 28 | } 29 | return config; 30 | }, error => { 31 | Promise.reject(error) 32 | }) 33 | 34 | service.interceptors.response.use( 35 | response => { 36 | if (response.data.code == 401) { 37 | window.sessionStorage.removeItem("token") 38 | window.sessionStorage.removeItem("username") 39 | window.sessionStorage.removeItem("logined") 40 | } 41 | return response.data; 42 | }, 43 | error => { 44 | return Promise.reject(error) 45 | } 46 | ) 47 | Vue.prototype.$axios = service 48 | Vue.use(ElementUI); 49 | Vue.config.productionTip = false 50 | Vue.prototype.$srsServerAPIURL = 'http://172.16.1.37:1985/'; 51 | Vue.prototype.$srsServerRTCURL = 'webrtc://172.16.1.37:8080/live/'; 52 | Vue.prototype.$meetingServerURL = "http://172.16.1.37:9898/meeting/" 53 | Vue.prototype.$meetingWebsocketURL = "http://172.16.1.37:9899/" 54 | 55 | 56 | new Vue({ 57 | router, 58 | render: h => h(App), 59 | }).$mount('#app') 60 | -------------------------------------------------------------------------------- /meeting4u/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import HomePage from '../views/homepage.vue' 4 | import Home from '../views/home.vue' 5 | import HistoryPage from '../views/historypage.vue' 6 | import MeetingPage from '../views/meetingpage.vue' 7 | 8 | Vue.use(VueRouter) 9 | 10 | const routes = [ 11 | { 12 | path: '/', 13 | name: 'home', 14 | component: Home, 15 | redirect:'/home', 16 | children: [ 17 | { 18 | path: '/home', 19 | name: 'HomePage', 20 | component: HomePage 21 | }, 22 | { 23 | path: '/history', 24 | name: 'HistoryPage', 25 | component: HistoryPage 26 | }, 27 | { 28 | path: '/meeting', 29 | name: 'MeetingPage', 30 | component: MeetingPage 31 | } 32 | ] 33 | 34 | }, 35 | ] 36 | 37 | const router = new VueRouter({ 38 | routes 39 | }) 40 | 41 | const whiteList = ["/history", "/meeting"] 42 | router.beforeEach((to, from, next) => { 43 | let hasToken = window.sessionStorage.getItem("token") 44 | if (whiteList.indexOf(to.path) !== -1) { 45 | if (hasToken) { 46 | next() 47 | } else { 48 | next("/") 49 | } 50 | } else { 51 | next() 52 | } 53 | }) 54 | export default router 55 | -------------------------------------------------------------------------------- /meeting4u/src/views/historypage.vue: -------------------------------------------------------------------------------- 1 | 82 | 219 | -------------------------------------------------------------------------------- /meeting4u/src/views/home.vue: -------------------------------------------------------------------------------- 1 | 38 | 73 | 74 | -------------------------------------------------------------------------------- /meeting4u/src/views/homepage.vue: -------------------------------------------------------------------------------- 1 | 137 | 138 | 425 | 426 | -------------------------------------------------------------------------------- /meeting4u/src/views/meetingpage.vue: -------------------------------------------------------------------------------- 1 | 122 | 719 | 924 | -------------------------------------------------------------------------------- /meeting4u/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | module.exports = defineConfig({ 3 | lintOnSave: false, 4 | transpileDependencies: true 5 | }) 6 | -------------------------------------------------------------------------------- /meetingdetail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringStudent/webrtc-meetings/4e5c2786fc9b186892430397533371bcd809a914/meetingdetail.png -------------------------------------------------------------------------------- /meetinghistory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringStudent/webrtc-meetings/4e5c2786fc9b186892430397533371bcd809a914/meetinghistory.png -------------------------------------------------------------------------------- /meetingstart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringStudent/webrtc-meetings/4e5c2786fc9b186892430397533371bcd809a914/meetingstart.png --------------------------------------------------------------------------------