Commit 61057290 by tangyi

v3.7.0

parent 11d480f6
Version v3.7.0 (2020-03-15)
--------------------------
新功能:
* 重构前台样式
* 支持二维码分享,移动端答题
* 考试默认封面图像,课程默认封面等
改进:
* 升级spring boot版本为2.2.5.RELEASE、spring cloud版本为Hoxton.SR3
* 网关集成ResourceServer
* 修复若干bug
Version v3.6.0 (2020-02-22)
--------------------------
新功能:
......
<h1 align="center">Welcome to spring-microservice-exam 👋</h1>
<p>
<img alt="Version" src="https://img.shields.io/badge/version-3.6.0-blue.svg?cacheSeconds=2592000" />
<img alt="Version" src="https://img.shields.io/badge/version-3.7.0-blue.svg?cacheSeconds=2592000" />
<a href="https://www.kancloud.cn/tangyi/spring-microservice-exam/1322864" target="_blank">
<img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg" />
</a>
......@@ -12,6 +12,8 @@
> 硕果云,基于Spring Cloud搭建的新一代微服务教学管理平台,提供多租户、权限管理、在线考试、练习等功能
>
> 题型支持单选题、多选题、不定项选择题、判断题、简答题等
>
> 支持二维码分享,移动端答题
### 🏠 [主页](https://gitee.com/wells2333/spring-microservice-exam)
......@@ -23,7 +25,6 @@
| 单位ID | 账号 | 密码 | 角色 |
| --------- | -------- | -------- | -------- |
| gitee | admin | ****** | 管理员 |
| gitee | preview | 123456 | 预览权限|
| gitee | student | 123456 | 学生 |
| gitee | teacher | 123456 | 老师 |
......@@ -52,12 +53,12 @@
| 名称 | 版本 |
| --------- | -------- |
| `Spring Boot` | `2.2.2.RELEASE` |
| `Spring Cloud` | `Hoxton.SR1` |
| `Spring Boot` | `2.2.5.RELEASE` |
| `Spring Cloud` | `Hoxton.SR3` |
## 系统架构
![image](docs/images/系统架构图v3.0.jpg)
![image](docs/images/系统架构图v3.0.png)
## 功能概述
......@@ -86,7 +87,7 @@
考务管理:提供课程、考试、题库、成绩等管理
- 课程管理:课程信息增删改查
- 考试管理:考试信息增删改查、题目管理、发布回收,题目管理支持简单文本、富文本输入、从题库添加等,题型支持单选题、多选题、不定项选择题、判断题、简答题
- 考试管理:考试信息增删改查、题目管理、发布回收,题目管理支持简单文本、富文本输入、从题库添加等,题型支持单选题、多选题、不定项选择题、判断题、简答题,生成二维码分享
- 题库管理:题目分类增删改查、题目信息增删改查,题型支持单选题、多选题、不定项选择题、判断题、简答题
- 成绩管理:查看成绩、成绩批改、导出等功能
- 知识库:知识库增删改查、上传附件
......@@ -104,8 +105,8 @@
<table>
<tr>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web_login.jpg" alt="登录"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web.jpg" alt="首页"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web.png" alt="首页"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web_home.png" alt="首页"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web_exams.png" alt="考试列表"/></td>
......@@ -115,13 +116,17 @@
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web_record.png" alt="考试记录"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web_incorrect_answer.png" alt="错题列表"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web_courses.png" alt="热门课程"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_web_login.png" alt="登录"/></td>
</tr>
</table>
### 后台功能(点击查看大图)
<table>
<tr>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_ui_login.jpg" alt="登录"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_ui_login.png" alt="登录"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_ui_exam.png" alt="首页"/></td>
</tr>
<tr>
......@@ -157,6 +162,10 @@
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_ui_swagger.png" alt="swagger文档"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_ui_elk.png" alt="elk日志"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_ui_zipkin1.png" alt="zipkin"/></td>
<td><img src="https://gitee.com/wells2333/spring-microservice-exam/raw/master/docs/images/image_ui_zipkin2.png" alt="zipkin"/></td>
</tr>
</table>
## 部署文档
......
......@@ -2,13 +2,29 @@
<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">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId>
<artifactId>common</artifactId>
<version>${revision}</version>
</parent>
<artifactId>zipkin-service</artifactId>
<packaging>pom</packaging>
<artifactId>common-basic</artifactId>
<name>${project.artifactId}</name>
<description>zipkin监控服务</description>
<description>基础公共依赖</description>
<dependencies>
<!-- common-core -->
<dependency>
<groupId>com.github.tangyi</groupId>
<artifactId>common-core</artifactId>
<scope>provided</scope>
</dependency>
<!-- common-security -->
<dependency>
<groupId>com.github.tangyi</groupId>
<artifactId>common-security</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
package com.github.tangyi.common.core.cache;
package com.github.tangyi.common.basic.cache;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.data.redis.cache.RedisCacheWriter;
......
package com.github.tangyi.common.core.cache;
package com.github.tangyi.common.basic.cache;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.security.utils.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cache.Cache;
......
package com.github.tangyi.common.core.cache.loadingcache;
package com.github.tangyi.common.basic.cache.loadingcache;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.google.common.cache.CacheBuilder;
......
package com.github.tangyi.common.core.config;
package com.github.tangyi.common.basic.config;
import com.github.tangyi.common.basic.properties.SysProperties;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.properties.SysProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
......@@ -23,10 +23,8 @@ public class AppStartupRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
log.info("================ start command line ================ ");
// 设置系统属性
if (StringUtils.isNotBlank(sysProperties.getCacheExpire()))
System.setProperty(CommonConstant.CACHE_EXPIRE, sysProperties.getCacheExpire());
log.info("================ end command line ================");
}
}
package com.github.tangyi.common.core.config;
package com.github.tangyi.common.basic.config;
import com.github.tangyi.common.core.cache.CustomRedisCacheWriter;
import com.github.tangyi.common.core.cache.MultitenantCacheManager;
import com.github.tangyi.common.basic.cache.CustomRedisCacheWriter;
import com.github.tangyi.common.basic.cache.MultitenantCacheManager;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers;
......
package com.github.tangyi.common.core.config;
package com.github.tangyi.common.basic.config;
import com.github.tangyi.common.core.properties.SnowflakeProperties;
import com.github.tangyi.common.basic.properties.SnowflakeProperties;
import com.github.tangyi.common.core.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
......
package com.github.tangyi.common.core.enums;
package com.github.tangyi.common.basic.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
......
package com.github.tangyi.common.core.enums;
package com.github.tangyi.common.basic.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
......@@ -21,17 +21,17 @@ public enum LoginTypeEnum {
/**
* 验证码登录
*/
SMS("SMS", "验证码登录", "/mobile/token"),
SMS("SMS", "验证码登录", "/api/v1/mobile/token"),
/**
* QQ登录
*/
QQ("QQ", "QQ登录", "/mobile/token"),
QQ("QQ", "QQ登录", "/api/v1/mobile/token"),
/**
* 微信登录
*/
WECHAT("WX", "微信登录", "/wx/token");
WECHAT("WX", "微信登录", "/api/v1/wx/token");
/**
* 类型
......
package com.github.tangyi.common.core.enums;
package com.github.tangyi.common.basic.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
......
package com.github.tangyi.common.core.model;
package com.github.tangyi.common.basic.model;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
......
package com.github.tangyi.common.core.properties;
package com.github.tangyi.common.basic.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
......
package com.github.tangyi.common.core.properties;
package com.github.tangyi.common.basic.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
......@@ -34,4 +34,24 @@ public class SysProperties {
private String cacheExpire;
private String gatewaySecret;
/**
* web端配置的静态封面路径
*/
private String webAvatar;
/**
* web端配置的静态封面数量
*/
private Integer webAvatarCount;
/**
* web端配置的静态封面的后缀名
*/
private String webAvatarSuffix;
/**
* 二维码生成链接
*/
private String qrCodeUrl;
}
package com.github.tangyi.common.core.utils.excel;
package com.github.tangyi.common.basic.utils.excel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
......
package com.github.tangyi.common.core.utils.excel;
package com.github.tangyi.common.basic.utils.excel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
......@@ -7,10 +7,10 @@ import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.github.tangyi.common.basic.utils.excel.annotation.ExcelModel;
import com.github.tangyi.common.basic.utils.excel.exception.ExcelException;
import com.github.tangyi.common.core.utils.DateUtils;
import com.github.tangyi.common.core.utils.Servlets;
import com.github.tangyi.common.core.utils.excel.annotation.ExcelModel;
import com.github.tangyi.common.core.utils.excel.exception.ExcelException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
......
package com.github.tangyi.common.core.utils.excel.annotation;
package com.github.tangyi.common.basic.utils.excel.annotation;
import java.lang.annotation.*;
......
package com.github.tangyi.common.core.utils.excel.converter;
package com.github.tangyi.common.basic.utils.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.github.tangyi.common.core.enums.GenderEnum;
import com.github.tangyi.common.basic.enums.GenderEnum;
/**
* 性别转换
......
package com.github.tangyi.common.core.utils.excel.converter;
package com.github.tangyi.common.basic.utils.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.github.tangyi.common.core.enums.StatusEnum;
import com.github.tangyi.common.basic.enums.StatusEnum;
/**
* 状态转换
......
package com.github.tangyi.common.core.vo;
package com.github.tangyi.common.basic.vo;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
......
package com.github.tangyi.common.core.vo;
package com.github.tangyi.common.basic.vo;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
......
package com.github.tangyi.common.core.vo;
package com.github.tangyi.common.basic.vo;
import com.github.tangyi.common.core.model.Log;
import com.github.tangyi.common.basic.model.Log;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
......
package com.github.tangyi.common.core.vo;
package com.github.tangyi.common.basic.vo;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
......
package com.github.tangyi.common.core.vo;
package com.github.tangyi.common.basic.vo;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
......
package com.github.tangyi.common.core.vo;
package com.github.tangyi.common.basic.vo;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
......@@ -16,6 +16,11 @@ import java.util.List;
public class UserVo extends BaseEntity<UserVo> {
/**
* 用户id
*/
private Long userId;
/**
* 授权类型,1:用户名密码,2:手机号,3:邮箱,4:微信,5:QQ
*/
private Integer identityType;
......
......@@ -84,7 +84,7 @@ public class SwaggerConfig implements WebMvcConfigurer {
.description("https://gitee.com/wells2333/spring-microservice-exam")
.termsOfServiceUrl("https://gitee.com/wells2333/spring-microservice-exam")
.contact(new Contact("tangyi", "https://gitee.com/wells2333/spring-microservice-exam", "1633736729@qq.com"))
.version("3.6.0")
.version("3.7.0")
.build();
}
......
......@@ -108,13 +108,6 @@
<artifactId>hibernate-validator</artifactId>
</dependency>
<!-- security -->
<dependency>
<groupId>com.github.tangyi</groupId>
<artifactId>common-security</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
......@@ -133,5 +126,19 @@
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<!-- 二维码 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
</dependency>
<dependency>
<groupId>QRCode</groupId>
<artifactId>QRCode</artifactId>
</dependency>
</dependencies>
</project>
......@@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.utils.DateUtils;
import com.github.tangyi.common.core.utils.IdGen;
import com.github.tangyi.common.core.utils.SysUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
......@@ -91,16 +90,6 @@ public class BaseEntity<T> implements Serializable {
*
* @param userCode 用户编码
* @param applicationCode 系统编号
*/
public void setCommonValue(String userCode, String applicationCode) {
setCommonValue(userCode, applicationCode, SysUtil.getTenantCode());
}
/**
* 设置基本属性
*
* @param userCode 用户编码
* @param applicationCode 系统编号
* @param tenantCode 租户编号
*/
public void setCommonValue(String userCode, String applicationCode, String tenantCode) {
......
package com.github.tangyi.common.security.properties;
package com.github.tangyi.common.core.properties;
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
......
package com.github.tangyi.common.core.utils;
import org.bouncycastle.util.encoders.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
* AES工具类
*
* @author tangyi
* @date 2020/3/5 1:17 下午
*/
public class AesUtil {
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/NOPadding";
/**
* des解密
*
* @param data data
* @param pass pass
* @return String
* @author tangyi
* @date 2019/03/18 11:39
*/
public static String decryptAES(String data, String pass) throws Exception {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(pass.getBytes(), KEY_ALGORITHM), new IvParameterSpec(pass.getBytes()));
byte[] result = cipher.doFinal(Base64.decode(data.getBytes(StandardCharsets.UTF_8)));
return new String(result, StandardCharsets.UTF_8);
}
}
package com.github.tangyi.common.core.utils.zxing;
import jp.sourceforge.qrcode.data.QRCodeImage;
import java.awt.image.BufferedImage;
/**
* @author tangyi
* @date 2019/3/22 15:27
*/
public class QRCode implements QRCodeImage {
BufferedImage bufImg;
public QRCode(BufferedImage bufImg) {
this.bufImg = bufImg;
}
@Override
public int getHeight() {
return bufImg.getHeight();
}
@Override
public int getPixel(int x, int y) {
return bufImg.getRGB(x, y);
}
@Override
public int getWidth() {
return bufImg.getWidth();
}
}
package com.github.tangyi.common.log.aspect;
import com.github.tangyi.common.core.utils.SpringContextHolder;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.log.event.LogEvent;
import com.github.tangyi.common.log.utils.LogUtil;
import com.github.tangyi.common.security.utils.SysUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
......@@ -27,14 +27,14 @@ public class LogAspect {
String strClassName = point.getTarget().getClass().getName();
String strMethodName = point.getSignature().getName();
logger.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);
com.github.tangyi.common.core.model.Log logVo = LogUtil.getLog();
com.github.tangyi.common.basic.model.Log logVo = LogUtil.getLog();
logVo.setTitle(log.value());
// 发送异步日志事件
Long startTime = System.currentTimeMillis();
Object obj = point.proceed();
Long endTime = System.currentTimeMillis();
logVo.setTime(String.valueOf(endTime - startTime));
logVo.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode());
logVo.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
SpringContextHolder.publishEvent(new LogEvent(logVo));
return obj;
}
......
package com.github.tangyi.common.log.event;
import com.github.tangyi.common.core.model.Log;
import com.github.tangyi.common.basic.model.Log;
import org.springframework.context.ApplicationEvent;
/**
......
package com.github.tangyi.common.log.event;
import com.github.tangyi.common.core.model.Log;
import com.github.tangyi.common.basic.model.Log;
import com.github.tangyi.user.api.feign.UserServiceClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
......
......@@ -3,8 +3,8 @@ package com.github.tangyi.common.log.utils;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import com.github.tangyi.common.basic.model.Log;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.model.Log;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
......
......@@ -13,6 +13,12 @@
<description>security公共依赖</description>
<dependencies>
<!-- common-core -->
<dependency>
<groupId>com.github.tangyi</groupId>
<artifactId>common-core</artifactId>
</dependency>
<!-- 单点登录 -->
<dependency>
<groupId>org.springframework.boot</groupId>
......
......@@ -3,7 +3,7 @@ package com.github.tangyi.common.security.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tangyi.common.security.handler.CustomAccessDeniedHandler;
import com.github.tangyi.common.security.mobile.MobileSecurityConfigurer;
import com.github.tangyi.common.security.properties.FilterIgnorePropertiesConfig;
import com.github.tangyi.common.core.properties.FilterIgnorePropertiesConfig;
import com.github.tangyi.common.security.wx.WxSecurityConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
......
......@@ -29,12 +29,12 @@ public class SecurityConstant {
/**
* 手机登录URL
*/
public static final String MOBILE_TOKEN_URL = "/mobile/token";
public static final String MOBILE_TOKEN_URL = "/api/v1/mobile/token";
/**
* 微信登录URL
*/
public static final String WX_TOKEN_URL = "/wx/token";
public static final String WX_TOKEN_URL = "/api/v1/wx/token";
/**
* 租户编号请求头
......
......@@ -2,7 +2,6 @@ package com.github.tangyi.common.security.exception;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.github.tangyi.common.security.serializer.CustomOauthExceptionSerializer;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
/**
......
package com.github.tangyi.common.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
......
package com.github.tangyi.common.security.mobile;
import com.github.tangyi.common.core.utils.SpringContextHolder;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import com.github.tangyi.common.security.event.CustomAuthenticationFailureEvent;
import com.github.tangyi.common.security.event.CustomAuthenticationSuccessEvent;
import com.github.tangyi.common.security.tenant.TenantContextHolder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
......@@ -27,8 +27,6 @@ public class MobileAuthenticationProvider implements AuthenticationProvider {
private CustomUserDetailsService customUserDetailsService;
private ApplicationEventPublisher publisher;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
MobileAuthenticationToken mobileAuthenticationToken = (MobileAuthenticationToken) authentication;
......@@ -36,12 +34,12 @@ public class MobileAuthenticationProvider implements AuthenticationProvider {
UserDetails userDetails = customUserDetailsService.loadUserBySocialAndTenantCode(TenantContextHolder.getTenantCode(), principal, mobileAuthenticationToken.getMobileUser());
if (userDetails == null) {
log.debug("Authentication failed: no credentials provided");
publisher.publishEvent(new CustomAuthenticationFailureEvent(authentication, userDetails));
SpringContextHolder.publishEvent(new CustomAuthenticationFailureEvent(authentication, userDetails));
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.noopBindAccount", "Noop Bind Account"));
}
MobileAuthenticationToken authenticationToken = new MobileAuthenticationToken(userDetails, userDetails.getAuthorities());
authenticationToken.setDetails(mobileAuthenticationToken.getDetails());
publisher.publishEvent(new CustomAuthenticationSuccessEvent(authentication, userDetails));
SpringContextHolder.publishEvent((new CustomAuthenticationSuccessEvent(authentication, userDetails)));
return authenticationToken;
}
......
......@@ -2,7 +2,7 @@ package com.github.tangyi.common.security.mobile;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.common.security.utils.SysUtil;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
......@@ -57,7 +57,7 @@ public class MobileLoginSuccessHandler implements AuthenticationSuccessHandler {
if (header == null || !header.startsWith(BASIC_))
throw new UnapprovedClientAuthenticationException("请求头中client信息为空");
try {
String[] tokens = SecurityUtil.extractAndDecodeHeader(header);
String[] tokens = SysUtil.extractAndDecodeHeader(header);
assert tokens.length == 2;
String clientId = tokens[0];
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
......
......@@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
......@@ -28,9 +27,6 @@ public class MobileSecurityConfigurer extends SecurityConfigurerAdapter<DefaultS
@Autowired
private AuthenticationEventPublisher defaultAuthenticationEventPublisher;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
private AuthenticationSuccessHandler mobileLoginSuccessHandler;
private CustomUserDetailsService userDetailsService;
......@@ -44,7 +40,6 @@ public class MobileSecurityConfigurer extends SecurityConfigurerAdapter<DefaultS
mobileAuthenticationFilter.setEventPublisher(defaultAuthenticationEventPublisher);
MobileAuthenticationProvider mobileAuthenticationProvider = new MobileAuthenticationProvider();
mobileAuthenticationProvider.setCustomUserDetailsService(userDetailsService);
mobileAuthenticationProvider.setPublisher(applicationEventPublisher);
// 增加手机登录的过滤器
http.authenticationProvider(mobileAuthenticationProvider).addFilterAfter(mobileAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
......
package com.github.tangyi.common.security.utils;
import com.github.tangyi.common.security.core.UserDetailsImpl;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.IOException;
import java.security.Principal;
/**
* 安全工具类
*
* @author tangyi
* @date 2019/3/17 11:44
*/
public class SecurityUtil {
/**
* 获取当前用户的租户标识
*
* @return String
* @author tangyi
* @date 2019/05/25 14:19
*/
public static String getCurrentUserTenantCode() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return principal instanceof UserDetails ? ((UserDetailsImpl) principal).getTenantCode() : "";
}
/**
* 获取当前登录的用户名
*
* @return String
* @author tangyi
* @date 2019/03/17 11:46
*/
public static String getCurrentUsername() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails)
return ((UserDetails) principal).getUsername();
if (principal instanceof Principal)
return ((Principal) principal).getName();
return String.valueOf(principal);
}
/**
* 获取当前用户的授权信息
*
* @return Authentication
* @author tangyi
* @date 2019/03/17 19:18
*/
public static Authentication getCurrentAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
/**
* 获取当前登录用户的授权信息
*
* @return Object
* @author tangyi
* @date 2019/03/17 11:48
*/
public static Object getCurrentPrincipal() {
return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
/**
* 从header 请求中的clientId/clientsecect
*
* @param header header中的参数
* @throws RuntimeException if the Basic header is not present or is not valid
* Base64
*/
public static String[] extractAndDecodeHeader(String header) throws IOException {
byte[] base64Token = header.substring(6).getBytes("UTF-8");
byte[] decoded;
try {
decoded = Base64.decode(base64Token);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Failed to decode basic authentication token");
}
String token = new String(decoded, "UTF8");
int delim = token.indexOf(":");
if (delim == -1)
throw new RuntimeException("Invalid basic authentication token");
return new String[]{token.substring(0, delim), token.substring(delim + 1)};
}
}
package com.github.tangyi.common.core.utils;
package com.github.tangyi.common.security.utils;
import com.github.tangyi.common.core.utils.SpringContextHolder;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.tenant.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
......@@ -14,10 +16,8 @@ import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
......@@ -30,10 +30,6 @@ import java.security.Principal;
@Slf4j
public class SysUtil {
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/NOPadding";
/**
* 获取当前登录的用户名
*
......@@ -102,20 +98,47 @@ public class SysUtil {
return tenantCode;
}
/**
* 获取当前用户的授权信息
*
* @return Authentication
* @author tangyi
* @date 2019/03/17 19:18
*/
public static Authentication getCurrentAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
/**
* des解密
* 获取当前登录用户的授权信息
*
* @param data data
* @param pass pass
* @return String
* @return Object
* @author tangyi
* @date 2019/03/18 11:39
* @date 2019/03/17 11:48
*/
public static String decryptAES(String data, String pass) throws Exception {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(pass.getBytes(), KEY_ALGORITHM), new IvParameterSpec(pass.getBytes()));
byte[] result = cipher.doFinal(Base64.decode(data.getBytes(StandardCharsets.UTF_8)));
return new String(result, StandardCharsets.UTF_8);
public static Object getCurrentPrincipal() {
return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
/**
* 从header 请求中的clientId/clientsecect
*
* @param header header中的参数
* @throws RuntimeException if the Basic header is not present or is not valid
* Base64
*/
public static String[] extractAndDecodeHeader(String header) throws IOException {
byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
byte[] decoded;
try {
decoded = Base64.decode(base64Token);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Failed to decode basic authentication token");
}
String token = new String(decoded, StandardCharsets.UTF_8);
int delim = token.indexOf(":");
if (delim == -1)
throw new RuntimeException("Invalid basic authentication token");
return new String[]{token.substring(0, delim), token.substring(delim + 1)};
}
}
package com.github.tangyi.common.security.wx;
import com.github.tangyi.common.core.utils.SpringContextHolder;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import com.github.tangyi.common.security.event.CustomAuthenticationFailureEvent;
import com.github.tangyi.common.security.event.CustomAuthenticationSuccessEvent;
import com.github.tangyi.common.security.tenant.TenantContextHolder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
......@@ -27,8 +27,6 @@ public class WxAuthenticationProvider implements AuthenticationProvider {
private CustomUserDetailsService customUserDetailsService;
private ApplicationEventPublisher publisher;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
WxAuthenticationToken wxAuthenticationToken = (WxAuthenticationToken) authentication;
......@@ -37,12 +35,12 @@ public class WxAuthenticationProvider implements AuthenticationProvider {
UserDetails userDetails = customUserDetailsService.loadUserByWxCodeAndTenantCode(principal, TenantContextHolder.getTenantCode(), wxAuthenticationToken.getWxUser());
if (userDetails == null) {
log.debug("Authentication failed: no credentials provided");
publisher.publishEvent(new CustomAuthenticationFailureEvent(authentication, userDetails));
SpringContextHolder.publishEvent(new CustomAuthenticationFailureEvent(authentication, userDetails));
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.noopBindAccount", "Noop Bind Account"));
}
WxAuthenticationToken authenticationToken = new WxAuthenticationToken(userDetails, userDetails.getAuthorities());
authenticationToken.setDetails(wxAuthenticationToken.getDetails());
publisher.publishEvent(new CustomAuthenticationSuccessEvent(authentication, userDetails));
SpringContextHolder.publishEvent(new CustomAuthenticationSuccessEvent(authentication, userDetails));
return authenticationToken;
}
......
......@@ -2,7 +2,7 @@ package com.github.tangyi.common.security.wx;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.common.security.utils.SysUtil;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
......@@ -57,7 +57,7 @@ public class WxLoginSuccessHandler implements AuthenticationSuccessHandler {
if (header == null || !header.startsWith(BASIC_))
throw new UnapprovedClientAuthenticationException("请求头中client信息为空");
try {
String[] tokens = SecurityUtil.extractAndDecodeHeader(header);
String[] tokens = SysUtil.extractAndDecodeHeader(header);
assert tokens.length == 2;
String clientId = tokens[0];
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
......
......@@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
......@@ -28,9 +27,6 @@ public class WxSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecur
@Autowired
private AuthenticationEventPublisher defaultAuthenticationEventPublisher;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
private AuthenticationSuccessHandler wxLoginSuccessHandler;
private CustomUserDetailsService userDetailsService;
......@@ -44,7 +40,6 @@ public class WxSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecur
wxAuthenticationFilter.setEventPublisher(defaultAuthenticationEventPublisher);
WxAuthenticationProvider wxAuthenticationProvider = new WxAuthenticationProvider();
wxAuthenticationProvider.setCustomUserDetailsService(userDetailsService);
wxAuthenticationProvider.setPublisher(applicationEventPublisher);
// 增加微信登录的过滤器
http.authenticationProvider(wxAuthenticationProvider).addFilterAfter(wxAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
......
......@@ -14,6 +14,7 @@
<modules>
<module>common-core</module>
<module>common-basic</module>
<module>common-security</module>
<module>common-feign</module>
<module>common-log</module>
......
......@@ -79,6 +79,10 @@ pagehelper:
# 系统配置
sys:
cacheExpire: 86400 # 缓存失效时间,单位秒,默认一天
webAvatar: /static/img/exam
webAvatarCount: 22
webAvatarSuffix: .jpeg
qrCodeUrl: http://${QR_CODE_URL:localhost}:${QR_CODE_PORT:8080}/#/mobile?id=
# feign相关配置
feign:
......@@ -122,6 +126,8 @@ ignore:
- /v1/user/anonymousUser/**
- /v1/tenant/anonymousUser/**
- /v1/menu/anonymousUser/**
- /v1/examination/anonymousUser/**
- /v1/answer/anonymousUser/**
- /v1/code/**
- /v1/attachment/download
- /v1/log/**
......
......@@ -24,6 +24,12 @@ spring:
port: ${RABBIT_PORT:5672}
username: ${RABBITMQ_DEFAULT_USER:guest}
password: ${RABBITMQ_DEFAULT_PASS:guest}
security:
# oauth2配置
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://${AUTH_SERVICE_HOST:localhost}:9182/v1/authentication/jwks.json
boot:
admin:
client:
......@@ -106,6 +112,20 @@ swagger:
- auth-service
- msc-service
ignore:
urls:
- /
- /error
- /favicon.ico
- /actuator/**
- /api/auth/**
- /api/user/**
- /api/exam/**
- /api/msc/**
- /health
- /metrics/**
- /loggers/**
# 演示环境
preview:
ignores:
......
......@@ -40,23 +40,23 @@
</appender>
<!-- 输出到logstash的appender, LOGSTASH_HOST为logstash的IP和端口,从环境变量注入-->
<!--<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${LOGSTASH_HOST}</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>-->
</appender>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="warn">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
<!--<appender-ref ref="logstash"/>-->
<appender-ref ref="logstash"/>
</root>
<logger name="com.github.tangyi" level="info" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
<!--<appender-ref ref="logstash"/>-->
<appender-ref ref="logstash"/>
</logger>
</configuration>
......@@ -41,3 +41,7 @@ redis的配置
## start.sh
启动脚本
## QRCode.jar
安装到本地maven仓库命令:mvn install:install-file -Dfile=C:\Users\Administrator\Downloads>\QRCode.jar -DgroupId=QRCode -DartifactId=QRCode -Dversion=3.0 -Dpackaging=jar
......@@ -72,6 +72,18 @@ services:
- net
# ---------------------------
# zipkin
# ---------------------------
zipkin-service:
image: openzipkin/zipkin
container_name: zipkin-service
restart: always
ports:
- "9411:9411"
networks:
- net
# ---------------------------
# 配置中心
# ---------------------------
config-service:
......
docs/images/image_ui_consul.png

300 KB | W: | H:

docs/images/image_ui_consul.png

74 KB | W: | H:

docs/images/image_ui_consul.png
docs/images/image_ui_consul.png
docs/images/image_ui_consul.png
docs/images/image_ui_consul.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_elk.png

433 KB | W: | H:

docs/images/image_ui_elk.png

118 KB | W: | H:

docs/images/image_ui_elk.png
docs/images/image_ui_elk.png
docs/images/image_ui_elk.png
docs/images/image_ui_elk.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_exam.png

210 KB | W: | H:

docs/images/image_ui_exam.png

51 KB | W: | H:

docs/images/image_ui_exam.png
docs/images/image_ui_exam.png
docs/images/image_ui_exam.png
docs/images/image_ui_exam.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_log_manage.png

426 KB | W: | H:

docs/images/image_ui_log_manage.png

123 KB | W: | H:

docs/images/image_ui_log_manage.png
docs/images/image_ui_log_manage.png
docs/images/image_ui_log_manage.png
docs/images/image_ui_log_manage.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_menu.png

279 KB | W: | H:

docs/images/image_ui_menu.png

70.3 KB | W: | H:

docs/images/image_ui_menu.png
docs/images/image_ui_menu.png
docs/images/image_ui_menu.png
docs/images/image_ui_menu.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_menu_manage.png

277 KB | W: | H:

docs/images/image_ui_menu_manage.png

71.1 KB | W: | H:

docs/images/image_ui_menu_manage.png
docs/images/image_ui_menu_manage.png
docs/images/image_ui_menu_manage.png
docs/images/image_ui_menu_manage.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_msg.png

388 KB | W: | H:

docs/images/image_ui_msg.png

130 KB | W: | H:

docs/images/image_ui_msg.png
docs/images/image_ui_msg.png
docs/images/image_ui_msg.png
docs/images/image_ui_msg.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_role_manage.png

287 KB | W: | H:

docs/images/image_ui_role_manage.png

69.6 KB | W: | H:

docs/images/image_ui_role_manage.png
docs/images/image_ui_role_manage.png
docs/images/image_ui_role_manage.png
docs/images/image_ui_role_manage.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_route_manage.png

278 KB | W: | H:

docs/images/image_ui_route_manage.png

70.6 KB | W: | H:

docs/images/image_ui_route_manage.png
docs/images/image_ui_route_manage.png
docs/images/image_ui_route_manage.png
docs/images/image_ui_route_manage.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_score_detail.png

353 KB | W: | H:

docs/images/image_ui_score_detail.png

91.4 KB | W: | H:

docs/images/image_ui_score_detail.png
docs/images/image_ui_score_detail.png
docs/images/image_ui_score_detail.png
docs/images/image_ui_score_detail.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_score_manage.png

252 KB | W: | H:

docs/images/image_ui_score_manage.png

60.8 KB | W: | H:

docs/images/image_ui_score_manage.png
docs/images/image_ui_score_manage.png
docs/images/image_ui_score_manage.png
docs/images/image_ui_score_manage.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_ui_score_mark.png

281 KB | W: | H:

docs/images/image_ui_score_mark.png

69.7 KB | W: | H:

docs/images/image_ui_score_mark.png
docs/images/image_ui_score_mark.png
docs/images/image_ui_score_mark.png
docs/images/image_ui_score_mark.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_web_exam.png

176 KB | W: | H:

docs/images/image_web_exam.png

33.9 KB | W: | H:

docs/images/image_web_exam.png
docs/images/image_web_exam.png
docs/images/image_web_exam.png
docs/images/image_web_exam.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_web_exams.png

302 KB | W: | H:

docs/images/image_web_exams.png

189 KB | W: | H:

docs/images/image_web_exams.png
docs/images/image_web_exams.png
docs/images/image_web_exams.png
docs/images/image_web_exams.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/image_web_record.png

131 KB | W: | H:

docs/images/image_web_record.png

35 KB | W: | H:

docs/images/image_web_record.png
docs/images/image_web_record.png
docs/images/image_web_record.png
docs/images/image_web_record.png
  • 2-up
  • Swipe
  • Onion skin
docs/images/系统架构图.png

67.8 KB | W: | H:

docs/images/系统架构图.png

24.9 KB | W: | H:

docs/images/系统架构图.png
docs/images/系统架构图.png
docs/images/系统架构图.png
docs/images/系统架构图.png
  • 2-up
  • Swipe
  • Onion skin
import request from '@/router/axios'
const baseExaminationUrl = '/api/exam/v1/examination/'
import { apiList } from '@/const/constant'
export function fetchList (query) {
return request({
url: baseExaminationUrl + 'examinationList',
url: apiList.exam + 'examinationList',
method: 'get',
params: query
})
......@@ -12,7 +11,7 @@ export function fetchList (query) {
export function fetchSubjectListById (query) {
return request({
url: baseExaminationUrl + 'subjectList',
url: apiList.exam + 'subjectList',
method: 'get',
params: query
})
......@@ -20,14 +19,14 @@ export function fetchSubjectListById (query) {
export function getObj (id) {
return request({
url: baseExaminationUrl + id,
url: apiList.exam + id,
method: 'get'
})
}
export function getSubjectIds (id, query) {
return request({
url: baseExaminationUrl + id + '/subjectIds',
url: apiList.exam + id + '/subjectIds',
method: 'get',
params: query
})
......@@ -35,7 +34,7 @@ export function getSubjectIds (id, query) {
export function addObj (obj) {
return request({
url: baseExaminationUrl,
url: apiList.exam,
method: 'post',
data: obj
})
......@@ -43,7 +42,7 @@ export function addObj (obj) {
export function putObj (obj) {
return request({
url: baseExaminationUrl,
url: apiList.exam,
method: 'put',
data: obj
})
......@@ -51,15 +50,22 @@ export function putObj (obj) {
export function delObj (id) {
return request({
url: baseExaminationUrl + id,
url: apiList.exam + id,
method: 'delete'
})
}
export function delAllObj (obj) {
return request({
url: baseExaminationUrl + 'deleteAll',
url: apiList.exam + 'deleteAll',
method: 'post',
data: obj
})
}
export function generateQrCode (id) {
return request({
url: apiList.exam + 'anonymousUser/generateQrCode/' + id,
method: 'get'
})
}
......@@ -8,3 +8,7 @@ export const answerType = {
'true': 'right',
'false': 'incorrect'
}
export const apiList = {
exam: '/api/exam/v1/examination/'
}
......@@ -195,6 +195,7 @@ export default {
score: '得分',
modifyDate: '修改时间',
modifier: '修改人',
share: '分享',
subject: {
serialNumber: '序号',
type: '类型',
......
This source diff could not be displayed because it is too large. You can view the blob instead.
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