mirror of
https://github.com/PlayEdu/PlayEdu
synced 2025-06-27 23:52:58 +08:00
Compare commits
21 Commits
a1d5a9c648
...
03403d2c33
Author | SHA1 | Date | |
---|---|---|---|
|
03403d2c33 | ||
|
893ab33811 | ||
|
5e4c35f9bf | ||
|
877aec3b01 | ||
|
e8399362ea | ||
|
5dcde4f911 | ||
|
12c4b810c2 | ||
|
3ea07739d1 | ||
|
9f27aaac42 | ||
|
be6264dcd3 | ||
|
3c9b354aea | ||
|
040dcdfaed | ||
|
8c905c6552 | ||
|
8f27bb9fda | ||
|
23ff7068f7 | ||
|
82c53ed87f | ||
|
acb8b79edd | ||
|
21c016af62 | ||
|
f0f316c504 | ||
|
ee1f009966 | ||
|
179a7d5f62 |
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@ -6,9 +6,12 @@ on:
|
||||
- main
|
||||
- dev
|
||||
- 'feat/**'
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
IMAGE_FQDN: registry.cn-hangzhou.aliyuncs.com/playedu/api
|
||||
IMAGE_TAG: ''
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
@ -33,4 +36,16 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.IMAGE_FQDN }}:1.4
|
||||
${{ env.IMAGE_FQDN }}:${{ env.IMAGE_TAG }}
|
||||
env:
|
||||
IMAGE_TAG: ${{ startsWith(github.ref, 'refs/heads/main') && 'latest' || startsWith(github.ref, 'refs/heads/dev') && 'dev' || github.ref_name }}
|
||||
if: startsWith(github.ref, 'refs/heads/')
|
||||
- name: Build with Tag
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.IMAGE_FQDN }}:${{ github.ref_name }}
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
35
README.md
35
README.md
@ -1,42 +1,41 @@
|
||||
<p align="center">
|
||||
<img src="https://meedu.cloud.oss.meedu.vip/playedu/%E5%A4%B4%E5%9B%BE.jpg"/>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://playedu.xyz">官网</a> | <a href="https://playedu.xyz/docs/docs/intro/">文档</a> | <a href="https://playedu.xyz/docs/docs/function">功能列表</a> | <a href="https://playedu.xyz/docs/docs/install/quick">快速上手</a>
|
||||
</p>
|
||||
|
||||
### 系统介绍
|
||||
## 系统介绍
|
||||
|
||||
PlayEdu 是由白书科技团队经营多年线上教育系统打造出的一款全新的企业培训方案,致力于为更多企业机构搭建私有化内部培训平台。PlayEdu 基于 Java + MySQL 开发,采用前后端分离模式,前台采用 React18 为核心框架,后台采用 SpringBoot3 为核心框架。
|
||||
**PlayEdu** 是由白书科技团队经营多年线上教育系统打造出的一款全新的企业培训解决方案,致力于为更多企业机构搭建私有化内部培训平台。**PlayEdu** 基于 Java + MySQL 开发;采用前后端分离模式;前端采用 React18 为核心框架,后端采用 SpringBoot3 为核心框架。提供部门管理、学员管理、在线视频学习、学员进度追踪、视频私有化存储等培训主要功能。**与此同时,我们在开源版本的基础上还提供了功能更加丰富的企业版本。企业版本在开源功能的基础上提供了包括视频云端存储、视频加密、音频学习、文档(PDF|WORD|PPT)在线学习、在线考试、学习计划培训等功能。企业版本更多信息请点击下方的企业版链接查看。**
|
||||
|
||||
### 系统演示
|
||||
## 常用链接
|
||||
|
||||
| - | 站点 | 账号 | 密码 |
|
||||
| ------------ | ------------------------------------------------------ | ------------------- | -------- |
|
||||
| 学员端口 | [https://demo.playedu.xyz](https://demo.playedu.xyz) | `1@playedu.xyz` | `123123` |
|
||||
| 后台管理端口 | [https://admin.playedu.xyz](https://admin.playedu.xyz) | `admin@playedu.xyz` | `123123` |
|
||||
| 站点 | 链接 |
|
||||
| ---------- | ---------------------------------------------------------------------------- |
|
||||
| 官网 | [http://www.playedu.xyz](http://www.playedu.xyz) |
|
||||
| **企业版** | [https://www.playedu.xyz/commercial](https://www.playedu.xyz/commercial) |
|
||||
| 部署文档 | [https://www.playedu.xyz/book](https://www.playedu.xyz/book) |
|
||||
| 系统演示 | [https://www.playedu.xyz/demo](https://www.playedu.xyz/demo) |
|
||||
| 问答社区 | [https://www.playedu.xyz/qa](https://www.playedu.xyz/qa) |
|
||||
|
||||
### 依赖项目
|
||||
## 依赖前端项目
|
||||
|
||||
- [PC 界面程序](https://github.com/PlayEdu/frontend)
|
||||
- [后台界面程序](https://github.com/PlayEdu/backend)
|
||||
- [PC 界面程序](https://github.com/PlayEdu/frontend)
|
||||
- [H5 界面程序](https://github.com/PlayEdu/h5)
|
||||
|
||||
### 官方交流群
|
||||
|
||||
<p><img src="https://meedu.cloud.oss.meedu.vip/playedu/PlayEduk%E5%AE%A2%E6%9C%8D-zhu.png" width="200" /></p>
|
||||
|
||||
### 界面预览
|
||||
## 界面预览
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 使用协议
|
||||
## 使用协议
|
||||
|
||||
● 要求
|
||||
|
||||
- 保留页脚处版权信息。
|
||||
- 保留源代码中的协议。
|
||||
- 如果修改了代码,则必须在文件中进行说明。
|
||||
|
||||
● 允许
|
||||
|
||||
- 私用、商用、修改。
|
||||
|
@ -20,6 +20,7 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import xyz.playedu.common.config.UniqueNameGeneratorConfig;
|
||||
@ -27,6 +28,7 @@ import xyz.playedu.common.config.UniqueNameGeneratorConfig;
|
||||
@SpringBootApplication
|
||||
@EnableAsync
|
||||
@EnableTransactionManagement
|
||||
@EnableScheduling
|
||||
@ComponentScan(
|
||||
basePackages = {"xyz.playedu"},
|
||||
nameGenerator = UniqueNameGeneratorConfig.class)
|
||||
|
@ -23,14 +23,12 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import xyz.playedu.api.event.UserLoginEvent;
|
||||
import xyz.playedu.common.domain.LdapUser;
|
||||
import xyz.playedu.common.bus.LDAPBus;
|
||||
import xyz.playedu.common.domain.User;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.*;
|
||||
import xyz.playedu.common.util.HelperUtil;
|
||||
import xyz.playedu.common.util.IpUtil;
|
||||
import xyz.playedu.common.util.RequestUtil;
|
||||
import xyz.playedu.common.util.StringUtil;
|
||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -41,14 +39,10 @@ public class LoginBus {
|
||||
|
||||
@Autowired private FrontendAuthService authService;
|
||||
|
||||
@Autowired private DepartmentService departmentService;
|
||||
|
||||
@Autowired private LdapUserService ldapUserService;
|
||||
|
||||
@Autowired private UserService userService;
|
||||
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@Autowired private LDAPBus ldapBus;
|
||||
|
||||
@Autowired private ApplicationContext ctx;
|
||||
|
||||
public HashMap<String, Object> tokenByUser(User user) {
|
||||
@ -72,72 +66,7 @@ public class LoginBus {
|
||||
@Transactional
|
||||
public HashMap<String, Object> tokenByLdapTransformUser(LdapTransformUser ldapTransformUser)
|
||||
throws ServiceException {
|
||||
// LDAP用户的名字
|
||||
String ldapUserName = ldapTransformUser.getCn();
|
||||
|
||||
// 将LDAP用户所属的部门同步到本地
|
||||
Integer depId = departmentService.createWithChainList(ldapTransformUser.getOu());
|
||||
Integer[] depIds = depId == 0 ? null : new Integer[] {depId};
|
||||
|
||||
// LDAP用户在本地的缓存记录
|
||||
LdapUser ldapUser = ldapUserService.findByUUID(ldapTransformUser.getId());
|
||||
User user;
|
||||
|
||||
// 计算将LDAP用户关联到本地users表的email字段值
|
||||
String localUserEmail = ldapTransformUser.getUid();
|
||||
if (StringUtil.isNotEmpty(ldapTransformUser.getEmail())) {
|
||||
localUserEmail = ldapTransformUser.getEmail();
|
||||
}
|
||||
|
||||
if (ldapUser == null) {
|
||||
// 检测localUserEmail是否存在
|
||||
if (userService.find(localUserEmail) != null) {
|
||||
throw new ServiceException(String.format("已有其它账号在使用:%s", localUserEmail));
|
||||
}
|
||||
// LDAP用户数据缓存到本地
|
||||
ldapUser = ldapUserService.store(ldapTransformUser);
|
||||
// 创建本地user
|
||||
user =
|
||||
userService.createWithDepIds(
|
||||
localUserEmail,
|
||||
ldapUserName,
|
||||
appConfigService.defaultAvatar(),
|
||||
HelperUtil.randomString(20),
|
||||
"",
|
||||
depIds);
|
||||
// 将LDAP缓存数据与本地user关联
|
||||
ldapUserService.updateUserId(ldapUser.getId(), user.getId());
|
||||
} else {
|
||||
user = userService.find(ldapUser.getUserId());
|
||||
// 账号修改[账号有可能是email也有可能是uid]
|
||||
if (!localUserEmail.equals(user.getEmail())) {
|
||||
// 检测localUserEmail是否存在
|
||||
if (userService.find(localUserEmail) != null) {
|
||||
throw new ServiceException(String.format("已有其它账号在使用:%s", localUserEmail));
|
||||
}
|
||||
userService.updateEmail(user.getId(), localUserEmail);
|
||||
}
|
||||
// ldap-email的变化
|
||||
if (!ldapUser.getEmail().equals(ldapTransformUser.getEmail())) {
|
||||
ldapUserService.updateEmail(ldapUser.getId(), ldapTransformUser.getEmail());
|
||||
}
|
||||
// ldap-uid的变化
|
||||
if (!ldapUser.getUid().equals(ldapTransformUser.getUid())) {
|
||||
ldapUserService.updateUid(ldapUser.getId(), ldapTransformUser.getUid());
|
||||
}
|
||||
// 名字同步修改
|
||||
if (!ldapUserName.equals(ldapUser.getCn())) {
|
||||
userService.updateName(user.getId(), ldapUserName);
|
||||
ldapUserService.updateCN(ldapUser.getId(), ldapUserName);
|
||||
}
|
||||
// 部门修改同步
|
||||
String newOU = String.join(",", ldapTransformUser.getOu());
|
||||
if (!newOU.equals(ldapUser.getOu())) {
|
||||
userService.updateDepId(user.getId(), depIds);
|
||||
ldapUserService.updateOU(ldapUser.getId(), newOU);
|
||||
}
|
||||
}
|
||||
|
||||
User user = ldapBus.singleUserSync(ldapTransformUser, appConfigService.defaultAvatar());
|
||||
return tokenByUser(user);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package xyz.playedu.api.controller;
|
||||
|
||||
import com.amazonaws.services.s3.model.AmazonS3Exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
@ -40,7 +42,7 @@ public class ExceptionController {
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public JsonResponse exceptionHandler(Exception e) {
|
||||
log.error(e.getMessage());
|
||||
log.error("出现异常", e);
|
||||
return JsonResponse.error("系统错误", 500);
|
||||
}
|
||||
|
||||
@ -95,4 +97,10 @@ public class ExceptionController {
|
||||
public JsonResponse serviceExceptionHandler(LimitException e) {
|
||||
return JsonResponse.error("请稍后再试", 429);
|
||||
}
|
||||
|
||||
@ExceptionHandler(AmazonS3Exception.class)
|
||||
public JsonResponse serviceExceptionHandler(AmazonS3Exception e) {
|
||||
log.error("s3错误={}", e.getMessage());
|
||||
return JsonResponse.error("存储配置有问题或存储无法无法正常访问", 500);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package xyz.playedu.api.controller.backend;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
@ -28,9 +29,11 @@ import xyz.playedu.api.event.CourseDestroyEvent;
|
||||
import xyz.playedu.api.request.backend.CourseRequest;
|
||||
import xyz.playedu.common.annotation.BackendPermission;
|
||||
import xyz.playedu.common.annotation.Log;
|
||||
import xyz.playedu.common.bus.BackendBus;
|
||||
import xyz.playedu.common.constant.BPermissionConstant;
|
||||
import xyz.playedu.common.constant.BusinessTypeConstant;
|
||||
import xyz.playedu.common.context.BCtx;
|
||||
import xyz.playedu.common.domain.AdminUser;
|
||||
import xyz.playedu.common.exception.NotFoundException;
|
||||
import xyz.playedu.common.service.*;
|
||||
import xyz.playedu.common.types.JsonResponse;
|
||||
@ -71,8 +74,12 @@ public class CourseController {
|
||||
|
||||
@Autowired private DepartmentService departmentService;
|
||||
|
||||
@Autowired private AdminUserService adminUserService;
|
||||
|
||||
@Autowired private ApplicationContext ctx;
|
||||
|
||||
@Autowired private BackendBus backendBus;
|
||||
|
||||
@BackendPermission(slug = BPermissionConstant.COURSE)
|
||||
@GetMapping("/index")
|
||||
@Log(title = "线上课-列表", businessType = BusinessTypeConstant.GET)
|
||||
@ -95,6 +102,10 @@ public class CourseController {
|
||||
filter.setDepIds(depIds);
|
||||
filter.setIsRequired(isRequired);
|
||||
|
||||
if (!backendBus.isSuperAdmin()) {
|
||||
filter.setAdminId(BCtx.getId());
|
||||
}
|
||||
|
||||
PaginationResult<Course> result = courseService.paginate(page, size, filter);
|
||||
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
@ -107,6 +118,17 @@ public class CourseController {
|
||||
data.put("categories", categoryService.id2name());
|
||||
data.put("departments", departmentService.id2name());
|
||||
|
||||
// 操作人
|
||||
data.put("admin_users", new HashMap<>());
|
||||
if (!result.getData().isEmpty()) {
|
||||
Map<Integer, String> adminUsers =
|
||||
adminUserService
|
||||
.chunks(result.getData().stream().map(Course::getAdminId).toList())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(AdminUser::getId, AdminUser::getName));
|
||||
data.put("admin_users", adminUsers);
|
||||
}
|
||||
|
||||
return JsonResponse.data(data);
|
||||
}
|
||||
|
||||
@ -134,7 +156,8 @@ public class CourseController {
|
||||
req.getIsRequired(),
|
||||
req.getIsShow(),
|
||||
req.getCategoryIds(),
|
||||
req.getDepIds());
|
||||
req.getDepIds(),
|
||||
BCtx.getId());
|
||||
|
||||
Date now = new Date();
|
||||
int classHourCount = 0;
|
||||
@ -240,6 +263,10 @@ public class CourseController {
|
||||
@Log(title = "线上课-编辑", businessType = BusinessTypeConstant.GET)
|
||||
public JsonResponse edit(@PathVariable(name = "id") Integer id) throws NotFoundException {
|
||||
Course course = courseService.findOrFail(id);
|
||||
if (!backendBus.isSuperAdmin() && !course.getAdminId().equals(BCtx.getId())) {
|
||||
return JsonResponse.error("无权限操作");
|
||||
}
|
||||
|
||||
List<Integer> depIds = courseService.getDepIdsByCourseId(course.getId());
|
||||
List<Integer> categoryIds = courseService.getCategoryIdsByCourseId(course.getId());
|
||||
List<CourseChapter> chapters = chapterService.getChaptersByCourseId(course.getId());
|
||||
@ -280,6 +307,10 @@ public class CourseController {
|
||||
@PathVariable(name = "id") Integer id, @RequestBody @Validated CourseRequest req)
|
||||
throws NotFoundException {
|
||||
Course course = courseService.findOrFail(id);
|
||||
if (!backendBus.isSuperAdmin() && !course.getAdminId().equals(BCtx.getId())) {
|
||||
return JsonResponse.error("无权限操作");
|
||||
}
|
||||
|
||||
courseService.updateWithCategoryIdsAndDepIds(
|
||||
course,
|
||||
req.getTitle(),
|
||||
@ -290,15 +321,24 @@ public class CourseController {
|
||||
req.getPublishedAt(),
|
||||
req.getCategoryIds(),
|
||||
req.getDepIds());
|
||||
|
||||
return JsonResponse.success();
|
||||
}
|
||||
|
||||
@BackendPermission(slug = BPermissionConstant.COURSE)
|
||||
@DeleteMapping("/{id}")
|
||||
@Log(title = "线上课-删除", businessType = BusinessTypeConstant.DELETE)
|
||||
@SneakyThrows
|
||||
public JsonResponse destroy(@PathVariable(name = "id") Integer id) {
|
||||
Course course = courseService.findOrFail(id);
|
||||
if (!backendBus.isSuperAdmin() && !course.getAdminId().equals(BCtx.getId())) {
|
||||
return JsonResponse.error("无权限操作");
|
||||
}
|
||||
|
||||
courseService.removeById(id);
|
||||
|
||||
ctx.publishEvent(new CourseDestroyEvent(this, BCtx.getId(), id));
|
||||
|
||||
return JsonResponse.success();
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import xyz.playedu.common.types.JsonResponse;
|
||||
import xyz.playedu.common.types.mapper.UserCourseHourRecordUserFirstCreatedAtMapper;
|
||||
import xyz.playedu.common.types.paginate.PaginationResult;
|
||||
import xyz.playedu.common.types.paginate.UserPaginateFilter;
|
||||
import xyz.playedu.course.domain.UserCourseHourRecord;
|
||||
import xyz.playedu.course.domain.UserCourseRecord;
|
||||
import xyz.playedu.course.service.CourseService;
|
||||
import xyz.playedu.course.service.UserCourseHourRecordService;
|
||||
@ -141,6 +142,14 @@ public class CourseUserController {
|
||||
userService.getDepIdsGroup(result.getData().stream().map(User::getId).toList()));
|
||||
data.put("departments", departmentService.id2name());
|
||||
|
||||
// 获取每个学员的最早学习时间
|
||||
List<UserCourseHourRecord> perUserEarliestRecords =
|
||||
userCourseHourRecordService.getCoursePerUserEarliestRecord(courseId);
|
||||
data.put(
|
||||
"per_user_earliest_records",
|
||||
perUserEarliestRecords.stream()
|
||||
.collect(Collectors.toMap(UserCourseHourRecord::getUserId, e -> e)));
|
||||
|
||||
return JsonResponse.data(data);
|
||||
}
|
||||
|
||||
|
@ -30,20 +30,18 @@ import xyz.playedu.api.request.backend.DepartmentRequest;
|
||||
import xyz.playedu.api.request.backend.DepartmentSortRequest;
|
||||
import xyz.playedu.common.annotation.BackendPermission;
|
||||
import xyz.playedu.common.annotation.Log;
|
||||
import xyz.playedu.common.bus.LDAPBus;
|
||||
import xyz.playedu.common.constant.BPermissionConstant;
|
||||
import xyz.playedu.common.constant.BusinessTypeConstant;
|
||||
import xyz.playedu.common.context.BCtx;
|
||||
import xyz.playedu.common.domain.Department;
|
||||
import xyz.playedu.common.domain.User;
|
||||
import xyz.playedu.common.exception.NotFoundException;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.service.DepartmentService;
|
||||
import xyz.playedu.common.service.UserService;
|
||||
import xyz.playedu.common.types.JsonResponse;
|
||||
import xyz.playedu.common.types.LdapConfig;
|
||||
import xyz.playedu.common.types.paginate.PaginationResult;
|
||||
import xyz.playedu.common.types.paginate.UserPaginateFilter;
|
||||
import xyz.playedu.common.util.ldap.LdapUtil;
|
||||
import xyz.playedu.course.domain.Course;
|
||||
import xyz.playedu.course.domain.UserCourseRecord;
|
||||
import xyz.playedu.course.service.CourseDepartmentService;
|
||||
@ -70,7 +68,7 @@ public class DepartmentController {
|
||||
|
||||
@Autowired private ApplicationContext ctx;
|
||||
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
@Autowired private LDAPBus ldapBus;
|
||||
|
||||
@GetMapping("/index")
|
||||
@Log(title = "部门-列表", businessType = BusinessTypeConstant.GET)
|
||||
@ -104,7 +102,7 @@ public class DepartmentController {
|
||||
@Log(title = "部门-新建", businessType = BusinessTypeConstant.INSERT)
|
||||
public JsonResponse store(@RequestBody @Validated DepartmentRequest req)
|
||||
throws NotFoundException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
if (ldapBus.enabledLDAP()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
departmentService.create(req.getName(), req.getParentId(), req.getSort());
|
||||
@ -124,7 +122,7 @@ public class DepartmentController {
|
||||
@Log(title = "部门-编辑", businessType = BusinessTypeConstant.UPDATE)
|
||||
public JsonResponse update(@PathVariable Integer id, @RequestBody DepartmentRequest req)
|
||||
throws NotFoundException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
if (ldapBus.enabledLDAP()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
Department department = departmentService.findOrFail(id);
|
||||
@ -136,7 +134,7 @@ public class DepartmentController {
|
||||
@GetMapping("/{id}/destroy")
|
||||
@Log(title = "部门-批量删除", businessType = BusinessTypeConstant.DELETE)
|
||||
public JsonResponse preDestroy(@PathVariable Integer id) {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
if (ldapBus.enabledLDAP()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
List<Integer> courseIds = courseDepartmentService.getCourseIdsByDepId(id);
|
||||
@ -180,7 +178,7 @@ public class DepartmentController {
|
||||
@DeleteMapping("/{id}")
|
||||
@Log(title = "部门-删除", businessType = BusinessTypeConstant.DELETE)
|
||||
public JsonResponse destroy(@PathVariable Integer id) throws NotFoundException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
if (ldapBus.enabledLDAP()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
Department department = departmentService.findOrFail(id);
|
||||
@ -202,7 +200,7 @@ public class DepartmentController {
|
||||
@Log(title = "部门-更新父级", businessType = BusinessTypeConstant.UPDATE)
|
||||
public JsonResponse updateParent(@RequestBody @Validated DepartmentParentRequest req)
|
||||
throws NotFoundException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
if (ldapBus.enabledLDAP()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
departmentService.changeParent(req.getId(), req.getParentId(), req.getIds());
|
||||
@ -315,57 +313,8 @@ public class DepartmentController {
|
||||
@Log(title = "部门-LDAP同步", businessType = BusinessTypeConstant.INSERT)
|
||||
@SneakyThrows
|
||||
public JsonResponse ldapSync() {
|
||||
LdapConfig ldapConfig = appConfigService.ldapConfig();
|
||||
|
||||
List<String> ouList =
|
||||
LdapUtil.departments(
|
||||
ldapConfig.getUrl(),
|
||||
ldapConfig.getAdminUser(),
|
||||
ldapConfig.getAdminPass(),
|
||||
ldapConfig.getBaseDN());
|
||||
|
||||
if (ouList == null || ouList.isEmpty()) {
|
||||
return JsonResponse.error("部门为空");
|
||||
}
|
||||
|
||||
HashMap<String, Integer> depIdKeyByName = new HashMap<>();
|
||||
Integer sort = 0;
|
||||
|
||||
for (String department : ouList) {
|
||||
String[] tmp = department.toLowerCase().split(",");
|
||||
String prevName = "";
|
||||
for (String s : tmp) {
|
||||
// 控制部门排序
|
||||
sort++;
|
||||
// 当前的子部门名
|
||||
String tmpName = s.replace("ou=", "");
|
||||
// 父部门id
|
||||
Integer parentId = 0;
|
||||
// 部门的链名=>父部门1,父部门2,子部门
|
||||
String fullName = tmpName;
|
||||
if (!prevName.isEmpty()) {
|
||||
fullName = prevName + "," + tmpName;
|
||||
parentId = depIdKeyByName.get(prevName);
|
||||
}
|
||||
|
||||
// 检查是否已经创建
|
||||
Integer depId = depIdKeyByName.get(tmpName);
|
||||
if (depId == null) {
|
||||
// 检查是否已经创建
|
||||
Department tmpDep = departmentService.findByName(tmpName, parentId);
|
||||
if (tmpDep == null) {
|
||||
// 创建部门
|
||||
Integer tmpDepId = departmentService.create(tmpName, parentId, sort);
|
||||
depIdKeyByName.put(fullName, tmpDepId);
|
||||
} else {
|
||||
depIdKeyByName.put(fullName, tmpDep.getId());
|
||||
}
|
||||
}
|
||||
|
||||
prevName = fullName;
|
||||
}
|
||||
}
|
||||
|
||||
ldapBus.departmentSync();
|
||||
ldapBus.userSync();
|
||||
return JsonResponse.success();
|
||||
}
|
||||
}
|
||||
|
@ -34,10 +34,11 @@ import xyz.playedu.common.domain.AdminUser;
|
||||
import xyz.playedu.common.exception.NotFoundException;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.AdminUserService;
|
||||
import xyz.playedu.common.service.MinioService;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.types.JsonResponse;
|
||||
import xyz.playedu.common.types.paginate.PaginationResult;
|
||||
import xyz.playedu.common.types.paginate.ResourcePaginateFilter;
|
||||
import xyz.playedu.common.util.S3Util;
|
||||
import xyz.playedu.resource.domain.Resource;
|
||||
import xyz.playedu.resource.domain.ResourceVideo;
|
||||
import xyz.playedu.resource.service.ResourceService;
|
||||
@ -56,7 +57,7 @@ public class ResourceController {
|
||||
|
||||
@Autowired private ResourceVideoService resourceVideoService;
|
||||
|
||||
@Autowired private MinioService minioService;
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@Autowired private BackendBus backendBus;
|
||||
|
||||
@ -134,7 +135,8 @@ public class ResourceController {
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
minioService.removeByPath(resource.getPath());
|
||||
S3Util s3Util = new S3Util(appConfigService.getS3Config());
|
||||
s3Util.removeByPath(resource.getPath());
|
||||
// 如果是视频资源文件则删除对应的时长关联记录
|
||||
if (BackendConstant.RESOURCE_TYPE_VIDEO.equals(resource.getType())) {
|
||||
resourceVideoService.removeByRid(resource.getId());
|
||||
@ -157,6 +159,8 @@ public class ResourceController {
|
||||
return JsonResponse.success();
|
||||
}
|
||||
|
||||
S3Util s3Util = new S3Util(appConfigService.getS3Config());
|
||||
|
||||
for (Resource resourceItem : resources) {
|
||||
// 权限校验
|
||||
if (!backendBus.isSuperAdmin()) {
|
||||
@ -166,7 +170,7 @@ public class ResourceController {
|
||||
}
|
||||
|
||||
// 删除资源源文件
|
||||
minioService.removeByPath(resourceItem.getPath());
|
||||
s3Util.removeByPath(resourceItem.getPath());
|
||||
// 如果是视频资源的话还需要删除视频的关联资源,如: 封面截图
|
||||
if (BackendConstant.RESOURCE_TYPE_VIDEO.equals(resourceItem.getType())) {
|
||||
resourceVideoService.removeByRid(resourceItem.getId());
|
||||
|
@ -31,9 +31,10 @@ import xyz.playedu.common.constant.BackendConstant;
|
||||
import xyz.playedu.common.constant.BusinessTypeConstant;
|
||||
import xyz.playedu.common.context.BCtx;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.MinioService;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.types.JsonResponse;
|
||||
import xyz.playedu.common.util.HelperUtil;
|
||||
import xyz.playedu.common.util.S3Util;
|
||||
import xyz.playedu.resource.domain.Resource;
|
||||
import xyz.playedu.resource.service.ResourceService;
|
||||
import xyz.playedu.resource.service.UploadService;
|
||||
@ -44,10 +45,11 @@ import java.util.HashMap;
|
||||
@Slf4j
|
||||
@RequestMapping("/backend/v1/upload")
|
||||
public class UploadController {
|
||||
@Autowired private MinioService minioService;
|
||||
|
||||
@Autowired private UploadService uploadService;
|
||||
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@Autowired private ResourceService resourceService;
|
||||
|
||||
@BackendPermission(slug = BPermissionConstant.UPLOAD)
|
||||
@ -74,9 +76,11 @@ public class UploadController {
|
||||
return JsonResponse.error("该格式文件不支持上传");
|
||||
}
|
||||
|
||||
S3Util s3Util = new S3Util(appConfigService.getS3Config());
|
||||
|
||||
String filename = HelperUtil.randomString(32) + "." + extension; // 文件名
|
||||
String path = BackendConstant.RESOURCE_TYPE_2_DIR.get(type) + filename; // 存储路径
|
||||
String uploadId = minioService.uploadId(path);
|
||||
String uploadId = s3Util.uploadId(path);
|
||||
|
||||
HashMap<String, String> data = new HashMap<>();
|
||||
data.put("resource_type", type);
|
||||
@ -94,7 +98,9 @@ public class UploadController {
|
||||
Integer partNumber = MapUtils.getInteger(params, "part_number");
|
||||
String filename = MapUtils.getString(params, "filename");
|
||||
|
||||
String url = minioService.chunkPreSignUrl(filename, partNumber + "", uploadId);
|
||||
S3Util s3Util = new S3Util(appConfigService.getS3Config());
|
||||
|
||||
String url = s3Util.generatePartUploadPreSignUrl(filename, partNumber + "", uploadId);
|
||||
|
||||
HashMap<String, String> data = new HashMap<>();
|
||||
data.put("url", url);
|
||||
@ -115,7 +121,8 @@ public class UploadController {
|
||||
String originalFilename = req.getOriginalFilename().replaceAll("(?i)." + extension, "");
|
||||
|
||||
// 合并资源文件
|
||||
String url = minioService.merge(req.getFilename(), req.getUploadId());
|
||||
S3Util s3Util = new S3Util(appConfigService.getS3Config());
|
||||
String url = s3Util.merge(req.getFilename(), req.getUploadId());
|
||||
|
||||
// 资源素材保存
|
||||
Resource videoResource =
|
||||
@ -162,7 +169,9 @@ public class UploadController {
|
||||
return JsonResponse.error("uploadId必填");
|
||||
}
|
||||
|
||||
String url = minioService.merge(filename, uploadId);
|
||||
S3Util s3Util = new S3Util(appConfigService.getS3Config());
|
||||
|
||||
String url = s3Util.merge(filename, uploadId);
|
||||
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
data.put("url", url);
|
||||
|
@ -515,6 +515,10 @@ public class UserController {
|
||||
UserCourseHourRecordCourseCountMapper::getCourseId,
|
||||
UserCourseHourRecordCourseCountMapper::getTotal));
|
||||
|
||||
// 获取学员每个课程最早的学习课时记录
|
||||
List<UserCourseHourRecord> perCourseEarliestRecords =
|
||||
userCourseHourRecordService.getUserPerCourseEarliestRecord(id);
|
||||
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
data.put("open_courses", openCourses);
|
||||
data.put("departments", departments);
|
||||
@ -524,6 +528,10 @@ public class UserController {
|
||||
userCourseRecords.stream()
|
||||
.collect(Collectors.toMap(UserCourseRecord::getCourseId, e -> e)));
|
||||
data.put("user_course_hour_count", userCourseHourCount);
|
||||
data.put(
|
||||
"per_course_earliest_records",
|
||||
perCourseEarliestRecords.stream()
|
||||
.collect(Collectors.toMap(UserCourseHourRecord::getCourseId, e -> e)));
|
||||
|
||||
return JsonResponse.data(data);
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ public class HourController {
|
||||
String lockKey = String.format("record:%d", FCtx.getId());
|
||||
boolean tryLock = redisDistributedLock.tryLock(lockKey, 5, TimeUnit.SECONDS);
|
||||
if (!tryLock) {
|
||||
return JsonResponse.error("请稍后再试");
|
||||
return JsonResponse.success();
|
||||
}
|
||||
|
||||
try {
|
||||
@ -166,7 +166,7 @@ public class HourController {
|
||||
String lockKey = String.format("ping:%d", FCtx.getId());
|
||||
boolean tryLock = redisDistributedLock.tryLock(lockKey, 5, TimeUnit.SECONDS);
|
||||
if (!tryLock) {
|
||||
return JsonResponse.error("请稍后再试");
|
||||
return JsonResponse.success();
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -32,19 +32,18 @@ import xyz.playedu.api.cache.LoginLockCache;
|
||||
import xyz.playedu.api.event.UserLogoutEvent;
|
||||
import xyz.playedu.api.request.frontend.LoginLdapRequest;
|
||||
import xyz.playedu.api.request.frontend.LoginPasswordRequest;
|
||||
import xyz.playedu.common.constant.ConfigConstant;
|
||||
import xyz.playedu.common.context.FCtx;
|
||||
import xyz.playedu.common.domain.User;
|
||||
import xyz.playedu.common.exception.LimitException;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.*;
|
||||
import xyz.playedu.common.types.JsonResponse;
|
||||
import xyz.playedu.common.types.LdapConfig;
|
||||
import xyz.playedu.common.util.*;
|
||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||
import xyz.playedu.common.util.ldap.LdapUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth/login")
|
||||
@ -100,15 +99,7 @@ public class LoginController {
|
||||
public JsonResponse ldap(@RequestBody @Validated LoginLdapRequest req) {
|
||||
String username = req.getUsername();
|
||||
|
||||
// 系统配置
|
||||
Map<String, String> config = appConfigService.keyValues();
|
||||
String url = config.get(ConfigConstant.LDAP_URL);
|
||||
String adminUser = config.get(ConfigConstant.LDAP_ADMIN_USER);
|
||||
String adminPass = config.get(ConfigConstant.LDAP_ADMIN_PASS);
|
||||
String baseDN = config.get(ConfigConstant.LDAP_BASE_DN);
|
||||
if (url.isEmpty() || adminUser.isEmpty() || adminPass.isEmpty() || baseDN.isEmpty()) {
|
||||
return JsonResponse.error("LDAP服务未配置");
|
||||
}
|
||||
LdapConfig ldapConfig = appConfigService.ldapConfig();
|
||||
|
||||
String mail = null;
|
||||
String uid = null;
|
||||
@ -129,7 +120,13 @@ public class LoginController {
|
||||
try {
|
||||
LdapTransformUser ldapTransformUser =
|
||||
LdapUtil.loginByMailOrUid(
|
||||
url, adminUser, adminPass, baseDN, mail, uid, req.getPassword());
|
||||
ldapConfig.getUrl(),
|
||||
ldapConfig.getAdminUser(),
|
||||
ldapConfig.getAdminPass(),
|
||||
ldapConfig.getBaseDN(),
|
||||
mail,
|
||||
uid,
|
||||
req.getPassword());
|
||||
if (ldapTransformUser == null) {
|
||||
return JsonResponse.error("登录失败.请检查账号和密码");
|
||||
}
|
||||
|
@ -37,6 +37,6 @@ public class UserCourseHourRecordDestroyListener {
|
||||
|
||||
@EventListener
|
||||
public void updateUserCourseRecord(UserCourseHourRecordDestroyEvent e) {
|
||||
userCourseRecordService.decrease(e.getUserId(), e.getCourseId(), 1);
|
||||
userCourseRecordService.updateUserCourseLearnProgress(e.getUserId(), e.getCourseId(), 1);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.api.schedule;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import xyz.playedu.common.bus.LDAPBus;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class LDAPSchedule {
|
||||
|
||||
@Autowired private LDAPBus ldapBus;
|
||||
|
||||
private int times;
|
||||
|
||||
@Scheduled(fixedRate = 3600000)
|
||||
public void sync() {
|
||||
// 系统刚启动不执行
|
||||
if (times == 0) {
|
||||
times++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ldapBus.enabledLDAP()) {
|
||||
log.info("未配置LDAP服务");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ldapBus.departmentSync();
|
||||
} catch (Exception e) {
|
||||
log.error("LDAP-部门同步失败", e);
|
||||
}
|
||||
|
||||
try {
|
||||
ldapBus.userSync();
|
||||
} catch (Exception e) {
|
||||
log.error("LDAP-学员同步失败", e);
|
||||
}
|
||||
|
||||
log.info("LDAP同步成功");
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ spring:
|
||||
max-file-size: 10MB
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: "jdbc:mysql://127.0.0.1:3306/dbname?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false"
|
||||
url: "jdbc:mysql://127.0.0.1:3306/dbname?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
|
||||
username: ""
|
||||
password: ""
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
@ -63,6 +63,7 @@ sa-token:
|
||||
is-share: false
|
||||
jwt-secret-key: "playeduxyz"
|
||||
token-prefix: "Bearer"
|
||||
is-log: false
|
||||
|
||||
playedu:
|
||||
core:
|
||||
|
259
playedu-common/src/main/java/xyz/playedu/common/bus/LDAPBus.java
Normal file
259
playedu-common/src/main/java/xyz/playedu/common/bus/LDAPBus.java
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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.common.bus;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import xyz.playedu.common.domain.Department;
|
||||
import xyz.playedu.common.domain.LdapDepartment;
|
||||
import xyz.playedu.common.domain.LdapUser;
|
||||
import xyz.playedu.common.domain.User;
|
||||
import xyz.playedu.common.exception.NotFoundException;
|
||||
import xyz.playedu.common.service.*;
|
||||
import xyz.playedu.common.types.LdapConfig;
|
||||
import xyz.playedu.common.util.HelperUtil;
|
||||
import xyz.playedu.common.util.StringUtil;
|
||||
import xyz.playedu.common.util.ldap.LdapTransformDepartment;
|
||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||
import xyz.playedu.common.util.ldap.LdapUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class LDAPBus {
|
||||
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@Autowired private DepartmentService departmentService;
|
||||
|
||||
@Autowired private LdapDepartmentService ldapDepartmentService;
|
||||
|
||||
@Autowired private LdapUserService ldapUserService;
|
||||
|
||||
@Autowired private UserService userService;
|
||||
|
||||
public boolean enabledLDAP() {
|
||||
return appConfigService.enabledLdapLogin();
|
||||
}
|
||||
|
||||
public void departmentSync() throws NamingException, NotFoundException {
|
||||
LdapConfig ldapConfig = appConfigService.ldapConfig();
|
||||
|
||||
List<LdapTransformDepartment> ouList =
|
||||
LdapUtil.departments(
|
||||
ldapConfig.getUrl(),
|
||||
ldapConfig.getAdminUser(),
|
||||
ldapConfig.getAdminPass(),
|
||||
ldapConfig.getBaseDN());
|
||||
|
||||
if (ouList == null || ouList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取已经同步的记录
|
||||
Map<String, LdapDepartment> ldapDepartments =
|
||||
ldapDepartmentService.all().stream()
|
||||
.collect(Collectors.toMap(LdapDepartment::getUuid, e -> e));
|
||||
|
||||
// 本地缓存表
|
||||
HashMap<String, Integer> depIdKeyByName = new HashMap<>();
|
||||
// 全局排序计数
|
||||
Integer sort = 0;
|
||||
|
||||
// 新建+编辑的处理
|
||||
for (LdapTransformDepartment ldapTransformDepartment : ouList) {
|
||||
String uuid = ldapTransformDepartment.getUuid();
|
||||
String dn = ldapTransformDepartment.getDn();
|
||||
String[] tmpChains = dn.replace("ou=", "").split(",");
|
||||
String prevName = "";
|
||||
|
||||
// 同步记录
|
||||
LdapDepartment tmpLdapDepartment = ldapDepartments.get(uuid);
|
||||
if (tmpLdapDepartment != null && tmpLdapDepartment.getDn().equals(dn)) {
|
||||
// 当前部门已经同步 && 未发生改变
|
||||
continue;
|
||||
}
|
||||
|
||||
// 执行到这里的有两种情况:
|
||||
// 1.部门未同步
|
||||
// 2.部门已同步,但是发生了变化
|
||||
// 2.1 部门名称修改
|
||||
// 2.2 部门上级修改
|
||||
|
||||
int length = tmpChains.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
sort++;
|
||||
int parentId = 0;
|
||||
|
||||
String tmpName = tmpChains[i];
|
||||
|
||||
// 部门的链名=>父部门1,父部门2,子部门
|
||||
String fullName = tmpName;
|
||||
if (!prevName.isEmpty()) {
|
||||
fullName = prevName + "," + tmpName;
|
||||
// 取父级ID
|
||||
parentId = depIdKeyByName.get(prevName);
|
||||
}
|
||||
|
||||
// 最后一个记录 && 已存在部门-发生了变动
|
||||
if (i + 1 == length && tmpLdapDepartment != null) {
|
||||
Department tmpDepartment =
|
||||
departmentService.findOrFail(tmpLdapDepartment.getDepartmentId());
|
||||
departmentService.update(tmpDepartment, tmpName, parentId, sort);
|
||||
} else {
|
||||
// 检查本地是否有缓存
|
||||
Integer depId = depIdKeyByName.get(fullName);
|
||||
if (depId == null) {
|
||||
Department tmpDep = departmentService.findByName(tmpName, parentId);
|
||||
if (tmpDep != null) {
|
||||
depId = tmpDep.getId();
|
||||
} else {
|
||||
depId = departmentService.create(tmpName, parentId, sort);
|
||||
// 创建同步记录
|
||||
ldapDepartmentService.create(depId, uuid, dn);
|
||||
}
|
||||
// 写入本地缓存
|
||||
depIdKeyByName.put(fullName, depId);
|
||||
}
|
||||
}
|
||||
|
||||
// 父级叠加
|
||||
prevName = fullName;
|
||||
}
|
||||
}
|
||||
|
||||
// 删除的处理
|
||||
List<String> uuidList = ouList.stream().map(LdapTransformDepartment::getUuid).toList();
|
||||
List<LdapDepartment> ldapDepartmentList =
|
||||
ldapDepartmentService.notChunkByUUIDList(uuidList);
|
||||
for (LdapDepartment ldapDepartment : ldapDepartmentList) {
|
||||
// 删除本地部门
|
||||
departmentService.destroy(ldapDepartment.getDepartmentId());
|
||||
// 删除关联记录
|
||||
ldapDepartmentService.destroy(ldapDepartment.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void userSync() throws NamingException {
|
||||
LdapConfig ldapConfig = appConfigService.ldapConfig();
|
||||
|
||||
List<LdapTransformUser> userList =
|
||||
LdapUtil.users(
|
||||
ldapConfig.getUrl(),
|
||||
ldapConfig.getAdminUser(),
|
||||
ldapConfig.getAdminPass(),
|
||||
ldapConfig.getBaseDN());
|
||||
|
||||
if (userList == null || userList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String defaultAvatar = appConfigService.defaultAvatar();
|
||||
|
||||
for (LdapTransformUser ldapTransformUser : userList) {
|
||||
singleUserSync(ldapTransformUser, defaultAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
public User singleUserSync(LdapTransformUser ldapTransformUser, String defaultAvatar) {
|
||||
// LDAP用户的名字
|
||||
String ldapUserName = ldapTransformUser.getCn();
|
||||
|
||||
// 将LDAP用户所属的部门同步到本地
|
||||
Integer depId = departmentService.createWithChainList(ldapTransformUser.getOu());
|
||||
Integer[] depIds = depId == 0 ? null : new Integer[] {depId};
|
||||
|
||||
// LDAP用户在本地的缓存记录
|
||||
LdapUser ldapUser = ldapUserService.findByUUID(ldapTransformUser.getId());
|
||||
User user;
|
||||
|
||||
// 计算将LDAP用户关联到本地users表的email字段值
|
||||
String localUserEmail = ldapTransformUser.getUid();
|
||||
if (StringUtil.isNotEmpty(ldapTransformUser.getEmail())) {
|
||||
localUserEmail = ldapTransformUser.getEmail();
|
||||
}
|
||||
|
||||
if (ldapUser == null) {
|
||||
// 检测localUserEmail是否存在
|
||||
if (userService.find(localUserEmail) != null) {
|
||||
localUserEmail = HelperUtil.randomString(5) + "_" + localUserEmail;
|
||||
}
|
||||
// LDAP用户数据缓存到本地
|
||||
ldapUser = ldapUserService.store(ldapTransformUser);
|
||||
// 创建本地user
|
||||
user =
|
||||
userService.createWithDepIds(
|
||||
localUserEmail,
|
||||
ldapUserName,
|
||||
defaultAvatar,
|
||||
HelperUtil.randomString(10),
|
||||
"",
|
||||
depIds);
|
||||
// 将LDAP缓存数据与本地user关联
|
||||
ldapUserService.updateUserId(ldapUser.getId(), user.getId());
|
||||
} else {
|
||||
user = userService.find(ldapUser.getUserId());
|
||||
if (user == null) {
|
||||
user =
|
||||
userService.createWithDepIds(
|
||||
localUserEmail,
|
||||
ldapUserName,
|
||||
defaultAvatar,
|
||||
HelperUtil.randomString(10),
|
||||
"",
|
||||
depIds);
|
||||
}
|
||||
// 账号修改[账号有可能是email也有可能是uid]
|
||||
if (!localUserEmail.equals(user.getEmail())) {
|
||||
// 检测localUserEmail是否存在
|
||||
if (userService.find(localUserEmail) != null) {
|
||||
localUserEmail = HelperUtil.randomString(5) + "_" + localUserEmail;
|
||||
}
|
||||
userService.updateEmail(user.getId(), localUserEmail);
|
||||
}
|
||||
// ldap-email的变化
|
||||
if (!ldapUser.getEmail().equals(ldapTransformUser.getEmail())) {
|
||||
ldapUserService.updateEmail(ldapUser.getId(), ldapTransformUser.getEmail());
|
||||
}
|
||||
// ldap-uid的变化
|
||||
if (!ldapUser.getUid().equals(ldapTransformUser.getUid())) {
|
||||
ldapUserService.updateUid(ldapUser.getId(), ldapTransformUser.getUid());
|
||||
}
|
||||
// 名字同步修改
|
||||
if (!ldapUserName.equals(ldapUser.getCn())) {
|
||||
userService.updateName(user.getId(), ldapUserName);
|
||||
ldapUserService.updateCN(ldapUser.getId(), ldapUserName);
|
||||
}
|
||||
// 部门修改同步
|
||||
String newOU = String.join(",", ldapTransformUser.getOu());
|
||||
if (!newOU.equals(ldapUser.getOu())) {
|
||||
userService.updateDepId(user.getId(), depIds);
|
||||
ldapUserService.updateOU(ldapUser.getId(), newOU);
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
import io.minio.CreateMultipartUploadResponse;
|
||||
import io.minio.ListPartsResponse;
|
||||
import io.minio.MinioAsyncClient;
|
||||
import io.minio.messages.Part;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author 杭州白书科技有限公司
|
||||
*
|
||||
* @create 2023/3/6 16:12
|
||||
*/
|
||||
public class PlayEduMinioClientConfig extends MinioAsyncClient {
|
||||
public PlayEduMinioClientConfig(MinioAsyncClient client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String uploadId(String bucket, String filename) {
|
||||
CreateMultipartUploadResponse response =
|
||||
super.createMultipartUpload(bucket, null, filename, null, null);
|
||||
return response.result().uploadId();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void merge(String bucketName, String objectName, String uploadId) {
|
||||
ListPartsResponse listPartsResponse =
|
||||
super.listParts(bucketName, null, objectName, 10000, 0, uploadId, null, null);
|
||||
List<Part> partList = listPartsResponse.result().partList();
|
||||
Part[] parts = new Part[10000];
|
||||
int partNumber = 1;
|
||||
for (Part part : partList) {
|
||||
parts[partNumber - 1] = new Part(partNumber, part.etag());
|
||||
partNumber++;
|
||||
}
|
||||
super.completeMultipartUpload(bucketName, null, objectName, uploadId, parts, null, null);
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ public class BPermissionConstant {
|
||||
public static final String USER_LEARN_DESTROY = "user-learn-destroy";
|
||||
|
||||
public static final String COURSE = "course";
|
||||
public static final String COURSE_CUD = "course-cud";
|
||||
public static final String COURSE_USER = "course-user";
|
||||
public static final String COURSE_USER_DESTROY = "course-user-destroy";
|
||||
|
||||
|
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.common.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@TableName(value = "ldap_department")
|
||||
public class LdapDepartment implements Serializable {
|
||||
/** */
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
/** 唯一特征值 */
|
||||
private String uuid;
|
||||
|
||||
/** 部门ID */
|
||||
@JsonProperty("department_id")
|
||||
private Integer departmentId;
|
||||
|
||||
/** dn */
|
||||
private String dn;
|
||||
|
||||
/** */
|
||||
@JsonProperty("created_at")
|
||||
private Date createdAt;
|
||||
|
||||
/** */
|
||||
@JsonProperty("updated_at")
|
||||
private Date updatedAt;
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** */
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/** */
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/** 唯一特征值 */
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/** 唯一特征值 */
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
/** 部门ID */
|
||||
public Integer getDepartmentId() {
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
/** 部门ID */
|
||||
public void setDepartmentId(Integer departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
/** dn */
|
||||
public String getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
/** dn */
|
||||
public void setDn(String dn) {
|
||||
this.dn = dn;
|
||||
}
|
||||
|
||||
/** */
|
||||
public Date getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
/** */
|
||||
public void setCreatedAt(Date createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
/** */
|
||||
public Date getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
/** */
|
||||
public void setUpdatedAt(Date updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
if (that == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
LdapDepartment other = (LdapDepartment) that;
|
||||
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
|
||||
&& (this.getUuid() == null
|
||||
? other.getUuid() == null
|
||||
: this.getUuid().equals(other.getUuid()))
|
||||
&& (this.getDepartmentId() == null
|
||||
? other.getDepartmentId() == null
|
||||
: this.getDepartmentId().equals(other.getDepartmentId()))
|
||||
&& (this.getDn() == null
|
||||
? other.getDn() == null
|
||||
: this.getDn().equals(other.getDn()))
|
||||
&& (this.getCreatedAt() == null
|
||||
? other.getCreatedAt() == null
|
||||
: this.getCreatedAt().equals(other.getCreatedAt()))
|
||||
&& (this.getUpdatedAt() == null
|
||||
? other.getUpdatedAt() == null
|
||||
: this.getUpdatedAt().equals(other.getUpdatedAt()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
|
||||
result = prime * result + ((getUuid() == null) ? 0 : getUuid().hashCode());
|
||||
result = prime * result + ((getDepartmentId() == null) ? 0 : getDepartmentId().hashCode());
|
||||
result = prime * result + ((getDn() == null) ? 0 : getDn().hashCode());
|
||||
result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
|
||||
result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getClass().getSimpleName());
|
||||
sb.append(" [");
|
||||
sb.append("Hash = ").append(hashCode());
|
||||
sb.append(", id=").append(id);
|
||||
sb.append(", uuid=").append(uuid);
|
||||
sb.append(", departmentId=").append(departmentId);
|
||||
sb.append(", dn=").append(dn);
|
||||
sb.append(", createdAt=").append(createdAt);
|
||||
sb.append(", updatedAt=").append(updatedAt);
|
||||
sb.append(", serialVersionUID=").append(serialVersionUID);
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.common.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import xyz.playedu.common.domain.LdapDepartment;
|
||||
|
||||
/**
|
||||
* @author tengyongzhi
|
||||
* @description 针对表【ldap_department】的数据库操作Mapper
|
||||
* @createDate 2023-11-13 15:46:06 @Entity xyz.playedu.common.domain.LdapDepartment
|
||||
*/
|
||||
public interface LdapDepartmentMapper extends BaseMapper<LdapDepartment> {}
|
@ -29,7 +29,7 @@ import java.util.List;
|
||||
*/
|
||||
public interface AdminPermissionService extends IService<AdminPermission> {
|
||||
|
||||
HashMap<String, Boolean> allSlugs();
|
||||
HashMap<String, Integer> allSlugs();
|
||||
|
||||
List<AdminPermission> listOrderBySortAsc();
|
||||
|
||||
|
@ -19,7 +19,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import xyz.playedu.common.domain.AppConfig;
|
||||
import xyz.playedu.common.types.LdapConfig;
|
||||
import xyz.playedu.common.types.config.MinioConfig;
|
||||
import xyz.playedu.common.types.config.S3Config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -35,7 +35,7 @@ public interface AppConfigService extends IService<AppConfig> {
|
||||
|
||||
Map<String, String> keyValues();
|
||||
|
||||
MinioConfig getMinioConfig();
|
||||
S3Config getS3Config();
|
||||
|
||||
boolean enabledLdapLogin();
|
||||
|
||||
|
@ -15,26 +15,19 @@
|
||||
*/
|
||||
package xyz.playedu.common.service;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @Author 杭州白书科技有限公司
|
||||
*
|
||||
* @create 2023/3/7 13:29
|
||||
*/
|
||||
public interface MinioService {
|
||||
import xyz.playedu.common.domain.LdapDepartment;
|
||||
|
||||
String url(String path);
|
||||
import java.util.List;
|
||||
|
||||
String saveFile(MultipartFile file, String savePath, String contentType);
|
||||
public interface LdapDepartmentService extends IService<LdapDepartment> {
|
||||
|
||||
String saveBytes(byte[] file, String savePath, String contentType);
|
||||
List<LdapDepartment> all();
|
||||
|
||||
String uploadId(String path);
|
||||
List<LdapDepartment> notChunkByUUIDList(List<String> uuidList);
|
||||
|
||||
String chunkPreSignUrl(String filename, String partNumber, String uploadId);
|
||||
void destroy(Integer id);
|
||||
|
||||
String merge(String filename, String uploadId);
|
||||
|
||||
void removeByPath(String path);
|
||||
void create(Integer depId, String uuid, String dn);
|
||||
}
|
@ -37,11 +37,11 @@ public class AdminPermissionServiceImpl extends ServiceImpl<AdminPermissionMappe
|
||||
implements AdminPermissionService {
|
||||
|
||||
@Override
|
||||
public HashMap<String, Boolean> allSlugs() {
|
||||
public HashMap<String, Integer> allSlugs() {
|
||||
List<AdminPermission> data = list();
|
||||
HashMap<String, Boolean> map = new HashMap<>();
|
||||
HashMap<String, Integer> map = new HashMap<>();
|
||||
for (AdminPermission permission : data) {
|
||||
map.put(permission.getSlug(), true);
|
||||
map.put(permission.getSlug(), permission.getId());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ package xyz.playedu.common.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import xyz.playedu.common.constant.ConfigConstant;
|
||||
@ -25,7 +27,8 @@ import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.mapper.AppConfigMapper;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.types.LdapConfig;
|
||||
import xyz.playedu.common.types.config.MinioConfig;
|
||||
import xyz.playedu.common.types.config.S3Config;
|
||||
import xyz.playedu.common.util.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -33,12 +36,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author tengteng
|
||||
* @description 针对表【app_config】的数据库操作Service实现
|
||||
* @createDate 2023-03-09 11:13:33
|
||||
*/
|
||||
@Service
|
||||
@Log4j2
|
||||
public class AppConfigServiceImpl extends ServiceImpl<AppConfigMapper, AppConfig>
|
||||
implements AppConfigService {
|
||||
|
||||
@ -95,15 +94,30 @@ public class AppConfigServiceImpl extends ServiceImpl<AppConfigMapper, AppConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinioConfig getMinioConfig() {
|
||||
MinioConfig minioConfig = new MinioConfig();
|
||||
public S3Config getS3Config() {
|
||||
S3Config s3Config = new S3Config();
|
||||
Map<String, String> config = keyValues();
|
||||
minioConfig.setAccessKey(config.get(ConfigConstant.MINIO_ACCESS_KEY));
|
||||
minioConfig.setSecretKey(config.get(ConfigConstant.MINIO_SECRET_KEY));
|
||||
minioConfig.setBucket(config.get(ConfigConstant.MINIO_BUCKET));
|
||||
minioConfig.setEndpoint(config.get(ConfigConstant.MINIO_ENDPOINT));
|
||||
minioConfig.setDomain(config.get(ConfigConstant.MINIO_DOMAIN));
|
||||
return minioConfig;
|
||||
s3Config.setAccessKey(config.get(ConfigConstant.MINIO_ACCESS_KEY));
|
||||
s3Config.setSecretKey(config.get(ConfigConstant.MINIO_SECRET_KEY));
|
||||
s3Config.setBucket(config.get(ConfigConstant.MINIO_BUCKET));
|
||||
s3Config.setEndpoint(config.get(ConfigConstant.MINIO_ENDPOINT));
|
||||
s3Config.setRegion(null);
|
||||
s3Config.setService("minio");
|
||||
|
||||
String domain = config.get(ConfigConstant.MINIO_DOMAIN);
|
||||
if (s3Config.getService().equals("minio") && StringUtil.isNotEmpty(domain)) {
|
||||
// 移除 / 后缀
|
||||
if (StringUtil.endsWith(domain, "/")) {
|
||||
domain = domain.substring(0, domain.length() - 1);
|
||||
}
|
||||
// 判断是否携带了bucket
|
||||
if (!StringUtil.endsWith(domain, s3Config.getBucket())) {
|
||||
domain += "/" + s3Config.getBucket();
|
||||
}
|
||||
s3Config.setDomain(domain);
|
||||
}
|
||||
|
||||
return s3Config;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.common.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import xyz.playedu.common.domain.LdapDepartment;
|
||||
import xyz.playedu.common.mapper.LdapDepartmentMapper;
|
||||
import xyz.playedu.common.service.LdapDepartmentService;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class LdapDepartmentServiceImpl extends ServiceImpl<LdapDepartmentMapper, LdapDepartment>
|
||||
implements LdapDepartmentService {
|
||||
|
||||
@Override
|
||||
public List<LdapDepartment> all() {
|
||||
return list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LdapDepartment> notChunkByUUIDList(List<String> uuidList) {
|
||||
return list(query().getWrapper().notIn("uuid", uuidList));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(Integer id) {
|
||||
remove(query().getWrapper().eq("id", id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(Integer depId, String uuid, String dn) {
|
||||
LdapDepartment ldapDepartment = new LdapDepartment();
|
||||
ldapDepartment.setDepartmentId(depId);
|
||||
ldapDepartment.setDn(dn);
|
||||
ldapDepartment.setUuid(uuid);
|
||||
ldapDepartment.setCreatedAt(new Date());
|
||||
ldapDepartment.setUpdatedAt(new Date());
|
||||
|
||||
save(ldapDepartment);
|
||||
}
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
* 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.common.service.impl;
|
||||
|
||||
import io.minio.*;
|
||||
import io.minio.http.Method;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import xyz.playedu.common.config.PlayEduMinioClientConfig;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.service.MinioService;
|
||||
import xyz.playedu.common.types.config.MinioConfig;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class MinioServiceImpl implements MinioService {
|
||||
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@SneakyThrows
|
||||
private MinioConfig getMinioConfig() {
|
||||
MinioConfig c = appConfigService.getMinioConfig();
|
||||
if (c.getAccessKey().isBlank()
|
||||
|| c.getSecretKey().isBlank()
|
||||
|| c.getBucket().isBlank()
|
||||
|| c.getDomain().isBlank()
|
||||
|| c.getEndpoint().isBlank()) {
|
||||
throw new ServiceException("MinIO服务未配置");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private String bucket() {
|
||||
return getMinioConfig().getBucket();
|
||||
}
|
||||
|
||||
public MinioClient getMinioClient() {
|
||||
MinioConfig c = getMinioConfig();
|
||||
|
||||
return MinioClient.builder()
|
||||
.endpoint(c.getEndpoint())
|
||||
.credentials(c.getAccessKey(), c.getSecretKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
public PlayEduMinioClientConfig getPlayEduMinioClient() {
|
||||
MinioConfig c = getMinioConfig();
|
||||
|
||||
MinioAsyncClient client =
|
||||
PlayEduMinioClientConfig.builder()
|
||||
.endpoint(c.getEndpoint())
|
||||
.credentials(c.getAccessKey(), c.getSecretKey())
|
||||
.build();
|
||||
|
||||
return new PlayEduMinioClientConfig(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String url(String path) {
|
||||
MinioConfig c = getMinioConfig();
|
||||
|
||||
return c.getDomain()
|
||||
+ (c.getDomain().endsWith("/") ? "" : "/")
|
||||
+ c.getBucket()
|
||||
+ "/"
|
||||
+ path;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String saveFile(MultipartFile file, String savePath, String contentType) {
|
||||
PutObjectArgs objectArgs =
|
||||
PutObjectArgs.builder().bucket(bucket()).object(savePath).stream(
|
||||
file.getInputStream(), file.getSize(), -1)
|
||||
.contentType(contentType)
|
||||
.build();
|
||||
getMinioClient().putObject(objectArgs);
|
||||
|
||||
return url(savePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadId(String path) {
|
||||
return getPlayEduMinioClient().uploadId(bucket(), path);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String chunkPreSignUrl(String filename, String partNumber, String uploadId) {
|
||||
Map<String, String> extraQueryParams = new HashMap<>();
|
||||
extraQueryParams.put("partNumber", partNumber);
|
||||
extraQueryParams.put("uploadId", uploadId);
|
||||
|
||||
return getMinioClient()
|
||||
.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.bucket(bucket())
|
||||
.object(filename)
|
||||
.method(Method.PUT)
|
||||
.expiry(60 * 60 * 24)
|
||||
.extraQueryParams(extraQueryParams)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String merge(String filename, String uploadId) {
|
||||
getPlayEduMinioClient().merge(bucket(), filename, uploadId);
|
||||
return url(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeByPath(String path) {
|
||||
getMinioClient()
|
||||
.removeObject(RemoveObjectArgs.builder().bucket(bucket()).object(path).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String saveBytes(byte[] file, String savePath, String contentType) {
|
||||
InputStream inputStream = new ByteArrayInputStream(file);
|
||||
|
||||
PutObjectArgs objectArgs =
|
||||
PutObjectArgs.builder().bucket(bucket()).object(savePath).stream(
|
||||
inputStream, file.length, -1)
|
||||
.contentType(contentType)
|
||||
.build();
|
||||
|
||||
getMinioClient().putObject(objectArgs);
|
||||
|
||||
return url(savePath);
|
||||
}
|
||||
}
|
@ -18,10 +18,12 @@ package xyz.playedu.common.types.config;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MinioConfig {
|
||||
public class S3Config {
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
private String bucket;
|
||||
private String endpoint;
|
||||
private String domain;
|
||||
private String region;
|
||||
private String service;
|
||||
}
|
@ -42,4 +42,6 @@ public class CoursePaginateFiler {
|
||||
private Integer pageStart;
|
||||
|
||||
private Integer pageSize;
|
||||
|
||||
private Integer adminId;
|
||||
}
|
||||
|
178
playedu-common/src/main/java/xyz/playedu/common/util/S3Util.java
Normal file
178
playedu-common/src/main/java/xyz/playedu/common/util/S3Util.java
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import com.amazonaws.HttpMethod;
|
||||
import com.amazonaws.auth.AWSCredentials;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import com.amazonaws.services.s3.model.*;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.types.config.S3Config;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class S3Util {
|
||||
|
||||
private S3Config defaultConfig;
|
||||
|
||||
public S3Config getS3Config() {
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
public S3Util(S3Config s3Config) {
|
||||
defaultConfig = s3Config;
|
||||
}
|
||||
|
||||
public S3Util setConfig(S3Config config) {
|
||||
defaultConfig = config;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean configIsEmpty() {
|
||||
return defaultConfig == null
|
||||
|| StringUtil.isEmpty(defaultConfig.getDomain())
|
||||
|| StringUtil.isEmpty(defaultConfig.getEndpoint())
|
||||
|| StringUtil.isEmpty(defaultConfig.getAccessKey())
|
||||
|| StringUtil.isEmpty(defaultConfig.getSecretKey());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private AmazonS3 getClient() {
|
||||
if (defaultConfig == null) {
|
||||
throw new ServiceException("存储服务未配置");
|
||||
}
|
||||
AWSCredentials credentials =
|
||||
new BasicAWSCredentials(defaultConfig.getAccessKey(), defaultConfig.getSecretKey());
|
||||
|
||||
AwsClientBuilder.EndpointConfiguration endpointConfiguration =
|
||||
new AwsClientBuilder.EndpointConfiguration(
|
||||
defaultConfig.getEndpoint(), defaultConfig.getRegion());
|
||||
|
||||
AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard();
|
||||
// 开启路径访问
|
||||
builder.setPathStyleAccessEnabled(true);
|
||||
|
||||
return builder.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(endpointConfiguration)
|
||||
.build();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String saveFile(MultipartFile file, String savePath, String contentType) {
|
||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||
objectMetadata.setContentType(contentType);
|
||||
objectMetadata.setContentLength(file.getInputStream().available());
|
||||
getClient()
|
||||
.putObject(
|
||||
defaultConfig.getBucket(), savePath, file.getInputStream(), objectMetadata);
|
||||
return generateEndpointPreSignUrl(savePath);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String saveBytes(byte[] file, String savePath, String contentType) {
|
||||
InputStream inputStream = new ByteArrayInputStream(file);
|
||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||
objectMetadata.setContentType(contentType);
|
||||
objectMetadata.setContentLength(inputStream.available());
|
||||
getClient().putObject(defaultConfig.getBucket(), savePath, inputStream, objectMetadata);
|
||||
return generateEndpointPreSignUrl(savePath);
|
||||
}
|
||||
|
||||
public String uploadId(String path) {
|
||||
InitiateMultipartUploadRequest request =
|
||||
new InitiateMultipartUploadRequest(defaultConfig.getBucket(), path);
|
||||
InitiateMultipartUploadResult result = getClient().initiateMultipartUpload(request);
|
||||
return result.getUploadId();
|
||||
}
|
||||
|
||||
public String generatePartUploadPreSignUrl(
|
||||
String filename, String partNumber, String uploadId) {
|
||||
GeneratePresignedUrlRequest request =
|
||||
new GeneratePresignedUrlRequest(
|
||||
defaultConfig.getBucket(), filename, HttpMethod.PUT);
|
||||
request.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)); // 一个小时有效期
|
||||
request.addRequestParameter("partNumber", partNumber); // 分块索引
|
||||
request.addRequestParameter("uploadId", uploadId); // uploadId
|
||||
return getClient().generatePresignedUrl(request).toString();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String merge(String filename, String uploadId) {
|
||||
AmazonS3 client = getClient();
|
||||
|
||||
ListPartsRequest listPartsRequest =
|
||||
new ListPartsRequest(defaultConfig.getBucket(), filename, uploadId);
|
||||
PartListing parts = client.listParts(listPartsRequest);
|
||||
if (parts.getParts().isEmpty()) {
|
||||
throw new ServiceException("没有已上传的分片文件");
|
||||
}
|
||||
|
||||
List<PartETag> eTags = new ArrayList<>();
|
||||
parts.getParts()
|
||||
.forEach(
|
||||
item -> {
|
||||
eTags.add(new PartETag(item.getPartNumber(), item.getETag()));
|
||||
});
|
||||
|
||||
CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest();
|
||||
request.setBucketName(defaultConfig.getBucket());
|
||||
request.setKey(filename);
|
||||
request.setUploadId(uploadId);
|
||||
request.setPartETags(eTags);
|
||||
|
||||
client.completeMultipartUpload(request);
|
||||
|
||||
return generateEndpointPreSignUrl(filename);
|
||||
}
|
||||
|
||||
public void removeByPath(String path) {
|
||||
DeleteObjectRequest request = new DeleteObjectRequest(defaultConfig.getBucket(), path);
|
||||
getClient().deleteObject(request);
|
||||
}
|
||||
|
||||
public boolean exists(String path) {
|
||||
return getClient().doesObjectExist(defaultConfig.getBucket(), path);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String getContent(String path) {
|
||||
S3Object s3Object = getClient().getObject(defaultConfig.getBucket(), path);
|
||||
return new String(s3Object.getObjectContent().readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public String generateEndpointPreSignUrl(String path) {
|
||||
if (defaultConfig.getService().equals("minio")) {
|
||||
return defaultConfig.getDomain() + "/" + path;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.common.util.ldap;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LdapTransformDepartment {
|
||||
private String uuid;
|
||||
private String dn;
|
||||
}
|
@ -48,20 +48,17 @@ public class LdapUtil {
|
||||
"entryUUID",
|
||||
|
||||
// Window AD 域的属性
|
||||
"memberOf",
|
||||
"name",
|
||||
"userPrincipalName",
|
||||
"departmentNumber",
|
||||
"telephoneNumber",
|
||||
"mobile",
|
||||
"department",
|
||||
"distinguishedName",
|
||||
"sAMAccountName",
|
||||
"displayName",
|
||||
"uSNCreated", // AD域的唯一属性
|
||||
|
||||
// 公用属性
|
||||
"mail",
|
||||
};
|
||||
private static final String[] OU_RETURN_ATTRS = new String[] {"ou"};
|
||||
private static final String[] OU_RETURN_ATTRS = new String[] {"ou", "usncreated"};
|
||||
|
||||
public static LdapContext initContext(String url, String adminUser, String adminPass)
|
||||
throws NamingException {
|
||||
@ -76,8 +73,10 @@ public class LdapUtil {
|
||||
return new InitialLdapContext(context, null);
|
||||
}
|
||||
|
||||
public static List<HashMap<String, String>> users(LdapContext ldapContext, String baseDN)
|
||||
throws NamingException {
|
||||
public static List<LdapTransformUser> users(
|
||||
String url, String adminUser, String adminPass, String baseDN) throws NamingException {
|
||||
LdapContext ldapContext = initContext(url, adminUser, adminPass);
|
||||
|
||||
SearchControls controls = new SearchControls();
|
||||
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
controls.setReturningAttributes(USER_RETURN_ATTRS);
|
||||
@ -97,20 +96,20 @@ public class LdapUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<HashMap<String, String>> users = new ArrayList<>();
|
||||
List<LdapTransformUser> users = new ArrayList<>();
|
||||
while (result.hasMoreElements()) {
|
||||
SearchResult item = result.nextElement();
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
Attributes attributes = item.getAttributes();
|
||||
log.info("name={},attributes={}", item.getName(), attributes);
|
||||
LdapTransformUser ldapTransformUser = parseTransformUser(item, baseDN);
|
||||
users.add(ldapTransformUser);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
public static List<String> departments(
|
||||
public static List<LdapTransformDepartment> departments(
|
||||
String url, String adminUser, String adminPass, String baseDN) throws NamingException {
|
||||
LdapContext ldapContext = initContext(url, adminUser, adminPass);
|
||||
|
||||
@ -137,12 +136,24 @@ public class LdapUtil {
|
||||
// baseDN中的ou作用域
|
||||
String ouScopesStr = baseDNOuScope(baseDN);
|
||||
|
||||
List<String> units = new ArrayList<>();
|
||||
List<LdapTransformDepartment> units = new ArrayList<>();
|
||||
while (result.hasMoreElements()) {
|
||||
SearchResult item = result.nextElement();
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
Attributes attributes = item.getAttributes();
|
||||
if (attributes == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 唯一特征值
|
||||
String uSNCreated = (String) attributes.get("uSNCreated").get();
|
||||
if (StringUtil.isEmpty(uSNCreated)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 组织DN
|
||||
String name = item.getName();
|
||||
if (name.isEmpty()) {
|
||||
name = ouScopesStr;
|
||||
@ -150,20 +161,19 @@ public class LdapUtil {
|
||||
name = name + (ouScopesStr.isEmpty() ? "" : "," + ouScopesStr);
|
||||
}
|
||||
|
||||
units.add(name);
|
||||
// 将DN反转
|
||||
List<String> tmp = new ArrayList<>(List.of(name.split(",")));
|
||||
Collections.reverse(tmp);
|
||||
name = String.join(",", tmp);
|
||||
|
||||
LdapTransformDepartment ldapDepartment = new LdapTransformDepartment();
|
||||
ldapDepartment.setUuid(uSNCreated);
|
||||
ldapDepartment.setDn(name.toLowerCase());
|
||||
|
||||
units.add(ldapDepartment);
|
||||
}
|
||||
|
||||
List<String> reverseUnits = new ArrayList<>();
|
||||
if (!units.isEmpty()) {
|
||||
units.forEach(
|
||||
item -> {
|
||||
List<String> tmp = new ArrayList<>(List.of(item.split(",")));
|
||||
Collections.reverse(tmp);
|
||||
reverseUnits.add(String.join(",", tmp));
|
||||
});
|
||||
}
|
||||
|
||||
return reverseUnits;
|
||||
return units;
|
||||
}
|
||||
|
||||
public static LdapTransformUser loginByMailOrUid(
|
||||
@ -200,30 +210,62 @@ public class LdapUtil {
|
||||
try {
|
||||
result = ldapContext.search(baseDN, filter, controls);
|
||||
} catch (NamingException e) {
|
||||
log.error("通过mail或uid登录失败", e);
|
||||
log.error("LDAP-通过mail或uid登录失败", e);
|
||||
} finally {
|
||||
closeContext(ldapContext);
|
||||
}
|
||||
|
||||
if (result == null || !result.hasMoreElements()) {
|
||||
log.info("用户不存在");
|
||||
log.info("LDAP-用户不存在");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 根据mail或uid查询出来的用户
|
||||
SearchResult item = result.nextElement();
|
||||
Attributes attributes = item.getAttributes();
|
||||
LdapTransformUser ldapUser = parseTransformUser(result.nextElement(), baseDN);
|
||||
if (ldapUser == null) {
|
||||
log.info("LDAP-用户不存在");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用用户dn+提交的密码去登录ldap系统
|
||||
// 登录成功则意味着密码正确
|
||||
// 登录失败则意味着密码错误
|
||||
try {
|
||||
ldapContext = initContext(url, ldapUser.getDn() + "," + baseDN, password);
|
||||
return ldapUser;
|
||||
} catch (Exception e) {
|
||||
// 无法登录->密码错误
|
||||
log.error("LDAP-登录失败", e);
|
||||
return null;
|
||||
} finally {
|
||||
ldapContext.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static LdapTransformUser parseTransformUser(SearchResult item, String baseDN)
|
||||
throws NamingException {
|
||||
Attributes attributes = item.getAttributes();
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LdapTransformUser ldapUser = new LdapTransformUser();
|
||||
ldapUser.setDn(item.getName());
|
||||
|
||||
// name解析
|
||||
String displayName = (String) attributes.get("displayName").get();
|
||||
if (StringUtil.isEmpty(displayName)) {
|
||||
displayName = (String) attributes.get("cn").get();
|
||||
}
|
||||
ldapUser.setCn(displayName);
|
||||
|
||||
// 邮箱解析
|
||||
String email =
|
||||
attributes.get("mail") == null ? null : (String) attributes.get("mail").get();
|
||||
if (email == null) {
|
||||
email = attributes.get("email") == null ? null : (String) attributes.get("email").get();
|
||||
}
|
||||
|
||||
LdapTransformUser ldapUser = new LdapTransformUser();
|
||||
ldapUser.setDn(item.getName());
|
||||
ldapUser.setEmail(email);
|
||||
ldapUser.setCn((String) attributes.get("cn").get());
|
||||
|
||||
if (attributes.get("uSNCreated") != null) {
|
||||
// Window AD域
|
||||
@ -251,22 +293,6 @@ public class LdapUtil {
|
||||
Collections.reverse(ou);
|
||||
ldapUser.setOu(ou);
|
||||
|
||||
log.info("ldapUser={}", ldapUser);
|
||||
|
||||
// 使用用户dn+提交的密码去登录ldap系统
|
||||
// 登录成功则意味着密码正确
|
||||
// 登录失败则意味着密码错误
|
||||
try {
|
||||
ldapContext = initContext(url, ldapUser.getDn() + "," + baseDN, password);
|
||||
log.info("LDAP登录成功");
|
||||
} catch (Exception e) {
|
||||
// 无法登录->密码错误
|
||||
log.info("LDAP用户提交的密码错误");
|
||||
return null;
|
||||
} finally {
|
||||
ldapContext.close();
|
||||
}
|
||||
|
||||
return ldapUser;
|
||||
}
|
||||
|
||||
@ -281,14 +307,14 @@ public class LdapUtil {
|
||||
return String.join(",", ouScopes);
|
||||
}
|
||||
|
||||
public static void closeContext(LdapContext ldapCtx) {
|
||||
private static void closeContext(LdapContext ldapCtx) {
|
||||
if (ldapCtx == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ldapCtx.close();
|
||||
} catch (NamingException e) {
|
||||
log.error("Failed to close ldap context", e);
|
||||
log.error("LDAP-资源释放失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="xyz.playedu.common.mapper.LdapDepartmentMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="xyz.playedu.common.domain.LdapDepartment">
|
||||
<id property="id" column="id" jdbcType="INTEGER"/>
|
||||
<result property="uuid" column="uuid" jdbcType="VARCHAR"/>
|
||||
<result property="departmentId" column="department_id" jdbcType="INTEGER"/>
|
||||
<result property="dn" column="dn" jdbcType="VARCHAR"/>
|
||||
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
|
||||
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id,uuid,department_id,
|
||||
dn,created_at,updated_at
|
||||
</sql>
|
||||
</mapper>
|
@ -69,6 +69,9 @@ public class Course implements Serializable {
|
||||
@JsonProperty("created_at")
|
||||
private Date createdAt;
|
||||
|
||||
@JsonProperty("admin_id")
|
||||
private Integer adminId;
|
||||
|
||||
@JsonIgnore private Date updatedAt;
|
||||
|
||||
@JsonIgnore private Date deletedAt;
|
||||
|
@ -48,4 +48,8 @@ public interface UserCourseHourRecordMapper extends BaseMapper<UserCourseHourRec
|
||||
List<UserCourseHourRecord> paginate(UserCourseHourRecordPaginateFilter filter);
|
||||
|
||||
Long paginateCount(UserCourseHourRecordPaginateFilter filter);
|
||||
|
||||
List<UserCourseHourRecord> getUserPerCourseEarliestRecord(Integer userId);
|
||||
|
||||
List<UserCourseHourRecord> getCoursePerUserEarliestRecord(Integer courseId);
|
||||
}
|
||||
|
@ -42,7 +42,8 @@ public interface CourseService extends IService<Course> {
|
||||
Integer isRequired,
|
||||
Integer isShow,
|
||||
Integer[] categoryIds,
|
||||
Integer[] depIds);
|
||||
Integer[] depIds,
|
||||
Integer adminId);
|
||||
|
||||
void updateWithCategoryIdsAndDepIds(
|
||||
Course course,
|
||||
|
@ -66,4 +66,8 @@ public interface UserCourseHourRecordService extends IService<UserCourseHourReco
|
||||
|
||||
PaginationResult<UserCourseHourRecord> paginate(
|
||||
int page, int size, UserCourseHourRecordPaginateFilter filter);
|
||||
|
||||
List<UserCourseHourRecord> getUserPerCourseEarliestRecord(Integer userId);
|
||||
|
||||
List<UserCourseHourRecord> getCoursePerUserEarliestRecord(Integer courseId);
|
||||
}
|
||||
|
@ -51,5 +51,5 @@ public interface UserCourseRecordService extends IService<UserCourseRecord> {
|
||||
|
||||
List<UserCourseRecord> chunks(List<Integer> ids, List<String> fields);
|
||||
|
||||
void decrease(Integer userId, Integer courseId, int count);
|
||||
void updateUserCourseLearnProgress(Integer userId, Integer courseId, int count);
|
||||
}
|
||||
|
@ -68,7 +68,8 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> impleme
|
||||
Integer isRequired,
|
||||
Integer isShow,
|
||||
Integer[] categoryIds,
|
||||
Integer[] depIds) {
|
||||
Integer[] depIds,
|
||||
Integer adminId) {
|
||||
// 创建课程
|
||||
Course course = new Course();
|
||||
course.setTitle(title);
|
||||
@ -79,6 +80,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> impleme
|
||||
course.setPublishedAt(new Date());
|
||||
course.setCreatedAt(new Date());
|
||||
course.setUpdatedAt(new Date());
|
||||
course.setAdminId(adminId);
|
||||
save(course);
|
||||
// 关联分类
|
||||
relateCategories(course, categoryIds);
|
||||
|
@ -123,7 +123,7 @@ public class UserCourseHourRecordServiceImpl
|
||||
@Override
|
||||
public List<UserCourseHourRecordCourseCountMapper> getUserCourseHourCount(
|
||||
Integer userId, List<Integer> courseIds, Integer isFinished) {
|
||||
if (courseIds == null || courseIds.size() == 0) {
|
||||
if (courseIds == null || courseIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return getBaseMapper().getUserCourseHourCount(userId, courseIds, isFinished);
|
||||
@ -132,7 +132,7 @@ public class UserCourseHourRecordServiceImpl
|
||||
@Override
|
||||
public List<UserCourseHourRecordUserCountMapper> getUserCourseHourUserCount(
|
||||
Integer courseId, List<Integer> userIds, Integer isFinished) {
|
||||
if (userIds == null || userIds.size() == 0) {
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return getBaseMapper().getUserCourseHourUserCount(courseId, userIds, isFinished);
|
||||
@ -174,9 +174,19 @@ public class UserCourseHourRecordServiceImpl
|
||||
@Override
|
||||
public List<UserCourseHourRecordUserFirstCreatedAtMapper> getUserCourseHourUserFirstCreatedAt(
|
||||
Integer courseId, List<Integer> userIds) {
|
||||
if (userIds == null || userIds.size() == 0) {
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return getBaseMapper().getUserCourseHourUserFirstCreatedAt(courseId, userIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserCourseHourRecord> getUserPerCourseEarliestRecord(Integer userId) {
|
||||
return getBaseMapper().getUserPerCourseEarliestRecord(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserCourseHourRecord> getCoursePerUserEarliestRecord(Integer courseId) {
|
||||
return getBaseMapper().getCoursePerUserEarliestRecord(courseId);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package xyz.playedu.course.service.impl;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import xyz.playedu.common.types.paginate.PaginationResult;
|
||||
import xyz.playedu.common.types.paginate.UserCourseRecordPaginateFilter;
|
||||
@ -144,7 +145,8 @@ public class UserCourseRecordServiceImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decrease(Integer userId, Integer courseId, int count) {
|
||||
@Transactional
|
||||
public void updateUserCourseLearnProgress(Integer userId, Integer courseId, int count) {
|
||||
UserCourseRecord record = find(userId, courseId);
|
||||
if (record == null) {
|
||||
return;
|
||||
@ -153,12 +155,22 @@ public class UserCourseRecordServiceImpl
|
||||
int finishedCount = record.getFinishedCount() - count;
|
||||
|
||||
UserCourseRecord newRecord = new UserCourseRecord();
|
||||
newRecord.setId(record.getId());
|
||||
newRecord.setUserId(record.getUserId());
|
||||
newRecord.setCourseId(record.getCourseId());
|
||||
newRecord.setHourCount(record.getHourCount());
|
||||
newRecord.setFinishedCount(finishedCount);
|
||||
newRecord.setFinishedAt(null);
|
||||
newRecord.setProgress(finishedCount * 10000 / record.getHourCount());
|
||||
newRecord.setIsFinished(0);
|
||||
newRecord.setCreatedAt(record.getCreatedAt());
|
||||
newRecord.setUpdatedAt(new Date());
|
||||
|
||||
updateById(newRecord);
|
||||
// 删除老记录
|
||||
remove(query().getWrapper().eq("id", record.getId()));
|
||||
|
||||
if (0 == finishedCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
save(newRecord);
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,9 @@
|
||||
<if test="title != null and title != ''">
|
||||
AND `courses`.`title` LIKE concat('%',#{title},'%')
|
||||
</if>
|
||||
<if test="adminId != null">
|
||||
AND `courses`.`admin_id` = #{adminId}
|
||||
</if>
|
||||
<if test="isRequired != null">
|
||||
AND `courses`.`is_required` = #{isRequired}
|
||||
</if>
|
||||
@ -164,6 +167,9 @@
|
||||
<if test="title != null and title != ''">
|
||||
AND `courses`.`title` LIKE concat('%',#{title},'%')
|
||||
</if>
|
||||
<if test="adminId != null">
|
||||
AND `courses`.`admin_id` = #{adminId}
|
||||
</if>
|
||||
<if test="isRequired != null">
|
||||
AND `courses`.`is_required` = #{isRequired}
|
||||
</if>
|
||||
|
@ -128,6 +128,7 @@
|
||||
</if>
|
||||
GROUP BY `user_id`;
|
||||
</select>
|
||||
|
||||
<select id="getUserCourseHourUserFirstCreatedAt"
|
||||
resultType="xyz.playedu.common.types.mapper.UserCourseHourRecordUserFirstCreatedAtMapper">
|
||||
SELECT `t1`.`created_at`, `t1`.`user_id`
|
||||
@ -146,4 +147,46 @@
|
||||
AND `t1`.`user_id` IN (<foreach collection="userIds" item="userId" separator=",">#{userId}</foreach>)
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="getUserPerCourseEarliestRecord" resultType="xyz.playedu.course.domain.UserCourseHourRecord">
|
||||
SELECT
|
||||
`a`.*
|
||||
FROM
|
||||
`user_course_hour_records` AS `a`
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
min(`created_at`) AS `min_created_at`,
|
||||
`course_id`,
|
||||
`user_id`
|
||||
FROM
|
||||
`user_course_hour_records`
|
||||
WHERE
|
||||
`user_id` = #{userId}
|
||||
GROUP BY
|
||||
`user_id`,
|
||||
`course_id`) AS `b` ON `b`.`min_created_at` = `a`.`created_at`
|
||||
AND `b`.`course_id` = `a`.`course_id`
|
||||
AND `b`.`user_id` = `a`.`user_id`;
|
||||
</select>
|
||||
|
||||
<select id="getCoursePerUserEarliestRecord" resultType="xyz.playedu.course.domain.UserCourseHourRecord">
|
||||
SELECT
|
||||
`a`.*
|
||||
FROM
|
||||
`user_course_hour_records` AS `a`
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
min(`created_at`) AS `min_created_at`,
|
||||
`course_id`,
|
||||
`user_id`
|
||||
FROM
|
||||
`user_course_hour_records`
|
||||
WHERE
|
||||
`course_id` = #{courseId}
|
||||
GROUP BY
|
||||
`course_id`,
|
||||
`user_id`) AS `b` ON `b`.`min_created_at` = `a`.`created_at`
|
||||
AND `b`.`course_id` = `a`.`course_id`
|
||||
AND `b`.`user_id` = `a`.`user_id`;
|
||||
</select>
|
||||
</mapper>
|
||||
|
@ -26,29 +26,25 @@ import xyz.playedu.common.constant.BackendConstant;
|
||||
import xyz.playedu.common.constant.FrontendConstant;
|
||||
import xyz.playedu.common.domain.UserUploadImageLog;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.MinioService;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.service.UserUploadImageLogService;
|
||||
import xyz.playedu.common.types.UploadFileInfo;
|
||||
import xyz.playedu.common.util.Base64Util;
|
||||
import xyz.playedu.common.util.HelperUtil;
|
||||
import xyz.playedu.common.util.S3Util;
|
||||
import xyz.playedu.resource.domain.Resource;
|
||||
import xyz.playedu.resource.service.ResourceService;
|
||||
import xyz.playedu.resource.service.UploadService;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Author 杭州白书科技有限公司
|
||||
*
|
||||
* @create 2023/3/8 14:02
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UploadServiceImpl implements UploadService {
|
||||
|
||||
@Autowired private ResourceService resourceService;
|
||||
|
||||
@Autowired private MinioService minioService;
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@Autowired private UserUploadImageLogService userUploadImageLogService;
|
||||
|
||||
@ -79,13 +75,14 @@ public class UploadServiceImpl implements UploadService {
|
||||
// 自定义新的存储文件名
|
||||
fileInfo.setSaveName(HelperUtil.randomString(32) + "." + fileInfo.getExtension());
|
||||
// 生成保存的相对路径
|
||||
if (dir == null || dir.length() == 0) {
|
||||
if (dir == null || dir.isEmpty()) {
|
||||
dir = BackendConstant.RESOURCE_TYPE_2_DIR.get(fileInfo.getResourceType());
|
||||
}
|
||||
fileInfo.setSavePath(dir + fileInfo.getSaveName());
|
||||
// 保存文件并生成访问url
|
||||
S3Util s3Util = new S3Util(appConfigService.getS3Config());
|
||||
String url =
|
||||
minioService.saveFile(
|
||||
s3Util.saveFile(
|
||||
file,
|
||||
fileInfo.getSavePath(),
|
||||
BackendConstant.RESOURCE_EXT_2_CONTENT_TYPE.get(fileInfo.getExtension()));
|
||||
@ -134,8 +131,9 @@ public class UploadServiceImpl implements UploadService {
|
||||
String savePath = BackendConstant.RESOURCE_TYPE_2_DIR.get(type) + filename;
|
||||
|
||||
// 保存文件
|
||||
S3Util s3Util = new S3Util(appConfigService.getS3Config());
|
||||
String url =
|
||||
minioService.saveBytes(
|
||||
s3Util.saveBytes(
|
||||
binary, savePath, BackendConstant.RESOURCE_EXT_2_CONTENT_TYPE.get(ext));
|
||||
// 上传记录
|
||||
return resourceService.create(
|
||||
|
@ -37,76 +37,19 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
||||
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_MENU);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(0);
|
||||
@ -116,27 +59,25 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
||||
.RESOURCE_CATEGORY);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(10);
|
||||
setName("左侧菜单");
|
||||
setSlug(
|
||||
BPermissionConstant
|
||||
.RESOURCE_CATEGORY_MENU);
|
||||
}
|
||||
},
|
||||
});
|
||||
// 资源分类
|
||||
// 资源管理
|
||||
put(
|
||||
"资源管理",
|
||||
new AdminPermission[] {
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(10);
|
||||
setName("左侧菜单");
|
||||
setSort(0);
|
||||
setName("列表");
|
||||
setSlug(BPermissionConstant.RESOURCE_MENU);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(10);
|
||||
setName("资源上传");
|
||||
setSlug(BPermissionConstant.UPLOAD);
|
||||
}
|
||||
},
|
||||
});
|
||||
// 学员
|
||||
put(
|
||||
@ -173,20 +114,41 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(40);
|
||||
setName("学习");
|
||||
setName("学习进度-查看");
|
||||
setSlug(BPermissionConstant.USER_LEARN);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(50);
|
||||
setName("学习-删除");
|
||||
setName("学习进度-记录删除");
|
||||
setSlug(
|
||||
BPermissionConstant
|
||||
.USER_LEARN_DESTROY);
|
||||
}
|
||||
},
|
||||
});
|
||||
// 部门
|
||||
put(
|
||||
"部门",
|
||||
new AdminPermission[] {
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(0);
|
||||
setName("新增|编辑|删除");
|
||||
setSlug(BPermissionConstant.DEPARTMENT_CUD);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(10);
|
||||
setName("查看部门学员学习进度");
|
||||
setSlug(
|
||||
BPermissionConstant
|
||||
.DEPARTMENT_USER_LEARN);
|
||||
}
|
||||
},
|
||||
});
|
||||
// 线上课
|
||||
put(
|
||||
"线上课",
|
||||
@ -194,10 +156,17 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(0);
|
||||
setName("新增|编辑|删除");
|
||||
setName("列表");
|
||||
setSlug(BPermissionConstant.COURSE);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(5);
|
||||
setName("新增|编辑|删除");
|
||||
setSlug(BPermissionConstant.COURSE_CUD);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(10);
|
||||
@ -215,23 +184,9 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
||||
}
|
||||
},
|
||||
});
|
||||
// 其它
|
||||
put(
|
||||
"其它",
|
||||
new AdminPermission[] {
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(0);
|
||||
setName("修改登录密码");
|
||||
setSlug(
|
||||
BPermissionConstant
|
||||
.PASSWORD_CHANGE);
|
||||
}
|
||||
},
|
||||
});
|
||||
// 系统配置
|
||||
put(
|
||||
"系统配置",
|
||||
"系统",
|
||||
new AdminPermission[] {
|
||||
new AdminPermission() {
|
||||
{
|
||||
@ -240,16 +195,43 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
||||
setSlug(BPermissionConstant.SYSTEM_CONFIG);
|
||||
}
|
||||
},
|
||||
});
|
||||
// 其它
|
||||
put(
|
||||
"其它权限",
|
||||
new AdminPermission[] {
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(0);
|
||||
setName("文件上传");
|
||||
setSlug(BPermissionConstant.UPLOAD);
|
||||
setSort(10);
|
||||
setName("管理员日志");
|
||||
setSlug(BPermissionConstant.ADMIN_LOG);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(15);
|
||||
setName("管理员角色");
|
||||
setSlug(BPermissionConstant.ADMIN_ROLE);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(20);
|
||||
setName("管理员-列表");
|
||||
setSlug(
|
||||
BPermissionConstant
|
||||
.ADMIN_USER_INDEX);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(25);
|
||||
setName("管理员-新增|编辑|删除");
|
||||
setSlug(BPermissionConstant.ADMIN_USER_CUD);
|
||||
}
|
||||
},
|
||||
new AdminPermission() {
|
||||
{
|
||||
setSort(30);
|
||||
setName("修改登录密码");
|
||||
setSlug(
|
||||
BPermissionConstant
|
||||
.PASSWORD_CHANGE);
|
||||
}
|
||||
},
|
||||
});
|
||||
@ -312,7 +294,7 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
HashMap<String, Boolean> slugs = permissionService.allSlugs();
|
||||
HashMap<String, Integer> slugs = permissionService.allSlugs();
|
||||
List<AdminPermission> list = new ArrayList<>();
|
||||
Date now = new Date();
|
||||
|
||||
@ -320,25 +302,28 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
||||
(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(
|
||||
for (AdminPermission permissionItem : item) {
|
||||
AdminPermission newPermissionItem =
|
||||
new AdminPermission() {
|
||||
{
|
||||
setType(typeValue);
|
||||
setGroupName(groupNameValue);
|
||||
setSort(permissionItem.getSort());
|
||||
setName(permissionItem.getName());
|
||||
setSlug(permissionItem.getSlug());
|
||||
setCreatedAt(now);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Integer existsId = slugs.get(permissionItem.getSlug());
|
||||
if (existsId != null && existsId > 0) {
|
||||
newPermissionItem.setId(existsId);
|
||||
permissionService.updateById(newPermissionItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 不存在
|
||||
newPermissionItem.setCreatedAt(now);
|
||||
newPermissionItem.setSlug(permissionItem.getSlug());
|
||||
list.add(newPermissionItem);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -701,6 +701,39 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
""");
|
||||
}
|
||||
});
|
||||
add(
|
||||
new HashMap<>() {
|
||||
{
|
||||
put("table", "");
|
||||
put("name", "20231113_10_00_add_admin_id_for_courses");
|
||||
put(
|
||||
"sql",
|
||||
"""
|
||||
ALTER TABLE `courses` add `admin_id` int(10) NOT NULL DEFAULT 0 COMMENT '管理员id';
|
||||
""");
|
||||
}
|
||||
});
|
||||
add(
|
||||
new HashMap<>() {
|
||||
{
|
||||
put("table", "ldap_department");
|
||||
put("name", "20231113_15_44_17_ldap_department");
|
||||
put(
|
||||
"sql",
|
||||
"""
|
||||
CREATE TABLE `ldap_department` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(64) NOT NULL DEFAULT '' COMMENT '唯一特征值',
|
||||
`department_id` int(11) NOT NULL DEFAULT 0 COMMENT '部门ID',
|
||||
`dn` varchar(120) NOT NULL DEFAULT '' COMMENT 'dn',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_uuid` (`uuid`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
38
pom.xml
38
pom.xml
@ -5,9 +5,10 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.5</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>xyz.playedu</groupId>
|
||||
<artifactId>playedu</artifactId>
|
||||
<version>1.2</version>
|
||||
@ -96,29 +97,15 @@
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
|
||||
<!--JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
<version>1.12.572</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-gson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.5.2</version>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@ -155,12 +142,6 @@
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
<version>1.34.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.0.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -186,9 +167,8 @@
|
||||
<style>AOSP</style>
|
||||
<reflowLongStrings>true</reflowLongStrings>
|
||||
</googleJavaFormat>
|
||||
|
||||
<licenseHeader>
|
||||
<file>license-header.txt</file>
|
||||
<file>header.txt</file>
|
||||
</licenseHeader>
|
||||
</java>
|
||||
</configuration>
|
||||
|
Loading…
x
Reference in New Issue
Block a user