Commit 4e925978 by 慕容若冰 Committed by Gitee

!4 dev to master

Merge pull request !4 from 慕容若冰/dev
parents 58877f5e bcd1715b
Version v3.1.0 (2019-7-18)
--------------------------
改进:
* 调整项目结构
* 调整版本号
* 去掉热部署部分内容,修复读取缓存报ClassCastException问题
* 完善登录日志,增加登录IP、耗时等信息
Version v3.0.0 (2019-7-15)
--------------------------
改进:
* 优化优化网关的动态路由
* 学生增加城市id、县id
Version v3.0.0 (2019-7-6) Version v3.0.0 (2019-7-6)
-------------------------- --------------------------
改进: 改进:
......
...@@ -30,9 +30,9 @@ QQ群号:996208878 ...@@ -30,9 +30,9 @@ QQ群号:996208878
### 部署文档 ### 部署文档
- [在线考试系统部署文档](docs/在线考试系统V2.0_部署文档.md) - [在线考试系统部署文档](docs/在线考试系统V3.0_部署文档.md)
- [基于docker-compose部署](docs/在线考试系统V2.0_部署文档(docker版).md) - [基于docker-compose部署](docs/在线考试系统V3.0_部署文档(docker版).md)
### 源码地址 ### 源码地址
...@@ -52,7 +52,7 @@ QQ群号:996208878 ...@@ -52,7 +52,7 @@ QQ群号:996208878
- 分布式配置中心:`Spring Cloud Config` - 分布式配置中心:`Spring Cloud Config`
- 分布式调用链监控:`Spring Cloud Sleuth` + `Zipkin` - 分布式调用链监控:`Spring Cloud Sleuth` + `Zipkin`
- 数据库:`MySQL 5.7` - 数据库:`MySQL 5.7`
- 部署:`Docker` - 部署:`Docker` + `docker-compose`
- 构建工具:`Maven` - 构建工具:`Maven`
- 后台 API 文档:`Swagger` - 后台 API 文档:`Swagger`
- 消息队列:`RabbitMQ` - 消息队列:`RabbitMQ`
...@@ -79,9 +79,9 @@ QQ群号:996208878 ...@@ -79,9 +79,9 @@ QQ群号:996208878
后台管理分为:系统管理、系统监控、考务管理、附件管理、个人管理 后台管理分为:系统管理、系统监控、考务管理、附件管理、个人管理
![image](docs/产品设计/功能概述.png) ![image](docs/产品设计/系统功能.jpg)
- [在线考试系统V2.0功能概述](docs/在线考试系统V2.0_功能概述.md) - [在线考试系统V3.0功能概述](docs/在线考试系统V3.0_功能概述.md)
## 功能演示 ## 功能演示
...@@ -128,7 +128,7 @@ QQ群号:996208878 ...@@ -128,7 +128,7 @@ QQ群号:996208878
- [ ] 作业、考试、知识点管理、考试成绩排名、图表展示 - [ ] 作业、考试、知识点管理、考试成绩排名、图表展示
- [ ] 学生签到、请假 - [ ] 学生签到、请假
- [ ] 短信验证码,第三方登录 - [x] 短信验证码,第三方登录
- [ ] 简答题,题库完善,智能组卷,在线学习,成绩排名 - [ ] 简答题,题库完善,智能组卷,在线学习,成绩排名
## 问题反馈 ## 问题反馈
...@@ -139,17 +139,21 @@ QQ群号:996208878 ...@@ -139,17 +139,21 @@ QQ群号:996208878
## 参考资料 ## 参考资料
- [在线考试系统V2.0设计文档-数据库设计](docs/在线考试系统V2.0_数据库设计.md) - [在线考试系统V3.0系统架构图](https://www.processon.com/view/link/5cf88937e4b0a64c88aaa3e4)
- [在线考试系统V2.0部署文档](docs/在线考试系统V2.0_部署文档.md) - [在线考试系统V3.0使用文档](docs/在线考试系统V3.0_使用文档.md)
- [在线考试系统V2.0部署文档(docker版)](docs/在线考试系统V2.0_部署文档(docker版).md) - [在线考试系统V3.0设计文档-数据库设计](docs/在线考试系统V3.0_数据库设计.md)
- [在线考试系统V2.0项目结构说明文档](docs/在线考试系统V2.0_项目结构说明.md) - [在线考试系统V3.0部署文档](docs/在线考试系统V3.0_部署文档.md)
- [在线考试系统V3.0部署文档(docker版)](docs/在线考试系统V3.0_部署文档(docker版).md)
- [在线考试系统V3.0项目结构说明文档](docs/在线考试系统V3.0_项目结构说明.md)
- [微服务架构下的安全设计方案](http://ehedgehog.net/2019/03/23/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%B8%8B%E7%9A%84%E5%AE%89%E5%85%A8%E8%AE%BE%E8%AE%A1%E6%96%B9%E6%A1%88/) - [微服务架构下的安全设计方案](http://ehedgehog.net/2019/03/23/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%B8%8B%E7%9A%84%E5%AE%89%E5%85%A8%E8%AE%BE%E8%AE%A1%E6%96%B9%E6%A1%88/)
- [在线考试系统V2.0镜像构建、推送、部署](http://ehedgehog.net/2019/04/22/%E5%9C%A8%E7%BA%BF%E8%80%83%E8%AF%95%E7%B3%BB%E7%BB%9FV2.0%E9%95%9C%E5%83%8F%E6%9E%84%E5%BB%BA%E3%80%81%E6%8E%A8%E9%80%81%E3%80%81%E9%83%A8%E7%BD%B2/) - [在线考试系统V3.0镜像构建、推送、部署](http://ehedgehog.net/2019/04/22/%E5%9C%A8%E7%BA%BF%E8%80%83%E8%AF%95%E7%B3%BB%E7%BB%9FV2.0%E9%95%9C%E5%83%8F%E6%9E%84%E5%BB%BA%E3%80%81%E6%8E%A8%E9%80%81%E3%80%81%E9%83%A8%E7%BD%B2/)
*** ***
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>common-core</artifactId> <artifactId>common-core</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
......
...@@ -4,6 +4,7 @@ import com.github.tangyi.common.core.constant.CommonConstant; ...@@ -4,6 +4,7 @@ import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.properties.SysProperties; import com.github.tangyi.common.core.properties.SysProperties;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
...@@ -25,6 +26,8 @@ public class AppStartupRunner implements CommandLineRunner { ...@@ -25,6 +26,8 @@ public class AppStartupRunner implements CommandLineRunner {
log.info("start command line..."); log.info("start command line...");
log.info("set system properties..."); log.info("set system properties...");
// 设置系统属性 // 设置系统属性
if (StringUtils.isNotBlank(sysProperties.getCacheExpire()))
System.setProperty(CommonConstant.CACHE_EXPIRE, sysProperties.getCacheExpire()); System.setProperty(CommonConstant.CACHE_EXPIRE, sysProperties.getCacheExpire());
log.info("end command line...");
} }
} }
...@@ -7,21 +7,6 @@ package com.github.tangyi.common.core.constant; ...@@ -7,21 +7,6 @@ package com.github.tangyi.common.core.constant;
public class MqConstant { public class MqConstant {
/** /**
* 修改路由
*/
public static final String EDIT_GATEWAY_ROUTE_QUEUE = "edit_gateway_route_queue";
/**
* 删除路由
*/
public static final String DEL_GATEWAY_ROUTE_QUEUE = "del_gateway_route_queue";
/**
* 刷新路由
*/
public static final String REFRESH_GATEWAY_ROUTE_QUEUE = "refresh_gateway_route_queue";
/**
* 提交考试 * 提交考试
*/ */
public static final String SUBMIT_EXAMINATION_QUEUE = "submit_examination_queue"; public static final String SUBMIT_EXAMINATION_QUEUE = "submit_examination_queue";
......
...@@ -16,22 +16,22 @@ public enum LoginType { ...@@ -16,22 +16,22 @@ public enum LoginType {
/** /**
* 账号密码登录 * 账号密码登录
*/ */
PWD("PWD", "账号密码登录"), PWD("PWD", "账号密码登录", "/oauth/token"),
/** /**
* 验证码登录 * 验证码登录
*/ */
SMS("SMS", "验证码登录"), SMS("SMS", "验证码登录", "/mobile/token"),
/** /**
* QQ登录 * QQ登录
*/ */
QQ("QQ", "QQ登录"), QQ("QQ", "QQ登录", "/mobile/token"),
/** /**
* 微信登录 * 微信登录
*/ */
WECHAT("WX", "微信登录"); WECHAT("WX", "微信登录", "/wx/token");
/** /**
* 类型 * 类型
...@@ -42,4 +42,9 @@ public enum LoginType { ...@@ -42,4 +42,9 @@ public enum LoginType {
* 描述 * 描述
*/ */
private String description; private String description;
/**
* 接口uri
*/
private String uri;
} }
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>common-feign</artifactId> <artifactId>common-feign</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>common-log</artifactId> <artifactId>common-log</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
...@@ -27,7 +28,7 @@ ...@@ -27,7 +28,7 @@
<!-- user-api --> <!-- user-api -->
<dependency> <dependency>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>user-api</artifactId> <artifactId>user-service-api</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>common-security</artifactId> <artifactId>common-security</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId> <artifactId>spring-microservice-exam</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId> <artifactId>spring-microservice-exam</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>config-service</artifactId> <artifactId>config-service</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
......
server: server:
port: 8000 port: 8000
spring: spring:
datasource:
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/microservice-gateway?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CTT&characterEncoding=UTF-8
username: ${MYSQL_USERNAME:root}
password: ${MYSQL_PASSWORD:11}
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
redis: redis:
host: ${REDIS_HOST:localhost} host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379} port: ${REDIS_PORT:6379}
...@@ -28,6 +34,21 @@ spring: ...@@ -28,6 +34,21 @@ spring:
zipkin: zipkin:
base-url: http://${ZIPKIN_HOST:localhost}:${ZIPKIN_PORT:9411} # 指定了Zipkin服务器的地址 base-url: http://${ZIPKIN_HOST:localhost}:${ZIPKIN_PORT:9411} # 指定了Zipkin服务器的地址
# mybatis配置
mybatis:
type-aliases-package: com.github.tangyi.gateway.module
configuration:
# 驼峰转换
map-underscore-to-camel-case: true
# 延迟加载
lazy-loading-enabled: true
mapper-locations: classpath:mapper/*.xml
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
# 系统配置 # 系统配置
sys: sys:
adminUser: ${ADMIN_USER:admin} # 管理员账号,默认是admin adminUser: ${ADMIN_USER:admin} # 管理员账号,默认是admin
...@@ -39,6 +60,10 @@ sys: ...@@ -39,6 +60,10 @@ sys:
# feign相关配置 # feign相关配置
feign: feign:
httpclient:
enabled: false
okhttp:
enabled: true
hystrix: hystrix:
enabled: true enabled: true
......
...@@ -70,7 +70,7 @@ services: ...@@ -70,7 +70,7 @@ services:
# 配置中心 # 配置中心
# --------------------------- # ---------------------------
config-service: config-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/config-service:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/config-service:3.1.0
container_name: config-service container_name: config-service
env_file: docker-compose.env # 从文件中获取配置 env_file: docker-compose.env # 从文件中获取配置
restart: always restart: always
......
...@@ -4,7 +4,7 @@ services: ...@@ -4,7 +4,7 @@ services:
# 前台 # 前台
# --------------------------- # ---------------------------
spring-microservice-exam-web: spring-microservice-exam-web:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/spring-microservice-exam-web:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/spring-microservice-exam-web:3.1.0
volumes: volumes:
# 挂载nginx的配置文件 # 挂载nginx的配置文件
- ./nginx.conf:/etc/nginx/nginx.conf - ./nginx.conf:/etc/nginx/nginx.conf
...@@ -20,7 +20,7 @@ services: ...@@ -20,7 +20,7 @@ services:
# 后台 # 后台
# --------------------------- # ---------------------------
spring-microservice-exam-ui: spring-microservice-exam-ui:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/spring-microservice-exam-ui:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/spring-microservice-exam-ui:3.1.0
volumes: volumes:
# 挂载nginx的配置文件 # 挂载nginx的配置文件
- ./nginx.conf:/etc/nginx/nginx.conf - ./nginx.conf:/etc/nginx/nginx.conf
......
...@@ -4,7 +4,7 @@ services: ...@@ -4,7 +4,7 @@ services:
# api网关 # api网关
# --------------------------- # ---------------------------
gateway-service: gateway-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/gateway-service:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/gateway-service:3.1.0
container_name: gateway-service container_name: gateway-service
env_file: docker-compose.env # 从文件中获取配置 env_file: docker-compose.env # 从文件中获取配置
restart: always restart: always
...@@ -17,7 +17,7 @@ services: ...@@ -17,7 +17,7 @@ services:
# 授权服务 # 授权服务
# --------------------------- # ---------------------------
auth-service: auth-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/auth-service:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/auth-service:3.1.0
container_name: auth-service container_name: auth-service
env_file: docker-compose.env # 从文件中获取配置 env_file: docker-compose.env # 从文件中获取配置
restart: always restart: always
...@@ -30,7 +30,7 @@ services: ...@@ -30,7 +30,7 @@ services:
# 用户服务 # 用户服务
# --------------------------- # ---------------------------
user-service: user-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/user-service:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/user-service:3.1.0
container_name: user-service container_name: user-service
env_file: docker-compose.env # 从文件中获取配置 env_file: docker-compose.env # 从文件中获取配置
restart: always restart: always
...@@ -43,7 +43,7 @@ services: ...@@ -43,7 +43,7 @@ services:
# 考试服务 # 考试服务
# --------------------------- # ---------------------------
exam-service: exam-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/exam-service:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/exam-service:3.1.0
container_name: exam-service container_name: exam-service
env_file: docker-compose.env # 从文件中获取配置 env_file: docker-compose.env # 从文件中获取配置
restart: always restart: always
...@@ -56,7 +56,7 @@ services: ...@@ -56,7 +56,7 @@ services:
# 消息服务 # 消息服务
# --------------------------- # ---------------------------
msc-service: msc-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/msc-service:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/msc-service:3.1.0
container_name: msc-service container_name: msc-service
env_file: docker-compose.env # 从文件中获取配置 env_file: docker-compose.env # 从文件中获取配置
restart: always restart: always
...@@ -69,7 +69,7 @@ services: ...@@ -69,7 +69,7 @@ services:
# 监控服务 # 监控服务
# --------------------------- # ---------------------------
monitor-service: monitor-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/monitor-service:3.0.0 image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/monitor-service:3.1.0
container_name: monitor-service container_name: monitor-service
env_file: docker-compose.env # 从文件中获取配置 env_file: docker-compose.env # 从文件中获取配置
restart: always restart: always
......
/*
Navicat Premium Data Transfer
Source Server : mysql_localhost
Source Server Type : MySQL
Source Server Version : 50617
Source Host : localhost:3306
Source Schema : microservice-gateway
Target Server Type : MySQL
Target Server Version : 50617
File Encoding : 65001
Date: 18/07/2019 23:06:58
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_route
-- ----------------------------
DROP TABLE IF EXISTS `sys_route`;
CREATE TABLE `sys_route` (
`id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`route_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由ID',
`route_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由名称',
`predicates` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '断言',
`filters` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '过滤器',
`uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'URI',
`sort` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '排序',
`status` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '启用禁用',
`creator` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`create_date` timestamp(0) NULL DEFAULT NULL COMMENT '创建时间',
`modifier` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改人',
`modify_date` timestamp(0) NULL DEFAULT NULL COMMENT '修改时间',
`del_flag` int(11) NULL DEFAULT NULL COMMENT '删除标记',
`application_code` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '系统编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of sys_route
-- ----------------------------
INSERT INTO `sys_route` VALUES ('0f1aec35a545433c8cc0c8e78995f039', 'auth-service', '认证授权服务', '[\n {\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/api/auth/**\"\n }\n }\n]', '[\n {\n \"name\": \"StripPrefix\",\n \"args\": {\n \"_genkey_0\": \"2\"\n }\n },\n {\n \"name\": \"RemoveRequestHeader\",\n \"args\": {\n \"_genkey_0\": \"Cookie\",\n \"_genkey_1\": \"Set-Cookie\"\n }\n }\n]', 'lb://auth-service', '0', '0', 'admin', '2019-04-07 11:20:55', 'admin', '2019-04-26 22:45:28', 0, 'EXAM');
INSERT INTO `sys_route` VALUES ('5d9dd5f2cb1147aaad6f8b82a58586e8', 'exam-service', '考试服务', '[\n {\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/api/exam/**\"\n }\n }\n]', '[\n {\n \"name\": \"StripPrefix\",\n \"args\": {\n \"_genkey_0\": \"2\"\n }\n },\n {\n \"name\": \"RemoveRequestHeader\",\n \"args\": {\n \"_genkey_0\": \"Cookie\",\n \"_genkey_1\": \"Set-Cookie\"\n }\n }\n]', 'lb://exam-service', '0', '0', 'admin', '2019-04-02 21:39:30', 'admin', '2019-04-26 22:45:30', 0, 'EXAM');
INSERT INTO `sys_route` VALUES ('e9199257e8dc4f2d8fbb2a113c407eca', 'user-service', '用户服务', '[\n {\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/api/user/**\"\n }\n }\n]', '[\n {\n \"name\": \"StripPrefix\",\n \"args\": {\n \"_genkey_0\": \"2\"\n }\n },\n {\n \"name\": \"RemoveRequestHeader\",\n \"args\": {\n \"_genkey_0\": \"Cookie\",\n \"_genkey_1\": \"Set-Cookie\"\n }\n }\n]', 'lb://user-service', '0', '0', 'admin', '2019-04-07 11:22:05', 'admin', '2019-06-07 21:48:45', 0, 'EXAM');
SET FOREIGN_KEY_CHECKS = 1;
...@@ -5,7 +5,7 @@ DOCKERHOME=/spring-microservice-exam ...@@ -5,7 +5,7 @@ DOCKERHOME=/spring-microservice-exam
# 镜像名称前缀、标签 # 镜像名称前缀、标签
BASE_IMAGE_NAME=registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam BASE_IMAGE_NAME=registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam
BSEE_IMAGE_TAG=3.0.0 BSEE_IMAGE_TAG=3.1.0
# 各服务的镜像名称 # 各服务的镜像名称
CONFIG_SERVICE=$BASE_IMAGE_NAME/config-service:$BSEE_IMAGE_TAG CONFIG_SERVICE=$BASE_IMAGE_NAME/config-service:$BSEE_IMAGE_TAG
......
docs/images/deploy/config.png

88 KB | W: | H:

docs/images/deploy/config.png

248 KB | W: | H:

docs/images/deploy/config.png
docs/images/deploy/config.png
docs/images/deploy/config.png
docs/images/deploy/config.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/deploy/config_fdfs.png

127 KB | W: | H:

docs/images/deploy/config_fdfs.png

223 KB | W: | H:

docs/images/deploy/config_fdfs.png
docs/images/deploy/config_fdfs.png
docs/images/deploy/config_fdfs.png
docs/images/deploy/config_fdfs.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/deploy/config_redis.png

88.2 KB | W: | H:

docs/images/deploy/config_redis.png

303 KB | W: | H:

docs/images/deploy/config_redis.png
docs/images/deploy/config_redis.png
docs/images/deploy/config_redis.png
docs/images/deploy/config_redis.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/deploy/config_xms.png

89.9 KB | W: | H:

docs/images/deploy/config_xms.png

162 KB | W: | H:

docs/images/deploy/config_xms.png
docs/images/deploy/config_xms.png
docs/images/deploy/config_xms.png
docs/images/deploy/config_xms.png
  • 2-up
  • Swipe
  • Onion skin
### 在线考试系统V3.0使用文档
完整项目地址:
- 前台ui:[spring-microservice-exam-web](https://gitee.com/wells2333/spring-microservice-exam-web.git)
- 后台ui:[spring-microservice-exam-ui](https://gitee.com/wells2333/spring-microservice-exam-ui.git)
- 后端:[spring-microservice-exam](https://gitee.com/wells2333/spring-microservice-exam.git)
### 系列文章
- [登录]()
- [系统管理]()
- [考务管理]()
- [系统监控]()
- [附件管理]()
- [个人资料]()
主要介绍如何基于docker、docker-compose部署后端项目、前端项目,主要的步骤是本地构建镜像,推送的远程仓库,远程服务器从远程仓库拉去镜像运行 主要介绍如何基于docker、docker-compose部署后端项目、前端项目,主要的步骤是本地构建镜像,推送的远程仓库,远程服务器从远程仓库拉去镜像运行
...@@ -10,11 +10,13 @@ ...@@ -10,11 +10,13 @@
MySQL需要单独安装,创建数据库和执行初始化脚本: MySQL需要单独安装,创建数据库和执行初始化脚本:
`/docs/deploy/mysql/microservice-user.sql` `/docs/deploy/mysql/init/microservice-user.sql`
`/docs/deploy/mysql/microservice-exam.sql` `/docs/deploy/mysql/init/microservice-exam.sql`
`/docs/deploy/mysql/microservice-auth.sql` `/docs/deploy/mysql/init/microservice-auth.sql`
`/docs/deploy/mysql/init/microservice-gateway.sql`
## 安装docker、docker-compose ## 安装docker、docker-compose
...@@ -77,4 +79,4 @@ $ docker login --username=你的阿里云账号 registry.cn-hangzhou.aliyuncs.co ...@@ -77,4 +79,4 @@ $ docker login --username=你的阿里云账号 registry.cn-hangzhou.aliyuncs.co
- [安装docker-compose](https://www.jianshu.com/p/94760f35c98c) - [安装docker-compose](https://www.jianshu.com/p/94760f35c98c)
- [在线考试系统V2.0镜像构建、推送、部署](http://ehedgehog.net/2019/04/22/%E5%9C%A8%E7%BA%BF%E8%80%83%E8%AF%95%E7%B3%BB%E7%BB%9FV2.0%E9%95%9C%E5%83%8F%E6%9E%84%E5%BB%BA%E3%80%81%E6%8E%A8%E9%80%81%E3%80%81%E9%83%A8%E7%BD%B2/) - [在线考试系统V3.0镜像构建、推送、部署](http://ehedgehog.net/2019/04/22/%E5%9C%A8%E7%BA%BF%E8%80%83%E8%AF%95%E7%B3%BB%E7%BB%9FV2.0%E9%95%9C%E5%83%8F%E6%9E%84%E5%BB%BA%E3%80%81%E6%8E%A8%E9%80%81%E3%80%81%E9%83%A8%E7%BD%B2/)
\ No newline at end of file \ No newline at end of file
...@@ -47,6 +47,8 @@ fastDfs的IP和端口号: ...@@ -47,6 +47,8 @@ fastDfs的IP和端口号:
`/docs/deploy/mysql/microservice-auth.sql` `/docs/deploy/mysql/microservice-auth.sql`
`/docs/deploy/mysql/microservice-gateway.sql`
#### 启动后端项目 #### 启动后端项目
启动项目前要**先确认consul、MySQL、redis、rabbitMq是否已经启动** 启动项目前要**先确认consul、MySQL、redis、rabbitMq是否已经启动**
...@@ -62,6 +64,7 @@ fastDfs的IP和端口号: ...@@ -62,6 +64,7 @@ fastDfs的IP和端口号:
需要监控功能再启动: 需要监控功能再启动:
6. monitor-service 6. monitor-service
7. msc-service
内存不足的可以限制每个服务的内存:`config-service`可以分配64M(-Xmx64m -Xms64m)、其它服务分配128M(-Xmx128m -Xms128m) 内存不足的可以限制每个服务的内存:`config-service`可以分配64M(-Xmx64m -Xms64m)、其它服务分配128M(-Xmx128m -Xms128m)
......
...@@ -9,13 +9,20 @@ common -- 系统公共模块 ...@@ -9,13 +9,20 @@ common -- 系统公共模块
├── common-feign -- 系统公共feign模块 ├── common-feign -- 系统公共feign模块
├── common-log -- 系统公共日志模块 ├── common-log -- 系统公共日志模块
└── common-security -- 系统公共安全模块,包括资源服务器配置 └── common-security -- 系统公共安全模块,包括资源服务器配置
service-api -- 服务api公共模块 modules -- 业务模块
├── user-api -- 用户服务api |-- auth-service-parent -- 认证授权服务
└── exam-api -- 考试服务api |-- auth-service-api -- 认证授权服务api
service-api-impl -- 服务api模块实现 |-- auth-service -- 认证授权服务具体实现
├── auth-service -- 授权服务(OAuth2.0) |-- user-service-parent -- 用户服务
├── user-service -- 用户服务 |-- user-service-api -- 用户服务api
└── exam-service -- 考试服务 |-- user-service -- 用户服务具体实现
|── exam-service-parent -- 考试服务
|-- exam-service-api -- 考试服务api
|-- exam-service -- 考试服务具体实现
└── msc-service-parent -- 消息中心
|-- msc-service-api -- 消息中心api
|-- msc-service -- 消息中心具体实现
config-service -- 配置中心(本地仓库) config-service -- 配置中心(本地仓库)
gateway-service -- 网关(动态路由) gateway-service -- 网关(动态路由)
monitor-service -- spring boot amdin 监控服务 monitor-service -- spring boot amdin 监控服务
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId> <artifactId>spring-microservice-exam</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>gateway-service</artifactId> <artifactId>gateway-service</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
...@@ -35,6 +36,18 @@ ...@@ -35,6 +36,18 @@
<groupId>de.codecentric</groupId> <groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId> <artifactId>spring-boot-admin-starter-client</artifactId>
</dependency> </dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -3,13 +3,12 @@ package com.github.tangyi.gateway; ...@@ -3,13 +3,12 @@ package com.github.tangyi.gateway;
import com.github.tangyi.gateway.annotation.EnableGatewayTokenTransfer; import com.github.tangyi.gateway.annotation.EnableGatewayTokenTransfer;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
@EnableDiscoveryClient @EnableDiscoveryClient
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) @SpringBootApplication
@ComponentScan(basePackages = {"com.github.tangyi"}) @ComponentScan(basePackages = {"com.github.tangyi"})
@EnableCircuitBreaker @EnableCircuitBreaker
@EnableGatewayTokenTransfer @EnableGatewayTokenTransfer
......
package com.github.tangyi.gateway.config;
import com.github.tangyi.common.core.constant.MqConstant;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RabbitMq配置
*
* @author tangyi
* @date 2019/4/2 17:56
*/
@Configuration
public class RabbitConfig {
/**
* 修改路由
*
* @return Queue
*/
@Bean
public Queue editQueue() {
return new Queue(MqConstant.EDIT_GATEWAY_ROUTE_QUEUE);
}
/**
* 删除路由
*
* @return Queue
*/
@Bean
public Queue delQueue() {
return new Queue(MqConstant.DEL_GATEWAY_ROUTE_QUEUE);
}
/**
* 刷新路由
*
* @return Queue
*/
@Bean
public Queue refreshQueue() {
return new Queue(MqConstant.REFRESH_GATEWAY_ROUTE_QUEUE);
}
}
package com.github.tangyi.gateway.config; package com.github.tangyi.gateway.config;
import com.github.tangyi.common.core.constant.CommonConstant; import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.model.Route;
import com.github.tangyi.common.core.utils.JsonMapper; import com.github.tangyi.common.core.utils.JsonMapper;
import com.github.tangyi.common.core.vo.RoutePredicateVo; import com.github.tangyi.gateway.module.Route;
import com.github.tangyi.gateway.service.RouteService;
import com.github.tangyi.gateway.vo.RoutePredicateVo;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
...@@ -40,6 +41,8 @@ public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvide ...@@ -40,6 +41,8 @@ public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvide
private final RedisTemplate redisTemplate; private final RedisTemplate redisTemplate;
private final RouteService routeService;
@Override @Override
public List<SwaggerResource> get() { public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>(); List<SwaggerResource> resources = new ArrayList<>();
...@@ -50,11 +53,8 @@ public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvide ...@@ -50,11 +53,8 @@ public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvide
if (swaggerProviderConfig.getProviders().contains(route.getId())) if (swaggerProviderConfig.getProviders().contains(route.getId()))
routes.add(route.getId()); routes.add(route.getId());
}); });
// 结合配置的route-路径(Path),和route过滤,只获取有效的route节点 // 结合配置的route-路径(Path),和route过滤,只获取有效的route节点
Object object = redisTemplate.opsForValue().get(CommonConstant.ROUTE_KEY); List<Route> routeList = this.routeList();
if (object != null) {
List<Route> routeList = JsonMapper.getInstance().fromJson(object.toString(), JsonMapper.getInstance().createCollectionType(ArrayList.class, Route.class));
if (CollectionUtils.isNotEmpty(routeList)) { if (CollectionUtils.isNotEmpty(routeList)) {
for (Route route : routeList) { for (Route route : routeList) {
if (routes.contains(route.getRouteId())) { if (routes.contains(route.getRouteId())) {
...@@ -74,11 +74,30 @@ public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvide ...@@ -74,11 +74,30 @@ public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvide
} }
} }
} }
}
return resources; return resources;
} }
/** /**
* 获取路由列表
*
* @return List<Route>
*/
private List<Route> routeList() {
List<Route> routeList;
// 结合配置的route-路径(Path),和route过滤,只获取有效的route节点
Object object = redisTemplate.opsForValue().get(CommonConstant.ROUTE_KEY);
if (object == null) {
// 放入缓存
routeList = routeService.findList(new Route());
if (CollectionUtils.isNotEmpty(routeList))
redisTemplate.opsForValue().set(CommonConstant.ROUTE_KEY, JsonMapper.getInstance().toJson(routeList));
} else {
routeList = JsonMapper.getInstance().fromJson(object.toString(), JsonMapper.getInstance().createCollectionType(ArrayList.class, Route.class));
}
return routeList;
}
/**
* 服务信息 * 服务信息
* *
* @param name name * @param name name
......
package com.github.tangyi.gateway.config; package com.github.tangyi.gateway.config;
import com.github.tangyi.common.core.constant.CommonConstant; import com.github.tangyi.gateway.service.RouteService;
import com.github.tangyi.common.core.model.Route;
import com.github.tangyi.common.core.utils.JsonMapper;
import com.github.tangyi.gateway.receiver.GatewayRouteReceiver;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/** /**
* 动态路由实现:修改数据库路由配置,用户服务发送路由更新消息,网关消费消息,更新路由配置 * 初始化的时候加载路由数据
* 依赖Redis,如果Redis被清空,需要手动刷新加载路由列表
* *
* @author tangyi * @author tangyi
* @date 2019/4/2 14:40 * @date 2019/4/2 14:40
...@@ -26,21 +18,10 @@ import java.util.List; ...@@ -26,21 +18,10 @@ import java.util.List;
@Configuration @Configuration
public class RouteInitConfig { public class RouteInitConfig {
private final RedisTemplate redisTemplate; private final RouteService routeService;
private final GatewayRouteReceiver gatewayRouteReceiver;
@PostConstruct @PostConstruct
public void initRoute() { public void initRoute() {
// 重Redis加载路由列表 routeService.refresh();
Object object = redisTemplate.opsForValue().get(CommonConstant.ROUTE_KEY);
if (object != null) {
List<Route> routes = JsonMapper.getInstance().fromJson(object.toString(), JsonMapper.getInstance().createCollectionType(ArrayList.class, Route.class));
if (CollectionUtils.isNotEmpty(routes)) {
log.info("加载{}条路由记录", routes.size());
for (Route route : routes)
gatewayRouteReceiver.editRoute(route);
}
}
} }
} }
...@@ -51,4 +51,14 @@ public class GatewayConstant { ...@@ -51,4 +51,14 @@ public class GatewayConstant {
*/ */
public static final String GATEWAY_REFRESH_TOKENS = "gateway_refresh:"; public static final String GATEWAY_REFRESH_TOKENS = "gateway_refresh:";
/**
* 默认系统编号
*/
public static final String SYS_CODE = "EXAM";
/**
* 默认租户编号
*/
public static final String DEFAULT_TENANT_CODE = "gitee";
} }
package com.github.tangyi.gateway.controller;
import com.github.tangyi.common.core.constant.MqConstant;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.vo.RouteFilterVo;
import com.github.tangyi.common.core.vo.RoutePredicateVo;
import com.github.tangyi.common.core.vo.RouteVo;
import com.github.tangyi.gateway.service.DynamicRouteService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 动态路由Controller
*
* @author tangyi
* @date 2019/3/27 10:59
*/
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/api/route")
public class GatewayRouteController {
private final RouteDefinitionLocator routeDefinitionLocator;
private final RouteLocator routeLocator;
private final DynamicRouteService dynamicRouteService;
private final AmqpTemplate amqpTemplate;
/**
* 获取路由信息列表
*
* @return Mono
*/
@GetMapping("routeList")
public Mono<List<Map<String, Object>>> routes() {
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator
.getRouteDefinitions().collectMap(RouteDefinition::getId);
Mono<List<Route>> routes = this.routeLocator.getRoutes().collectList();
return Mono.zip(routeDefs, routes).map(tuple -> {
Map<String, RouteDefinition> defs = tuple.getT1();
List<Route> routeList = tuple.getT2();
List<Map<String, Object>> allRoutes = new ArrayList<>();
routeList.forEach(route -> {
HashMap<String, Object> r = new HashMap<>();
r.put("route_id", route.getId());
r.put("order", route.getOrder());
if (defs.containsKey(route.getId())) {
r.put("route_definition", defs.get(route.getId()));
} else {
HashMap<String, Object> obj = new HashMap<>();
obj.put("predicate", route.getPredicate().toString());
if (!route.getFilters().isEmpty()) {
ArrayList<String> filters = new ArrayList<>();
for (GatewayFilter filter : route.getFilters()) {
filters.add(filter.toString());
}
obj.put("filters", filters);
}
if (!obj.isEmpty()) {
r.put("route_object", obj);
}
}
allRoutes.add(r);
});
return allRoutes;
});
}
/**
* 增加路由
*
* @param gatewayRouteDefinition gatewayRouteDefinition
* @return ResponseBean
*/
@PostMapping
public ResponseBean<String> add(@RequestBody RouteVo gatewayRouteDefinition) {
try {
RouteDefinition definition = assembleRouteDefinition(gatewayRouteDefinition);
log.info("新增路由:{},{}", definition.getId(), definition);
return new ResponseBean<>(this.dynamicRouteService.add(definition));
} catch (Exception e) {
e.printStackTrace();
}
return new ResponseBean<>("success");
}
/**
* 删除路由
*
* @param id id
* @return ResponseEntity
*/
@DeleteMapping("/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
log.info("删除路由:{}", id);
return this.dynamicRouteService.delete(id);
}
/**
* 更新路由
*
* @param routeVo routeVo
* @return ResponseBean
*/
@PutMapping
public ResponseBean<String> update(@RequestBody RouteVo routeVo) {
RouteDefinition definition = assembleRouteDefinition(routeVo);
return new ResponseBean<>(this.dynamicRouteService.update(definition));
}
/**
* 刷新路由
*
* @return ResponseBean
* @author tangyi
* @date 2019/04/07 12:32
*/
@GetMapping("refresh")
public ResponseBean<Boolean> refresh() {
amqpTemplate.convertAndSend(MqConstant.REFRESH_GATEWAY_ROUTE_QUEUE, "refresh");
return new ResponseBean<>(Boolean.TRUE);
}
/**
* @param routeVo routeVo
* @return RouteDefinition
*/
private RouteDefinition assembleRouteDefinition(RouteVo routeVo) {
RouteDefinition definition = new RouteDefinition();
// id
definition.setId(routeVo.getRouteId());
List<PredicateDefinition> predicateDefinitions = new ArrayList<>();
// predicates
for (RoutePredicateVo routePredicateVo : routeVo.getPredicates()) {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setArgs(routePredicateVo.getArgs());
predicate.setName(routePredicateVo.getName());
predicateDefinitions.add(predicate);
}
definition.setPredicates(predicateDefinitions);
// filters
List<FilterDefinition> filterDefinitions = new ArrayList<>();
for (RouteFilterVo routeFilterVo : routeVo.getFilters()) {
FilterDefinition filterDefinition = new FilterDefinition();
filterDefinition.setName(routeFilterVo.getName());
filterDefinition.setArgs(routeFilterVo.getArgs());
filterDefinitions.add(filterDefinition);
}
definition.setFilters(filterDefinitions);
// uri
definition.setUri(URI.create(routeVo.getUri()));
return definition;
}
}
package com.github.tangyi.user.controller; package com.github.tangyi.gateway.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.github.tangyi.common.core.constant.CommonConstant; import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.constant.MqConstant; import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.model.ResponseBean; import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.model.Route;
import com.github.tangyi.common.core.utils.PageUtil; import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController; import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log; import com.github.tangyi.gateway.module.Route;
import com.github.tangyi.common.security.constant.SecurityConstant; import com.github.tangyi.gateway.service.RouteService;
import com.github.tangyi.user.service.RouteService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collections;
import java.util.List;
/** /**
* 路由controller * 路由controller
* TODO:增加security认证
* *
* @author tangyi * @author tangyi
* @date 2019/4/2 15:03 * @date 2019/4/2 15:03
*/ */
@Slf4j @Slf4j
@AllArgsConstructor @AllArgsConstructor
@Api("网关路由信息管理")
@RestController @RestController
@RequestMapping("/v1/route") @RequestMapping("/api/route/v1/route")
public class RouteController extends BaseController { public class RouteController extends BaseController {
private final RouteService routeService; private final RouteService routeService;
private final AmqpTemplate amqpTemplate;
/** /**
* 根据id获取路由 * 根据id获取路由
...@@ -54,15 +40,15 @@ public class RouteController extends BaseController { ...@@ -54,15 +40,15 @@ public class RouteController extends BaseController {
* @date 2019/4/2 15:09 * @date 2019/4/2 15:09
*/ */
@GetMapping("/{id}") @GetMapping("/{id}")
@ApiOperation(value = "获取路由信息", notes = "根据路由id获取路由详细信息")
@ApiImplicitParam(name = "id", value = "路由ID", required = true, dataType = "String", paramType = "path")
public Route get(@PathVariable String id) { public Route get(@PathVariable String id) {
try { try {
return routeService.get(id); Route route = new Route();
route.setId(id);
return routeService.get(route);
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
throw new CommonException(e.getMessage());
} }
return new Route();
} }
/** /**
...@@ -78,14 +64,6 @@ public class RouteController extends BaseController { ...@@ -78,14 +64,6 @@ public class RouteController extends BaseController {
* @date 2019/4/2 15:09 * @date 2019/4/2 15:09
*/ */
@GetMapping("routeList") @GetMapping("routeList")
@ApiOperation(value = "获取路由列表")
@ApiImplicitParams({
@ApiImplicitParam(name = CommonConstant.PAGE_NUM, value = "分页页码", defaultValue = CommonConstant.PAGE_NUM_DEFAULT, dataType = "String"),
@ApiImplicitParam(name = CommonConstant.PAGE_SIZE, value = "分页大小", defaultValue = CommonConstant.PAGE_SIZE_DEFAULT, dataType = "String"),
@ApiImplicitParam(name = CommonConstant.SORT, value = "排序字段", defaultValue = CommonConstant.PAGE_SORT_DEFAULT, dataType = "String"),
@ApiImplicitParam(name = CommonConstant.ORDER, value = "排序方向", defaultValue = CommonConstant.PAGE_ORDER_DEFAULT, dataType = "String"),
@ApiImplicitParam(name = "route", value = "路由信息", dataType = "Route")
})
public PageInfo<Route> userList(@RequestParam(value = CommonConstant.PAGE_NUM, required = false, defaultValue = CommonConstant.PAGE_NUM_DEFAULT) String pageNum, public PageInfo<Route> userList(@RequestParam(value = CommonConstant.PAGE_NUM, required = false, defaultValue = CommonConstant.PAGE_NUM_DEFAULT) String pageNum,
@RequestParam(value = CommonConstant.PAGE_SIZE, required = false, defaultValue = CommonConstant.PAGE_SIZE_DEFAULT) String pageSize, @RequestParam(value = CommonConstant.PAGE_SIZE, required = false, defaultValue = CommonConstant.PAGE_SIZE_DEFAULT) String pageSize,
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort, @RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
...@@ -103,22 +81,14 @@ public class RouteController extends BaseController { ...@@ -103,22 +81,14 @@ public class RouteController extends BaseController {
* @date 2019/4/2 15:09 * @date 2019/4/2 15:09
*/ */
@PutMapping @PutMapping
@PreAuthorize("hasAuthority('sys:route:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "更新路由信息", notes = "根据路由id更新路由的基本信息")
@ApiImplicitParam(name = "route", value = "路由实体route", required = true, dataType = "Route")
@Log("修改路由")
public ResponseBean<Boolean> updateRoute(@RequestBody @Valid Route route) { public ResponseBean<Boolean> updateRoute(@RequestBody @Valid Route route) {
route.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode()); try {
// 更新路由 // 更新路由
if (routeService.update(route) > 0) { return new ResponseBean<>(routeService.update(route) > 0);
// 发送消息 } catch (Exception e) {
if (Integer.parseInt(route.getStatus()) == CommonConstant.DEL_FLAG_DEL) { log.error(e.getMessage(), e);
amqpTemplate.convertAndSend(MqConstant.DEL_GATEWAY_ROUTE_QUEUE, route); throw new CommonException(e.getMessage());
} else {
amqpTemplate.convertAndSend(MqConstant.EDIT_GATEWAY_ROUTE_QUEUE, route);
}
} }
return new ResponseBean<>(Boolean.TRUE);
} }
/** /**
...@@ -130,17 +100,14 @@ public class RouteController extends BaseController { ...@@ -130,17 +100,14 @@ public class RouteController extends BaseController {
* @date 2019/4/2 15:09 * @date 2019/4/2 15:09
*/ */
@PostMapping @PostMapping
@PreAuthorize("hasAuthority('sys:route:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "创建路由", notes = "创建路由")
@ApiImplicitParam(name = "route", value = "路由实体route", required = true, dataType = "Route")
@Log("新增路由")
public ResponseBean<Boolean> add(@RequestBody @Valid Route route) { public ResponseBean<Boolean> add(@RequestBody @Valid Route route) {
route.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode()); try {
if (routeService.insert(route) > 0 && Integer.parseInt(route.getStatus()) == CommonConstant.DEL_FLAG_NORMAL) { // 新增路由
// 发送消息 return new ResponseBean<>(routeService.insert(route) > 0);
amqpTemplate.convertAndSend(MqConstant.EDIT_GATEWAY_ROUTE_QUEUE, route); } catch (Exception e) {
log.error(e.getMessage(), e);
throw new CommonException(e.getMessage());
} }
return new ResponseBean<>(Boolean.TRUE);
} }
/** /**
...@@ -152,21 +119,13 @@ public class RouteController extends BaseController { ...@@ -152,21 +119,13 @@ public class RouteController extends BaseController {
* @date 2019/4/2 15:09 * @date 2019/4/2 15:09
*/ */
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('sys:route:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "删除路由", notes = "根据ID删除路由")
@ApiImplicitParam(name = "id", value = "路由ID", required = true, paramType = "path")
@Log("删除路由")
public ResponseBean<Boolean> delete(@PathVariable String id) { public ResponseBean<Boolean> delete(@PathVariable String id) {
Route route = new Route(); try {
route.setId(id); return new ResponseBean<>(routeService.delete(id) > 0);
route = routeService.get(route); } catch (Exception e) {
route.setNewRecord(false); log.error(e.getMessage(), e);
route.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode()); throw new CommonException(e.getMessage());
if (routeService.delete(route) > 0) {
// 发送消息
amqpTemplate.convertAndSend(MqConstant.DEL_GATEWAY_ROUTE_QUEUE, Collections.singletonList(route));
} }
return new ResponseBean<>(Boolean.TRUE);
} }
/** /**
...@@ -178,26 +137,33 @@ public class RouteController extends BaseController { ...@@ -178,26 +137,33 @@ public class RouteController extends BaseController {
* @date 2019/4/2 15:09 * @date 2019/4/2 15:09
*/ */
@PostMapping("deleteAll") @PostMapping("deleteAll")
@PreAuthorize("hasAuthority('sys:route:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "批量删除路由", notes = "根据路由id批量删除路由")
@ApiImplicitParam(name = "route", value = "路由信息", dataType = "Route")
@Log("批量删除路由")
public ResponseBean<Boolean> deleteAll(@RequestBody Route route) { public ResponseBean<Boolean> deleteAll(@RequestBody Route route) {
boolean success = false; boolean success = false;
try { try {
if (StringUtils.isNotEmpty(route.getIdString())) { if (StringUtils.isNotEmpty(route.getIdString()))
// 先获取路由列表
List<Route> routeList = routeService.findListById(route);
// 删除
success = routeService.deleteAll(route.getIdString().split(",")) > 0; success = routeService.deleteAll(route.getIdString().split(",")) > 0;
if (success && CollectionUtils.isNotEmpty(routeList)) { return new ResponseBean<>(success);
// 发送消息 } catch (Exception e) {
amqpTemplate.convertAndSend(MqConstant.DEL_GATEWAY_ROUTE_QUEUE, route); log.error(e.getMessage(), e);
throw new CommonException(e.getMessage());
} }
} }
/**
* 刷新路由
*
* @return ResponseBean
* @author tangyi
* @date 2019/04/07 12:32
*/
@GetMapping("refresh")
public ResponseBean<Boolean> refresh() {
try {
return new ResponseBean<>(routeService.refresh());
} catch (Exception e) { } catch (Exception e) {
log.error("删除路由失败!", e); log.error(e.getMessage());
throw new CommonException(e.getMessage());
} }
return new ResponseBean<>(success);
} }
} }
package com.github.tangyi.user.mapper; package com.github.tangyi.gateway.mapper;
import com.github.tangyi.common.core.model.Route; import com.github.tangyi.gateway.module.Route;
import com.github.tangyi.common.core.persistence.CrudMapper; import com.github.tangyi.common.core.persistence.CrudMapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
......
package com.github.tangyi.common.core.model; package com.github.tangyi.gateway.module;
import com.github.tangyi.common.core.persistence.BaseEntity; import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data; import lombok.Data;
......
package com.github.tangyi.gateway.receiver;
import com.fasterxml.jackson.databind.JavaType;
import com.github.tangyi.common.core.constant.MqConstant;
import com.github.tangyi.common.core.model.Route;
import com.github.tangyi.common.core.utils.JsonMapper;
import com.github.tangyi.common.core.vo.RouteFilterVo;
import com.github.tangyi.common.core.vo.RoutePredicateVo;
import com.github.tangyi.gateway.service.DynamicRouteService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* 动态路由
*
* @author tangyi
* @date 2019/4/2 18:07
*/
@Slf4j
@AllArgsConstructor
@Service
public class GatewayRouteReceiver {
private final DynamicRouteService dynamicRouteService;
/**
* 修改路由
*
* @param route route
* @author tangyi
* @date 2019/04/02 20:51
*/
@RabbitListener(queues = {MqConstant.EDIT_GATEWAY_ROUTE_QUEUE})
public void editRoute(Route route) {
if (route.getRouteId() == null)
throw new IllegalArgumentException("routeId不能为空!");
log.info("更新{}路由", route.getRouteId());
dynamicRouteService.update(routeDefinition(route));
}
/**
* 删除路由
*
* @param routes routes
* @author tangyi
* @date 2019/04/02 20:51
*/
@RabbitListener(queues = {MqConstant.DEL_GATEWAY_ROUTE_QUEUE})
public void delRoute(List<Route> routes) {
if (routes == null || routes.isEmpty())
return;
for (Route route : routes) {
if (route.getRouteId() == null)
throw new IllegalArgumentException("routeId不能为空!");
log.info("删除{}路由", route.getRouteId());
dynamicRouteService.delete(route.getRouteId());
}
}
/**
* 初始化RouteDefinition
*
* @param route route
* @return RouteDefinition
* @author tangyi
* @date 2019/04/02 18:50
*/
private RouteDefinition routeDefinition(Route route) {
RouteDefinition routeDefinition = new RouteDefinition();
// id
routeDefinition.setId(route.getRouteId());
// predicates
if (StringUtils.isNotBlank(route.getPredicates()))
routeDefinition.setPredicates(predicateDefinitions(route));
// filters
if (StringUtils.isNotBlank(route.getFilters())) {
routeDefinition.setFilters(filterDefinitions(route));
}
// uri
routeDefinition.setUri(URI.create(route.getUri()));
return routeDefinition;
}
/**
* @param route route
* @return List
* @author tangyi
* @date 2019/04/02 21:28
*/
private List<PredicateDefinition> predicateDefinitions(Route route) {
List<PredicateDefinition> predicateDefinitions = new ArrayList<>();
try {
List<RoutePredicateVo> routePredicateVoList = JsonMapper.getInstance().fromJson(route.getPredicates(),
JsonMapper.getInstance().createCollectionType(ArrayList.class, RoutePredicateVo.class));
if (CollectionUtils.isNotEmpty(routePredicateVoList)) {
for (RoutePredicateVo routePredicateVo : routePredicateVoList) {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setArgs(routePredicateVo.getArgs());
predicate.setName(routePredicateVo.getName());
predicateDefinitions.add(predicate);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return predicateDefinitions;
}
/**
* @param route route
* @return List
* @author tangyi
* @date 2019/04/02 21:29
*/
private List<FilterDefinition> filterDefinitions(Route route) {
List<FilterDefinition> filterDefinitions = new ArrayList<>();
try {
JavaType javaType = JsonMapper.getInstance().createCollectionType(ArrayList.class, RouteFilterVo.class);
List<RouteFilterVo> gatewayFilterDefinitions = JsonMapper.getInstance().fromJson(route.getFilters(), javaType);
if (CollectionUtils.isNotEmpty(gatewayFilterDefinitions)) {
for (RouteFilterVo gatewayFilterDefinition : gatewayFilterDefinitions) {
FilterDefinition filterDefinition = new FilterDefinition();
filterDefinition.setName(gatewayFilterDefinition.getName());
filterDefinition.setArgs(gatewayFilterDefinition.getArgs());
filterDefinitions.add(filterDefinition);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return filterDefinitions;
}
}
package com.github.tangyi.gateway.service;
import com.fasterxml.jackson.databind.JavaType;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.JsonMapper;
import com.github.tangyi.gateway.constants.GatewayConstant;
import com.github.tangyi.gateway.mapper.RouteMapper;
import com.github.tangyi.gateway.module.Route;
import com.github.tangyi.gateway.vo.RouteFilterVo;
import com.github.tangyi.gateway.vo.RoutePredicateVo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* 路由service
*
* @author tangyi
* @date 2019/4/2 15:01
*/
@Slf4j
@AllArgsConstructor
@Service
public class RouteService extends CrudService<RouteMapper, Route> {
private final DynamicRouteService dynamicRouteService;
private final RedisTemplate redisTemplate;
/**
* 新增路由
*
* @param route route
* @return int
*/
@Override
public int insert(Route route) {
int update;
if (StringUtils.isBlank(route.getRouteId()))
throw new CommonException("服务ID不能为空!");
// 校验服务路由是否存在
Route condition = new Route();
condition.setRouteId(route.getRouteId());
List<Route> routes = this.findList(condition);
if (CollectionUtils.isNotEmpty(routes))
throw new CommonException("该服务的路由已存在!");
route.setCommonValue("", GatewayConstant.SYS_CODE, GatewayConstant.DEFAULT_TENANT_CODE);
if ((update = this.dao.insert(route)) > 0) {
dynamicRouteService.add(routeDefinition(route));
}
return update;
}
/**
* 更新路由
*
* @param route route
* @return int
*/
@Override
public int update(Route route) {
int update;
if (StringUtils.isBlank(route.getRouteId()))
throw new CommonException("服务ID不能为空!");
route.setNewRecord(false);
route.setCommonValue("", GatewayConstant.SYS_CODE, GatewayConstant.DEFAULT_TENANT_CODE);
if ((update = this.dao.update(route)) > 0) {
dynamicRouteService.update(routeDefinition(route));
}
return update;
}
/**
* 删除路由
*
* @param id id
* @return Mono
*/
@Transactional
public int delete(String id) {
Route route = new Route();
route.setId(id);
route.setNewRecord(false);
route.setCommonValue("", GatewayConstant.SYS_CODE, GatewayConstant.DEFAULT_TENANT_CODE);
int update = this.dao.delete(route);
dynamicRouteService.delete(id);
return update;
}
/**
* 刷新路由
*
* @return boolean
*/
public boolean refresh() {
Route init = new Route();
init.setStatus(CommonConstant.DEL_FLAG_NORMAL.toString());
List<Route> routes = this.findList(init);
if (CollectionUtils.isNotEmpty(routes)) {
log.info("加载{}条路由记录", routes.size());
for (Route route : routes)
dynamicRouteService.update(routeDefinition(route));
// 存入Redis
redisTemplate.opsForValue().set(CommonConstant.ROUTE_KEY, JsonMapper.getInstance().toJson(routes));
}
return true;
}
/**
* 初始化RouteDefinition
*
* @param route route
* @return RouteDefinition
* @author tangyi
* @date 2019/04/02 18:50
*/
private RouteDefinition routeDefinition(Route route) {
RouteDefinition routeDefinition = new RouteDefinition();
// id
routeDefinition.setId(route.getRouteId());
// predicates
if (StringUtils.isNotBlank(route.getPredicates()))
routeDefinition.setPredicates(predicateDefinitions(route));
// filters
if (StringUtils.isNotBlank(route.getFilters())) {
routeDefinition.setFilters(filterDefinitions(route));
}
// uri
routeDefinition.setUri(URI.create(route.getUri()));
return routeDefinition;
}
/**
* @param route route
* @return List
* @author tangyi
* @date 2019/04/02 21:28
*/
private List<PredicateDefinition> predicateDefinitions(Route route) {
List<PredicateDefinition> predicateDefinitions = new ArrayList<>();
try {
List<RoutePredicateVo> routePredicateVoList = JsonMapper.getInstance().fromJson(route.getPredicates(),
JsonMapper.getInstance().createCollectionType(ArrayList.class, RoutePredicateVo.class));
if (CollectionUtils.isNotEmpty(routePredicateVoList)) {
for (RoutePredicateVo routePredicateVo : routePredicateVoList) {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setArgs(routePredicateVo.getArgs());
predicate.setName(routePredicateVo.getName());
predicateDefinitions.add(predicate);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return predicateDefinitions;
}
/**
* @param route route
* @return List
* @author tangyi
* @date 2019/04/02 21:29
*/
private List<FilterDefinition> filterDefinitions(Route route) {
List<FilterDefinition> filterDefinitions = new ArrayList<>();
try {
JavaType javaType = JsonMapper.getInstance().createCollectionType(ArrayList.class, RouteFilterVo.class);
List<RouteFilterVo> gatewayFilterDefinitions = JsonMapper.getInstance().fromJson(route.getFilters(), javaType);
if (CollectionUtils.isNotEmpty(gatewayFilterDefinitions)) {
for (RouteFilterVo gatewayFilterDefinition : gatewayFilterDefinitions) {
FilterDefinition filterDefinition = new FilterDefinition();
filterDefinition.setName(gatewayFilterDefinition.getName());
filterDefinition.setArgs(gatewayFilterDefinition.getArgs());
filterDefinitions.add(filterDefinition);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return filterDefinitions;
}
}
package com.github.tangyi.common.core.vo; package com.github.tangyi.gateway.vo;
import lombok.Data; import lombok.Data;
......
package com.github.tangyi.common.core.vo; package com.github.tangyi.gateway.vo;
import lombok.Data; import lombok.Data;
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.github.tangyi.user.mapper.RouteMapper"> <mapper namespace="com.github.tangyi.gateway.mapper.RouteMapper">
<resultMap id="routeResultMap" type="com.github.tangyi.common.core.model.Route"> <resultMap id="routeResultMap" type="com.github.tangyi.gateway.module.Route">
<id column="id" property="id"/> <id column="id" property="id"/>
<result column="route_id" property="routeId"/> <result column="route_id" property="routeId"/>
<result column="route_name" property="routeName"/> <result column="route_name" property="routeName"/>
......
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>service-api</artifactId> <artifactId>auth-service-parent</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>auth-api</artifactId> <artifactId>auth-service-api</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
<description>授权服务api</description> <description>授权服务api</description>
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>service-api-impl</artifactId> <artifactId>auth-service-parent</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>auth-service</artifactId> <artifactId>auth-service</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
...@@ -30,22 +31,22 @@ ...@@ -30,22 +31,22 @@
<artifactId>common-log</artifactId> <artifactId>common-log</artifactId>
</dependency> </dependency>
<!-- user-api --> <!-- user-service-api -->
<dependency> <dependency>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>user-api</artifactId> <artifactId>user-service-api</artifactId>
</dependency> </dependency>
<!-- auth-api --> <!-- auth-service-api -->
<dependency> <dependency>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>auth-api</artifactId> <artifactId>auth-service-api</artifactId>
</dependency> </dependency>
<!-- exam-api --> <!-- exam-service-api -->
<dependency> <dependency>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>exam-api</artifactId> <artifactId>exam-service-api</artifactId>
</dependency> </dependency>
<!-- web 服务 --> <!-- web 服务 -->
......
package com.github.tangyi.auth.model; package com.github.tangyi.auth.model;
import com.github.tangyi.common.core.enums.LoginType;
import lombok.Data; import lombok.Data;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User;
...@@ -17,9 +18,22 @@ public class CustomUserDetails extends User { ...@@ -17,9 +18,22 @@ public class CustomUserDetails extends User {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* 租户标识
*/
private String tenantCode; private String tenantCode;
/** /**
* 开始授权时间
*/
private long start;
/**
* 登录类型
*/
private LoginType loginType;
/**
* 构造方法 * 构造方法
* *
* @param username username * @param username username
...@@ -27,9 +41,13 @@ public class CustomUserDetails extends User { ...@@ -27,9 +41,13 @@ public class CustomUserDetails extends User {
* @param enabled enabled * @param enabled enabled
* @param authorities authorities * @param authorities authorities
* @param tenantCode tenantCode * @param tenantCode tenantCode
* @param start start
* @param loginType loginType
*/ */
public CustomUserDetails(String username, String password, boolean enabled, Collection<? extends GrantedAuthority> authorities, String tenantCode) { public CustomUserDetails(String username, String password, boolean enabled, Collection<? extends GrantedAuthority> authorities, String tenantCode, long start, LoginType loginType) {
super(username, password, enabled, true, true, true, authorities); super(username, password, enabled, true, true, true, authorities);
this.tenantCode = tenantCode; this.tenantCode = tenantCode;
this.start = start;
this.loginType = loginType;
} }
} }
...@@ -5,10 +5,12 @@ import com.github.tangyi.common.core.constant.CommonConstant; ...@@ -5,10 +5,12 @@ import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.constant.ServiceConstant; import com.github.tangyi.common.core.constant.ServiceConstant;
import com.github.tangyi.common.core.model.Log; import com.github.tangyi.common.core.model.Log;
import com.github.tangyi.common.core.utils.SysUtil; import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.user.api.feign.UserServiceClient; import com.github.tangyi.user.api.feign.UserServiceClient;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.http.HttpMethod;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent; import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
...@@ -39,6 +41,8 @@ public class CustomAuthenticationSuccessHandler { ...@@ -39,6 +41,8 @@ public class CustomAuthenticationSuccessHandler {
@EventListener({AuthenticationSuccessEvent.class, InteractiveAuthenticationSuccessEvent.class}) @EventListener({AuthenticationSuccessEvent.class, InteractiveAuthenticationSuccessEvent.class})
public void processAuthenticationSuccessEvent(AbstractAuthenticationEvent event) { public void processAuthenticationSuccessEvent(AbstractAuthenticationEvent event) {
// 注意:登录包括oauth2客户端、用户名密码登录都会触发AuthenticationSuccessEvent,这里只记录用户名密码登录的日志 // 注意:登录包括oauth2客户端、用户名密码登录都会触发AuthenticationSuccessEvent,这里只记录用户名密码登录的日志
if (event.getSource() instanceof OAuth2Authentication)
return;
if (event.getAuthentication().getPrincipal() instanceof CustomUserDetails) { if (event.getAuthentication().getPrincipal() instanceof CustomUserDetails) {
CustomUserDetails userDetails = (CustomUserDetails) event.getAuthentication().getPrincipal(); CustomUserDetails userDetails = (CustomUserDetails) event.getAuthentication().getPrincipal();
String tenantCode = userDetails.getTenantCode(); String tenantCode = userDetails.getTenantCode();
...@@ -48,7 +52,10 @@ public class CustomAuthenticationSuccessHandler { ...@@ -48,7 +52,10 @@ public class CustomAuthenticationSuccessHandler {
Log logInfo = new Log(); Log logInfo = new Log();
logInfo.setCommonValue(username, SysUtil.getSysCode(), tenantCode); logInfo.setCommonValue(username, SysUtil.getSysCode(), tenantCode);
logInfo.setTitle("用户登录"); logInfo.setTitle("用户登录");
//logInfo.setType(CommonConstant.STATUS_NORMAL); logInfo.setType(CommonConstant.STATUS_NORMAL);
logInfo.setMethod(HttpMethod.POST.name());
logInfo.setTime(String.valueOf(System.currentTimeMillis() - userDetails.getStart()));
logInfo.setRequestUri(userDetails.getLoginType().getUri());
// 获取ip、浏览器信息 // 获取ip、浏览器信息
this.initLogInfo(logInfo, event.getSource()); this.initLogInfo(logInfo, event.getSource());
logInfo.setServiceId(ServiceConstant.AUTH_SERVICE); logInfo.setServiceId(ServiceConstant.AUTH_SERVICE);
...@@ -65,22 +72,17 @@ public class CustomAuthenticationSuccessHandler { ...@@ -65,22 +72,17 @@ public class CustomAuthenticationSuccessHandler {
* @param source source * @param source source
*/ */
private void initLogInfo(Log logInfo, Object source) { private void initLogInfo(Log logInfo, Object source) {
if (source instanceof OAuth2Authentication) { if (source instanceof UsernamePasswordAuthenticationToken) {
OAuth2Authentication auth2Authentication = (OAuth2Authentication) source; Object currentAuthentication = SecurityUtil.getCurrentAuthentication();
Object details = auth2Authentication.getDetails(); if (currentAuthentication instanceof UsernamePasswordAuthenticationToken) {
if (details instanceof OAuth2AuthenticationDetails) { Object currentDetails = ((UsernamePasswordAuthenticationToken) currentAuthentication).getDetails();
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) details; if (currentDetails instanceof WebAuthenticationDetails) {
logInfo.setIp(oAuth2AuthenticationDetails.getRemoteAddress()); WebAuthenticationDetails webAuthenticationDetails = (WebAuthenticationDetails) currentDetails;
}
} else if (source instanceof UsernamePasswordAuthenticationToken) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) source;
Object details = token.getDetails();
if (details instanceof WebAuthenticationDetails) {
WebAuthenticationDetails webAuthenticationDetails = (WebAuthenticationDetails) details;
logInfo.setIp(webAuthenticationDetails.getRemoteAddress()); logInfo.setIp(webAuthenticationDetails.getRemoteAddress());
} }
} }
} }
}
/** /**
* 获取用户名 * 获取用户名
......
...@@ -4,6 +4,7 @@ import com.github.tangyi.auth.api.module.WxSession; ...@@ -4,6 +4,7 @@ import com.github.tangyi.auth.api.module.WxSession;
import com.github.tangyi.auth.model.CustomUserDetails; import com.github.tangyi.auth.model.CustomUserDetails;
import com.github.tangyi.auth.service.WxSessionService; import com.github.tangyi.auth.service.WxSessionService;
import com.github.tangyi.common.core.constant.CommonConstant; import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.enums.LoginType;
import com.github.tangyi.common.core.exceptions.CommonException; import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.exceptions.TenantNotFoundException; import com.github.tangyi.common.core.exceptions.TenantNotFoundException;
import com.github.tangyi.common.core.model.ResponseBean; import com.github.tangyi.common.core.model.ResponseBean;
...@@ -61,11 +62,12 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { ...@@ -61,11 +62,12 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
*/ */
@Override @Override
public UserDetails loadUserByIdentifierAndTenantCode(String username, String tenantCode) throws UsernameNotFoundException, TenantNotFoundException { public UserDetails loadUserByIdentifierAndTenantCode(String username, String tenantCode) throws UsernameNotFoundException, TenantNotFoundException {
long start = System.currentTimeMillis();
Tenant tenant = this.validateTenantCode(tenantCode); Tenant tenant = this.validateTenantCode(tenantCode);
UserVo userVo = userServiceClient.findUserByIdentifier(username, tenantCode); UserVo userVo = userServiceClient.findUserByIdentifier(username, tenantCode);
if (userVo == null) if (userVo == null)
throw new UsernameNotFoundException("用户名不存在."); throw new UsernameNotFoundException("用户名不存在.");
return new CustomUserDetails(username, userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode()); return new CustomUserDetails(username, userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginType.PWD);
} }
/** /**
...@@ -79,11 +81,12 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { ...@@ -79,11 +81,12 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
*/ */
@Override @Override
public UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode) throws UsernameNotFoundException { public UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode) throws UsernameNotFoundException {
long start = System.currentTimeMillis();
Tenant tenant = this.validateTenantCode(tenantCode); Tenant tenant = this.validateTenantCode(tenantCode);
UserVo userVo = userServiceClient.findUserBySocial(social, tenantCode); UserVo userVo = userServiceClient.findUserBySocial(social, tenantCode);
if (userVo == null) if (userVo == null)
throw new UsernameNotFoundException("用户手机号未注册."); throw new UsernameNotFoundException("用户手机号未注册.");
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode()); return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginType.SMS);
} }
/** /**
...@@ -99,6 +102,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { ...@@ -99,6 +102,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
*/ */
@Override @Override
public UserDetails loadUserByWxCodeAndTenantCode(String code, String tenantCode, WxUser wxUser) throws UsernameNotFoundException { public UserDetails loadUserByWxCodeAndTenantCode(String code, String tenantCode, WxUser wxUser) throws UsernameNotFoundException {
long start = System.currentTimeMillis();
Tenant tenant = this.validateTenantCode(tenantCode); Tenant tenant = this.validateTenantCode(tenantCode);
// 根据code获取openId和sessionKey // 根据code获取openId和sessionKey
WxSession wxSession = wxService.code2Session(code); WxSession wxSession = wxService.code2Session(code);
...@@ -130,7 +134,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { ...@@ -130,7 +134,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
userDto.setLoginTime(DateUtils.asDate(LocalDateTime.now())); userDto.setLoginTime(DateUtils.asDate(LocalDateTime.now()));
//userServiceClient.updateUser(userDto); //userServiceClient.updateUser(userDto);
} }
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode()); return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginType.WECHAT);
} }
/** /**
......
...@@ -26,7 +26,6 @@ encrypt: ...@@ -26,7 +26,6 @@ encrypt:
alias: jwt alias: jwt
password: abc123 password: abc123
logging: logging:
level: level:
root: info root: info
......
...@@ -2,20 +2,17 @@ ...@@ -2,20 +2,17 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId>
<version>3.0.0</version>
</parent>
<artifactId>service-api-impl</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>api实现</description>
<modules> <modules>
<module>auth-service-api</module>
<module>auth-service</module> <module>auth-service</module>
<module>user-service</module>
<module>exam-service</module>
<module>msc-service</module>
</modules> </modules>
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>modules</artifactId>
<version>3.1.0</version>
</parent>
<artifactId>auth-service-parent</artifactId>
<name>${project.artifactId}</name>
<description>认证授权服务</description>
</project> </project>
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent> <parent>
<groupId>com.github.tangyi</groupId> <groupId>com.github.tangyi</groupId>
<artifactId>service-api</artifactId> <artifactId>exam-service-parent</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
</parent> </parent>
<artifactId>exam-api</artifactId> <artifactId>exam-service-api</artifactId>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
<description>考试服务api</description> <description>考试服务api</description>
......
package com.github.tangyi.exam.api.module;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
/**
* 考试权限
*
* @author tangyi
* @date 2019-07-16 14:01
*/
@Data
public class ExaminationAuth extends BaseEntity<ExaminationAuth> {
/**
* 学生ID
*/
private String userId;
/**
* 考试ID
*/
private String examinationId;
}
...@@ -28,7 +28,7 @@ public class ExaminationSubject extends BaseEntity<ExaminationSubject> { ...@@ -28,7 +28,7 @@ public class ExaminationSubject extends BaseEntity<ExaminationSubject> {
private Integer serialNumber; private Integer serialNumber;
/** /**
* 题目类型,0:选择题,1:简答题 * 题目类型,0:选择题,1:简答题,2:判断题,3:多选题
*/ */
private Integer type; private Integer type;
} }
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment