部门管理

This commit is contained in:
none 2023-02-19 12:15:57 +08:00
parent 166dc4c1a2
commit 9c97b774a0
15 changed files with 514 additions and 6 deletions

View File

@ -0,0 +1,22 @@
package xyz.playedu.api.bus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import xyz.playedu.api.config.PlayEduConfig;
import xyz.playedu.api.constant.SystemConstant;
/**
* @Author 杭州白书科技有限公司
* @create 2023/2/19 12:06
*/
@Component
public class AppBus {
@Autowired
private PlayEduConfig playEduConfig;
public boolean isDev() {
return !playEduConfig.getEnv().equals(SystemConstant.ENV_PROD);
}
}

View File

@ -0,0 +1,32 @@
package xyz.playedu.api.bus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import xyz.playedu.api.domain.Department;
import xyz.playedu.api.exception.NotFoundException;
import xyz.playedu.api.service.DepartmentService;
/**
* @Author 杭州白书科技有限公司
* @create 2023/2/19 11:02
*/
@Component
public class DepartmentBus {
@Autowired
private DepartmentService departmentService;
public String compParentChain(Integer parentId) throws NotFoundException {
String parentChain = "";
if (parentId != 0) {
Department parentDepartment = departmentService.getById(parentId);
if (parentDepartment == null) {
throw new NotFoundException("父级部门不存在");
}
parentChain = parentDepartment.getParentChain() + "," + parentId;
}
return parentChain;
}
}

View File

@ -1,5 +1,14 @@
package xyz.playedu.api.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@Data
public class PlayEduConfig {
public static String REDIS_PREFIX = "playedu:";
@Value("${spring.profiles.active}")
private String env;
}

View File

@ -2,6 +2,10 @@ package xyz.playedu.api.constant;
public class SystemConstant {
public final static String ENV_PROD = "prod";
public static String REDIS_PREFIX = "playedu:";
public final static String JWT_PRV_ADMIN_USER = "dc14511e97e7eb725fb2976bc939b375";
}

View File

