瀏覽代碼

1、增加职业技能相关管理

rayson 1 年之前
父節點
當前提交
417e256014
共有 17 個文件被更改,包括 724 次插入15 次删除
  1. 8 0
      menduner/menduner-system-api/src/main/java/com/citu/module/menduner/system/enums/ErrorCodeConstants.java
  2. 2 2
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/person/vo/skill/PersonSkillPageReqVO.java
  3. 3 3
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/person/vo/skill/PersonSkillRespVO.java
  4. 2 2
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/person/vo/skill/PersonSkillSaveReqVO.java
  5. 92 6
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/projectexp/ProjectExpController.java
  6. 90 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/skill/SkillController.java
  7. 31 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/skill/vo/SkillListReqVO.java
  8. 40 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/skill/vo/SkillRespVO.java
  9. 25 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/skill/vo/SkillSaveReqVO.java
  10. 2 2
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/person/PersonSkillDO.java
  11. 48 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/skill/SkillDO.java
  12. 38 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/mysql/skill/SkillMapper.java
  13. 55 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/skill/SkillService.java
  14. 134 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/skill/SkillServiceImpl.java
  15. 138 0
      menduner/menduner-system-biz/src/test/java/com/citu/module/menduner/system/service/skill/SkillServiceImplTest.java
  16. 1 0
      menduner/menduner-system-biz/src/test/resources/sql/clean.sql
  17. 15 0
      menduner/menduner-system-biz/src/test/resources/sql/create_tables.sql

+ 8 - 0
menduner/menduner-system-api/src/main/java/com/citu/module/menduner/system/enums/ErrorCodeConstants.java

@@ -128,4 +128,12 @@ public interface ErrorCodeConstants {
 
     // ========== 门墩儿-项目经历 1_200_016_000 ==========
     ErrorCode PROJECT_EXP_NOT_EXISTS = new ErrorCode(1_200_016_001, "项目经历不存在");
+
+    // ========== 门墩儿-职业技能 1_200_017_000 ==========
+    ErrorCode SKILL_NOT_EXISTS = new ErrorCode(1_200_017_001, "职业技能不存在");
+    ErrorCode SKILL_EXITS_CHILDREN = new ErrorCode(1_200_017_002, "存在存在子职业技能,无法删除");
+    ErrorCode SKILL_PARENT_NOT_EXITS = new ErrorCode(1_200_017_003,"父级职业技能不存在");
+    ErrorCode SKILL_PARENT_ERROR = new ErrorCode(1_200_017_004, "不能设置自己为父职业技能");
+    ErrorCode SKILL_NAME_CN_DUPLICATE = new ErrorCode(1_200_017_005, "已经存在该技能中文名称的职业技能");
+    ErrorCode SKILL_PARENT_IS_CHILD = new ErrorCode(1_200_017_006, "不能设置自己的子Skill为父Skill");
 }

+ 2 - 2
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/person/vo/skill/PersonSkillPageReqVO.java

