This commit is contained in:
2026-05-14 17:30:14 +08:00
parent 8f9d286bec
commit 3ec6216b0d
9 changed files with 735 additions and 159 deletions

View File

@@ -52,6 +52,26 @@ public class HotTrainingOutlineController extends BaseController {
return hotTrainingOutlineService.queryPageList(bo, pageQuery);
}
/**
* 查询全部年度培训大纲列表
*/
@GetMapping("/annuals")
@Operation(summary = "查询全部年度培训大纲列表")
public R<List<HotTrainingOutlineVo>> annuals(HotTrainingOutlineBo bo) {
return R.ok(hotTrainingOutlineService.queryAnnualList(bo));
}
/**
* 查询直属子级大纲列表
*/
@GetMapping("/children/{parentId}")
@Operation(summary = "根据父级大纲ID查询直属子级大纲列表")
public R<List<HotTrainingOutlineVo>> children(@NotNull(message = "父级大纲ID不能为空")
@Parameter(name = "parentId", description = "父级大纲ID", required = true, example = "1")
@PathVariable Long parentId) {
return R.ok(hotTrainingOutlineService.queryChildrenByParentId(parentId));
}
/**
* 导出培训大纲列表
*/

View File

@@ -12,8 +12,10 @@ import java.io.Serial;
/**
* 培训大纲对象 hot_training_outline
*
* 统一承载年度、月度、周度三层培训大纲。
*
* @author shihongwei
* @date 2026-01-14
* @date 2026-05-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@@ -30,24 +32,44 @@ public class HotTrainingOutline extends BaseEntity {
private Long id;
/**
* 年度
* 公司ID
*/
private Long outlineYear;
private Long companyId;
/**
* 培训类型组合,逗号分隔存储
* 上级大纲ID年度为0
*/
private String outlineTypes;
private Long parentId;
/**
* 是否启用 0=否,1=是
* 大纲层级1=年度2=月度3=周度
*/
private Long outlineLevel;
/**
* 大纲名称
*/
private String outlineName;
/**
* 大纲文件
*/
private String outlineFile;
/**
* 培训类型(字典值)
*/
private String trainingType;
/**
* 是否启用0=禁用1=启用
*/
private Long isEnabled;
/**
* 月度培训计划ID组合逗号分隔存储
* 排序号,越小越靠前
*/
private String monthPlanIds;
private Long sortNo;
/**
* 备注

View File

@@ -2,6 +2,7 @@ package com.hotwj.platform.securityManagement.trainingOutline.domain.bo;
import com.hotwj.platform.securityManagement.trainingOutline.domain.HotTrainingOutline;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -13,7 +14,7 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
* 培训大纲业务对象 hot_training_outline
*
* @author shihongwei
* @date 2026-01-14
* @date 2026-05-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@@ -27,25 +28,48 @@ public class HotTrainingOutlineBo extends BaseEntity {
private Long id;
/**
* 年度
* 公司ID
*/
@NotNull(message = "年度不能为空", groups = {AddGroup.class, EditGroup.class})
private Long outlineYear;
private Long companyId;
/**
* 培训类型组合,逗号分隔存储
* 上级大纲ID年度为0
*/
private String outlineTypes;
private Long parentId;
/**
* 是否启用 0=否,1=是
* 大纲层级1=年度2=月度3=周度
*/
@NotNull(message = "大纲层级不能为空", groups = {AddGroup.class, EditGroup.class})
private Long outlineLevel;
/**
* 大纲名称
*/
@NotBlank(message = "大纲名称不能为空", groups = {AddGroup.class, EditGroup.class})
private String outlineName;
/**
* 大纲文件
*/
@NotBlank(message = "大纲文件不能为空", groups = {AddGroup.class, EditGroup.class})
private String outlineFile;
/**
* 培训类型(字典值)
*/
@NotBlank(message = "培训类型不能为空", groups = {AddGroup.class, EditGroup.class})
private String trainingType;
/**
* 是否启用0=禁用1=启用
*/
private Long isEnabled;
/**
* 月度培训计划ID组合逗号分隔存储
* 排序号,越小越靠前
*/
private String monthPlanIds;
private Long sortNo;
/**
* 备注

View File

@@ -14,7 +14,7 @@ import java.io.Serializable;
* 培训大纲视图对象 hot_training_outline
*
* @author shihongwei
* @date 2026-01-14
* @date 2026-05-14
*/
@Data
@ExcelIgnoreUnannotated
@@ -31,28 +31,76 @@ public class HotTrainingOutlineVo implements Serializable {
private Long id;
/**
* 年度
* 公司ID
*/
@ExcelProperty(value = "年度")
private Long outlineYear;
@ExcelProperty(value = "公司ID")
private Long companyId;
/**
* 培训类型组合,逗号分隔存储
* 上级大纲ID年度为0
*/
@ExcelProperty(value = "培训类型组合,逗号分隔存储")
private String outlineTypes;
@ExcelProperty(value = "上级大纲ID")
private Long parentId;
/**
* 是否启用 0=否,1=是
* 上级大纲名称
*/
@ExcelProperty(value = "是否启用 0=否,1=是")
@ExcelProperty(value = "上级大纲名称")
private String parentName;
/**
* 大纲层级1=年度2=月度3=周度
*/
@ExcelProperty(value = "大纲层级")
private Long outlineLevel;
/**
* 大纲层级名称
*/
@ExcelProperty(value = "大纲层级名称")
private String outlineLevelName;
/**
* 大纲名称
*/
@ExcelProperty(value = "大纲名称")
private String outlineName;
/**
* 大纲文件
*/
@ExcelProperty(value = "大纲文件")
private String outlineFile;
/**
* 培训类型(字典值)
*/
@ExcelProperty(value = "培训类型")
private String trainingType;
/**
* 是否启用0=禁用1=启用
*/
@ExcelProperty(value = "是否启用")
private Long isEnabled;
/**
* 月度培训计划ID组合逗号分隔存储
* 培训类型名称
*/
@ExcelProperty(value = "月度培训计划ID组合逗号分隔存储")
private String monthPlanIds;
@ExcelProperty(value = "培训类型名称")
private String trainingTypeLabel;
/**
* 排序号,越小越靠前
*/
@ExcelProperty(value = "排序号")
private Long sortNo;
/**
* 是否存在子节点
*/
@ExcelProperty(value = "是否存在子节点")
private Boolean hasChildren;
/**
* 备注

View File

@@ -41,6 +41,22 @@ public interface IHotTrainingOutlineService {
*/
List<HotTrainingOutlineVo> queryList(HotTrainingOutlineBo bo);
/**
* 查询全部年度大纲列表
*
* @param bo 查询条件
* @return 年度大纲列表
*/
List<HotTrainingOutlineVo> queryAnnualList(HotTrainingOutlineBo bo);
/**
* 根据父级大纲ID查询直属子级列表
*
* @param parentId 父级大纲ID
* @return 直属子级列表
*/
List<HotTrainingOutlineVo> queryChildrenByParentId(Long parentId);
/**
* 新增培训大纲
*

View File

@@ -2,29 +2,18 @@ package com.hotwj.platform.securityManagement.trainingOutline.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hotwj.platform.common.utils.PdfWatermarkUtil;
import com.hotwj.platform.reportStatistics.integration.GotenbergClient;
import com.hotwj.platform.reportStatistics.template.VelocityTemplateRenderer;
import com.hotwj.platform.securityManagement.trainingOutline.domain.HotTrainingOutline;
import com.hotwj.platform.securityManagement.trainingOutline.domain.bo.HotTrainingOutlineBo;
import com.hotwj.platform.securityManagement.trainingOutline.domain.vo.HotTrainingOutlineVo;
import com.hotwj.platform.securityManagement.trainingOutline.mapper.HotTrainingOutlineMapper;
import com.hotwj.platform.securityManagement.trainingOutline.service.IHotTrainingOutlinePrintService;
import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.HotTrainingOutlineCourse;
import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.vo.HotTrainingOutlineCourseVo;
import com.hotwj.platform.securityManagement.trainingOutlineCourse.mapper.HotTrainingOutlineCourseMapper;
import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.HotTrainingOutlineMonth;
import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.vo.HotTrainingOutlineMonthVo;
import com.hotwj.platform.securityManagement.trainingOutlineMonth.mapper.HotTrainingOutlineMonthMapper;
import com.hotwj.platform.securityManagement.trainingOutline.service.IHotTrainingOutlineService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.VelocityContext;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -37,15 +26,17 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Slf4j
@Service
@RequiredArgsConstructor
public class HotTrainingOutlinePrintServiceImpl implements IHotTrainingOutlinePrintService {
private final HotTrainingOutlineMapper outlineMapper;
private final HotTrainingOutlineMonthMapper outlineMonthMapper;
private final HotTrainingOutlineCourseMapper outlineCourseMapper;
private static final long LEVEL_ANNUAL = 1L;
private static final long LEVEL_MONTHLY = 2L;
private final IHotTrainingOutlineService trainingOutlineService;
private final VelocityTemplateRenderer velocityTemplateRenderer;
private final GotenbergClient gotenbergClient;
@@ -54,82 +45,39 @@ public class HotTrainingOutlinePrintServiceImpl implements IHotTrainingOutlinePr
@Override
public void exportPdf(HotTrainingOutlineBo bo, HttpServletResponse response) {
// 1. 查询大纲列表
LambdaQueryWrapper<HotTrainingOutline> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(HotTrainingOutline::getId);
lqw.eq(bo.getOutlineYear() != null, HotTrainingOutline::getOutlineYear, bo.getOutlineYear());
lqw.eq(StringUtils.isNotBlank(bo.getOutlineTypes()), HotTrainingOutline::getOutlineTypes, bo.getOutlineTypes());
lqw.eq(bo.getIsEnabled() != null, HotTrainingOutline::getIsEnabled, bo.getIsEnabled());
lqw.eq(StringUtils.isNotBlank(bo.getMonthPlanIds()), HotTrainingOutline::getMonthPlanIds, bo.getMonthPlanIds());
lqw.eq(bo.getIsDeleted() != null, HotTrainingOutline::getIsDeleted, bo.getIsDeleted());
List<HotTrainingOutlineVo> outlineList = outlineMapper.selectVoList(lqw);
List<HotTrainingOutlineVo> outlineList = resolveAnnualOutlines(bo);
if (CollUtil.isEmpty(outlineList)) {
throw new ServiceException("未查询到培训大纲数据");
}
// 假设只打印第一个匹配的大纲,或者需要循环打印。通常"导出PDF"如果是列表查询,可能导出列表。
// 但根据前端逻辑,它似乎是在查看详情时打印,或者是针对特定年份的打印。
// 如果是列表导出,我们可能需要一个包含多个大纲的 PDF。
// 这里假设按照 vehicleThreeInspect 的逻辑,它导出的是一个列表。
// 但前端传参是 queryParams可能包含 id。
// 如果 bo.getId() 不为空,则只导出一个。
List<Map<String, Object>> printDataList = new ArrayList<>();
for (HotTrainingOutlineVo outline : outlineList) {
Map<String, Object> outlineData = new HashMap<>();
outlineData.put("outlineYear", outline.getOutlineYear());
// 查询月度计划
List<HotTrainingOutlineMonth> months = outlineMonthMapper.selectList(
Wrappers.<HotTrainingOutlineMonth>lambdaQuery()
.eq(HotTrainingOutlineMonth::getOutlineId, outline.getId())
.eq(HotTrainingOutlineMonth::getIsDeleted, 0)
.orderByAsc(HotTrainingOutlineMonth::getSortNo) // 假设有排序
);
List<HotTrainingOutlineMonthVo> monthVos = MapstructUtils.convert(months, HotTrainingOutlineMonthVo.class);
outlineData.put("outlineYear", outline.getOutlineName());
List<Map<String, Object>> flatRows = new ArrayList<>();
List<HotTrainingOutlineVo> monthVos = trainingOutlineService.queryChildrenByParentId(outline.getId());
if (CollUtil.isNotEmpty(monthVos)) {
for (HotTrainingOutlineMonthVo month : monthVos) {
// 查询课程
List<HotTrainingOutlineCourse> courses = outlineCourseMapper.selectList(
Wrappers.<HotTrainingOutlineCourse>lambdaQuery()
.eq(HotTrainingOutlineCourse::getMonthOutlineId, month.getId())
.eq(HotTrainingOutlineCourse::getIsDeleted, 0)
.orderByAsc(HotTrainingOutlineCourse::getSortNo)
);
List<HotTrainingOutlineCourseVo> courseVos = MapstructUtils.convert(courses, HotTrainingOutlineCourseVo.class);
if (CollUtil.isNotEmpty(courseVos)) {
for (int i = 0; i < courseVos.size(); i++) {
HotTrainingOutlineCourseVo course = courseVos.get(i);
for (HotTrainingOutlineVo month : monthVos) {
List<HotTrainingOutlineVo> weekVos = trainingOutlineService.queryChildrenByParentId(month.getId());
if (CollUtil.isNotEmpty(weekVos)) {
for (int i = 0; i < weekVos.size(); i++) {
HotTrainingOutlineVo week = weekVos.get(i);
Map<String, Object> row = new HashMap<>();
row.put("planMonth", month.getPlanMonth());
row.put("topic", month.getTopic());
row.put("courseName", course.getCourseName());
row.put("durationMinute", course.getDurationMinute());
row.put("courseDesc", course.getCourseDesc());
// 设置 rowSpan
if (i == 0) {
row.put("monthRowSpan", courseVos.size());
} else {
row.put("monthRowSpan", 0);
}
row.put("planMonth", month.getOutlineName());
row.put("topic", defaultString(month.getTrainingTypeLabel(), month.getTrainingType()));
row.put("courseName", week.getOutlineName());
row.put("durationMinute", "");
row.put("courseDesc", week.getOutlineFile());
row.put("monthRowSpan", i == 0 ? weekVos.size() : 0);
flatRows.add(row);
}
} else {
// 如果没有课程,也显示月度信息
Map<String, Object> row = new HashMap<>();
row.put("planMonth", month.getPlanMonth());
row.put("topic", month.getTopic());
row.put("planMonth", month.getOutlineName());
row.put("topic", defaultString(month.getTrainingTypeLabel(), month.getTrainingType()));
row.put("courseName", "");
row.put("durationMinute", "");
row.put("courseDesc", "");
row.put("courseDesc", month.getOutlineFile());
row.put("monthRowSpan", 1);
flatRows.add(row);
}
@@ -139,7 +87,6 @@ public class HotTrainingOutlinePrintServiceImpl implements IHotTrainingOutlinePr
printDataList.add(outlineData);
}
// 渲染 HTML
VelocityContext context = new VelocityContext();
context.put("printTime", DateUtil.now());
context.put("list", printDataList);
@@ -189,4 +136,39 @@ public class HotTrainingOutlinePrintServiceImpl implements IHotTrainingOutlinePr
}
}
}
private List<HotTrainingOutlineVo> resolveAnnualOutlines(HotTrainingOutlineBo bo) {
if (bo != null && bo.getId() != null) {
HotTrainingOutlineVo current = trainingOutlineService.queryById(bo.getId());
if (current == null) {
throw new ServiceException("培训大纲不存在");
}
HotTrainingOutlineVo annual = resolveAnnualOutline(current);
return annual == null ? List.of() : List.of(annual);
}
HotTrainingOutlineBo queryBo = bo == null ? new HotTrainingOutlineBo() : bo;
queryBo.setOutlineLevel(LEVEL_ANNUAL);
return trainingOutlineService.queryAnnualList(queryBo);
}
private HotTrainingOutlineVo resolveAnnualOutline(HotTrainingOutlineVo current) {
if (current == null) {
return null;
}
if (Objects.equals(current.getOutlineLevel(), LEVEL_ANNUAL)) {
return current;
}
if (Objects.equals(current.getOutlineLevel(), LEVEL_MONTHLY)) {
return trainingOutlineService.queryById(current.getParentId());
}
HotTrainingOutlineVo parent = trainingOutlineService.queryById(current.getParentId());
if (parent == null) {
return null;
}
return trainingOutlineService.queryById(parent.getParentId());
}
private String defaultString(String preferred, String fallback) {
return preferred != null && !preferred.isBlank() ? preferred : (fallback == null ? "" : fallback);
}
}

View File

@@ -3,6 +3,7 @@ package com.hotwj.platform.securityManagement.trainingOutline.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService;
import com.hotwj.platform.securityManagement.trainingOutline.domain.HotTrainingOutline;
import com.hotwj.platform.securityManagement.trainingOutline.domain.bo.HotTrainingOutlineBo;
import com.hotwj.platform.securityManagement.trainingOutline.domain.vo.HotTrainingOutlineVo;
@@ -10,87 +11,127 @@ import com.hotwj.platform.securityManagement.trainingOutline.mapper.HotTrainingO
import com.hotwj.platform.securityManagement.trainingOutline.service.IHotTrainingOutlineService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 培训大纲Service业务层处理
*
* @author shihongwei
* @date 2026-01-14
* @date 2026-05-14
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class HotTrainingOutlineServiceImpl implements IHotTrainingOutlineService {
private final HotTrainingOutlineMapper baseMapper;
private static final long LEVEL_ANNUAL = 1L;
private static final long LEVEL_MONTHLY = 2L;
private static final long LEVEL_WEEKLY = 3L;
private final HotTrainingOutlineMapper baseMapper;
private final DictService dictService;
/**
* 查询培训大纲
*
* @param id 主键
* @return 培训大纲
*/
@Override
public HotTrainingOutlineVo queryById(Long id) {
return baseMapper.selectVoById(id);
HotTrainingOutlineVo vo = baseMapper.selectVoById(id);
if (vo == null) {
return null;
}
if (shouldFilterDisabledForCurrentUser() && !Long.valueOf(1L).equals(vo.getIsEnabled())) {
return null;
}
fillExtraFields(List.of(vo));
return vo;
}
/**
* 分页查询培训大纲列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 培训大纲分页列表
*/
@Override
public TableDataInfo<HotTrainingOutlineVo> queryPageList(HotTrainingOutlineBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotTrainingOutline> lqw = buildQueryWrapper(bo);
Page<HotTrainingOutlineVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
List<HotTrainingOutlineVo> records = result.getRecords();
if (records != null && !records.isEmpty()) {
fillExtraFields(records);
}
return TableDataInfo.build(result);
}
/**
* 查询符合条件的培训大纲列表
*
* @param bo 查询条件
* @return 培训大纲列表
*/
@Override
public List<HotTrainingOutlineVo> queryList(HotTrainingOutlineBo bo) {
LambdaQueryWrapper<HotTrainingOutline> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
List<HotTrainingOutlineVo> list = baseMapper.selectVoList(buildQueryWrapper(bo));
fillExtraFields(list);
return list;
}
@Override
public List<HotTrainingOutlineVo> queryAnnualList(HotTrainingOutlineBo bo) {
HotTrainingOutlineBo queryBo = bo == null ? new HotTrainingOutlineBo() : bo;
queryBo.setOutlineLevel(LEVEL_ANNUAL);
queryBo.setParentId(0L);
List<HotTrainingOutlineVo> list = baseMapper.selectVoList(buildQueryWrapper(queryBo));
fillExtraFields(list);
return list;
}
@Override
public List<HotTrainingOutlineVo> queryChildrenByParentId(Long parentId) {
HotTrainingOutline parent = baseMapper.selectById(parentId);
if (parent == null || Long.valueOf(1L).equals(parent.getIsDeleted())) {
throw new ServiceException("父级培训大纲不存在");
}
List<HotTrainingOutlineVo> children = baseMapper.selectVoList(
Wrappers.<HotTrainingOutline>lambdaQuery()
.eq(HotTrainingOutline::getParentId, parentId)
.eq(shouldFilterDisabledForCurrentUser(), HotTrainingOutline::getIsEnabled, 1L)
.eq(HotTrainingOutline::getIsDeleted, 0L)
.orderByAsc(HotTrainingOutline::getSortNo)
.orderByAsc(HotTrainingOutline::getId)
);
fillExtraFields(children);
return children;
}
private LambdaQueryWrapper<HotTrainingOutline> buildQueryWrapper(HotTrainingOutlineBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotTrainingOutline> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(HotTrainingOutline::getId);
lqw.eq(bo.getOutlineYear() != null, HotTrainingOutline::getOutlineYear, bo.getOutlineYear());
lqw.eq(StringUtils.isNotBlank(bo.getOutlineTypes()), HotTrainingOutline::getOutlineTypes, bo.getOutlineTypes());
lqw.eq(bo.getId() != null, HotTrainingOutline::getId, bo.getId());
lqw.eq(bo.getCompanyId() != null, HotTrainingOutline::getCompanyId, bo.getCompanyId());
lqw.eq(bo.getParentId() != null, HotTrainingOutline::getParentId, bo.getParentId());
lqw.eq(bo.getOutlineLevel() != null, HotTrainingOutline::getOutlineLevel, bo.getOutlineLevel());
lqw.like(StringUtils.isNotBlank(bo.getOutlineName()), HotTrainingOutline::getOutlineName, bo.getOutlineName());
lqw.eq(StringUtils.isNotBlank(bo.getTrainingType()), HotTrainingOutline::getTrainingType, bo.getTrainingType());
lqw.eq(bo.getIsEnabled() != null, HotTrainingOutline::getIsEnabled, bo.getIsEnabled());
lqw.eq(StringUtils.isNotBlank(bo.getMonthPlanIds()), HotTrainingOutline::getMonthPlanIds, bo.getMonthPlanIds());
lqw.eq(bo.getIsDeleted() != null, HotTrainingOutline::getIsDeleted, bo.getIsDeleted());
if (shouldFilterDisabledForCurrentUser()) {
lqw.eq(HotTrainingOutline::getIsEnabled, 1L);
}
lqw.orderByAsc(HotTrainingOutline::getSortNo)
.orderByAsc(HotTrainingOutline::getId);
return lqw;
}
/**
* 新增培训大纲
*
* @param bo 培训大纲
* @return 是否新增成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(HotTrainingOutlineBo bo) {
HotTrainingOutline add = MapstructUtils.convert(bo, HotTrainingOutline.class);
prepareEntity(add, null);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
@@ -99,38 +140,211 @@ public class HotTrainingOutlineServiceImpl implements IHotTrainingOutlineService
return flag;
}
/**
* 修改培训大纲
*
* @param bo 培训大纲
* @return 是否修改成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(HotTrainingOutlineBo bo) {
HotTrainingOutline current = baseMapper.selectById(bo.getId());
if (current == null || Long.valueOf(1L).equals(current.getIsDeleted())) {
throw new ServiceException("培训大纲不存在");
}
HotTrainingOutline update = MapstructUtils.convert(bo, HotTrainingOutline.class);
prepareEntity(update, current);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(HotTrainingOutline entity) {
//TODO 做一些数据校验,如唯一约束
private void prepareEntity(HotTrainingOutline entity, HotTrainingOutline current) {
if (entity.getCompanyId() == null) {
entity.setCompanyId(current != null ? current.getCompanyId() : resolveCurrentCompanyId());
}
if (entity.getSortNo() == null) {
entity.setSortNo(current != null && current.getSortNo() != null ? current.getSortNo() : 0L);
}
if (!canManageEnabled()) {
entity.setIsEnabled(current != null && current.getIsEnabled() != null ? current.getIsEnabled() : 1L);
} else if (entity.getIsEnabled() == null) {
entity.setIsEnabled(current != null && current.getIsEnabled() != null ? current.getIsEnabled() : 1L);
}
if (entity.getIsDeleted() == null) {
entity.setIsDeleted(current != null && current.getIsDeleted() != null ? current.getIsDeleted() : 0L);
}
if (Objects.equals(entity.getOutlineLevel(), LEVEL_ANNUAL)) {
entity.setParentId(0L);
} else if (entity.getParentId() == null && current != null) {
entity.setParentId(current.getParentId());
}
}
private void validEntityBeforeSave(HotTrainingOutline entity) {
if (!isSupportedLevel(entity.getOutlineLevel())) {
throw new ServiceException("大纲层级仅支持年度、月度、周度");
}
if (StringUtils.isBlank(entity.getOutlineName())) {
throw new ServiceException("大纲名称不能为空");
}
if (StringUtils.isBlank(entity.getOutlineFile())) {
throw new ServiceException("大纲文件不能为空");
}
if (StringUtils.isBlank(entity.getTrainingType())) {
throw new ServiceException("培训类型不能为空");
}
if (entity.getIsEnabled() != null && !Objects.equals(entity.getIsEnabled(), 0L) && !Objects.equals(entity.getIsEnabled(), 1L)) {
throw new ServiceException("是否启用取值仅支持0和1");
}
if (Objects.equals(entity.getOutlineLevel(), LEVEL_MONTHLY)) {
HotTrainingOutline parent = requireParent(entity.getParentId(), "月度大纲");
if (!Objects.equals(parent.getOutlineLevel(), LEVEL_ANNUAL)) {
throw new ServiceException("月度大纲的上级必须是年度大纲");
}
inheritCompanyFromParent(entity, parent);
} else if (Objects.equals(entity.getOutlineLevel(), LEVEL_WEEKLY)) {
HotTrainingOutline parent = requireParent(entity.getParentId(), "周度大纲");
if (!Objects.equals(parent.getOutlineLevel(), LEVEL_MONTHLY)) {
throw new ServiceException("周度大纲的上级必须是月度大纲");
}
inheritCompanyFromParent(entity, parent);
}
LambdaQueryWrapper<HotTrainingOutline> duplicateWrapper = Wrappers.lambdaQuery();
duplicateWrapper.eq(HotTrainingOutline::getOutlineLevel, entity.getOutlineLevel())
.eq(HotTrainingOutline::getParentId, entity.getParentId())
.eq(HotTrainingOutline::getOutlineName, entity.getOutlineName())
.eq(HotTrainingOutline::getIsDeleted, 0L)
.ne(entity.getId() != null, HotTrainingOutline::getId, entity.getId());
if (entity.getCompanyId() != null) {
duplicateWrapper.eq(HotTrainingOutline::getCompanyId, entity.getCompanyId());
}
if (baseMapper.selectCount(duplicateWrapper) > 0) {
throw new ServiceException("同级下已存在同名培训大纲");
}
}
private HotTrainingOutline requireParent(Long parentId, String currentLevelName) {
if (parentId == null || parentId <= 0) {
throw new ServiceException(currentLevelName + "必须选择上级大纲");
}
HotTrainingOutline parent = baseMapper.selectById(parentId);
if (parent == null || Long.valueOf(1L).equals(parent.getIsDeleted())) {
throw new ServiceException("上级培训大纲不存在");
}
if (shouldFilterDisabledForCurrentUser() && !Long.valueOf(1L).equals(parent.getIsEnabled())) {
throw new ServiceException("上级培训大纲不存在");
}
return parent;
}
private void inheritCompanyFromParent(HotTrainingOutline entity, HotTrainingOutline parent) {
if (entity.getCompanyId() == null) {
entity.setCompanyId(parent.getCompanyId());
}
if (entity.getCompanyId() != null && parent.getCompanyId() != null
&& !Objects.equals(entity.getCompanyId(), parent.getCompanyId())) {
throw new ServiceException("子级大纲与上级大纲的公司归属必须一致");
}
}
/**
* 校验并批量删除培训大纲信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (ids == null || ids.isEmpty()) {
return false;
}
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
Long childCount = baseMapper.selectCount(
Wrappers.<HotTrainingOutline>lambdaQuery()
.in(HotTrainingOutline::getParentId, ids)
.eq(HotTrainingOutline::getIsDeleted, 0L)
);
if (childCount != null && childCount > 0) {
throw new ServiceException("存在下级培训大纲,无法删除");
}
}
return baseMapper.deleteByIds(ids) > 0;
}
private void fillExtraFields(List<HotTrainingOutlineVo> list) {
if (list == null || list.isEmpty()) {
return;
}
Set<Long> parentIds = list.stream()
.map(HotTrainingOutlineVo::getParentId)
.filter(Objects::nonNull)
.filter(parentId -> parentId > 0)
.collect(Collectors.toCollection(LinkedHashSet::new));
Map<Long, String> parentNameMap = new HashMap<>();
if (!parentIds.isEmpty()) {
parentNameMap = baseMapper.selectBatchIds(parentIds).stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(HotTrainingOutline::getId, HotTrainingOutline::getOutlineName, (a, b) -> a));
}
Set<Long> currentIds = list.stream()
.map(HotTrainingOutlineVo::getId)
.filter(Objects::nonNull)
.collect(Collectors.toCollection(LinkedHashSet::new));
Map<Long, Long> childCountMap = Collections.emptyMap();
if (!currentIds.isEmpty()) {
childCountMap = baseMapper.selectList(
Wrappers.<HotTrainingOutline>lambdaQuery()
.select(HotTrainingOutline::getId, HotTrainingOutline::getParentId)
.in(HotTrainingOutline::getParentId, currentIds)
.eq(HotTrainingOutline::getIsDeleted, 0L)
).stream().collect(Collectors.groupingBy(HotTrainingOutline::getParentId, Collectors.counting()));
}
for (HotTrainingOutlineVo vo : list) {
vo.setParentName(parentNameMap.get(vo.getParentId()));
vo.setOutlineLevelName(resolveLevelName(vo.getOutlineLevel()));
if (StringUtils.isNotBlank(vo.getTrainingType())) {
String label = dictService.getDictLabel("hot_training_type", vo.getTrainingType());
vo.setTrainingTypeLabel(StringUtils.isNotBlank(label) ? label : vo.getTrainingType());
}
vo.setHasChildren(childCountMap.getOrDefault(vo.getId(), 0L) > 0);
}
}
private String resolveLevelName(Long outlineLevel) {
if (Objects.equals(outlineLevel, LEVEL_ANNUAL)) {
return "年度";
}
if (Objects.equals(outlineLevel, LEVEL_MONTHLY)) {
return "月度";
}
if (Objects.equals(outlineLevel, LEVEL_WEEKLY)) {
return "周度";
}
return "";
}
private boolean isSupportedLevel(Long outlineLevel) {
return Objects.equals(outlineLevel, LEVEL_ANNUAL)
|| Objects.equals(outlineLevel, LEVEL_MONTHLY)
|| Objects.equals(outlineLevel, LEVEL_WEEKLY);
}
private Long resolveCurrentCompanyId() {
try {
return LoginHelper.getCompanyId();
} catch (Exception e) {
log.debug("resolveCurrentCompanyId failed", e);
return null;
}
}
private boolean shouldFilterDisabledForCurrentUser() {
if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
return false;
}
LoginUser loginUser = LoginHelper.getLoginUser();
return loginUser != null && ISysUserLoginPortService.ENTERPRISE_PORT.equals(loginUser.getLoginPort());
}
private boolean canManageEnabled() {
if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
return true;
}
LoginUser loginUser = LoginHelper.getLoginUser();
return loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort());
}
}