Преглед на файлове

add:中台增加权限管理(新增部门功能,排序功能)

lyx преди 1 седмица
родител
ревизия
338fa98309

+ 87 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeSysDept.java

@@ -0,0 +1,87 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 部门表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("life_sys_dept")
+@ApiModel(value = "LifeSysDept对象", description = "部门表")
+public class LifeSysDept {
+
+    @ApiModelProperty(value = "部门ID")
+    @TableId(value = "dept_id", type = IdType.AUTO)
+    private Long deptId;
+
+    @ApiModelProperty(value = "父部门ID(0表示根部门)")
+    @TableField("parent_id")
+    private Long parentId;
+
+    @ApiModelProperty(value = "祖级列表(用于数据权限查询,如:0,100,101)")
+    @TableField("ancestors")
+    private String ancestors;
+
+    @ApiModelProperty(value = "部门名称")
+    @TableField("dept_name")
+    private String deptName;
+
+    @ApiModelProperty(value = "显示顺序")
+    @TableField("dept_sort")
+    private Integer deptSort;
+
+    @ApiModelProperty(value = "部门负责人")
+    @TableField("leader")
+    private Integer leader;
+
+    @ApiModelProperty(value = "联系电话")
+    @TableField("phone")
+    private String phone;
+
+    @ApiModelProperty(value = "邮箱")
+    @TableField("email")
+    private String email;
+
+    @ApiModelProperty(value = "部门状态(0正常 1停用)")
+    @TableField("status")
+    private String status;
+
+    @ApiModelProperty(value = "删除标志(0存在 2删除)")
+    @TableField("del_flag")
+    @TableLogic(value = "0", delval = "2")
+    private String delFlag;
+
+    @ApiModelProperty(value = "创建者")
+    @TableField("create_by")
+    private String createBy;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新者")
+    @TableField("update_by")
+    private String updateBy;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "备注")
+    @TableField("description")
+    private String description;
+}
+

+ 45 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LifeSysDeptAddDto.java

@@ -0,0 +1,45 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * 部门新增DTO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value = "LifeSysDeptAddDto对象", description = "部门新增信息")
+public class LifeSysDeptAddDto {
+
+    @ApiModelProperty(value = "父部门ID(0表示根部门)")
+    private Long parentId;
+
+    @ApiModelProperty(value = "部门名称", required = true)
+    private String deptName;
+
+    @ApiModelProperty(value = "显示顺序")
+    private Integer deptSort;
+
+    @ApiModelProperty(value = "部门负责人")
+    private Integer leader;
+
+    @ApiModelProperty(value = "联系电话")
+    private String phone;
+
+    @ApiModelProperty(value = "邮箱")
+    private String email;
+
+    @ApiModelProperty(value = "部门状态(0正常 1停用),默认为0")
+    private String status = "0";
+
+    @ApiModelProperty(value = "部门描述")
+    private String description;
+}
+

+ 27 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LifeSysDeptSortDto.java

@@ -0,0 +1,27 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * 一级部门排序DTO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value = "LifeSysDeptSortDto对象", description = "一级部门排序信息")
+public class LifeSysDeptSortDto {
+
+    @ApiModelProperty(value = "部门ID", required = true)
+    private Long deptId;
+
+    @ApiModelProperty(value = "目标排序位置(新排序值)", required = true)
+    private Integer newSort;
+}
+

+ 16 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeSysDeptMapper.java

