mirror of
https://github.com/PlayEdu/PlayEdu
synced 2025-07-28 13:29:50 +08:00
Compare commits
9 Commits
82c53ed87f
...
12c4b810c2
Author | SHA1 | Date | |
---|---|---|---|
|
12c4b810c2 | ||
|
3ea07739d1 | ||
|
9f27aaac42 | ||
|
be6264dcd3 | ||
|
3c9b354aea | ||
|
040dcdfaed | ||
|
8c905c6552 | ||
|
8f27bb9fda | ||
|
23ff7068f7 |
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@ -6,9 +6,12 @@ on:
|
|||||||
- main
|
- main
|
||||||
- dev
|
- dev
|
||||||
- 'feat/**'
|
- 'feat/**'
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE_FQDN: registry.cn-hangzhou.aliyuncs.com/playedu/api
|
IMAGE_FQDN: registry.cn-hangzhou.aliyuncs.com/playedu/api
|
||||||
|
IMAGE_TAG: ''
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
@ -33,4 +36,16 @@ jobs:
|
|||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
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_slug }}
|
||||||
|
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 }}
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
@ -20,6 +20,7 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
import xyz.playedu.common.config.UniqueNameGeneratorConfig;
|
import xyz.playedu.common.config.UniqueNameGeneratorConfig;
|
||||||
@ -27,6 +28,7 @@ import xyz.playedu.common.config.UniqueNameGeneratorConfig;
|
|||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
|
@EnableScheduling
|
||||||
@ComponentScan(
|
@ComponentScan(
|
||||||
basePackages = {"xyz.playedu"},
|
basePackages = {"xyz.playedu"},
|
||||||
nameGenerator = UniqueNameGeneratorConfig.class)
|
nameGenerator = UniqueNameGeneratorConfig.class)
|
||||||
|
@ -23,14 +23,12 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import xyz.playedu.api.event.UserLoginEvent;
|
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.domain.User;
|
||||||
import xyz.playedu.common.exception.ServiceException;
|
import xyz.playedu.common.exception.ServiceException;
|
||||||
import xyz.playedu.common.service.*;
|
import xyz.playedu.common.service.*;
|
||||||
import xyz.playedu.common.util.HelperUtil;
|
|
||||||
import xyz.playedu.common.util.IpUtil;
|
import xyz.playedu.common.util.IpUtil;
|
||||||
import xyz.playedu.common.util.RequestUtil;
|
import xyz.playedu.common.util.RequestUtil;
|
||||||
import xyz.playedu.common.util.StringUtil;
|
|
||||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -41,14 +39,10 @@ public class LoginBus {
|
|||||||
|
|
||||||
@Autowired private FrontendAuthService authService;
|
@Autowired private FrontendAuthService authService;
|
||||||
|
|
||||||
@Autowired private DepartmentService departmentService;
|
|
||||||
|
|
||||||
@Autowired private LdapUserService ldapUserService;
|
|
||||||
|
|
||||||
@Autowired private UserService userService;
|
|
||||||
|
|
||||||
@Autowired private AppConfigService appConfigService;
|
@Autowired private AppConfigService appConfigService;
|
||||||
|
|
||||||
|
@Autowired private LDAPBus ldapBus;
|
||||||
|
|
||||||
@Autowired private ApplicationContext ctx;
|
@Autowired private ApplicationContext ctx;
|
||||||
|
|
||||||
public HashMap<String, Object> tokenByUser(User user) {
|
public HashMap<String, Object> tokenByUser(User user) {
|
||||||
@ -72,72 +66,7 @@ public class LoginBus {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public HashMap<String, Object> tokenByLdapTransformUser(LdapTransformUser ldapTransformUser)
|
public HashMap<String, Object> tokenByLdapTransformUser(LdapTransformUser ldapTransformUser)
|
||||||
throws ServiceException {
|
throws ServiceException {
|
||||||
// LDAP用户的名字
|
User user = ldapBus.singleUserSync(ldapTransformUser, appConfigService.defaultAvatar());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokenByUser(user);
|
return tokenByUser(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ public class ExceptionController {
|
|||||||
|
|
||||||
@ExceptionHandler(Exception.class)
|
@ExceptionHandler(Exception.class)
|
||||||
public JsonResponse exceptionHandler(Exception e) {
|
public JsonResponse exceptionHandler(Exception e) {
|
||||||
log.error("{}-{}", e, e.getMessage());
|
log.error("出现异常", e);
|
||||||
return JsonResponse.error("系统错误", 500);
|
return JsonResponse.error("系统错误", 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +101,6 @@ public class ExceptionController {
|
|||||||
@ExceptionHandler(AmazonS3Exception.class)
|
@ExceptionHandler(AmazonS3Exception.class)
|
||||||
public JsonResponse serviceExceptionHandler(AmazonS3Exception e) {
|
public JsonResponse serviceExceptionHandler(AmazonS3Exception e) {
|
||||||
log.error("s3错误={}", e.getMessage());
|
log.error("s3错误={}", e.getMessage());
|
||||||
return JsonResponse.error(e.getMessage(), 500);
|
return JsonResponse.error("存储配置有问题或存储无法无法正常访问", 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,20 +30,18 @@ import xyz.playedu.api.request.backend.DepartmentRequest;
|
|||||||
import xyz.playedu.api.request.backend.DepartmentSortRequest;
|
import xyz.playedu.api.request.backend.DepartmentSortRequest;
|
||||||
import xyz.playedu.common.annotation.BackendPermission;
|
import xyz.playedu.common.annotation.BackendPermission;
|
||||||
import xyz.playedu.common.annotation.Log;
|
import xyz.playedu.common.annotation.Log;
|
||||||
|
import xyz.playedu.common.bus.LDAPBus;
|
||||||
import xyz.playedu.common.constant.BPermissionConstant;
|
import xyz.playedu.common.constant.BPermissionConstant;
|
||||||
import xyz.playedu.common.constant.BusinessTypeConstant;
|
import xyz.playedu.common.constant.BusinessTypeConstant;
|
||||||
import xyz.playedu.common.context.BCtx;
|
import xyz.playedu.common.context.BCtx;
|
||||||
import xyz.playedu.common.domain.Department;
|
import xyz.playedu.common.domain.Department;
|
||||||
import xyz.playedu.common.domain.User;
|
import xyz.playedu.common.domain.User;
|
||||||
import xyz.playedu.common.exception.NotFoundException;
|
import xyz.playedu.common.exception.NotFoundException;
|
||||||
import xyz.playedu.common.service.AppConfigService;
|
|
||||||
import xyz.playedu.common.service.DepartmentService;
|
import xyz.playedu.common.service.DepartmentService;
|
||||||
import xyz.playedu.common.service.UserService;
|
import xyz.playedu.common.service.UserService;
|
||||||
import xyz.playedu.common.types.JsonResponse;
|
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.PaginationResult;
|
||||||
import xyz.playedu.common.types.paginate.UserPaginateFilter;
|
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.Course;
|
||||||
import xyz.playedu.course.domain.UserCourseRecord;
|
import xyz.playedu.course.domain.UserCourseRecord;
|
||||||
import xyz.playedu.course.service.CourseDepartmentService;
|
import xyz.playedu.course.service.CourseDepartmentService;
|
||||||
@ -70,7 +68,7 @@ public class DepartmentController {
|
|||||||
|
|
||||||
@Autowired private ApplicationContext ctx;
|
@Autowired private ApplicationContext ctx;
|
||||||
|
|
||||||
@Autowired private AppConfigService appConfigService;
|
@Autowired private LDAPBus ldapBus;
|
||||||
|
|
||||||
@GetMapping("/index")
|
@GetMapping("/index")
|
||||||
@Log(title = "部门-列表", businessType = BusinessTypeConstant.GET)
|
@Log(title = "部门-列表", businessType = BusinessTypeConstant.GET)
|
||||||
@ -104,7 +102,7 @@ public class DepartmentController {
|
|||||||
@Log(title = "部门-新建", businessType = BusinessTypeConstant.INSERT)
|
@Log(title = "部门-新建", businessType = BusinessTypeConstant.INSERT)
|
||||||
public JsonResponse store(@RequestBody @Validated DepartmentRequest req)
|
public JsonResponse store(@RequestBody @Validated DepartmentRequest req)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
if (appConfigService.enabledLdapLogin()) {
|
if (ldapBus.enabledLDAP()) {
|
||||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||||
}
|
}
|
||||||
departmentService.create(req.getName(), req.getParentId(), req.getSort());
|
departmentService.create(req.getName(), req.getParentId(), req.getSort());
|
||||||
@ -124,7 +122,7 @@ public class DepartmentController {
|
|||||||
@Log(title = "部门-编辑", businessType = BusinessTypeConstant.UPDATE)
|
@Log(title = "部门-编辑", businessType = BusinessTypeConstant.UPDATE)
|
||||||
public JsonResponse update(@PathVariable Integer id, @RequestBody DepartmentRequest req)
|
public JsonResponse update(@PathVariable Integer id, @RequestBody DepartmentRequest req)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
if (appConfigService.enabledLdapLogin()) {
|
if (ldapBus.enabledLDAP()) {
|
||||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||||
}
|
}
|
||||||
Department department = departmentService.findOrFail(id);
|
Department department = departmentService.findOrFail(id);
|
||||||
@ -136,7 +134,7 @@ public class DepartmentController {
|
|||||||
@GetMapping("/{id}/destroy")
|
@GetMapping("/{id}/destroy")
|
||||||
@Log(title = "部门-批量删除", businessType = BusinessTypeConstant.DELETE)
|
@Log(title = "部门-批量删除", businessType = BusinessTypeConstant.DELETE)
|
||||||
public JsonResponse preDestroy(@PathVariable Integer id) {
|
public JsonResponse preDestroy(@PathVariable Integer id) {
|
||||||
if (appConfigService.enabledLdapLogin()) {
|
if (ldapBus.enabledLDAP()) {
|
||||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||||
}
|
}
|
||||||
List<Integer> courseIds = courseDepartmentService.getCourseIdsByDepId(id);
|
List<Integer> courseIds = courseDepartmentService.getCourseIdsByDepId(id);
|
||||||
@ -180,7 +178,7 @@ public class DepartmentController {
|
|||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
@Log(title = "部门-删除", businessType = BusinessTypeConstant.DELETE)
|
@Log(title = "部门-删除", businessType = BusinessTypeConstant.DELETE)
|
||||||
public JsonResponse destroy(@PathVariable Integer id) throws NotFoundException {
|
public JsonResponse destroy(@PathVariable Integer id) throws NotFoundException {
|
||||||
if (appConfigService.enabledLdapLogin()) {
|
if (ldapBus.enabledLDAP()) {
|
||||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||||
}
|
}
|
||||||
Department department = departmentService.findOrFail(id);
|
Department department = departmentService.findOrFail(id);
|
||||||
@ -202,7 +200,7 @@ public class DepartmentController {
|
|||||||
@Log(title = "部门-更新父级", businessType = BusinessTypeConstant.UPDATE)
|
@Log(title = "部门-更新父级", businessType = BusinessTypeConstant.UPDATE)
|
||||||
public JsonResponse updateParent(@RequestBody @Validated DepartmentParentRequest req)
|
public JsonResponse updateParent(@RequestBody @Validated DepartmentParentRequest req)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
if (appConfigService.enabledLdapLogin()) {
|
if (ldapBus.enabledLDAP()) {
|
||||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||||
}
|
}
|
||||||
departmentService.changeParent(req.getId(), req.getParentId(), req.getIds());
|
departmentService.changeParent(req.getId(), req.getParentId(), req.getIds());
|
||||||
@ -315,57 +313,8 @@ public class DepartmentController {
|
|||||||
@Log(title = "部门-LDAP同步", businessType = BusinessTypeConstant.INSERT)
|
@Log(title = "部门-LDAP同步", businessType = BusinessTypeConstant.INSERT)
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public JsonResponse ldapSync() {
|
public JsonResponse ldapSync() {
|
||||||
LdapConfig ldapConfig = appConfigService.ldapConfig();
|
ldapBus.departmentSync();
|
||||||
|
ldapBus.userSync();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonResponse.success();
|
return JsonResponse.success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,6 +515,10 @@ public class UserController {
|
|||||||
UserCourseHourRecordCourseCountMapper::getCourseId,
|
UserCourseHourRecordCourseCountMapper::getCourseId,
|
||||||
UserCourseHourRecordCourseCountMapper::getTotal));
|
UserCourseHourRecordCourseCountMapper::getTotal));
|
||||||
|
|
||||||
|
// 获取学员每个课程最早的学习课时记录
|
||||||
|
List<UserCourseHourRecord> perCourseEarliestRecords =
|
||||||
|
userCourseHourRecordService.getUserPerCourseEarliestRecord(id);
|
||||||
|
|
||||||
HashMap<String, Object> data = new HashMap<>();
|
HashMap<String, Object> data = new HashMap<>();
|
||||||
data.put("open_courses", openCourses);
|
data.put("open_courses", openCourses);
|
||||||
data.put("departments", departments);
|
data.put("departments", departments);
|
||||||
@ -524,6 +528,10 @@ public class UserController {
|
|||||||
userCourseRecords.stream()
|
userCourseRecords.stream()
|
||||||
.collect(Collectors.toMap(UserCourseRecord::getCourseId, e -> e)));
|
.collect(Collectors.toMap(UserCourseRecord::getCourseId, e -> e)));
|
||||||
data.put("user_course_hour_count", userCourseHourCount);
|
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);
|
return JsonResponse.data(data);
|
||||||
}
|
}
|
||||||
|
@ -32,19 +32,18 @@ import xyz.playedu.api.cache.LoginLockCache;
|
|||||||
import xyz.playedu.api.event.UserLogoutEvent;
|
import xyz.playedu.api.event.UserLogoutEvent;
|
||||||
import xyz.playedu.api.request.frontend.LoginLdapRequest;
|
import xyz.playedu.api.request.frontend.LoginLdapRequest;
|
||||||
import xyz.playedu.api.request.frontend.LoginPasswordRequest;
|
import xyz.playedu.api.request.frontend.LoginPasswordRequest;
|
||||||
import xyz.playedu.common.constant.ConfigConstant;
|
|
||||||
import xyz.playedu.common.context.FCtx;
|
import xyz.playedu.common.context.FCtx;
|
||||||
import xyz.playedu.common.domain.User;
|
import xyz.playedu.common.domain.User;
|
||||||
import xyz.playedu.common.exception.LimitException;
|
import xyz.playedu.common.exception.LimitException;
|
||||||
import xyz.playedu.common.exception.ServiceException;
|
import xyz.playedu.common.exception.ServiceException;
|
||||||
import xyz.playedu.common.service.*;
|
import xyz.playedu.common.service.*;
|
||||||
import xyz.playedu.common.types.JsonResponse;
|
import xyz.playedu.common.types.JsonResponse;
|
||||||
|
import xyz.playedu.common.types.LdapConfig;
|
||||||
import xyz.playedu.common.util.*;
|
import xyz.playedu.common.util.*;
|
||||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||||
import xyz.playedu.common.util.ldap.LdapUtil;
|
import xyz.playedu.common.util.ldap.LdapUtil;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/auth/login")
|
@RequestMapping("/api/v1/auth/login")
|
||||||
@ -100,15 +99,7 @@ public class LoginController {
|
|||||||
public JsonResponse ldap(@RequestBody @Validated LoginLdapRequest req) {
|
public JsonResponse ldap(@RequestBody @Validated LoginLdapRequest req) {
|
||||||
String username = req.getUsername();
|
String username = req.getUsername();
|
||||||
|
|
||||||
// 系统配置
|
LdapConfig ldapConfig = appConfigService.ldapConfig();
|
||||||
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服务未配置");
|
|
||||||
}
|
|
||||||
|
|
||||||
String mail = null;
|
String mail = null;
|
||||||
String uid = null;
|
String uid = null;
|
||||||
@ -129,7 +120,13 @@ public class LoginController {
|
|||||||
try {
|
try {
|
||||||
LdapTransformUser ldapTransformUser =
|
LdapTransformUser ldapTransformUser =
|
||||||
LdapUtil.loginByMailOrUid(
|
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) {
|
if (ldapTransformUser == null) {
|
||||||
return JsonResponse.error("登录失败.请检查账号和密码");
|
return JsonResponse.error("登录失败.请检查账号和密码");
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,6 @@ public class UserCourseHourRecordDestroyListener {
|
|||||||
|
|
||||||
@EventListener
|
@EventListener
|
||||||
public void updateUserCourseRecord(UserCourseHourRecordDestroyEvent e) {
|
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 (!ldapBus.enabledLDAP()) {
|
||||||
|
log.info("未配置LDAP服务");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统刚启动不执行
|
||||||
|
if (times == 0) {
|
||||||
|
times++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ldapBus.departmentSync();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("LDAP-部门同步失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ldapBus.userSync();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("LDAP-学员同步失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("LDAP同步成功");
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -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> {}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
import xyz.playedu.common.domain.LdapDepartment;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface LdapDepartmentService extends IService<LdapDepartment> {
|
||||||
|
|
||||||
|
List<LdapDepartment> all();
|
||||||
|
|
||||||
|
List<LdapDepartment> notChunkByUUIDList(List<String> uuidList);
|
||||||
|
|
||||||
|
void destroy(Integer id);
|
||||||
|
|
||||||
|
void create(Integer depId, String uuid, String dn);
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -77,8 +77,11 @@ public class S3Util {
|
|||||||
new AwsClientBuilder.EndpointConfiguration(
|
new AwsClientBuilder.EndpointConfiguration(
|
||||||
defaultConfig.getEndpoint(), defaultConfig.getRegion());
|
defaultConfig.getEndpoint(), defaultConfig.getRegion());
|
||||||
|
|
||||||
return AmazonS3ClientBuilder.standard()
|
AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard();
|
||||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
// 开启路径访问
|
||||||
|
builder.setPathStyleAccessEnabled(true);
|
||||||
|
|
||||||
|
return builder.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||||
.withEndpointConfiguration(endpointConfiguration)
|
.withEndpointConfiguration(endpointConfiguration)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
@ -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",
|
"entryUUID",
|
||||||
|
|
||||||
// Window AD 域的属性
|
// Window AD 域的属性
|
||||||
"memberOf",
|
|
||||||
"name",
|
"name",
|
||||||
"userPrincipalName",
|
"userPrincipalName",
|
||||||
"departmentNumber",
|
"distinguishedName",
|
||||||
"telephoneNumber",
|
|
||||||
"mobile",
|
|
||||||
"department",
|
|
||||||
"sAMAccountName",
|
"sAMAccountName",
|
||||||
|
"displayName",
|
||||||
"uSNCreated", // AD域的唯一属性
|
"uSNCreated", // AD域的唯一属性
|
||||||
|
|
||||||
// 公用属性
|
// 公用属性
|
||||||
"mail",
|
"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)
|
public static LdapContext initContext(String url, String adminUser, String adminPass)
|
||||||
throws NamingException {
|
throws NamingException {
|
||||||
@ -76,8 +73,10 @@ public class LdapUtil {
|
|||||||
return new InitialLdapContext(context, null);
|
return new InitialLdapContext(context, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<HashMap<String, String>> users(LdapContext ldapContext, String baseDN)
|
public static List<LdapTransformUser> users(
|
||||||
throws NamingException {
|
String url, String adminUser, String adminPass, String baseDN) throws NamingException {
|
||||||
|
LdapContext ldapContext = initContext(url, adminUser, adminPass);
|
||||||
|
|
||||||
SearchControls controls = new SearchControls();
|
SearchControls controls = new SearchControls();
|
||||||
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||||
controls.setReturningAttributes(USER_RETURN_ATTRS);
|
controls.setReturningAttributes(USER_RETURN_ATTRS);
|
||||||
@ -97,20 +96,20 @@ public class LdapUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<HashMap<String, String>> users = new ArrayList<>();
|
List<LdapTransformUser> users = new ArrayList<>();
|
||||||
while (result.hasMoreElements()) {
|
while (result.hasMoreElements()) {
|
||||||
SearchResult item = result.nextElement();
|
SearchResult item = result.nextElement();
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Attributes attributes = item.getAttributes();
|
LdapTransformUser ldapTransformUser = parseTransformUser(item, baseDN);
|
||||||
log.info("name={},attributes={}", item.getName(), attributes);
|
users.add(ldapTransformUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> departments(
|
public static List<LdapTransformDepartment> departments(
|
||||||
String url, String adminUser, String adminPass, String baseDN) throws NamingException {
|
String url, String adminUser, String adminPass, String baseDN) throws NamingException {
|
||||||
LdapContext ldapContext = initContext(url, adminUser, adminPass);
|
LdapContext ldapContext = initContext(url, adminUser, adminPass);
|
||||||
|
|
||||||
@ -137,12 +136,24 @@ public class LdapUtil {
|
|||||||
// baseDN中的ou作用域
|
// baseDN中的ou作用域
|
||||||
String ouScopesStr = baseDNOuScope(baseDN);
|
String ouScopesStr = baseDNOuScope(baseDN);
|
||||||
|
|
||||||
List<String> units = new ArrayList<>();
|
List<LdapTransformDepartment> units = new ArrayList<>();
|
||||||
while (result.hasMoreElements()) {
|
while (result.hasMoreElements()) {
|
||||||
SearchResult item = result.nextElement();
|
SearchResult item = result.nextElement();
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
continue;
|
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();
|
String name = item.getName();
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
name = ouScopesStr;
|
name = ouScopesStr;
|
||||||
@ -150,20 +161,19 @@ public class LdapUtil {
|
|||||||
name = name + (ouScopesStr.isEmpty() ? "" : "," + ouScopesStr);
|
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<>();
|
return units;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LdapTransformUser loginByMailOrUid(
|
public static LdapTransformUser loginByMailOrUid(
|
||||||
@ -200,30 +210,62 @@ public class LdapUtil {
|
|||||||
try {
|
try {
|
||||||
result = ldapContext.search(baseDN, filter, controls);
|
result = ldapContext.search(baseDN, filter, controls);
|
||||||
} catch (NamingException e) {
|
} catch (NamingException e) {
|
||||||
log.error("通过mail或uid登录失败", e);
|
log.error("LDAP-通过mail或uid登录失败", e);
|
||||||
} finally {
|
} finally {
|
||||||
closeContext(ldapContext);
|
closeContext(ldapContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null || !result.hasMoreElements()) {
|
if (result == null || !result.hasMoreElements()) {
|
||||||
log.info("用户不存在");
|
log.info("LDAP-用户不存在");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据mail或uid查询出来的用户
|
// 根据mail或uid查询出来的用户
|
||||||
SearchResult item = result.nextElement();
|
LdapTransformUser ldapUser = parseTransformUser(result.nextElement(), baseDN);
|
||||||
Attributes attributes = item.getAttributes();
|
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 =
|
String email =
|
||||||
attributes.get("mail") == null ? null : (String) attributes.get("mail").get();
|
attributes.get("mail") == null ? null : (String) attributes.get("mail").get();
|
||||||
if (email == null) {
|
if (email == null) {
|
||||||
email = attributes.get("email") == null ? null : (String) attributes.get("email").get();
|
email = attributes.get("email") == null ? null : (String) attributes.get("email").get();
|
||||||
}
|
}
|
||||||
|
|
||||||
LdapTransformUser ldapUser = new LdapTransformUser();
|
|
||||||
ldapUser.setDn(item.getName());
|
|
||||||
ldapUser.setEmail(email);
|
ldapUser.setEmail(email);
|
||||||
ldapUser.setCn((String) attributes.get("cn").get());
|
|
||||||
|
|
||||||
if (attributes.get("uSNCreated") != null) {
|
if (attributes.get("uSNCreated") != null) {
|
||||||
// Window AD域
|
// Window AD域
|
||||||
@ -251,22 +293,6 @@ public class LdapUtil {
|
|||||||
Collections.reverse(ou);
|
Collections.reverse(ou);
|
||||||
ldapUser.setOu(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;
|
return ldapUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,14 +307,14 @@ public class LdapUtil {
|
|||||||
return String.join(",", ouScopes);
|
return String.join(",", ouScopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void closeContext(LdapContext ldapCtx) {
|
private static void closeContext(LdapContext ldapCtx) {
|
||||||
if (ldapCtx == null) {
|
if (ldapCtx == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ldapCtx.close();
|
ldapCtx.close();
|
||||||
} catch (NamingException e) {
|
} 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>
|
@ -48,4 +48,6 @@ public interface UserCourseHourRecordMapper extends BaseMapper<UserCourseHourRec
|
|||||||
List<UserCourseHourRecord> paginate(UserCourseHourRecordPaginateFilter filter);
|
List<UserCourseHourRecord> paginate(UserCourseHourRecordPaginateFilter filter);
|
||||||
|
|
||||||
Long paginateCount(UserCourseHourRecordPaginateFilter filter);
|
Long paginateCount(UserCourseHourRecordPaginateFilter filter);
|
||||||
|
|
||||||
|
List<UserCourseHourRecord> getUserPerCourseEarliestRecord(Integer userId);
|
||||||
}
|
}
|
||||||
|
@ -66,4 +66,6 @@ public interface UserCourseHourRecordService extends IService<UserCourseHourReco
|
|||||||
|
|
||||||
PaginationResult<UserCourseHourRecord> paginate(
|
PaginationResult<UserCourseHourRecord> paginate(
|
||||||
int page, int size, UserCourseHourRecordPaginateFilter filter);
|
int page, int size, UserCourseHourRecordPaginateFilter filter);
|
||||||
|
|
||||||
|
List<UserCourseHourRecord> getUserPerCourseEarliestRecord(Integer userId);
|
||||||
}
|
}
|
||||||
|
@ -51,5 +51,5 @@ public interface UserCourseRecordService extends IService<UserCourseRecord> {
|
|||||||
|
|
||||||
List<UserCourseRecord> chunks(List<Integer> ids, List<String> fields);
|
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);
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ public class UserCourseHourRecordServiceImpl
|
|||||||
@Override
|
@Override
|
||||||
public List<UserCourseHourRecordCourseCountMapper> getUserCourseHourCount(
|
public List<UserCourseHourRecordCourseCountMapper> getUserCourseHourCount(
|
||||||
Integer userId, List<Integer> courseIds, Integer isFinished) {
|
Integer userId, List<Integer> courseIds, Integer isFinished) {
|
||||||
if (courseIds == null || courseIds.size() == 0) {
|
if (courseIds == null || courseIds.isEmpty()) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return getBaseMapper().getUserCourseHourCount(userId, courseIds, isFinished);
|
return getBaseMapper().getUserCourseHourCount(userId, courseIds, isFinished);
|
||||||
@ -132,7 +132,7 @@ public class UserCourseHourRecordServiceImpl
|
|||||||
@Override
|
@Override
|
||||||
public List<UserCourseHourRecordUserCountMapper> getUserCourseHourUserCount(
|
public List<UserCourseHourRecordUserCountMapper> getUserCourseHourUserCount(
|
||||||
Integer courseId, List<Integer> userIds, Integer isFinished) {
|
Integer courseId, List<Integer> userIds, Integer isFinished) {
|
||||||
if (userIds == null || userIds.size() == 0) {
|
if (userIds == null || userIds.isEmpty()) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return getBaseMapper().getUserCourseHourUserCount(courseId, userIds, isFinished);
|
return getBaseMapper().getUserCourseHourUserCount(courseId, userIds, isFinished);
|
||||||
@ -174,9 +174,14 @@ public class UserCourseHourRecordServiceImpl
|
|||||||
@Override
|
@Override
|
||||||
public List<UserCourseHourRecordUserFirstCreatedAtMapper> getUserCourseHourUserFirstCreatedAt(
|
public List<UserCourseHourRecordUserFirstCreatedAtMapper> getUserCourseHourUserFirstCreatedAt(
|
||||||
Integer courseId, List<Integer> userIds) {
|
Integer courseId, List<Integer> userIds) {
|
||||||
if (userIds == null || userIds.size() == 0) {
|
if (userIds == null || userIds.isEmpty()) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return getBaseMapper().getUserCourseHourUserFirstCreatedAt(courseId, userIds);
|
return getBaseMapper().getUserCourseHourUserFirstCreatedAt(courseId, userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserCourseHourRecord> getUserPerCourseEarliestRecord(Integer userId) {
|
||||||
|
return getBaseMapper().getUserPerCourseEarliestRecord(userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ public class UserCourseRecordServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void decrease(Integer userId, Integer courseId, int count) {
|
public void updateUserCourseLearnProgress(Integer userId, Integer courseId, int count) {
|
||||||
UserCourseRecord record = find(userId, courseId);
|
UserCourseRecord record = find(userId, courseId);
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
return;
|
return;
|
||||||
@ -152,12 +152,17 @@ public class UserCourseRecordServiceImpl
|
|||||||
|
|
||||||
int finishedCount = record.getFinishedCount() - count;
|
int finishedCount = record.getFinishedCount() - count;
|
||||||
|
|
||||||
|
if (0 == finishedCount) {
|
||||||
|
remove(query().getWrapper().eq("id", record.getId()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
UserCourseRecord newRecord = new UserCourseRecord();
|
UserCourseRecord newRecord = new UserCourseRecord();
|
||||||
newRecord.setId(record.getId());
|
newRecord.setId(record.getId());
|
||||||
newRecord.setFinishedCount(finishedCount);
|
newRecord.setIsFinished(0);
|
||||||
newRecord.setFinishedAt(null);
|
newRecord.setFinishedAt(null);
|
||||||
newRecord.setProgress(finishedCount * 10000 / record.getHourCount());
|
newRecord.setProgress(finishedCount * 10000 / record.getHourCount());
|
||||||
newRecord.setIsFinished(0);
|
newRecord.setFinishedCount(finishedCount);
|
||||||
|
|
||||||
updateById(newRecord);
|
updateById(newRecord);
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,7 @@
|
|||||||
</if>
|
</if>
|
||||||
GROUP BY `user_id`;
|
GROUP BY `user_id`;
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="getUserCourseHourUserFirstCreatedAt"
|
<select id="getUserCourseHourUserFirstCreatedAt"
|
||||||
resultType="xyz.playedu.common.types.mapper.UserCourseHourRecordUserFirstCreatedAtMapper">
|
resultType="xyz.playedu.common.types.mapper.UserCourseHourRecordUserFirstCreatedAtMapper">
|
||||||
SELECT `t1`.`created_at`, `t1`.`user_id`
|
SELECT `t1`.`created_at`, `t1`.`user_id`
|
||||||
@ -146,4 +147,25 @@
|
|||||||
AND `t1`.`user_id` IN (<foreach collection="userIds" item="userId" separator=",">#{userId}</foreach>)
|
AND `t1`.`user_id` IN (<foreach collection="userIds" item="userId" separator=",">#{userId}</foreach>)
|
||||||
</if>
|
</if>
|
||||||
</select>
|
</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>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
@ -66,11 +66,18 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
|||||||
new AdminPermission[] {
|
new AdminPermission[] {
|
||||||
new AdminPermission() {
|
new AdminPermission() {
|
||||||
{
|
{
|
||||||
setSort(30);
|
setSort(0);
|
||||||
setName("列表");
|
setName("列表");
|
||||||
setSlug(BPermissionConstant.RESOURCE_MENU);
|
setSlug(BPermissionConstant.RESOURCE_MENU);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
new AdminPermission() {
|
||||||
|
{
|
||||||
|
setSort(10);
|
||||||
|
setName("资源上传");
|
||||||
|
setSlug(BPermissionConstant.UPLOAD);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
// 学员
|
// 学员
|
||||||
put(
|
put(
|
||||||
@ -227,13 +234,6 @@ public class AdminPermissionCheck implements CommandLineRunner {
|
|||||||
.PASSWORD_CHANGE);
|
.PASSWORD_CHANGE);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new AdminPermission() {
|
|
||||||
{
|
|
||||||
setSort(35);
|
|
||||||
setName("文件上传");
|
|
||||||
setSlug(BPermissionConstant.UPLOAD);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -713,6 +713,27 @@ public class MigrationCheck implements CommandLineRunner {
|
|||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
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;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
5
pom.xml
5
pom.xml
@ -102,6 +102,11 @@
|
|||||||
<artifactId>aws-java-sdk-s3</artifactId>
|
<artifactId>aws-java-sdk-s3</artifactId>
|
||||||
<version>1.12.572</version>
|
<version>1.12.572</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.xml.bind</groupId>
|
||||||
|
<artifactId>jaxb-api</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user