@ -6,6 +6,7 @@ import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import xyz.playedu.api.exception.NotFoundException;
import xyz.playedu.api.exception.ServiceException;
import xyz.playedu.api.types.JsonResponse;
@ -14,10 +15,10 @@ import java.util.List;
@RestControllerAdvice
public class ExceptionController {
// @ExceptionHandler(Exception.class)
// public JsonResponse<String> exceptionHandler(Exception e) {
// return JsonResponse.error("系统错误", 500);
// }
@ExceptionHandler(Exception.class)
public JsonResponse exceptionHandler(Exception e) {
return JsonResponse.error("系统错误", 500);
}
@ExceptionHandler(ServiceException.class)
public JsonResponse serviceExceptionHandler(ServiceException e) {
@ -45,4 +46,9 @@ public class ExceptionController {
return JsonResponse.error("请求method错误", 400);
}
@ExceptionHandler(NotFoundException.class)
public JsonResponse serviceExceptionHandler(NotFoundException e) {
return JsonResponse.error(e.getMessage(), 404);
}
}

View File

@ -0,0 +1,84 @@
package xyz.playedu.api.controller.backend;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import xyz.playedu.api.bus.DepartmentBus;
import xyz.playedu.api.domain.Department;
import xyz.playedu.api.exception.NotFoundException;
import xyz.playedu.api.request.backend.DepartmentRequest;
import xyz.playedu.api.service.DepartmentService;
import xyz.playedu.api.types.JsonResponse;
import java.util.Date;
import java.util.List;
/**
* @Author 杭州白书科技有限公司
* @create 2023/2/19 10:33
*/
@RestController
@Slf4j
@RequestMapping("/backend/v1/department")
public class DepartmentController {
@Autowired
private DepartmentService departmentService;
@Autowired
private DepartmentBus departmentBus;
@GetMapping("/index")
public JsonResponse index() {
List<Department> data = departmentService.list();
return JsonResponse.data(data);
}
@GetMapping("/create")
public JsonResponse create(@RequestParam(name = "parent_id", defaultValue = "0") Integer parentId) {
List<Department> data = departmentService.listByParentId(parentId);
return JsonResponse.data(data);
}
@PostMapping("/create")
public JsonResponse store(@RequestBody DepartmentRequest request) throws NotFoundException {
String parentChain = "";
if (request.getParentId() != 0) {
parentChain = departmentBus.compParentChain(request.getParentId());
}
Department department = new Department();
department.setName(request.getName());
department.setParentId(request.getParentId());
department.setParentChain(parentChain);
department.setSort(request.getSort());
department.setCreatedAt(new Date());
department.setUpdatedAt(new Date());
departmentService.save(department);
return JsonResponse.success();
}
@GetMapping("/{id}")
public JsonResponse edit(@PathVariable Integer id) throws NotFoundException {
Department department = departmentService.findOrFail(id);
return JsonResponse.data(department);
}
@PutMapping("/{id}")
public JsonResponse update(@PathVariable Integer id, @RequestBody DepartmentRequest request) throws NotFoundException {
Department department = departmentService.findOrFail(id);
departmentService.update(department, request.getName(), request.getParentId(), request.getSort());
return JsonResponse.success();
}
@DeleteMapping("/{id}")
public JsonResponse destroy(@PathVariable Integer id) throws NotFoundException {
Department department = departmentService.findOrFail(id);
departmentService.deleteById(department.getId());
return JsonResponse.success();
}
}

View File

@ -0,0 +1,117 @@
package xyz.playedu.api.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 java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
*
* @TableName departments
*/
@TableName(value ="departments")
@Data
public class Department implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 部门名
*/
private String name;
/**
* 父id
*/
private Integer parentId;
/**
* 父链
*/
private String parentChain;
/**
* 升序
*/
private Integer sort;
/**
*
*/
private Date createdAt;
/**
*
*/
private Date updatedAt;
/**
*
*/
private Date deletedAt;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
Department other = (Department) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName()))
&& (this.getParentId() == null ? other.getParentId() == null : this.getParentId().equals(other.getParentId()))
&& (this.getParentChain() == null ? other.getParentChain() == null : this.getParentChain().equals(other.getParentChain()))
&& (this.getSort() == null ? other.getSort() == null : this.getSort().equals(other.getSort()))
&& (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()))
&& (this.getUpdatedAt() == null ? other.getUpdatedAt() == null : this.getUpdatedAt().equals(other.getUpdatedAt()))
&& (this.getDeletedAt() == null ? other.getDeletedAt() == null : this.getDeletedAt().equals(other.getDeletedAt()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
result = prime * result + ((getParentId() == null) ? 0 : getParentId().hashCode());
result = prime * result + ((getParentChain() == null) ? 0 : getParentChain().hashCode());
result = prime * result + ((getSort() == null) ? 0 : getSort().hashCode());
result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode());
result = prime * result + ((getDeletedAt() == null) ? 0 : getDeletedAt().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(", name=").append(name);
sb.append(", parentId=").append(parentId);
sb.append(", parentChain=").append(parentChain);
sb.append(", sort=").append(sort);
sb.append(", createdAt=").append(createdAt);
sb.append(", updatedAt=").append(updatedAt);
sb.append(", deletedAt=").append(deletedAt);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,27 @@
package xyz.playedu.api.exception;
/**
* @Author 杭州白书科技有限公司
* @create 2023/2/19 11:04
*/
public class NotFoundException extends Exception {
public NotFoundException() {
super();
}
public NotFoundException(String message) {
super(message);
}
public NotFoundException(String message, Throwable cause) {
super(message, cause);
}
public NotFoundException(Throwable cause) {
super(cause);
}
protected NotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -0,0 +1,20 @@
package xyz.playedu.api.mapper;
import org.apache.ibatis.annotations.Mapper;
import xyz.playedu.api.domain.Department;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author tengteng
* @description 针对表departments的数据库操作Mapper
* @createDate 2023-02-19 10:39:57
* @Entity xyz.playedu.api.domain.Department
*/
@Mapper
public interface DepartmentMapper extends BaseMapper<Department> {
}

View File

@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import xyz.playedu.api.PlayEduThreadLocal;
import xyz.playedu.api.bus.AppBus;
import xyz.playedu.api.bus.BackendBus;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.domain.AdminUser;
@ -29,6 +30,9 @@ public class AdminAuthMiddleware implements HandlerInterceptor {
@Autowired
private AdminUserService adminUserService;
@Autowired
private AppBus appBus;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (BackendBus.inUnAuthWhitelist(request.getRequestURI())) {
@ -56,6 +60,9 @@ public class AdminAuthMiddleware implements HandlerInterceptor {
return HandlerInterceptor.super.preHandle(request, response, handler);
} catch (Exception e) {
if (appBus.isDev()) {
log.debug("jwt解析失败:" + e.getMessage());
}
responseTransform(response, 401, "请重新登录");
return false;
}

View File

@ -0,0 +1,33 @@
package xyz.playedu.api.request.backend;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import java.io.Serial;
import java.io.Serializable;
/**
* @Author 杭州白书科技有限公司
* @create 2023/2/19 10:42
*/
@Validated
@Data
public class DepartmentRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@NotNull(message = "请输入部门名称")
@Length(min = 1, max = 20, message = "部门名称长度在1-20个字符之间")
private String name;
@NotNull(message = "请选择部门上级")
private Integer parentId;
@NotNull(message = "请输入排序值")
@Min(value = 0, message = "排序值不能小于0")
private Integer sort;
}

View File

@ -0,0 +1,24 @@
package xyz.playedu.api.service;
import xyz.playedu.api.domain.Department;
import com.baomidou.mybatisplus.extension.service.IService;
import xyz.playedu.api.exception.NotFoundException;
import java.util.List;
/**
* @author tengteng
* @description 针对表departments的数据库操作Service
* @createDate 2023-02-19 10:39:57
*/
public interface DepartmentService extends IService<Department> {
List<Department> listByParentId(Integer id);
Department findOrFail(Integer id) throws NotFoundException;
void deleteById(Integer id) throws NotFoundException;
void update(Department department, String name, Integer parentId, Integer sort) throws NotFoundException;
}

View File

@ -0,0 +1,99 @@
package xyz.playedu.api.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import xyz.playedu.api.domain.Department;
import xyz.playedu.api.exception.NotFoundException;
import xyz.playedu.api.service.DepartmentService;
import xyz.playedu.api.mapper.DepartmentMapper;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author tengteng
* @description 针对表departments的数据库操作Service实现
* @createDate 2023-02-19 10:39:57
*/
@Service
@Slf4j
public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Department> implements DepartmentService {
@Override
public List<Department> listByParentId(Integer id) {
return this.getBaseMapper().selectList(query().eq("parent_id", id));
}
@Override
public Department findOrFail(Integer id) throws NotFoundException {
Department department = this.getBaseMapper().selectById(id);
if (department == null) {
throw new NotFoundException("部门不存在");
}
return department;
}
@Override
@Transactional
public void deleteById(Integer id) throws NotFoundException {
Department department = findOrFail(id);
updateParentChain(department);
removeById(department.getId());
}
@Override
@Transactional
public void update(Department department, String name, Integer parentId, Integer sort) throws NotFoundException {
Department data = new Department();
if (!department.getName().equals(name)) {
data.setName(name);
}
if (!department.getParentId().equals(parentId)) {
data.setParentId(parentId);
Department parent = findOrFail(parentId);
data.setParentChain(parent.getParentChain() + "," + parent.getId());
}
if (!department.getSort().equals(sort)) {
data.setSort(sort);
}
save(data);
department = getById(department.getId());
updateParentChain(department);
}
private void updateParentChain(Department department) {
if (department.getParentId().equals(0)) {
return;
}
//需要重置chain的子部门
List<Department> children = list(query().like("parent_chain", "%" + department.getParentChain()));
if (children.size() == 0) {
return;
}
// 计算新的parentChain前缀
String[] chainIds = department.getParentChain().split(",");
String newChainPrefix = String.join(",", Arrays.copyOfRange(chainIds, 0, chainIds.length - 1));
log.info("新的前缀:" + newChainPrefix);
ArrayList<Department> updateRows = new ArrayList<>();
for (Department tmpDepartment : children) {
Department tmpUpdateDepartment = new Department();
tmpUpdateDepartment.setId(tmpDepartment.getId());
tmpUpdateDepartment.setParentChain(tmpDepartment.getParentChain().replaceFirst(department.getParentChain(), newChainPrefix));
updateRows.add(tmpUpdateDepartment);
}
updateBatchById(updateRows);
}
}

View File

@ -9,6 +9,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import xyz.playedu.api.config.PlayEduConfig;
import xyz.playedu.api.constant.SystemConstant;
import java.util.*;
import java.util.concurrent.TimeUnit;
@ -17,7 +18,7 @@ import java.util.concurrent.TimeUnit;
public class RedisUtil {
private static RedisTemplate<String, Object> redisTemplate;
private static final String redisPrefix = PlayEduConfig.REDIS_PREFIX;
private static final String redisPrefix = SystemConstant.REDIS_PREFIX;
/**
* 注入Redis

View File

@ -0,0 +1,23 @@
<?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.api.mapper.DepartmentMapper">
<resultMap id="BaseResultMap" type="xyz.playedu.api.domain.Department">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="parentId" column="parent_id" jdbcType="INTEGER"/>
<result property="parentChain" column="parent_chain" jdbcType="VARCHAR"/>
<result property="sort" column="sort" jdbcType="INTEGER"/>
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
<result property="deletedAt" column="deleted_at" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,name,parent_id,
parent_chain,sort,created_at,
updated_at,deleted_at
</sql>
</mapper>