@@ -0,0 +1,16 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.LifeSysDept;
+
+/**
+ * 部门表 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface LifeSysDeptMapper extends BaseMapper<LifeSysDept> {
+}
+

+ 62 - 0
alien-store/src/main/java/shop/alien/store/controller/LifeSysDeptController.java

@@ -0,0 +1,62 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeSysDept;
+import shop.alien.entity.store.dto.LifeSysDeptAddDto;
+import shop.alien.entity.store.dto.LifeSysDeptSortDto;
+import shop.alien.store.service.LifeSysDeptService;
+
+/**
+ * 部门表 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"数据中台-部门管理"})
+@ApiSort(2)
+@CrossOrigin
+@RestController
+@RequestMapping("/sys/dept")
+@RequiredArgsConstructor
+public class LifeSysDeptController {
+
+    private final LifeSysDeptService lifeSysDeptService;
+
+    @ApiOperation("新增部门")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "parentId", value = "父部门ID(0表示根部门)", dataType = "Long", paramType = "body"),
+            @ApiImplicitParam(name = "deptName", value = "部门名称", dataType = "String", paramType = "body", required = true),
+            @ApiImplicitParam(name = "deptSort", value = "显示顺序", dataType = "Integer", paramType = "body"),
+            @ApiImplicitParam(name = "leader", value = "部门负责人", dataType = "Integer", paramType = "body"),
+            @ApiImplicitParam(name = "phone", value = "联系电话", dataType = "String", paramType = "body"),
+            @ApiImplicitParam(name = "email", value = "邮箱", dataType = "String", paramType = "body"),
+            @ApiImplicitParam(name = "status", value = "部门状态(0正常 1停用)", dataType = "String", paramType = "body"),
+            @ApiImplicitParam(name = "description", value = "部门描述", dataType = "String", paramType = "body"),
+    })
+    @PostMapping(value = "/addDept")
+    public R<LifeSysDept> addDept(@RequestBody LifeSysDeptAddDto addDto) {
+        log.info("LifeSysDeptController.addDept => deptName={}, parentId={}, deptSort={}, leader={}, phone={}, email={}, status={}, description={}",
+                addDto.getDeptName(), addDto.getParentId(), addDto.getDeptSort(),
+                addDto.getLeader(), addDto.getPhone(), addDto.getEmail(), addDto.getStatus(), addDto.getDescription());
+        return lifeSysDeptService.addDept(addDto);
+    }
+
+    @ApiOperation("更新一级部门排序")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "deptId", value = "部门ID", dataType = "Long", paramType = "body", required = true),
+            @ApiImplicitParam(name = "newSort", value = "目标排序位置(新排序值)", dataType = "Integer", paramType = "body", required = true),
+    })
+    @PostMapping(value = "/updateFirstLevelDeptSort")
+    public R<Boolean> updateFirstLevelDeptSort(@RequestBody LifeSysDeptSortDto sortDto) {
+        log.info("LifeSysDeptController.updateFirstLevelDeptSort => deptId={}, newSort={}", sortDto.getDeptId(), sortDto.getNewSort());
+        return lifeSysDeptService.updateFirstLevelDeptSort(sortDto);
+    }
+}
+

+ 32 - 0
alien-store/src/main/java/shop/alien/store/service/LifeSysDeptService.java

@@ -0,0 +1,32 @@
+package shop.alien.store.service;
+
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeSysDept;
+import shop.alien.entity.store.dto.LifeSysDeptAddDto;
+import shop.alien.entity.store.dto.LifeSysDeptSortDto;
+
+/**
+ * 部门表 服务接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LifeSysDeptService {
+
+    /**
+     * 新增部门
+     *
+     * @param addDto 新增部门信息
+     * @return R<LifeSysDept>
+     */
+    R<LifeSysDept> addDept(LifeSysDeptAddDto addDto);
+
+    /**
+     * 更新一级部门排序
+     *
+     * @param sortDto 排序信息(部门ID列表,按排序顺序)
+     * @return R<Boolean>
+     */
+    R<Boolean> updateFirstLevelDeptSort(LifeSysDeptSortDto sortDto);
+}
+

+ 218 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeSysDeptServiceImpl.java