@@ -17,8 +17,8 @@ public class PersonSkillPageReqVO extends PageParam {
     @Schema(description = "用户id", example = "7946")
     private Long userId;
 
-    @Schema(description = "技能名称", example = "芋艿")
-    private String name;
+    @Schema(description = "技能id", example = "1")
+    private String skillId;
 
     @Schema(description = "技能等级(0一般 1良好 2熟练 3 精通)")
     private Integer level;

+ 3 - 3
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/person/vo/skill/PersonSkillRespVO.java

@@ -23,9 +23,9 @@ public class PersonSkillRespVO {
     @ExcelProperty("用户id")
     private Long userId;
 
-    @Schema(description = "技能名称", example = "芋艿")
-    @ExcelProperty("技能名称")
-    private String name;
+    @Schema(description = "技能id", example = "1")
+    @ExcelProperty("技能id")
+    private String skillId;
 
     @Schema(description = "技能等级(0一般 1良好 2熟练 3 精通)")
     @ExcelProperty(value = "技能等级(0一般 1良好 2熟练 3 精通)", converter = DictConvert.class)

+ 2 - 2
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/person/vo/skill/PersonSkillSaveReqVO.java

@@ -16,8 +16,8 @@ public class PersonSkillSaveReqVO {
     @NotNull(message = "用户id不能为空")
     private Long userId;
 
-    @Schema(description = "技能名称", example = "芋艿")
-    private String name;
+    @Schema(description = "技能id", example = "1")
+    private String skillId;
 
     @Schema(description = "技能等级(0一般 1良好 2熟练 3 精通)")
     private Integer level;

+ 92 - 6
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/projectexp/ProjectExpController.java

@@ -1,9 +1,95 @@
 package com.citu.module.menduner.system.controller.admin.projectexp;
 
-/**
- * @author rayson
- * @description ProjectExpController
- * @create 2024/5/21 下午2:49
- **/
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import com.citu.framework.common.pojo.PageParam;
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.framework.common.util.object.BeanUtils;
+import static com.citu.framework.common.pojo.CommonResult.success;
+
+import com.citu.framework.excel.core.util.ExcelUtils;
+
+import com.citu.framework.apilog.core.annotation.ApiAccessLog;
+import static com.citu.framework.apilog.core.enums.OperateTypeEnum.*;
+
+import com.citu.module.menduner.system.controller.admin.projectexp.vo.*;
+import com.citu.module.menduner.system.dal.dataobject.projectexp.ProjectExpDO;
+import com.citu.module.menduner.system.service.projectexp.ProjectExpService;
+
+@Tag(name = "管理后台 - 门墩儿-项目经历")
+@RestController
+@RequestMapping("/menduner/system/project-exp")
+@Validated
 public class ProjectExpController {
-}
+
+    @Resource
+    private ProjectExpService projectExpService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建门墩儿-项目经历")
+    @PreAuthorize("@ss.hasPermission('menduner:system:project-exp:create')")
+    public CommonResult<Long> createProjectExp(@Valid @RequestBody ProjectExpSaveReqVO createReqVO) {
+        return success(projectExpService.createProjectExp(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新门墩儿-项目经历")
+    @PreAuthorize("@ss.hasPermission('menduner:system:project-exp:update')")
+    public CommonResult<Boolean> updateProjectExp(@Valid @RequestBody ProjectExpSaveReqVO updateReqVO) {
+        projectExpService.updateProjectExp(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除门墩儿-项目经历")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('menduner:system:project-exp:delete')")
+    public CommonResult<Boolean> deleteProjectExp(@RequestParam("id") Long id) {
+        projectExpService.deleteProjectExp(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得门墩儿-项目经历")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('menduner:system:project-exp:query')")
+    public CommonResult<ProjectExpRespVO> getProjectExp(@RequestParam("id") Long id) {
+        ProjectExpDO projectExp = projectExpService.getProjectExp(id);
+        return success(BeanUtils.toBean(projectExp, ProjectExpRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得门墩儿-项目经历分页")
+    @PreAuthorize("@ss.hasPermission('menduner:system:project-exp:query')")
+    public CommonResult<PageResult<ProjectExpRespVO>> getProjectExpPage(@Valid ProjectExpPageReqVO pageReqVO) {
+        PageResult<ProjectExpDO> pageResult = projectExpService.getProjectExpPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, ProjectExpRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出门墩儿-项目经历 Excel")
+    @PreAuthorize("@ss.hasPermission('menduner:system:project-exp:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportProjectExpExcel(@Valid ProjectExpPageReqVO pageReqVO,
+                                      HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<ProjectExpDO> list = projectExpService.getProjectExpPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "门墩儿-项目经历.xls", "数据", ProjectExpRespVO.class,
+                BeanUtils.toBean(list, ProjectExpRespVO.class));
+    }
+
+}

+ 90 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/skill/SkillController.java

@@ -0,0 +1,90 @@
+package com.citu.module.menduner.system.controller.admin.skill;
+
+import com.citu.framework.apilog.core.annotation.ApiAccessLog;
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.framework.common.util.object.BeanUtils;
+import com.citu.framework.excel.core.util.ExcelUtils;
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillListReqVO;
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillRespVO;
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.skill.SkillDO;
+import com.citu.module.menduner.system.service.skill.SkillService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static com.citu.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.citu.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 门墩儿-职业技能")
+@RestController
+@RequestMapping("/menduner/system/skill")
+@Validated
+public class SkillController {
+
+    @Resource
+    private SkillService skillService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建门墩儿-职业技能")
+    @PreAuthorize("@ss.hasPermission('menduner:system:skill:create')")
+    public CommonResult<Long> createSkill(@Valid @RequestBody SkillSaveReqVO createReqVO) {
+        return success(skillService.createSkill(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新门墩儿-职业技能")
+    @PreAuthorize("@ss.hasPermission('menduner:system:skill:update')")
+    public CommonResult<Boolean> updateSkill(@Valid @RequestBody SkillSaveReqVO updateReqVO) {
+        skillService.updateSkill(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除门墩儿-职业技能")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('menduner:system:skill:delete')")
+    public CommonResult<Boolean> deleteSkill(@RequestParam("id") Long id) {
+        skillService.deleteSkill(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得门墩儿-职业技能")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('menduner:system:skill:query')")
+    public CommonResult<SkillRespVO> getSkill(@RequestParam("id") Long id) {
+        SkillDO skill = skillService.getSkill(id);
+        return success(BeanUtils.toBean(skill, SkillRespVO.class));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "获得门墩儿-职业技能列表")
+    @PreAuthorize("@ss.hasPermission('menduner:system:skill:query')")
+    public CommonResult<List<SkillRespVO>> getSkillList(@Valid SkillListReqVO listReqVO) {
+        List<SkillDO> list = skillService.getSkillList(listReqVO);
+        return success(BeanUtils.toBean(list, SkillRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出门墩儿-职业技能 Excel")
+    @PreAuthorize("@ss.hasPermission('menduner:system:skill:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportSkillExcel(@Valid SkillListReqVO listReqVO,
+                                 HttpServletResponse response) throws IOException {
+        List<SkillDO> list = skillService.getSkillList(listReqVO);
+        // 导出 Excel
+        ExcelUtils.write(response, "门墩儿-职业技能.xls", "数据", SkillRespVO.class,
+                BeanUtils.toBean(list, SkillRespVO.class));
+    }
+
+}

+ 31 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/skill/vo/SkillListReqVO.java

@@ -0,0 +1,31 @@
+package com.citu.module.menduner.system.controller.admin.skill.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.citu.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 门墩儿-职业技能列表 Request VO")
+@Data
+public class SkillListReqVO {
+
+    @Schema(description = "技能中文名称")
+    private String nameCn;
+
+    @Schema(description = "技能英文名称")
+    private String nameEn;
+
+    @Schema(description = "上级id", example = "17516")
+    private Long parentId;
+
+    @Schema(description = "层级")
+    private Boolean level;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 40 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/skill/vo/SkillRespVO.java

@@ -0,0 +1,40 @@
+package com.citu.module.menduner.system.controller.admin.skill.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 门墩儿-职业技能 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class SkillRespVO {
+
+    @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "5236")
+    @ExcelProperty("id")
+    private Long id;
+
+    @Schema(description = "技能中文名称")
+    @ExcelProperty("技能中文名称")
+    private String nameCn;
+
+    @Schema(description = "技能英文名称")
+    @ExcelProperty("技能英文名称")
+    private String nameEn;
+
+    @Schema(description = "上级id", example = "17516")
+    @ExcelProperty("上级id")
+    private Long parentId;
+
+    @Schema(description = "层级")
+    @ExcelProperty("层级")
+    private Boolean level;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 25 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/skill/vo/SkillSaveReqVO.java

@@ -0,0 +1,25 @@
+package com.citu.module.menduner.system.controller.admin.skill.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 门墩儿-职业技能新增/修改 Request VO")
+@Data
+public class SkillSaveReqVO {
+
+    @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "5236")
+    private Long id;
+
+    @Schema(description = "技能中文名称")
+    private String nameCn;
+
+    @Schema(description = "技能英文名称")
+    private String nameEn;
+
+    @Schema(description = "上级id", example = "17516")
+    private Long parentId;
+
+    @Schema(description = "层级")
+    private Boolean level;
+
+}

+ 2 - 2
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/person/PersonSkillDO.java

@@ -30,9 +30,9 @@ public class PersonSkillDO extends BaseDO {
      */
     private Long userId;
     /**
-     * 技能名称
+     * 技能id
      */
-    private String name;
+    private String skillId;
     /**
      * 技能等级(0一般 1良好 2熟练 3 精通)
      *

+ 48 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/skill/SkillDO.java

@@ -0,0 +1,48 @@
+package com.citu.module.menduner.system.dal.dataobject.skill;
+
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.citu.framework.mybatis.core.dataobject.BaseDO;
+import lombok.*;
+
+/**
+ * 门墩儿-职业技能 DO
+ *
+ * @author Rayson
+ */
+@TableName("mde_skill")
+@KeySequence("mde_skill_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SkillDO extends BaseDO {
+
+    public static final Long PARENT_ID_ROOT = 0L;
+
+    /**
+     * id
+     */
+    @TableId
+    private Long id;
+    /**
+     * 技能中文名称
+     */
+    private String nameCn;
+    /**
+     * 技能英文名称
+     */
+    private String nameEn;
+    /**
+     * 上级id
+     */
+    private Long parentId;
+    /**
+     * 层级
+     */
+    private Boolean level;
+
+}

+ 38 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/mysql/skill/SkillMapper.java

@@ -0,0 +1,38 @@
+package com.citu.module.menduner.system.dal.mysql.skill;
+
+import java.util.*;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.citu.framework.mybatis.core.mapper.BaseMapperX;
+import com.citu.module.menduner.system.dal.dataobject.skill.SkillDO;
+import org.apache.ibatis.annotations.Mapper;
+import com.citu.module.menduner.system.controller.admin.skill.vo.*;
+
+/**
+ * 门墩儿-职业技能 Mapper
+ *
+ * @author Rayson
+ */
+@Mapper
+public interface SkillMapper extends BaseMapperX<SkillDO> {
+
+    default List<SkillDO> selectList(SkillListReqVO reqVO) {
+        return selectList(new LambdaQueryWrapperX<SkillDO>()
+                .likeIfPresent(SkillDO::getNameCn, reqVO.getNameCn())
+                .likeIfPresent(SkillDO::getNameEn, reqVO.getNameEn())
+                .eqIfPresent(SkillDO::getParentId, reqVO.getParentId())
+                .eqIfPresent(SkillDO::getLevel, reqVO.getLevel())
+                .betweenIfPresent(SkillDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(SkillDO::getId));
+    }
+
+    default SkillDO selectByParentIdAndNameCn(Long parentId, String nameCn) {
+        return selectOne(SkillDO::getParentId, parentId, SkillDO::getNameCn, nameCn);
+    }
+
+    default Long selectCountByParentId(Long parentId) {
+        return selectCount(SkillDO::getParentId, parentId);
+    }
+
+}

+ 55 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/skill/SkillService.java

@@ -0,0 +1,55 @@
+package com.citu.module.menduner.system.service.skill;
+
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillListReqVO;
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.skill.SkillDO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 门墩儿-职业技能 Service 接口
+ *
+ * @author Rayson
+ */
+public interface SkillService {
+
+    /**
+     * 创建门墩儿-职业技能
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createSkill(@Valid SkillSaveReqVO createReqVO);
+
+    /**
+     * 更新门墩儿-职业技能
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateSkill(@Valid SkillSaveReqVO updateReqVO);
+
+    /**
+     * 删除门墩儿-职业技能
+     *
+     * @param id 编号
+     */
+    void deleteSkill(Long id);
+
+    /**
+     * 获得门墩儿-职业技能
+     *
+     * @param id 编号
+     * @return 门墩儿-职业技能
+     */
+    SkillDO getSkill(Long id);
+
+    /**
+     * 获得门墩儿-职业技能列表
+     *
+     * @param listReqVO 查询条件
+     * @return 门墩儿-职业技能列表
+     */
+    List<SkillDO> getSkillList(SkillListReqVO listReqVO);
+
+}

+ 134 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/skill/SkillServiceImpl.java

@@ -0,0 +1,134 @@
+package com.citu.module.menduner.system.service.skill;
+
+import com.citu.framework.common.util.object.BeanUtils;
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillListReqVO;
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.skill.SkillDO;
+import com.citu.module.menduner.system.dal.mysql.skill.SkillMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Objects;
+
+import static com.citu.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.*;
+
+/**
+ * 门墩儿-职业技能 Service 实现类
+ *
+ * @author Rayson
+ */
+@Service
+@Validated
+public class SkillServiceImpl implements SkillService {
+
+    @Resource
+    private SkillMapper skillMapper;
+
+    @Override
+    public Long createSkill(SkillSaveReqVO createReqVO) {
+        // 校验上级id的有效性
+        validateParentSkill(null, createReqVO.getParentId());
+        // 校验技能中文名称的唯一性
+        validateSkillNameCnUnique(null, createReqVO.getParentId(), createReqVO.getNameCn());
+
+        // 插入
+        SkillDO skill = BeanUtils.toBean(createReqVO, SkillDO.class);
+        skillMapper.insert(skill);
+        // 返回
+        return skill.getId();
+    }
+
+    @Override
+    public void updateSkill(SkillSaveReqVO updateReqVO) {
+        // 校验存在
+        validateSkillExists(updateReqVO.getId());
+        // 校验上级id的有效性
+        validateParentSkill(updateReqVO.getId(), updateReqVO.getParentId());
+        // 校验技能中文名称的唯一性
+        validateSkillNameCnUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getNameCn());
+
+        // 更新
+        SkillDO updateObj = BeanUtils.toBean(updateReqVO, SkillDO.class);
+        skillMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteSkill(Long id) {
+        // 校验存在
+        validateSkillExists(id);
+        // 校验是否有子门墩儿-职业技能
+        if (skillMapper.selectCountByParentId(id) > 0) {
+            throw exception(SKILL_EXITS_CHILDREN);
+        }
+        // 删除
+        skillMapper.deleteById(id);
+    }
+
+    private void validateSkillExists(Long id) {
+        if (skillMapper.selectById(id) == null) {
+            throw exception(SKILL_NOT_EXISTS);
+        }
+    }
+
+    private void validateParentSkill(Long id, Long parentId) {
+        if (parentId == null || SkillDO.PARENT_ID_ROOT.equals(parentId)) {
+            return;
+        }
+        // 1. 不能设置自己为父门墩儿-职业技能
+        if (Objects.equals(id, parentId)) {
+            throw exception(SKILL_PARENT_ERROR);
+        }
+        // 2. 父门墩儿-职业技能不存在
+        SkillDO parentSkill = skillMapper.selectById(parentId);
+        if (parentSkill == null) {
+            throw exception(SKILL_PARENT_NOT_EXITS);
+        }
+        // 3. 递归校验父门墩儿-职业技能,如果父门墩儿-职业技能是自己的子门墩儿-职业技能,则报错,避免形成环路
+        if (id == null) { // id 为空,说明新增,不需要考虑环路
+            return;
+        }
+        for (int i = 0; i < Short.MAX_VALUE; i++) {
+            // 3.1 校验环路
+            parentId = parentSkill.getParentId();
+            if (Objects.equals(id, parentId)) {
+                throw exception(SKILL_PARENT_IS_CHILD);
+            }
+            // 3.2 继续递归下一级父门墩儿-职业技能
+            if (parentId == null || SkillDO.PARENT_ID_ROOT.equals(parentId)) {
+                break;
+            }
+            parentSkill = skillMapper.selectById(parentId);
+            if (parentSkill == null) {
+                break;
+            }
+        }
+    }
+
+    private void validateSkillNameCnUnique(Long id, Long parentId, String nameCn) {
+        SkillDO skill = skillMapper.selectByParentIdAndNameCn(parentId, nameCn);
+        if (skill == null) {
+            return;
+        }
+        // 如果 id 为空,说明不用比较是否为相同 id 的门墩儿-职业技能
+        if (id == null) {
+            throw exception(SKILL_NAME_CN_DUPLICATE);
+        }
+        if (!Objects.equals(skill.getId(), id)) {
+            throw exception(SKILL_NAME_CN_DUPLICATE);
+        }
+    }
+
+    @Override
+    public SkillDO getSkill(Long id) {
+        return skillMapper.selectById(id);
+    }
+
+    @Override
+    public List<SkillDO> getSkillList(SkillListReqVO listReqVO) {
+        return skillMapper.selectList(listReqVO);
+    }
+
+}

+ 138 - 0
menduner/menduner-system-biz/src/test/java/com/citu/module/menduner/system/service/skill/SkillServiceImplTest.java

@@ -0,0 +1,138 @@
+package com.citu.module.menduner.system.service.skill;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillListReqVO;
+import com.citu.module.menduner.system.controller.admin.skill.vo.SkillSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.skill.SkillDO;
+import com.citu.module.menduner.system.dal.mysql.skill.SkillMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.SKILL_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link SkillServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(SkillServiceImpl.class)
+public class SkillServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private SkillServiceImpl skillService;
+
+    @Resource
+    private SkillMapper skillMapper;
+
+    @Test
+    public void testCreateSkill_success() {
+        // 准备参数
+        SkillSaveReqVO createReqVO = randomPojo(SkillSaveReqVO.class).setId(null);
+
+        // 调用
+        Long skillId = skillService.createSkill(createReqVO);
+        // 断言
+        assertNotNull(skillId);
+        // 校验记录的属性是否正确
+        SkillDO skill = skillMapper.selectById(skillId);
+        assertPojoEquals(createReqVO, skill, "id");
+    }
+
+    @Test
+    public void testUpdateSkill_success() {
+        // mock 数据
+        SkillDO dbSkill = randomPojo(SkillDO.class);
+        skillMapper.insert(dbSkill);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        SkillSaveReqVO updateReqVO = randomPojo(SkillSaveReqVO.class, o -> {
+            o.setId(dbSkill.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        skillService.updateSkill(updateReqVO);
+        // 校验是否更新正确
+        SkillDO skill = skillMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, skill);
+    }
+
+    @Test
+    public void testUpdateSkill_notExists() {
+        // 准备参数
+        SkillSaveReqVO updateReqVO = randomPojo(SkillSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> skillService.updateSkill(updateReqVO), SKILL_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteSkill_success() {
+        // mock 数据
+        SkillDO dbSkill = randomPojo(SkillDO.class);
+        skillMapper.insert(dbSkill);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbSkill.getId();
+
+        // 调用
+        skillService.deleteSkill(id);
+        // 校验数据不存在了
+        assertNull(skillMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteSkill_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> skillService.deleteSkill(id), SKILL_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetSkillList() {
+        // mock 数据
+        SkillDO dbSkill = randomPojo(SkillDO.class, o -> { // 等会查询到
+            o.setNameCn(null);
+            o.setNameEn(null);
+            o.setParentId(null);
+            o.setLevel(null);
+            o.setCreateTime(null);
+        });
+        skillMapper.insert(dbSkill);
+        // 测试 nameCn 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setNameCn(null)));
+        // 测试 nameEn 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setNameEn(null)));
+        // 测试 parentId 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setParentId(null)));
+        // 测试 level 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setLevel(null)));
+        // 测试 createTime 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setCreateTime(null)));
+        // 准备参数
+        SkillListReqVO reqVO = new SkillListReqVO();
+        reqVO.setNameCn(null);
+        reqVO.setNameEn(null);
+        reqVO.setParentId(null);
+        reqVO.setLevel(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        List<SkillDO> list = skillService.getSkillList(reqVO);
+        // 断言
+        assertEquals(1, list.size());
+        assertPojoEquals(dbSkill, list.get(0));
+    }
+
+}

+ 1 - 0
menduner/menduner-system-biz/src/test/resources/sql/clean.sql

@@ -23,3 +23,4 @@ DELETE FROM "mde_person_browse_record";
 DELETE FROM "mde_person_job_collection";
 DELETE FROM "mde_train_exp";
 DELETE FROM "mde_project_exp";
+DELETE FROM "mde_skill";

+ 15 - 0
menduner/menduner-system-biz/src/test/resources/sql/create_tables.sql

@@ -456,3 +456,18 @@ CREATE TABLE IF NOT EXISTS "mde_project_exp" (
     "deleted" bit NOT NULL DEFAULT FALSE,
     PRIMARY KEY ("id")
 ) COMMENT '门墩儿-项目经历表';
+
+CREATE TABLE IF NOT EXISTS "mde_skill" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "name_cn" varchar,
+    "name_en" varchar,
+    "parent_id" bigint,
+    "level" bit,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '门墩儿-职业技能表';