Commit 11d480f6 by tangyi

优化登录

parent b24c210c
......@@ -21,4 +21,4 @@ intellij/
atlassian-ide-plugin.xml
!etc/eclipse/.checkstyle
.checkstyle
docs/deploy/dev.env
\ No newline at end of file
.dev.env
\ No newline at end of file
......@@ -96,6 +96,11 @@ public class ApiMsg {
public static final int KEY_ACCESS = 404;
/**
* 认证
*/
public static final int KEY_AUTHENTICATION = 405;
/**
* code和提示内容的对应关系
*/
private static final Map<Integer, String> CODE_MAP = new HashMap<>();
......
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.security.wx.WxSecurityConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandler;
/**
* 资源服务器配置
......@@ -38,16 +43,23 @@ public class CustomResourceServerConfig extends ResourceServerConfigurerAdapter
*/
private final WxSecurityConfigurer wxSecurityConfigurer;
private final ObjectMapper objectMapper;
@Autowired
public CustomResourceServerConfig(FilterIgnorePropertiesConfig filterIgnorePropertiesConfig, MobileSecurityConfigurer mobileSecurityConfigurer, WxSecurityConfigurer wxSecurityConfigurer) {
public CustomResourceServerConfig(FilterIgnorePropertiesConfig filterIgnorePropertiesConfig,
MobileSecurityConfigurer mobileSecurityConfigurer,
WxSecurityConfigurer wxSecurityConfigurer,
ObjectMapper objectMapper) {
this.filterIgnorePropertiesConfig = filterIgnorePropertiesConfig;
this.mobileSecurityConfigurer = mobileSecurityConfigurer;
this.wxSecurityConfigurer = wxSecurityConfigurer;
this.objectMapper = objectMapper;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
resources.accessDeniedHandler(accessDeniedHandler());
}
@Override
......@@ -65,4 +77,10 @@ public class CustomResourceServerConfig extends ResourceServerConfigurerAdapter
// 微信登录
http.apply(wxSecurityConfigurer);
}
@Bean
@ConditionalOnMissingBean(AccessDeniedHandler.class)
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler(objectMapper);
}
}
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;
/**
* 自定义OauthException
*
* @author tangyi
* @date 2020/2/29 14:12
*/
@JsonSerialize(using = CustomOauthExceptionSerializer.class)
public class CustomOauthException extends OAuth2Exception {
public CustomOauthException(String msg) {
super(msg);
}
}
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;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义AccessDeniedHandler
*
* @author tangyi
* @date 2020-02-29 15:39
*/
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private final ObjectMapper objectMapper;
public CustomAccessDeniedHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
Map<String, Object> map = new HashMap<>();
map.put("code", 400);
map.put("msg", e.getMessage());
map.put("data", e.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(objectMapper.writeValueAsString(map));
}
}
package com.github.tangyi.common.security.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.github.tangyi.common.security.exception.CustomOauthException;
import java.io.IOException;
import java.util.Map;
/**
* 自定义OauthException Serializer,定制异常返回结果
*
* @author tangyi
* @date 2020/2/29 14:12
*/
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {
public CustomOauthExceptionSerializer() {
super(CustomOauthException.class);
}
@Override
public void serialize(CustomOauthException e, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField("code", e.getHttpErrorCode());
jsonGenerator.writeStringField("msg", e.getMessage());
jsonGenerator.writeObjectField("data", e.getOAuth2ErrorCode());
if (e.getAdditionalInformation() != null) {
for (Map.Entry<String, String> entry : e.getAdditionalInformation().entrySet()) {
jsonGenerator.writeStringField(entry.getKey(), entry.getValue());
}
}
jsonGenerator.writeEndObject();
}
}
......@@ -91,12 +91,11 @@ ignore:
- /actuator/**
- /hystrix.sender
- /v1/sms/**
- /v1/user/findUserByIdentifier/**
- /v1/tenant/findTenantByTenantCode/**
- /v1/user/anonymousUser/**
- /v1/tenant/anonymousUser/**
- /v1/user/checkExist/**
- /v1/user/updatePassword
- /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu
- /v1/menu/anonymousUser/**
- /v1/code/**
- /v1/attachment/download
- /v1/log/**
......
......@@ -119,12 +119,9 @@ ignore:
- /actuator/**
- /hystrix.sender
- /v1/sms/**
- /v1/user/findUserByIdentifier/**
- /v1/tenant/findTenantByTenantCode/**
- /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu
- /v1/user/checkExist/**
- /v1/user/updatePassword
- /v1/user/anonymousUser/**
- /v1/tenant/anonymousUser/**
- /v1/menu/anonymousUser/**
- /v1/code/**
- /v1/attachment/download
- /v1/log/**
......
......@@ -110,8 +110,7 @@ swagger:
preview:
ignores:
- api/auth # 授权服务
- register
- checkExist
- anonymousUser
- updateInfo
- updateAvatar
- attachment
......
......@@ -153,13 +153,9 @@ ignore:
- /hystrix.sender
- /v1/sms/**
- /v1/mobile/**
- /v1/user/findUserByIdentifier/**
- /v1/tenant/findTenantByTenantCode/**
- /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu
- /v1/user/register
- /v1/user/checkExist/**
- /v1/user/updatePassword
- /v1/user/anonymousUser/**
- /v1/tenant/anonymousUser/**
- /v1/menu/anonymousUser/**
- /v1/code/**
- /v1/attachment/download
- /v1/log/**
......
/*
Navicat Premium Data Transfer
Source Server : 腾讯云-上海
Source Server Type : MySQL
Source Server Version : 50644
Source Host : 118.25.138.130:3306
Source Schema : dev_microservice_user
Target Server Type : MySQL
Target Server Version : 50644
File Encoding : 65001
Date: 23/02/2020 16:29:29
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
......
docs/images/image_web_record.png

196 KB | W: | H:

docs/images/image_web_record.png

131 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
......@@ -50,7 +50,7 @@ export function updateObjInfo (obj) {
export function updatePassword (obj) {
return request({
url: baseUserUrl + 'updatePassword',
url: baseUserUrl + 'anonymousUser/updatePassword',
method: 'put',
data: obj
})
......@@ -86,7 +86,7 @@ export function exportObj (obj) {
// 重置密码
export function resetPassword (obj) {
return request({
url: baseUserUrl + 'resetPassword',
url: baseUserUrl + 'anonymousUser/resetPassword',
method: 'put',
data: obj
})
......
......@@ -5,5 +5,8 @@ export default {
'403': '当前操作没有权限',
'400': '用户名不存在或密码错误',
'500': '服务器内部错误,请反馈给管理员',
'4015': '服务不可用',
'4020': '验证码错误',
'4026': '验证码已过期,请重新获取',
'default': '系统未知错误,请反馈给管理员'
}
......@@ -13,7 +13,7 @@ function hasPermission (roles, permissionRoles) {
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
const whiteList = ['/login', '/auth-redirect', '/404', '/401', '/lock']// no redirect whitelist
const whiteList = ['/login', '/auth-redirect', '/404', '/401', '/500', '/lock']// no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
......
import axios from 'axios'
import store from '../store'
import router from './index'
import { getToken, setToken, getRefreshToken, getTenantCode } from '@/utils/auth'
import { isNotEmpty, isSuccess } from '@/utils/util'
import { refreshToken } from '@/api/admin/login'
......@@ -39,7 +40,9 @@ axios.interceptors.response.use(data => {
NProgress.done()
// 请求失败,弹出提示信息
if (!isSuccess(data.data)) {
Message({ message: data.data.msg, type: 'error' })
const { code, msg } = data.data
const errMsg = errorCode[String(code)] || msg || errorCode['default']
Message({ message: errMsg, type: 'error' })
}
return data
}, error => {
......@@ -67,11 +70,22 @@ axios.interceptors.response.use(data => {
})
} else if (error.response.status === 423) {
Message({ message: '演示环境不能操作', type: 'warning' })
} else if (error.response.status === 404) {
// 跳转到404页面
router.replace({
path: '404',
query: { redirect: router.currentRoute.fullPath }
})
} else if (error.response.status === 500) {
// 500提示页面
router.replace({
path: '500',
query: { redirect: router.currentRoute.fullPath }
})
} else {
// 其它错误则弹出提示
const { code, data } = error.response.data
const errMsg = data || errorCode[code] || errorCode['default']
// TODO 跳转到对应的404、500提示页面
const { code, msg } = error.response.data
const errMsg = errorCode[String(code)] || msg || errorCode['default']
Message({ message: errMsg, type: 'error' })
}
}
......
......@@ -47,6 +47,19 @@ export const constantRouterMap = [
hidden: true
},
{
path: '/500',
component: Layout,
children: [
{
path: '',
component: () => import('@/views/errorPage/500'),
name: '500',
title: '500'
}
],
hidden: true
},
{
path: '/404',
component: Layout,
children: [
......
<template>
<div class="wscn-http500-container">
<div class="wscn-http500">
<div class="bullshit">
<div class="bullshit__oops">500</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">服务器内部错误,请联系管理员</div>
<a href="" class="bullshit__return-home">返回首页</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Page500',
computed: {
message () {
return '服务器内部错误......'
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-http500-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http500 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-500 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>
......@@ -30,12 +30,12 @@
<span>{{ scope.row.score }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.subject.modifyDate')" property="updateTime" width="150">
<el-table-column :label="$t('table.modifyDate')" property="updateTime" width="150">
<template slot-scope="scope">
<span>{{ scope.row.modifyDate | fmtDate('yyyy-MM-dd hh:mm') }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.subject.modifier')" property="modifier" width="120">
<el-table-column :label="$t('table.modifier')" property="modifier" width="120">
<template slot-scope="scope">
<span>{{ scope.row.modifier }}</span>
</template>
......
......@@ -53,7 +53,7 @@
<span>{{ scope.row.loginTime | fmtDate('yyyy-MM-dd hh:mm') }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.actions')" class-name="status-col">
<el-table-column :label="$t('table.actions')" class-name="status-col" width="300">
<template slot-scope="scope">
<el-button v-if="user_btn_edit" type="primary" size="mini" @click="handleUpdate(scope.row)">{{ $t('table.edit') }}</el-button>
<el-button v-if="user_btn_edit" type="warning" size="mini" @click="handleResetPassword(scope.row)">{{ $t('table.resetPassword') }}</el-button>
......
......@@ -38,7 +38,7 @@ export function loginBySocial (social, code) {
export function registerByUsername (identifier, email, credential, code, randomStr) {
return request({
url: '/api/user/v1/user/register',
url: '/api/user/v1/user/anonymousUser/register',
method: 'post',
params: {identifier, email, credential, randomStr, code},
data: {identifier, email, credential}
......
......@@ -50,7 +50,7 @@ export function updateObjInfo (obj) {
export function updatePassword (obj) {
return request({
url: baseUserUrl + 'updatePassword',
url: baseUserUrl + 'anonymousUser/updatePassword',
method: 'put',
data: obj
})
......@@ -74,7 +74,7 @@ export function delAllObj (obj) {
export function checkExist (username, tenantCode) {
return request({
url: baseUserUrl + 'checkExist/' + username,
url: baseUserUrl + 'anonymousUser/checkExist/' + username,
method: 'get',
params: { tenantCode, identityType: 1 }
})
......
......@@ -40,3 +40,11 @@ export function getCurrentTime () {
method: 'get'
})
}
// 查询成绩详情
export function examRecordDetails (id) {
return request({
url: baseExamRecordUrl + id + '/details',
method: 'get'
})
}
......@@ -5,5 +5,8 @@ export default {
'403': '当前操作没有权限',
'400': '用户名不存在或密码错误',
'500': '服务器内部错误,请反馈给管理员',
'4015': '服务不可用',
'4020': '验证码错误',
'4026': '验证码已过期,请重新获取',
'default': '系统未知错误,请反馈给管理员'
}
import { formatDate, commonFilter, isNotEmpty } from '../utils/util'
import { statusType, examType, subjectType, subjectTypeTag } from '../utils/constant'
/**
* 日期格式化
* @param date
* @param format
* @returns {*}
*/
export function fmtDate (date, format) {
if (!isNotEmpty(date)) {
return ''
}
return formatDate(new Date(date), format)
}
/**
* 通用状态
* @param status
* @returns {*}
*/
export function statusTypeFilter (status) {
return statusType[status]
}
/**
* 截取指定长度
* @param str
* @param length
* @returns {string}
*/
export function simpleStrFilter (str, length) {
if (length === undefined) {
length = 20
}
return commonFilter(str, length)
}
/**
* 考试类型
* @param type
* @returns {*}
*/
export function examTypeFilter (type) {
return examType[type]
}
/**
* 发布类型
* @param status
* @returns {string}
*/
export function publicStatusFilter (status) {
return parseInt(status) === 0 ? '已发布' : '草稿'
}
/**
* 题目类型
* @param type
* @returns {*}
*/
export function subjectTypeFilter (type) {
return subjectType[type]
}
/**
* 题目标签
* @param type
* @returns {*}
*/
export function subjectTypeTagFilter (type) {
return subjectTypeTag[type]
}
/**
* 考试提交状态
* @param type
* @returns {*}
*/
export function submitStatusFilter (type) {
const typeMap = {
0: '待批改',
1: '已批改',
2: '待批改',
3: '统计完成'
}
return typeMap[type]
}
/**
* success状态
* @param status, 自动传入
* @param expectStatus
* @returns {string}
*/
export function simpleTagStatusFilter (status, expectStatus) {
return status === expectStatus ? 'success' : 'warning'
}
......@@ -8,11 +8,16 @@ import store from './store'
import './permission' // 权限控制
import 'element-ui/lib/theme-chalk/index.css'
import './icons' // icon
import * as filters from './filters' // global filters
Vue.config.productionTip = false
Vue.use(ElementUI)
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
/* eslint-disable no-new */
new Vue({
el: '#app',
......
import axios from 'axios'
import store from '../store'
import router from './index'
import { getToken, setToken, getRefreshToken, getTenantCode } from '@/utils/auth'
import { isNotEmpty, isSuccess } from '@/utils/util'
import { refreshToken } from '@/api/admin/login'
......@@ -39,7 +40,9 @@ axios.interceptors.response.use(data => {
NProgress.done()
// 请求失败,弹出提示信息
if (!isSuccess(data.data)) {
Message({ message: data.data.msg, type: 'error' })
const { code, msg } = data.data
const errMsg = errorCode[String(code)] || msg || errorCode['default']
Message({ message: errMsg, type: 'error' })
}
return data
}, error => {
......@@ -67,11 +70,16 @@ axios.interceptors.response.use(data => {
})
} else if (error.response.status === 423) {
Message({ message: '演示环境不能操作', type: 'warning' })
} else if (error.response.status === 404) {
// 跳转到404页面
router.replace({
path: '404',
query: { redirect: router.currentRoute.fullPath }
})
} else {
// 其它错误则弹出提示
const { code, data } = error.response.data
const errMsg = data || errorCode[code] || errorCode['default']
// TODO 跳转到对应的404、500提示页面
const { code, msg } = error.response.data
const errMsg = errorCode[String(code)] || msg || errorCode['default']
Message({ message: errMsg, type: 'error' })
}
}
......
export const dialogStatusConstant = {
create: 'create',
update: 'update'
}
export const statusType = {
0: 'success',
1: 'warning'
}
// 考试类型
export const examType = {
0: '正式考试',
1: '模拟考试',
2: '在线练习'
}
// 题目类型
export const subjectType = {
0: '单选题',
2: '判断题',
1: '简答题',
3: '多选题'
}
export const subjectTypeTag = {
0: 'success',
1: 'info',
2: 'warning',
3: 'default'
}
......@@ -284,3 +284,27 @@ export const cropStr = (str, len) => {
}
return result
}
/**
* 截取指定长度
* @param str
* @param length
* @returns {string}
*/
export const commonFilter = (str, length) => {
if (str.length > length) {
return str.substring(0, length) + '...'
}
return str
}
/**
* 新建状态
*/
export const isCreate = (status) => {
return status === 'create'
}
export const trimComma = (str) => {
return str.replace(new RegExp('^,*|,*$', 'gm'), '')
}
......@@ -17,22 +17,17 @@
</el-table-column>
<el-table-column label="考试类型" min-width="90" align="center">
<template slot-scope="scope">
<span>{{ scope.row.type | typeFilter }}</span>
</template>
</el-table-column>
<el-table-column label="所属课程" min-width="90" align="center">
<template slot-scope="scope">
<span>{{ scope.row.type | typeFilter }}</span>
<span>{{ scope.row.type | examTypeFilter }}</span>
</template>
</el-table-column>
<el-table-column label="考试时间" sortable prop="start_time" min-width="90" align="center">
<template slot-scope="scope">
<span>{{ scope.row.startTime | timeFilter }}</span>
<span>{{ scope.row.startTime | fmtDate('yyyy-MM-dd hh:mm') }}</span>
</template>
</el-table-column>
<el-table-column label="状态" min-width="90" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.submitStatus | submitStatusTypeFilter">{{ scope.row.submitStatus | submitStatusFilter }}</el-tag>
<el-tag :type="scope.row.submitStatus | simpleTagStatusFilter(3)">{{ scope.row.submitStatus | submitStatusFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column label="成绩" sortable prop="score" align="center" width="120px">
......@@ -43,7 +38,6 @@
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="success" size="mini" @click="handleDetail(scope.row)" :disabled="scope.row.submitStatus !== 3">成绩详情</el-button>
<el-button type="danger" size="mini" @click="incorrectAnswer(scope.row)" :disabled="scope.row.submitStatus !== 3">查看错题</el-button>
</template>
</el-table-column>
</el-table>
......@@ -52,73 +46,19 @@
</div>
</el-col>
</el-row>
<!-- 成绩详情 -->
<el-dialog :visible.sync="dialogDetailVisible" title="成绩详情">
<el-row>
<el-col :span="24">
<div slot="header" class="score-gray-box-title">
<span>考试成绩</span>
</div>
<div class="score">
<h4>成绩: <span type="success">{{ tempScore.score }}</span></h4>
<h4>正确题数: <span type="success">{{ tempScore.correctNumber }}</span></h4>
<h4>错误题数: <span type="success">{{ tempScore.inCorrectNumber }}</span></h4>
<h4>开始时间: <span type="success">{{ tempScore.startTime | timeFilter }}</span></h4>
<h4>结束时间: <span type="success">{{ tempScore.endTime | timeFilter }}</span></h4>
</div>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script>
import { mapState } from 'vuex'
import { fetchList } from '@/api/exam/examRecord'
import store from '@/store'
import { formatDate, cropStr } from '@/utils/util'
export default {
filters: {
typeFilter (type) {
const typeMap = {
0: '正式考试',
1: '模拟考试',
2: '在线练习'
}
return typeMap[type]
},
submitStatusFilter (type) {
const typeMap = {
0: '未提交',
1: '已提交',
2: '待批改',
3: '批改完成'
}
return typeMap[type]
},
submitStatusTypeFilter (status) {
const statusMap = {
0: 'warning',
1: 'warning',
2: 'warning',
3: 'success'
}
return statusMap[status]
},
timeFilter (time) {
return formatDate(new Date(time), 'yyyy-MM-dd hh:mm')
},
examinationNameFilter (name) {
return cropStr(name, 8)
}
},
data () {
return {
examRecodeList: [],
total: 0,
listLoading: true,
dialogDetailVisible: false,
tableKey: 0,
listQuery: {
pageNum: 1,
......@@ -172,11 +112,6 @@ export default {
},
// 查看成绩详情
handleDetail (row) {
this.tempScore = row
this.dialogDetailVisible = true
},
// 查看错题
incorrectAnswer (row) {
store.dispatch('SetIncorrectRecord', { id: row.id }).then(() => {
this.$router.push({ name: 'incorrect' })
}).catch((error) => {
......
<template>
<div class="app-container">
<el-row>
<el-col :span="20" :offset="2">
<div class="subject-content" v-for="(tempIncorrectAnswer, index) in list" :key="tempIncorrectAnswer.id">
<div class="subject-content-option">
<div class="subject-title">
<span class="subject-title-number">{{index + 1}} .</span>
{{tempIncorrectAnswer.subject.subjectName}}{{tempIncorrectAnswer.subject.score}}分)
<el-card>
<spinner-loading v-if="listLoading"/>
<el-row>
<el-col :span="20" :offset="2">
<el-divider>成绩详情</el-divider>
</el-col>
</el-row>
<el-row>
<el-col :span="20" :offset="2">
<el-row>
<el-col :span="8">
<label class="el-form-item__label">考试名称: {{ examRecordDetail.examinationName }}</label>
</el-col>
<el-col :span="8">
<label class="el-form-item__label">考生姓名: {{ examRecordDetail.userName }}</label>
</el-col>
<el-col :span="8">
<label class="el-form-item__label">总得分: {{ examRecordDetail.score }}</label>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<label class="el-form-item__label">开始时间: {{ examRecordDetail.startTime | fmtDate('yyyy-MM-dd hh:mm') }}</label>
</el-col>
<el-col :span="8">
<label class="el-form-item__label">结束时间: {{ examRecordDetail.endTime | fmtDate('yyyy-MM-dd hh:mm') }}</label>
</el-col>
<el-col :span="8">
<label class="el-form-item__label">耗时: {{ examRecordDetail.duration }}</label>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<label class="el-form-item__label">评卷人: 系统自动评分</label>
</el-col>
<el-col :span="8">
<label class="el-form-item__label">正确题数: </label><el-tag type="success" size="small" effect="dark">{{ examRecordDetail.correctNumber }}</el-tag>
</el-col>
<el-col :span="8">
<label class="el-form-item__label">错误题数: </label><el-tag type="danger" size="small" effect="dark">{{ examRecordDetail.inCorrectNumber }}</el-tag>
</el-col>
</el-row>
</el-col>
</el-row>
<el-row>
<el-col :span="20" :offset="2">
<el-divider>错题列表</el-divider>
</el-col>
</el-row>
<el-row>
<el-col :span="20" :offset="2">
<div class="subject-content" v-for="(tempIncorrectAnswer, index) in list" :key="tempIncorrectAnswer.id">
<div class="subject-content-option">
<div class="subject-title">
<span class="subject-title-number">{{index + 1}} .</span>
{{tempIncorrectAnswer.subject.subjectName}}{{tempIncorrectAnswer.subject.score}}分)
</div>
<!-- 选择题 -->
<div>
<ul class="subject-options" v-for="option in tempIncorrectAnswer.subject.options" :key="option.id">
<li class="subject-option" :class="getClass(option.right)">
<label><span class="subject-option-prefix">{{ option.optionName }}&nbsp;</span><span v-html="option.optionContent" class="subject-option-prefix"></span></label>
</li>
</ul>
</div>
</div>
<!-- 选择题 -->
<div>
<ul class="subject-options" v-for="option in tempIncorrectAnswer.subject.options" :key="option.id">
<li class="subject-option" :class="getClass(option.right)">
<label><span class="subject-option-prefix">{{ option.optionName }}&nbsp;</span><span v-html="option.optionContent" class="subject-option-prefix"></span></label>
</li>
</ul>
<!-- 简答题 -->
<div v-if="tempIncorrectAnswer.subject.type === 1">
<p>
<span v-html="tempIncorrectAnswer.subject.answer.answer"></span>
</p>
</div>
</div>
<!-- 简答题 -->
<div v-if="tempIncorrectAnswer.subject.type === 1">
<p>
<span v-html="tempIncorrectAnswer.subject.answer"></span>
<p class="subject-content-answer">
参考答案:{{tempIncorrectAnswer.subject.answer.answer}}
</p>
<p class="subject-content-analysis">
解析:{{tempIncorrectAnswer.subject.analysis}}
</p>
</div>
<p class="subject-content-answer">
参考答案:{{tempIncorrectAnswer.subject.answer.answer}}
</p>
<p class="subject-content-analysis">
解析:{{tempIncorrectAnswer.subject.analysis}}
</p>
</div>
<div class="pagination-container">
<el-pagination v-show="total > 0" :current-page="listQuery.pageNum" :page-sizes="[10,20,30, 50]" :page-size="listQuery.pageSize" :total="total" background layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange"/>
</div>
</el-col>
</el-row>
<div class="pagination-container">
<el-pagination v-show="total > 0" :current-page="listQuery.pageNum" :page-sizes="[10,20,30, 50]" :page-size="listQuery.pageSize" :total="total" background layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange"/>
</div>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script>
import { mapState } from 'vuex'
import { getAnswerListInfo } from '@/api/exam/answer'
import { examRecordDetails } from '@/api/exam/examRecord'
import { notifyFail } from '@/utils/util'
import { answerType } from '@/const/constant'
import SpinnerLoading from '@/components/SpinnerLoading'
export default {
name: 'Incorrect',
components: {
SpinnerLoading
},
data () {
return {
total: 0,
listLoading: true,
total: 0,
tableKey: 0,
list: [],
listQuery: {
......@@ -56,6 +112,17 @@ export default {
pageNum: 1,
pageSize: 10,
answerType: 1
},
examRecordDetail: {
correctNumber: 0,
endTime: 0,
duration: '',
examinationName: '',
inCorrectNumber: 0,
score: 0,
startTime: 0,
submitStatus: 0,
totalScore: 0
}
}
},
......@@ -67,16 +134,20 @@ export default {
})
},
created () {
this.getList()
this.getRecordDetail()
},
methods: {
getList () {
getAnswerListInfo(this.incorrectRecord.id, this.listQuery).then(response => {
this.list = response.data.list
this.total = response.data.total
this.listLoading = false
}).catch(() => {
notifyFail(this, '加载错题失败')
getRecordDetail () {
this.listLoading = true
examRecordDetails(this.incorrectRecord.id).then(response => {
this.examRecordDetail = response.data.data
getAnswerListInfo(this.incorrectRecord.id, this.listQuery).then(response => {
this.list = response.data.list
this.total = response.data.total
this.listLoading = false
}).catch(() => {
notifyFail(this, '加载错题失败')
})
})
},
handleSizeChange (val) {
......
......@@ -2,17 +2,24 @@ package com.github.tangyi.auth.config;
import com.github.tangyi.auth.security.CustomTokenConverter;
import com.github.tangyi.common.security.core.ClientDetailsServiceImpl;
import com.github.tangyi.common.security.exception.CustomOauthException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
......@@ -107,7 +114,9 @@ public class CustomAuthorizationServerConfigurer extends AuthorizationServerConf
// 将token存储到redis
.tokenStore(tokenStore())
// token增强
.tokenEnhancer(jwtTokenEnhancer());
.tokenEnhancer(jwtTokenEnhancer())
// 异常转换
.exceptionTranslator(webResponseExceptionTranslator());
}
/**
......@@ -125,5 +134,22 @@ public class CustomAuthorizationServerConfigurer extends AuthorizationServerConf
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Bean
@Lazy
public WebResponseExceptionTranslator<OAuth2Exception> webResponseExceptionTranslator() {
return new DefaultWebResponseExceptionTranslator() {
@Override
public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
if (e instanceof OAuth2Exception) {
OAuth2Exception exception = (OAuth2Exception) e;
// 转换返回结果
return ResponseEntity.status(exception.getHttpErrorCode()).body(new CustomOauthException(e.getMessage()));
} else {
throw e;
}
}
};
}
}
......@@ -64,6 +64,7 @@ public class LoginSuccessListener implements ApplicationListener<CustomAuthentic
logInfo.setServiceId(ServiceConstant.AUTH_SERVICE);
// 记录日志和登录时间
UserDto userDto = new UserDto();
userDto.setId(customUserDetails.getId());
userDto.setIdentifier(username);
userDto.setLoginTime(DateUtils.asDate(LocalDateTime.now()));
saveLoginInfo(logInfo, userDto);
......@@ -82,7 +83,7 @@ public class LoginSuccessListener implements ApplicationListener<CustomAuthentic
public void saveLoginInfo(Log logInfo, UserDto userDto) {
try {
userServiceClient.saveLog(logInfo);
//userServiceClient.updateUser(userDto);
userServiceClient.updateLoginInfo(userDto);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
......
......@@ -24,6 +24,11 @@ public class CustomUserDetails extends User {
private String tenantCode;
/**
* id
*/
private Long id;
/**
* 开始授权时间
*/
private long start;
......@@ -44,9 +49,10 @@ public class CustomUserDetails extends User {
* @param start start
* @param loginType loginType
*/
public CustomUserDetails(String username, String password, boolean enabled, Collection<? extends GrantedAuthority> authorities, String tenantCode, long start, LoginTypeEnum loginType) {
public CustomUserDetails(String username, String password, boolean enabled, Collection<? extends GrantedAuthority> authorities, String tenantCode, Long id, long start, LoginTypeEnum loginType) {
super(username, password, enabled, true, true, true, authorities);
this.tenantCode = tenantCode;
this.id = id;
this.start = start;
this.loginType = loginType;
}
......
......@@ -43,7 +43,7 @@ public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetails
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
log.debug("Authentication failed: password is blank");
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "密码为空."));
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "密码为空"));
}
// 获取密码
String presentedPassword = authentication.getCredentials().toString();
......@@ -51,7 +51,7 @@ public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetails
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
log.debug("Authentication failed: invalid password");
publisher.publishEvent(new CustomAuthenticationFailureEvent(authentication, userDetails));
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "密码错误."));
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "用户名或密码错误"));
}
publisher.publishEvent(new CustomAuthenticationSuccessEvent(authentication, userDetails));
}
......@@ -77,16 +77,14 @@ public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetails
try {
// 加载用户信息
loadedUser = this.userDetailsService.loadUserByIdentifierAndTenantCode(TenantContextHolder.getTenantCode(), authentication.getPrincipal().toString());
} catch (TenantNotFoundException tenantNotFound) {
throw new InternalAuthenticationServiceException(tenantNotFound.getMessage(), tenantNotFound);
} catch (UsernameNotFoundException notFound) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
passwordEncoder.matches(presentedPassword, userNotFoundEncodedPassword);
}
throw notFound;
} catch (Exception repositoryProblem) {
throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
} catch (Exception tenantNotFound) {
throw new InternalAuthenticationServiceException(tenantNotFound.getMessage(), tenantNotFound);
}
if (loadedUser == null) {
throw new InternalAuthenticationServiceException("get user information failed");
......
......@@ -63,7 +63,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
UserVo userVo = userVoResponseBean.getData();
if (userVo == null)
throw new UsernameNotFoundException("user does not exist");
return new CustomUserDetails(username, userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginTypeEnum.PWD);
return new CustomUserDetails(username, userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), userVo.getId(), start, LoginTypeEnum.PWD);
}
/**
......@@ -103,7 +103,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
throw new ServiceException(GET_USER_INFO_FAIL + userVoResponseBean.getMsg());
userVo = userVoResponseBean.getData();
}
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginTypeEnum.SMS);
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), userVo.getId(), start, LoginTypeEnum.SMS);
}
/**
......@@ -149,7 +149,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
throw new ServiceException(GET_USER_INFO_FAIL + userVoResponseBean.getMsg());
userVo = userVoResponseBean.getData();
}
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginTypeEnum.WECHAT);
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), userVo.getId(), start, LoginTypeEnum.WECHAT);
}
/**
......
......@@ -6,6 +6,7 @@ import com.github.tangyi.exam.api.dto.SubjectDto;
import com.github.tangyi.exam.api.module.Answer;
import com.github.tangyi.exam.enums.SubjectTypeEnum;
import com.github.tangyi.exam.handler.AbstractAnswerHandler;
import com.github.tangyi.exam.utils.AnswerHandlerUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
......@@ -41,8 +42,8 @@ public class MultipleChoicesAnswerHandler extends AbstractAnswerHandler {
String userAnswer = answer.getAnswer();
String correctAnswer = subject.getAnswer().getAnswer();
if (StringUtils.isNotBlank(userAnswer) && StringUtils.isNotBlank(correctAnswer)) {
String[] userAnswers = userAnswer.split(CommonConstant.COMMA);
String[] correctAnswers = correctAnswer.split(CommonConstant.COMMA);
String[] userAnswers = AnswerHandlerUtil.replaceComma(userAnswer).split(CommonConstant.COMMA);
String[] correctAnswers = AnswerHandlerUtil.replaceComma(correctAnswer).split(CommonConstant.COMMA);
subject.getOptions().forEach(option -> {
if (ArrayUtils.contains(correctAnswers, option.getOptionName())) {
option.setRight(ArrayUtils.contains(userAnswers, option.getOptionName()) ? TRUE : FALSE);
......@@ -53,7 +54,7 @@ public class MultipleChoicesAnswerHandler extends AbstractAnswerHandler {
@Override
public boolean judgeRight(Answer answer, SubjectDto subject) {
String[] correctAnswers = subject.getAnswer().getAnswer().split(CommonConstant.COMMA);
String[] correctAnswers = AnswerHandlerUtil.replaceComma(subject.getAnswer().getAnswer()).split(CommonConstant.COMMA);
for (String as : answer.getAnswer().split(CommonConstant.COMMA)) {
if (!ArrayUtils.contains(correctAnswers, as)) {
return false;
......
......@@ -35,7 +35,7 @@ public interface UserServiceClient {
* @author tangyi
* @date 2019/03/17 12:14
*/
@GetMapping("/v1/user/findUserByIdentifier/{identifier}")
@GetMapping("/v1/user/anonymousUser/findUserByIdentifier/{identifier}")
ResponseBean<UserVo> findUserByIdentifier(@PathVariable("identifier") String identifier, @RequestParam("tenantCode") String tenantCode);
/**
......@@ -48,7 +48,7 @@ public interface UserServiceClient {
* @author tangyi
* @date 2019/07/06 14:14:11
*/
@GetMapping("/v1/user/findUserByIdentifier/{identifier}")
@GetMapping("/v1/user/anonymousUser/findUserByIdentifier/{identifier}")
ResponseBean<UserVo> findUserByIdentifier(@PathVariable("identifier") String identifier, @RequestParam(value = "identityType", required = false) Integer identityType, @RequestParam("tenantCode") String tenantCode);
......@@ -131,7 +131,7 @@ public interface UserServiceClient {
* @author tangyi
* @date 2019/04/08 20:42
*/
@GetMapping("/v1/menu/findMenuByRole/{role}")
@GetMapping("/v1/menu/anonymousUser/findMenuByRole/{role}")
ResponseBean<List<Menu>> findMenuByRole(@PathVariable("role") String role, @RequestParam("tenantCode") String tenantCode);
/**
......@@ -142,7 +142,7 @@ public interface UserServiceClient {
* @author tangyi
* @date 2019/04/26 11:48
*/
@GetMapping("/v1/menu/findAllMenu")
@GetMapping("/v1/menu/anonymousUser/findAllMenu")
ResponseBean<List<Menu>> findAllMenu(@RequestParam("tenantCode") String tenantCode);
/**
......@@ -153,7 +153,7 @@ public interface UserServiceClient {
* @author tangyi
* @date 2019/05/26 10:21
*/
@GetMapping("/v1/tenant/findTenantByTenantCode/{tenantCode}")
@GetMapping("/v1/tenant/anonymousUser/findTenantByTenantCode/{tenantCode}")
ResponseBean<Tenant> findTenantByTenantCode(@PathVariable("tenantCode") String tenantCode);
/**
......@@ -176,17 +176,17 @@ public interface UserServiceClient {
* @author tangyi
* @date 2019/07/05 20:57:31
*/
@PostMapping("/v1/user/register")
@PostMapping("/v1/user/anonymousUser/register")
ResponseBean<Boolean> registerUser(@RequestBody UserDto userDto);
/**
* 更新用户
* 更新用户登录信息
*
* @param userDto userDto
* @return ResponseBean
* @author tangyi
* @date 2019/07/05 20:59:06
*/
@PutMapping("/v1/user")
ResponseBean<Boolean> updateUser(UserDto userDto);
@PutMapping("/v1/user/anonymousUser/updateLoginInfo")
ResponseBean<Boolean> updateLoginInfo(UserDto userDto);
}
......@@ -203,14 +203,14 @@ public class UserServiceClientFallbackImpl implements UserServiceClient {
}
/**
* 更新用户
* 更新用户登录信息
*
* @param userDto userDto
* @return ResponseBean
*/
@Override
public ResponseBean<Boolean> updateUser(UserDto userDto) {
log.error("Feign updateUser failed, {}, {}, {}", userDto.getIdentityType(), userDto.getIdentifier(), throwable);
public ResponseBean<Boolean> updateLoginInfo(UserDto userDto) {
log.error("Feign updateLoginInfo failed, {}, {}, {}", userDto.getIdentityType(), userDto.getIdentifier(), throwable);
return null;
}
......
......@@ -179,7 +179,7 @@ public class MenuController extends BaseController {
* @author tangyi
* @date 2018/8/27 15:58
*/
@GetMapping("findMenuByRole/{role}")
@GetMapping("anonymousUser/findMenuByRole/{role}")
@ApiOperation(value = "根据角色查找菜单", notes = "根据角色id获取角色菜单")
@ApiImplicitParam(name = "role", value = "角色名称", required = true, dataType = "String", paramType = "path")
public ResponseBean<List<Menu>> findMenuByRole(@PathVariable String role, @RequestParam @NotBlank String tenantCode) {
......@@ -194,7 +194,7 @@ public class MenuController extends BaseController {
* @author tangyi
* @date 2019/04/26 11:50
*/
@GetMapping("findAllMenu")
@GetMapping("anonymousUser/findAllMenu")
@ApiOperation(value = "查询所有菜单", notes = "查询所有菜单")
public ResponseBean<List<Menu>> findAllMenu(@RequestParam @NotBlank String tenantCode) {
Menu menu = new Menu();
......
......@@ -61,7 +61,7 @@ public class TenantController extends BaseController {
* @author tangyi
* @date 2019/05/26 10:23
*/
@GetMapping("findTenantByTenantCode/{tenantCode}")
@GetMapping("anonymousUser/findTenantByTenantCode/{tenantCode}")
public ResponseBean<Tenant> findTenantByTenantCode(@PathVariable String tenantCode) {
return new ResponseBean<>(tenantService.getByTenantCode(tenantCode));
}
......
......@@ -105,7 +105,7 @@ public class UserController extends BaseController {
* @param tenantCode tenantCode
* @return ResponseBean
*/
@GetMapping("/findUserByIdentifier/{identifier}")
@GetMapping("anonymousUser/findUserByIdentifier/{identifier}")
@ApiOperation(value = "获取用户信息", notes = "根据用户name获取用户详细信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "identifier", value = "用户唯一标识", required = true, dataType = "String", paramType = "path"),
......@@ -235,7 +235,7 @@ public class UserController extends BaseController {
* @author tangyi
* @date 2019/06/21 20:09
*/
@PutMapping("updatePassword")
@PutMapping("anonymousUser/updatePassword")
@ApiOperation(value = "修改用户密码", notes = "修改用户密码")
@ApiImplicitParam(name = "userDto", value = "用户实体user", required = true, dataType = "UserDto")
@Log("更新用户密码")
......@@ -403,7 +403,7 @@ public class UserController extends BaseController {
@ApiImplicitParam(name = "randomStr", value = "随机数", dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "mobile", value = "手机号", dataType = "String", paramType = "query")
})
@PostMapping("register")
@PostMapping("anonymousUser/register")
@Log("注册用户")
public ResponseBean<Boolean> register(@RequestBody @Valid UserDto userDto) {
return new ResponseBean<>(userService.register(userDto));
......@@ -425,7 +425,7 @@ public class UserController extends BaseController {
@ApiImplicitParam(name = "identifier", value = "用户唯一标识", required = true, dataType = "String", paramType = "path"),
@ApiImplicitParam(name = "tenantCode", value = "租户标识", required = true, dataType = "String"),
})
@GetMapping("checkExist/{identifier}")
@GetMapping("anonymousUser/checkExist/{identifier}")
public ResponseBean<Boolean> checkExist(@PathVariable("identifier") String identifier, @RequestParam Integer identityType, @RequestHeader(SecurityConstant.TENANT_CODE_HEADER) String tenantCode) {
return new ResponseBean<>(userService.checkIdentifierIsExist(identityType, identifier, tenantCode));
}
......@@ -451,7 +451,7 @@ public class UserController extends BaseController {
* @author tangyi
* @date 2019/6/7 12:00
*/
@PutMapping("/resetPassword")
@PutMapping("anonymousUser/resetPassword")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "重置密码", notes = "根据用户id重置密码")
@ApiImplicitParam(name = "userDto", value = "用户实体user", required = true, dataType = "UserDto")
......@@ -459,4 +459,34 @@ public class UserController extends BaseController {
public ResponseBean<Boolean> resetPassword(@RequestBody UserDto userDto) {
return new ResponseBean<>(userService.resetPassword(userDto));
}
/**
* 更新用户的基本信息
*
* @param userDto userDto
* @return ResponseBean
* @author tangyi
* @date 2020/02/29 16:55
*/
@PutMapping("anonymousUser/updateLoginInfo")
@ApiOperation(value = "更新用户登录信息", notes = "根据用户id更新用户的登录信息")
@ApiImplicitParam(name = "userDto", value = "用户实体user", required = true, dataType = "UserDto")
@Log("更新用户登录信息")
public ResponseBean<Boolean> updateLoginInfo(@RequestBody UserDto userDto) {
Boolean success = Boolean.FALSE;
if (StringUtils.isNotBlank(userDto.getIdentifier())) {
UserAuths userAuths = new UserAuths();
userAuths.setIdentifier(userDto.getIdentifier());
userAuths = userAuthsService.getByIdentifier(userAuths);
if (userAuths != null) {
User user = new User();
user.setId(userAuths.getUserId());
user.setLoginTime(userDto.getLoginTime());
user.setModifyDate(userDto.getLoginTime());
user.setModifier(userAuths.getIdentifier());
success = userService.update(user) > 0;
}
}
return new ResponseBean<>(success);
}
}
......@@ -657,8 +657,10 @@ public class UserService extends CrudService<UserMapper, User> {
userVos = userStream.map(tempUser -> {
UserVo tempUserVo = new UserVo();
BeanUtils.copyProperties(tempUser, tempUserVo);
attachment.setId(tempUser.getAvatarId());
tempUserVo.setAvatarUrl(attachmentService.getPreviewUrl(attachment));
if (tempUser.getAvatarId() != null) {
attachment.setId(tempUser.getAvatarId());
tempUserVo.setAvatarUrl(attachmentService.getPreviewUrl(attachment));
}
return tempUserVo;
}).collect(Collectors.toList());
}
......
......@@ -249,7 +249,9 @@
<if test="applicationCode != null">
application_code = #{applicationCode} ,
</if>
modifier = #{modifier} ,
<if test="modifier != null">
modifier = #{modifier} ,
</if>
modify_date = #{modifyDate, jdbcType=TIMESTAMP, javaType=java.util.Date}
where id = #{id}
</update>
......
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