@@ -0,0 +1,218 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeSysDept;
+import shop.alien.entity.store.dto.LifeSysDeptAddDto;
+import shop.alien.entity.store.dto.LifeSysDeptSortDto;
+import shop.alien.mapper.LifeSysDeptMapper;
+import shop.alien.store.service.LifeSysDeptService;
+
+import java.util.List;
+
+/**
+ * 部门表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class LifeSysDeptServiceImpl implements LifeSysDeptService {
+
+    private final LifeSysDeptMapper lifeSysDeptMapper;
+
+    @Override
+    public R<LifeSysDept> addDept(LifeSysDeptAddDto addDto) {
+        log.info("LifeSysDeptServiceImpl.addDept?addDto={}", addDto);
+
+        // 参数校验
+        if (addDto == null) {
+            log.warn("新增部门失败:参数为空");
+            return R.fail("参数不能为空");
+        }
+
+        // 校验必填字段:部门名称
+        if (!StringUtils.hasText(addDto.getDeptName())) {
+            return R.fail("部门名称不能为空");
+        }
+
+        // 校验部门名称是否已存在(同一父部门下不能有重名)
+        QueryWrapper<LifeSysDept> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("dept_name", addDto.getDeptName());
+        queryWrapper.eq("del_flag", "0"); // 未删除的部门
+        if (addDto.getParentId() != null && addDto.getParentId() != 0) {
+            queryWrapper.eq("parent_id", addDto.getParentId());
+        } else {
+            queryWrapper.eq("parent_id", 0);
+        }
+        LifeSysDept existingDept = lifeSysDeptMapper.selectOne(queryWrapper);
+        if (existingDept != null) {
+            return R.fail("该部门名称已存在,请更换其他名称");
+        }
+
+        // 如果指定了父部门,校验父部门是否存在
+        if (addDto.getParentId() != null && addDto.getParentId() != 0) {
+            LifeSysDept parentDept = lifeSysDeptMapper.selectById(addDto.getParentId());
+            if (parentDept == null || !"0".equals(parentDept.getDelFlag())) {
+                return R.fail("父部门不存在或已被删除");
+            }
+        }
+
+        // 确定父部门ID
+        Long parentId = addDto.getParentId() != null ? addDto.getParentId() : 0L;
+        
+        // 查询同级别部门的最大排序值,实现排序累加
+        QueryWrapper<LifeSysDept> sortQueryWrapper = new QueryWrapper<>();
+        sortQueryWrapper.eq("parent_id", parentId);
+        sortQueryWrapper.eq("del_flag", "0"); // 只统计未删除的部门
+        sortQueryWrapper.orderByDesc("dept_sort");
+        sortQueryWrapper.last("LIMIT 1");
+        LifeSysDept maxSortDept = lifeSysDeptMapper.selectOne(sortQueryWrapper);
+        
+        // 计算新的排序值:如果存在同级别部门,则取最大排序值+1,否则从1开始
+        Integer newDeptSort = 1;
+        if (maxSortDept != null && maxSortDept.getDeptSort() != null) {
+            newDeptSort = maxSortDept.getDeptSort() + 1;
+        }
+
+        // 创建新部门
+        LifeSysDept lifeSysDept = new LifeSysDept();
+        lifeSysDept.setDeptName(addDto.getDeptName());
+        lifeSysDept.setParentId(parentId);
+        lifeSysDept.setDeptSort(newDeptSort); // 使用自动累加的排序值
+        lifeSysDept.setLeader(addDto.getLeader());
+        lifeSysDept.setPhone(addDto.getPhone());
+        lifeSysDept.setEmail(addDto.getEmail());
+        lifeSysDept.setStatus(StringUtils.hasText(addDto.getStatus()) ? addDto.getStatus() : "0");
+        lifeSysDept.setDelFlag("0"); // 未删除
+        lifeSysDept.setDescription(addDto.getDescription());
+
+        // 设置祖级列表
+        if (lifeSysDept.getParentId() != null && lifeSysDept.getParentId() != 0) {
+            LifeSysDept parentDept = lifeSysDeptMapper.selectById(lifeSysDept.getParentId());
+            if (parentDept != null && StringUtils.hasText(parentDept.getAncestors())) {
+                lifeSysDept.setAncestors(parentDept.getAncestors() + "," + parentDept.getDeptId());
+            } else if (parentDept != null) {
+                lifeSysDept.setAncestors(String.valueOf(parentDept.getDeptId()));
+            } else {
+                lifeSysDept.setAncestors("0");
+            }
+        } else {
+            lifeSysDept.setAncestors("0");
+        }
+
+        // 保存部门
+        int result = lifeSysDeptMapper.insert(lifeSysDept);
+        if (result > 0) {
+            log.info("新增部门成功,部门ID={}", lifeSysDept.getDeptId());
+            return R.data(lifeSysDept, "新增部门成功");
+        }
+        return R.fail("新增部门失败,请稍后重试");
+    }
+
+    @Override
+    public R<Boolean> updateFirstLevelDeptSort(LifeSysDeptSortDto sortDto) {
+        log.info("LifeSysDeptServiceImpl.updateFirstLevelDeptSort?sortDto={}", sortDto);
+
+        // 参数校验
+        if (sortDto == null || sortDto.getDeptId() == null) {
+            log.warn("更新一级部门排序失败:参数为空或部门ID为空");
+            return R.fail("参数不能为空,且部门ID不能为空");
+        }
+
+        if (sortDto.getNewSort() == null || sortDto.getNewSort() < 1) {
+            log.warn("更新一级部门排序失败:新排序值无效");
+            return R.fail("新排序值不能为空且必须大于1");
+        }
+
+        Long deptId = sortDto.getDeptId();
+        Integer newSort = sortDto.getNewSort();
+
+        // 查询当前部门信息
+        LifeSysDept currentDept = lifeSysDeptMapper.selectById(deptId);
+        if (currentDept == null) {
+            return R.fail("部门不存在");
+        }
+
+        // 校验是否是一级部门且未删除
+        if (currentDept.getParentId() == null || currentDept.getParentId() != 0) {
+            return R.fail("该部门不是一级部门");
+        }
+        if (!"0".equals(currentDept.getDelFlag())) {
+            return R.fail("该部门已被删除");
+        }
+
+        // 获取旧排序值
+        Integer oldSort = currentDept.getDeptSort();
+        if (oldSort == null) {
+            oldSort = 0; // 如果旧排序值为空,默认为0
+        }
+
+        // 如果新旧排序值相同,无需更新
+        if (oldSort.equals(newSort)) {
+            log.info("部门排序值未变化,部门ID={}, 排序值={}", deptId, oldSort);
+            return R.success("排序值未变化");
+        }
+
+        try {
+            // 查询所有一级部门(排除当前部门),按排序值排序
+            QueryWrapper<LifeSysDept> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("parent_id", 0);
+            queryWrapper.eq("del_flag", "0");
+            queryWrapper.ne("dept_id", deptId); // 排除当前部门
+            queryWrapper.orderByAsc("dept_sort");
+            List<LifeSysDept> otherDepts = lifeSysDeptMapper.selectList(queryWrapper);
+
+            // 根据新旧位置差异,调整其他部门的排序 TODO 有优化空间,批量更新
+            if (newSort < oldSort) {
+                // 向上移动:旧位置到新位置之间的部门排序值 +1
+                for (LifeSysDept dept : otherDepts) {
+                    if (dept.getDeptSort() != null && dept.getDeptSort() >= newSort && dept.getDeptSort() < oldSort) {
+                        LambdaUpdateWrapper<LifeSysDept> updateWrapper = new LambdaUpdateWrapper<>();
+                        updateWrapper.eq(LifeSysDept::getDeptId, dept.getDeptId())
+                                .set(LifeSysDept::getDeptSort, dept.getDeptSort() + 1);
+                        lifeSysDeptMapper.update(null, updateWrapper);
+                        log.debug("向上移动:部门ID={}的排序值从{}调整为{}", dept.getDeptId(), dept.getDeptSort(), dept.getDeptSort() + 1);
+                    }
+                }
+            } else {
+                // 向下移动:旧位置到新位置之间的部门排序值 -1
+                for (LifeSysDept dept : otherDepts) {
+                    if (dept.getDeptSort() != null && dept.getDeptSort() > oldSort && dept.getDeptSort() <= newSort) {
+                        LambdaUpdateWrapper<LifeSysDept> updateWrapper = new LambdaUpdateWrapper<>();
+                        updateWrapper.eq(LifeSysDept::getDeptId, dept.getDeptId())
+                                .set(LifeSysDept::getDeptSort, dept.getDeptSort() - 1);
+                        lifeSysDeptMapper.update(null, updateWrapper);
+                        log.debug("向下移动:部门ID={}的排序值从{}调整为{}", dept.getDeptId(), dept.getDeptSort(), dept.getDeptSort() - 1);
+                    }
+                }
+            }
+
+            // 更新当前部门的排序值
+            LambdaUpdateWrapper<LifeSysDept> currentUpdateWrapper = new LambdaUpdateWrapper<>();
+            currentUpdateWrapper.eq(LifeSysDept::getDeptId, deptId)
+                    .set(LifeSysDept::getDeptSort, newSort);
+            int result = lifeSysDeptMapper.update(null, currentUpdateWrapper);
+
+            if (result > 0) {
+                log.info("更新一级部门排序成功,部门ID={}, 从位置{}移动到位置{}", deptId, oldSort, newSort);
+                return R.success("更新排序成功");
+            } else {
+                return R.fail("更新排序失败");
+            }
+        } catch (Exception e) {
+            log.error("更新一级部门排序失败", e);
+            return R.fail("更新排序失败:" + e.getMessage());
+        }
+    }
+}
+