拆分模块框架初始化

This commit is contained in:
wsw
2023-08-04 13:49:13 +08:00
parent 86cd51a0d6
commit e72d351d25
316 changed files with 2636 additions and 2765 deletions

21
playedu-system/pom.xml Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>xyz.playedu</groupId>
<artifactId>playedu</artifactId>
<version>1.2</version>
</parent>
<artifactId>playedu-system</artifactId>
<dependencies>
<dependency>
<groupId>xyz.playedu</groupId>
<artifactId>playedu-common</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.aspectj;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import xyz.playedu.common.constant.SystemConstant;
import xyz.playedu.common.domain.AdminLog;
import xyz.playedu.common.domain.AdminUser;
import xyz.playedu.common.service.AdminLogService;
import xyz.playedu.common.service.AdminUserService;
import xyz.playedu.common.service.BackendAuthService;
import xyz.playedu.common.util.IpUtil;
import xyz.playedu.common.util.RequestUtil;
import xyz.playedu.common.util.StringUtil;
import xyz.playedu.common.annotation.Log;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
@Aspect
@Component
@Slf4j
public class AdminLogAspect {
@Autowired private BackendAuthService authService;
@Autowired private AdminUserService adminUserService;
@Autowired private AdminLogService adminLogService;
/** 排除敏感属性字段 */
public static final String EXCLUDE_PROPERTIES = "password,oldPassword,newPassword,confirmPassword,token";
/** Controller层切点 注解拦截 */
@Pointcut("@annotation(xyz.playedu.common.annotation.Log)")
public void logPointCut() {}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
handleLog(joinPoint, null, jsonResult);
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
handleLog(joinPoint, e, null);
}
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
try {
// 获取注解信息
Log controllerLog = getAnnotationLog(joinPoint);
if (null == controllerLog) {
return;
}
AdminUser adminUser = adminUserService.findById(authService.userId());
if (null == adminUser) {
return;
}
// 日志
AdminLog adminLog = new AdminLog();
adminLog.setAdminId(adminUser.getId());
adminLog.setAdminName(adminUser.getName());
adminLog.setModule("BACKEND");
adminLog.setTitle(controllerLog.title());
adminLog.setOpt(controllerLog.businessType().ordinal());
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
adminLog.setMethod(className + "." + methodName + "()");
HttpServletRequest request = RequestUtil.handler();
if (null == request) {
return;
}
adminLog.setRequestMethod(request.getMethod());
adminLog.setUrl(request.getRequestURL().toString());
String params = "";
Map<String, String[]> parameterMap = request.getParameterMap();
if (StringUtil.isNotEmpty(parameterMap)) {
params = JSONUtil.toJsonStr(parameterMap);
} else {
Object[] args = joinPoint.getArgs();
if (StringUtil.isNotNull(args)) {
params = StringUtil.arrayToString(args);
}
}
if (StringUtil.isNotEmpty(params)) {
JSONObject paramObj = excludeProperties(params);
adminLog.setParam(JSONUtil.toJsonStr(paramObj));
}
if (null != jsonResult) {
jsonResult = excludeProperties(JSONUtil.toJsonStr(jsonResult));
adminLog.setResult(JSONUtil.toJsonStr(jsonResult));
}
adminLog.setIp(IpUtil.getIpAddress());
adminLog.setIpArea(IpUtil.getRealAddressByIP(IpUtil.getIpAddress()));
if (null != e) {
adminLog.setErrorMsg(e.getMessage());
}
adminLog.setCreatedAt(new Date());
// 保存数据库
adminLogService.save(adminLog);
} catch (Exception exp) {
// 记录本地异常日志
log.error("异常信息:" + exp.getMessage(), e);
}
}
/** 是否存在注解,如果存在就获取 */
private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(Log.class);
}
return null;
}
public JSONObject excludeProperties(String jsonData) {
JSONObject jsonObjectResult = new JSONObject();
// 把传入String类型转换成JSONObject对象
if (JSONUtil.isTypeJSONObject(jsonData)) {
JSONObject jsonObject = JSONUtil.parseObj(jsonData);
for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (StringUtil.isNotNull(value)) {
// 如果value依旧是json类型的话继续递归解析
if (JSONUtil.isTypeJSONObject(value.toString())) {
jsonObjectResult.put(key, excludeProperties(entry.getValue().toString()));
} else {
// 如果value是单纯的数据,执行脱敏操作
if(EXCLUDE_PROPERTIES.contains(key)){
jsonObjectResult.put(key, SystemConstant.CONFIG_MASK);
}else {
jsonObjectResult.put(key, value);
}
}
}
}
}
return jsonObjectResult;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.aspectj;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import xyz.playedu.common.context.BCtx;
import xyz.playedu.common.bus.BackendBus;
import xyz.playedu.common.types.JsonResponse;
import xyz.playedu.common.annotation.BackendPermission;
import java.util.HashMap;
@Aspect
@Component
@Slf4j
public class BackendPermissionAspect {
@Autowired private BackendBus backendBus;
@Pointcut("@annotation(xyz.playedu.common.annotation.BackendPermission)")
private void doPointcut() {}
@Around("doPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
BackendPermission middleware =
signature.getMethod().getAnnotation(BackendPermission.class);
Integer adminUserId = BCtx.getId();
HashMap<String, Boolean> permissions = backendBus.adminUserPermissions(adminUserId);
if (permissions.get(middleware.slug()) == null) {
return JsonResponse.error("权限不足", 403);
}
return joinPoint.proceed();
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import xyz.playedu.common.exception.LimitException;
import xyz.playedu.common.util.RedisDistributedLock;
import xyz.playedu.common.annotation.Lock;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class LockAspect {
private final RedisDistributedLock redisDistributedLock;
public LockAspect(RedisDistributedLock redisDistributedLock) {
this.redisDistributedLock = redisDistributedLock;
}
@Around("@annotation(xyz.playedu.common.annotation.Lock)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Lock distributedLock = method.getAnnotation(Lock.class);
String key = distributedLock.key();
long expire = distributedLock.expire();
TimeUnit timeUnit = distributedLock.timeUnit();
boolean success = redisDistributedLock.tryLock(key, expire, timeUnit);
if (!success) {
throw new LimitException("请稍后再试");
}
try {
return joinPoint.proceed();
} finally {
redisDistributedLock.releaseLock(key);
}
}
}

View File

@@ -0,0 +1,317 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.checks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import xyz.playedu.common.constant.BPermissionConstant;
import xyz.playedu.common.domain.AdminPermission;
import xyz.playedu.common.service.AdminPermissionService;
import java.util.*;
@Order(1020)
@Component
public class AdminPermissionCheck implements ApplicationRunner {
private final Map<String, Map<String, AdminPermission[]>> permissions =
new HashMap<>() {
{
put(
BPermissionConstant.TYPE_ACTION,
new HashMap<>() {
{
// 管理员
put(
"管理员",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("列表");
setSlug(
BPermissionConstant
.ADMIN_USER_INDEX);
}
},
new AdminPermission() {
{
setSort(10);
setName("新增|编辑|删除");
setSlug(BPermissionConstant.ADMIN_USER_CUD);
}
},
});
// 管理员角色
put(
"管理员角色",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("新增|编辑|删除");
setSlug(BPermissionConstant.ADMIN_ROLE);
}
},
});
// 管理员日志
put(
"管理员日志",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("列表");
setSlug(BPermissionConstant.ADMIN_LOG);
}
},
});
// 部门
put(
"部门",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("新增|编辑|删除");
setSlug(BPermissionConstant.DEPARTMENT_CUD);
}
},
new AdminPermission() {
{
setSort(10);
setName("学员学习");
setSlug(
BPermissionConstant
.DEPARTMENT_USER_LEARN);
}
},
});
// 资源分类
put(
"资源分类",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("新增|编辑|删除");
setSlug(
BPermissionConstant
.RESOURCE_CATEGORY);
}
},
});
// 学员
put(
"学员",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("列表");
setSlug(BPermissionConstant.USER_INDEX);
}
},
new AdminPermission() {
{
setSort(10);
setName("新增");
setSlug(BPermissionConstant.USER_STORE);
}
},
new AdminPermission() {
{
setSort(20);
setName("编辑");
setSlug(BPermissionConstant.USER_UPDATE);
}
},
new AdminPermission() {
{
setSort(30);
setName("删除");
setSlug(BPermissionConstant.USER_DESTROY);
}
},
new AdminPermission() {
{
setSort(40);
setName("学习");
setSlug(BPermissionConstant.USER_LEARN);
}
},
new AdminPermission() {
{
setSort(50);
setName("学习-删除");
setSlug(
BPermissionConstant
.USER_LEARN_DESTROY);
}
},
});
// 线上课
put(
"线上课",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("新增|编辑|删除");
setSlug(BPermissionConstant.COURSE);
}
},
new AdminPermission() {
{
setSort(10);
setName("学员学习记录-列表");
setSlug(BPermissionConstant.COURSE_USER);
}
},
new AdminPermission() {
{
setSort(20);
setName("学员学习记录-删除");
setSlug(
BPermissionConstant
.COURSE_USER_DESTROY);
}
},
});
// 其它
put(
"其它",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("修改登录密码");
setSlug(
BPermissionConstant
.PASSWORD_CHANGE);
}
},
});
// 系统配置
put(
"系统配置",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("系统配置");
setSlug(BPermissionConstant.SYSTEM_CONFIG);
}
},
});
}
});
put(
BPermissionConstant.TYPE_DATA,
new HashMap<>() {
{
// 管理员
put(
"管理员",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("邮箱");
setSlug(
BPermissionConstant
.DATA_ADMIN_EMAIL);
}
},
});
// 学员
put(
"学员",
new AdminPermission[] {
new AdminPermission() {
{
setSort(0);
setName("邮箱");
setSlug(
BPermissionConstant
.DATA_USER_EMAIL);
}
},
new AdminPermission() {
{
setSort(10);
setName("姓名");
setSlug(BPermissionConstant.DATA_USER_NAME);
}
},
new AdminPermission() {
{
setSort(20);
setName("身份证号");
setSlug(
BPermissionConstant
.DATA_USER_ID_CARD);
}
},
});
}
});
}
};
@Autowired private AdminPermissionService permissionService;
@Override
public void run(ApplicationArguments args) throws Exception {
HashMap<String, Boolean> slugs = permissionService.allSlugs();
List<AdminPermission> list = new ArrayList<>();
Date now = new Date();
permissions.forEach(
(typeValue, group) -> {
group.forEach(
(groupNameValue, item) -> {
for (int i = 0; i < item.length; i++) {
AdminPermission permissionItem = item[i];
if (slugs.get(permissionItem.getSlug()) != null) {
continue;
}
// 不存在
list.add(
new AdminPermission() {
{
setType(typeValue);
setGroupName(groupNameValue);
setSort(permissionItem.getSort());
setName(permissionItem.getName());
setSlug(permissionItem.getSlug());
setCreatedAt(now);
}
});
}
});
});
if (list.size() > 0) {
permissionService.saveBatch(list);
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.checks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import xyz.playedu.common.constant.BackendConstant;
import xyz.playedu.common.domain.AdminRole;
import xyz.playedu.common.service.AdminRoleService;
import java.util.Date;
@Order(1010)
@Component
public class AdminRoleCheck implements ApplicationRunner {
@Autowired private AdminRoleService adminRoleService;
private static final AdminRole superRole =
new AdminRole() {
{
setName("超级管理员");
setSlug(BackendConstant.SUPER_ADMIN_ROLE);
setCreatedAt(new Date());
setCreatedAt(new Date());
}
};
@Override
public void run(ApplicationArguments args) throws Exception {
AdminRole adminRole = adminRoleService.getBySlug(BackendConstant.SUPER_ADMIN_ROLE);
if (adminRole != null) { // 已存在超级管理权限
return;
}
adminRoleService.save(superRole);
}
}

View File

@@ -0,0 +1,256 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.checks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import xyz.playedu.common.constant.BackendConstant;
import xyz.playedu.common.constant.ConfigConstant;
import xyz.playedu.common.domain.AppConfig;
import xyz.playedu.common.service.AppConfigService;
import java.util.*;
@Component
@Order(1000)
public class AppConfigCheck implements ApplicationRunner {
private static final HashMap<String, AppConfig[]> configs =
new HashMap<>() {
{
// 系统配置
put(
"系统",
new AppConfig[] {
new AppConfig() {
{
setName("网站名");
setSort(10);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_INPUT);
setKeyName(ConfigConstant.SYSTEM_NAME);
setKeyValue("");
setHelp("请输入网站名");
}
},
new AppConfig() {
{
setName("Logo");
setSort(20);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_IMAGE);
setKeyName(ConfigConstant.SYSTEM_LOGO);
setKeyValue("");
}
},
new AppConfig() {
{
setName("API访问地址");
setSort(30);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_INPUT);
setKeyName(ConfigConstant.SYSTEM_API_URL);
setKeyValue("");
setHelp("请输入API访问地址");
}
},
new AppConfig() {
{
setName("PC端口访问地址");
setSort(40);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_INPUT);
setKeyName(ConfigConstant.SYSTEM_PC_URL);
setKeyValue("");
setHelp("请输入PC端访问地址");
}
},
new AppConfig() {
{
setName("H5端口访问地址");
setSort(50);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_INPUT);
setKeyName(ConfigConstant.SYSTEM_H5_URL);
setKeyValue("");
setHelp("请输入H5端访问地址");
}
},
new AppConfig() {
{
setName("网站页脚");
setSort(60);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_INPUT);
setKeyName("system.pc_index_footer_msg");
setKeyValue("");
setHelp("自定义一句话显示在前台页脚");
}
},
});
// 播放配置
put(
"播放配置",
new AppConfig[] {
new AppConfig() {
{
setName("播放器封面");
setSort(10);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_IMAGE);
setKeyName("player.poster");
setKeyValue("");
setHelp("播放器封面在学员观看视频时默认显示");
}
},
new AppConfig() {
{
setName("启用跑马灯");
setSort(20);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_SWITCH);
setKeyName("player.is_enabled_bullet_secret");
setKeyValue("0");
setHelp("开启之后视频播放器将会随机显示学员信息");
}
},
new AppConfig() {
{
setName("跑马灯内容");
setSort(30);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
setKeyName("player.bullet_secret_text");
setKeyValue("");
setHelp("请配置跑马灯显示的内容模板");
}
},
new AppConfig() {
{
setName("跑马灯颜色");
setSort(40);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
setKeyName("player.bullet_secret_color");
setKeyValue("");
}
},
new AppConfig() {
{
setName("跑马灯透明度");
setSort(50);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
setKeyName("player.bullet_secret_opacity");
setKeyValue("1");
}
},
new AppConfig() {
{
setName("禁止拖拽播放");
setSort(60);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_SWITCH);
setKeyName("player.disabled_drag");
setKeyValue("0");
}
},
});
put(
"学员配置",
new AppConfig[] {
new AppConfig() {
{
setName("默认头像");
setSort(10);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_IMAGE);
setKeyName(ConfigConstant.MEMBER_DEFAULT_AVATAR);
setKeyValue("");
}
},
});
put(
"MinIO",
new AppConfig[] {
new AppConfig() {
{
setName("AccessKey");
setSort(10);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
setKeyName(ConfigConstant.MINIO_ACCESS_KEY);
setKeyValue("");
}
},
new AppConfig() {
{
setName("SecretKey");
setSort(20);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
setKeyName(ConfigConstant.MINIO_SECRET_KEY);
setKeyValue("");
setIsPrivate(1);
}
},
new AppConfig() {
{
setName("Bucket");
setSort(30);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
setKeyName(ConfigConstant.MINIO_BUCKET);
setKeyValue("");
}
},
new AppConfig() {
{
setName("Endpoint");
setSort(40);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
setKeyName(ConfigConstant.MINIO_ENDPOINT);
setKeyValue("");
}
},
new AppConfig() {
{
setName("Domain");
setSort(50);
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
setKeyName(ConfigConstant.MINIO_DOMAIN);
setKeyValue("");
}
},
});
}
};
@Autowired private AppConfigService configService;
@Override
public void run(ApplicationArguments args) throws Exception {
Map<String, Long> keys = configService.allKeys();
List<AppConfig> list = new ArrayList<>();
Date now = new Date();
configs.forEach(
(groupNameValue, items) -> {
for (int i = 0; i < items.length; i++) {
AppConfig configItem = items[i];
if (keys.get(configItem.getKeyName()) != null) {
continue;
}
configItem.setGroupName(groupNameValue);
configItem.setCreatedAt(now);
list.add(configItem);
}
});
if (list.size() > 0) {
configService.saveBatch(list);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.checks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import xyz.playedu.common.domain.AppConfig;
import xyz.playedu.common.service.AdminPermissionService;
import xyz.playedu.common.service.AppConfigService;
import java.util.ArrayList;
@Order(10000)
@Component
public class UpgradeCheck implements ApplicationRunner {
@Autowired private AppConfigService appConfigService;
@Autowired private AdminPermissionService permissionService;
@Override
public void run(ApplicationArguments args) throws Exception {
upgrade_v1_beta7();
}
private void upgrade_v1_beta7() {
appConfigService.update(
new AppConfig() {
{
setIsPrivate(1);
}
},
appConfigService.query().getWrapper().eq("key_name", "minio.secret_key"));
permissionService.remove(
permissionService
.query()
.getWrapper()
.in(
"slug",
new ArrayList<>() {
{
add("resource-destroy");
}
}));
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import xyz.playedu.common.context.BCtx;
import xyz.playedu.common.bus.BackendBus;
import xyz.playedu.common.config.PlayEduConfig;
import xyz.playedu.common.domain.AdminUser;
import xyz.playedu.common.service.AdminUserService;
import xyz.playedu.common.service.AppConfigService;
import xyz.playedu.common.service.BackendAuthService;
import xyz.playedu.common.service.RateLimiterService;
import xyz.playedu.common.types.JsonResponse;
import xyz.playedu.common.util.HelperUtil;
import xyz.playedu.common.util.IpUtil;
import java.io.IOException;
import java.util.Map;
@Component
@Slf4j
public class AdminInterceptor implements HandlerInterceptor {
@Autowired private BackendAuthService authService;
@Autowired private AdminUserService adminUserService;
@Autowired private BackendBus backendBus;
@Autowired private AppConfigService configService;
@Autowired private RateLimiterService rateLimiterService;
@Autowired private PlayEduConfig playEduConfig;
@Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if ("OPTIONS".equals(request.getMethod())) {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
String reqCountKey = "api-limiter:" + IpUtil.getIpAddress();
Long reqCount = rateLimiterService.current(reqCountKey, playEduConfig.getLimiterDuration());
if (reqCount > playEduConfig.getLimiterLimit()) {
return responseTransform(response, 429, "太多请求");
}
// 读取全局配置
Map<String, String> systemConfig = configService.keyValues();
BCtx.setConfig(systemConfig);
if (BackendBus.inUnAuthWhitelist(request.getRequestURI())) {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
if (!authService.check()) {
return responseTransform(response, 401, "请登录");
}
AdminUser adminUser = adminUserService.findById(authService.userId());
if (adminUser == null) {
return responseTransform(response, 401, "管理员不存在");
}
if (adminUser.getIsBanLogin() == 1) {
return responseTransform(response, 403, "当前管理员禁止登录");
}
BCtx.setId(authService.userId());
BCtx.setAdminUser(adminUser);
BCtx.setAdminPer(backendBus.adminUserPermissions(adminUser.getId()));
return HandlerInterceptor.super.preHandle(request, response, handler);
}
private boolean responseTransform(HttpServletResponse response, int code, String msg)
throws IOException {
response.setStatus(code);
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(HelperUtil.toJsonStr(JsonResponse.error(msg)));
return false;
}
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
BCtx.remove();
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import xyz.playedu.common.context.FCtx;
import xyz.playedu.common.config.PlayEduConfig;
import xyz.playedu.common.constant.FrontendConstant;
import xyz.playedu.common.domain.User;
import xyz.playedu.common.service.FrontendAuthService;
import xyz.playedu.common.service.RateLimiterService;
import xyz.playedu.common.service.UserService;
import xyz.playedu.common.types.JsonResponse;
import xyz.playedu.common.util.HelperUtil;
import xyz.playedu.common.util.IpUtil;
import java.io.IOException;
@Component
@Slf4j
public class FrontInterceptor implements HandlerInterceptor {
@Autowired private FrontendAuthService authService;
@Autowired private UserService userService;
@Autowired private RateLimiterService rateLimiterService;
@Autowired private PlayEduConfig playEduConfig;
@Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if ("OPTIONS".equals(request.getMethod())) {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
String reqCountKey = "api-limiter:" + IpUtil.getIpAddress();
Long reqCount = rateLimiterService.current(reqCountKey, playEduConfig.getLimiterDuration());
if (reqCount > playEduConfig.getLimiterLimit()) {
return responseTransform(response, 429, "太多请求");
}
if (FrontendConstant.UN_AUTH_URI_WHITELIST.contains(request.getRequestURI())) {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
if (!authService.check()) {
return responseTransform(response, 401, "请登录");
}
User user = userService.find(authService.userId());
if (user == null) {
return responseTransform(response, 401, "请重新登录");
}
if (user.getIsLock() == 1) {
return responseTransform(response, 403, "当前学员已锁定无法登录");
}
FCtx.setUser(user);
FCtx.setId(user.getId());
FCtx.setJWtJti(authService.jti());
return HandlerInterceptor.super.preHandle(request, response, handler);
}
private boolean responseTransform(HttpServletResponse response, int code, String msg)
throws IOException {
response.setStatus(code);
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(HelperUtil.toJsonStr(JsonResponse.error(msg)));
return false;
}
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
FCtx.remove();
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.system.interceptor;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@Slf4j
public class WebMvcConfig implements WebMvcConfigurer {
@Resource private AdminInterceptor adminInterceptor;
@Autowired private FrontInterceptor frontInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminInterceptor).addPathPatterns("/backend/**");
registry.addInterceptor(frontInterceptor).addPathPatterns("/api/v1/**");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(false)
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("GET", "PUT", "POST", "DELETE")
.exposedHeaders("*");
}
}