【新增】 WPS相关提交

This commit is contained in:
DESKTOP-932OMT8\REN
2025-06-23 14:23:46 +08:00
parent bb9306dcdb
commit e1b84f4f53
28 changed files with 2729 additions and 1365 deletions

View File

@@ -0,0 +1,83 @@
package pc.exam.pp.module.exam.controller.admin.wps;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import pc.exam.pp.framework.common.enums.CommonStatusEnum;
import pc.exam.pp.framework.common.pojo.CommonResult;
import pc.exam.pp.framework.common.util.object.BeanUtils;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxListReqVO;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxRespVO;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxSaveReqVO;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxSimpleRespVO;
import pc.exam.pp.module.exam.dal.dataobject.wps.ExamWpsPptx;
import pc.exam.pp.module.exam.service.wps.pptx.ExamWpsPptxService;
import java.util.List;
import static pc.exam.pp.framework.common.pojo.CommonResult.success;
@Tag(name = "考试系统 - PPTX考点")
@RestController
@RequestMapping("/exam/pptx")
@Validated
public class ExamWpsPptxController {
@Resource
private ExamWpsPptxService examWpsPptxService;
@PostMapping("create")
@Operation(summary = "创建PPTX考点")
public CommonResult<Long> createPptx(@Valid @RequestBody PptxSaveReqVO createReqVO) {
Long pptxId = examWpsPptxService.createPptx(createReqVO);
return success(pptxId);
}
@PutMapping("update")
@Operation(summary = "更新PPTX考点")
public CommonResult<Boolean> updatePptx(@Valid @RequestBody PptxSaveReqVO updateReqVO) {
examWpsPptxService.updatePptx(updateReqVO);
return success(true);
}
@DeleteMapping("delete")
@Operation(summary = "删除PPTX考点")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<Boolean> deletePptx(@RequestParam("id") Long id) {
examWpsPptxService.deletePptx(id);
return success(true);
}
@GetMapping("/list")
@Operation(summary = "获取PPTX考点列表")
public CommonResult<List<PptxRespVO>> getPptxList(PptxListReqVO reqVO) {
List<ExamWpsPptx> list = examWpsPptxService.getPptxList(reqVO);
return success(BeanUtils.toBean(list, PptxRespVO.class));
}
@GetMapping(value = {"/list-all-simple", "/simple-list"})
@Operation(summary = "获取PPTX考点精简信息列表", description = "只包含被开启的PPTX考点主要用于前端的下拉选项")
public CommonResult<List<PptxSimpleRespVO>> getSimplePptxList() {
List<ExamWpsPptx> list = examWpsPptxService.getPptxList(
new PptxListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
return success(BeanUtils.toBean(list, PptxSimpleRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得PPTX考点信息")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<PptxRespVO> getPptx(@RequestParam("id") Long id) {
ExamWpsPptx pptx = examWpsPptxService.getPptx(id);
return success(BeanUtils.toBean(pptx, PptxRespVO.class));
}
@GetMapping("/getByNameList")
public CommonResult<List<PptxRespVO>> getPptxByNameList(@RequestParam("title") String title) {
ExamWpsPptx pptx = examWpsPptxService.getPptxByTitle(title);
return success(BeanUtils.toBean(examWpsPptxService.getChildPptxList(pptx.getId()), PptxRespVO.class));
}
}

View File

@@ -0,0 +1,14 @@
package pc.exam.pp.module.exam.controller.admin.wps.vo.pptx;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "考试模块 - PPT考点列表 Request VO")
@Data
public class PptxListReqVO {
private String name;
private Integer status;
}

View File

@@ -0,0 +1,40 @@
package pc.exam.pp.module.exam.controller.admin.wps.vo.pptx;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "考试模块 - PPT考点信息 Response VO")
@Data
public class PptxRespVO {
private Long id;
private String name;
private Long parentId;
private Integer sort;
private Integer status;
private String title;
private String chineseName;
private String dataType;
private Integer isText;
private String valueList;
private Integer isTrue;
private Integer titleType;
private Integer isParameter;
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,42 @@
package pc.exam.pp.module.exam.controller.admin.wps.vo.pptx;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import pc.exam.pp.framework.common.enums.CommonStatusEnum;
import pc.exam.pp.framework.common.validation.InEnum;
@Schema(description = "考试模块 - PPT考点创建/修改 Request VO")
@Data
public class PptxSaveReqVO {
private Long id;
private String name;
private Long parentId;
private Integer sort;
private Integer status;
private String title;
private String chineseName;
private String dataType;
private Integer isText;
private String valueList;
private Integer isTrue;
private Integer titleType;
private Integer isParameter;
}

View File

@@ -0,0 +1,20 @@
package pc.exam.pp.module.exam.controller.admin.wps.vo.pptx;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "考试模块 - 精简信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PptxSimpleRespVO {
private Long id;
private String name;
private Long parentId;
}

View File

@@ -0,0 +1,41 @@
package pc.exam.pp.module.exam.dal.dataobject.wps;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import pc.exam.pp.framework.tenant.core.db.TenantBaseDO;
import java.util.ArrayList;
import java.util.List;
/**
* wps pptx关系对应表
*
* @author REN
*/
@TableName("exam_wps_pptx")
@Data
@EqualsAndHashCode(callSuper = true)
public class ExamWpsPptx extends TenantBaseDO {
public static final Long PARENT_ID_ROOT = 0L;
@TableId
private Long id;
private String name;
private Long parentId;
private String title;
private Integer sort;
private String chineseName;
private String dataType;
private Integer status;
private String valueList;
private Integer isTrue;
private Integer isText;
private Integer titleType;
private Integer isParameter;
@TableField(exist = false)
private List<ExamWpsPptx> children = new ArrayList<>();
}

View File

@@ -0,0 +1,36 @@
package pc.exam.pp.module.exam.dal.mysql.wps;
import org.apache.ibatis.annotations.Mapper;
import pc.exam.pp.framework.mybatis.core.mapper.BaseMapperX;
import pc.exam.pp.framework.mybatis.core.query.LambdaQueryWrapperX;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxListReqVO;
import pc.exam.pp.module.exam.dal.dataobject.wps.ExamWpsPptx;
import java.util.Collection;
import java.util.List;
@Mapper
public interface ExamWpsPptxMapper extends BaseMapperX<ExamWpsPptx> {
default List<ExamWpsPptx> selectList(PptxListReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<ExamWpsPptx>()
.likeIfPresent(ExamWpsPptx::getName, reqVO.getName())
.eqIfPresent(ExamWpsPptx::getStatus, reqVO.getStatus()));
}
default ExamWpsPptx selectByParentIdAndName(Long parentId, String name) {
return selectOne(ExamWpsPptx::getParentId, parentId, ExamWpsPptx::getName, name);
}
default ExamWpsPptx selectByTitle(String title) {
return selectOne(ExamWpsPptx::getTitle, title);
}
default Long selectCountByParentId(Long parentId) {
return selectCount(ExamWpsPptx::getParentId, parentId);
}
default List<ExamWpsPptx> selectListByParentId(Collection<Long> parentIds) {
return selectList(ExamWpsPptx::getParentId, parentIds);
}
}

View File

@@ -0,0 +1,117 @@
package pc.exam.pp.module.exam.service.wps.pptx;
import pc.exam.pp.framework.common.util.collection.CollectionUtils;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxListReqVO;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxSaveReqVO;
import pc.exam.pp.module.exam.dal.dataobject.wps.ExamWpsPptx;
import java.util.*;
/**
* Pptx考点 Service 接口
*
* @author 朋辰
*/
public interface ExamWpsPptxService {
/**
* 创建Pptx考点
*
* @param createReqVO Pptx考点信息
* @return Pptx考点编号
*/
Long createPptx(PptxSaveReqVO createReqVO);
/**
* 更新Pptx考点
*
* @param updateReqVO Pptx考点信息
*/
void updatePptx(PptxSaveReqVO updateReqVO);
/**
* 删除Pptx考点
*
* @param id Pptx考点编号
*/
void deletePptx(Long id);
/**
* 获得Pptx考点信息
*
* @param id Pptx考点编号
* @return Pptx考点信息
*/
ExamWpsPptx getPptx(Long id);
/**
* 获得Pptx考点信息
*
* @param title Pptx考点标签
* @return Pptx考点信息
*/
ExamWpsPptx getPptxByTitle(String title);
/**
* 获得Pptx考点信息数组
*
* @param ids Pptx考点编号数组
* @return Pptx考点信息数组
*/
List<ExamWpsPptx> getPptxList(Collection<Long> ids);
/**
* 筛选Pptx考点列表
*
* @param reqVO 筛选条件请求 VO
* @return Pptx考点列表
*/
List<ExamWpsPptx> getPptxList(PptxListReqVO reqVO);
/**
* 获得指定编号的Pptx考点 Map
*
* @param ids Pptx考点编号数组
* @return Pptx考点 Map
*/
default Map<Long, ExamWpsPptx> getPptxMap(Collection<Long> ids) {
List<ExamWpsPptx> list = getPptxList(ids);
return CollectionUtils.convertMap(list, ExamWpsPptx::getId);
}
/**
* 获得指定Pptx考点的所有子Pptx考点
*
* @param id Pptx考点编号
* @return 子Pptx考点列表
*/
default List<ExamWpsPptx> getChildPptxList(Long id) {
return getChildPptxList(Collections.singleton(id));
}
/**
* 获得指定Pptx考点的所有子Pptx考点
*
* @param ids Pptx考点编号数组
* @return 子Pptx考点列表
*/
List<ExamWpsPptx> getChildPptxList(Collection<Long> ids);
/**
* 获得所有子Pptx考点从缓存中
*
* @param id 父Pptx考点编号
* @return 子Pptx考点列表
*/
Set<Long> getChildPptxIdListFromCache(Long id);
/**
* 校验Pptx考点们是否有效。如下情况视为无效
* 1. Pptx考点编号不存在
* 2. Pptx考点被禁用
*
* @param ids 角色编号数组
*/
void validatePptxList(Collection<Long> ids);
}

View File

@@ -0,0 +1,221 @@
package pc.exam.pp.module.exam.service.wps.pptx;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.annotations.VisibleForTesting;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import pc.exam.pp.framework.common.enums.CommonStatusEnum;
import pc.exam.pp.framework.common.util.object.BeanUtils;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxListReqVO;
import pc.exam.pp.module.exam.controller.admin.wps.vo.pptx.PptxSaveReqVO;
import pc.exam.pp.module.exam.dal.dataobject.wps.ExamWpsPptx;
import pc.exam.pp.module.exam.dal.mysql.wps.ExamWpsPptxMapper;
import java.util.*;
import static pc.exam.pp.framework.common.exception.util.ServiceExceptionUtil.exception;
import static pc.exam.pp.framework.common.util.collection.CollectionUtils.convertSet;
import static pc.exam.pp.module.system.enums.ErrorCodeConstants.*;
/**
* PPTX考点 Service 实现类
*
* @author 朋辰
*/
@Service
@Validated
@Slf4j
public class ExamWpsPptxServiceImpl implements ExamWpsPptxService {
@Resource
private ExamWpsPptxMapper pptxMapper;
@Override
public Long createPptx(PptxSaveReqVO createReqVO) {
if (createReqVO.getParentId() == null) {
createReqVO.setParentId(ExamWpsPptx.PARENT_ID_ROOT);
}
// 校验父PPTX考点的有效性
validateParentPptx(null, createReqVO.getParentId());
// 校验PPTX考点名的唯一性
validatePptxNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
// 插入PPTX考点
ExamWpsPptx pptx = BeanUtils.toBean(createReqVO, ExamWpsPptx.class);
pptxMapper.insert(pptx);
return pptx.getId();
}
@Override
public void updatePptx(PptxSaveReqVO updateReqVO) {
if (updateReqVO.getParentId() == null) {
updateReqVO.setParentId(ExamWpsPptx.PARENT_ID_ROOT);
}
// 校验自己存在
validatePptxExists(updateReqVO.getId());
// 校验父PPTX考点的有效性
validateParentPptx(updateReqVO.getId(), updateReqVO.getParentId());
// 校验PPTX考点名的唯一性
validatePptxNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
// 更新PPTX考点
ExamWpsPptx updateObj = BeanUtils.toBean(updateReqVO, ExamWpsPptx.class);
pptxMapper.updateById(updateObj);
}
@Override
public void deletePptx(Long id) {
// 校验是否存在
validatePptxExists(id);
// 校验是否有子PPTX考点
if (pptxMapper.selectCountByParentId(id) > 0) {
throw exception(PPTX_EXITS_CHILDREN);
}
// 删除PPTX考点
pptxMapper.deleteById(id);
}
@VisibleForTesting
void validatePptxExists(Long id) {
if (id == null) {
return;
}
ExamWpsPptx pptx = pptxMapper.selectById(id);
if (pptx == null) {
throw exception(PPTX_NOT_FOUND);
}
}
@VisibleForTesting
void validateParentPptx(Long id, Long parentId) {
if (parentId == null || ExamWpsPptx.PARENT_ID_ROOT.equals(parentId)) {
return;
}
// 1. 不能设置自己为父PPTX考点
if (Objects.equals(id, parentId)) {
throw exception(PPTX_PARENT_ERROR);
}
// 2. 父PPTX考点不存在
ExamWpsPptx parent = pptxMapper.selectById(parentId);
if (parent == null) {
throw exception(PPTX_PARENT_NOT_EXITS);
}
// 3. 递归校验父PPTX考点如果父PPTX考点是自己的子PPTX考点则报错避免形成环路
if (id == null) { // id 为空,说明新增,不需要考虑环路
return;
}
for (int i = 0; i < Short.MAX_VALUE; i++) {
// 3.1 校验环路
parentId = parent.getParentId();
if (Objects.equals(id, parentId)) {
throw exception(PPTX_PARENT_IS_CHILD);
}
// 3.2 继续递归下一级父PPTX考点
if (parentId == null || ExamWpsPptx.PARENT_ID_ROOT.equals(parentId)) {
break;
}
parent = pptxMapper.selectById(parentId);
if (parent == null) {
break;
}
}
}
@VisibleForTesting
void validatePptxNameUnique(Long id, Long parentId, String name) {
ExamWpsPptx dept = pptxMapper.selectByParentIdAndName(parentId, name);
if (dept == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同 id 的PPTX考点
if (id == null) {
throw exception(PPTX_NAME_DUPLICATE);
}
if (ObjectUtil.notEqual(dept.getId(), id)) {
throw exception(PPTX_NAME_DUPLICATE);
}
}
@Override
public ExamWpsPptx getPptx(Long id) {
return pptxMapper.selectById(id);
}
@Override
public ExamWpsPptx getPptxByTitle(String title) {
return pptxMapper.selectByTitle(title);
}
@Override
public List<ExamWpsPptx> getPptxList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return Collections.emptyList();
}
return pptxMapper.selectBatchIds(ids);
}
@Override
public List<ExamWpsPptx> getPptxList(PptxListReqVO reqVO) {
List<ExamWpsPptx> list = pptxMapper.selectList(reqVO);
list.sort(Comparator.comparing(ExamWpsPptx::getSort));
return list;
}
@Override
public Map<Long, ExamWpsPptx> getPptxMap(Collection<Long> ids) {
return ExamWpsPptxService.super.getPptxMap(ids);
}
@Override
public List<ExamWpsPptx> getChildPptxList(Long id) {
return ExamWpsPptxService.super.getChildPptxList(id);
}
@Override
public List<ExamWpsPptx> getChildPptxList(Collection<Long> ids) {
List<ExamWpsPptx> children = new LinkedList<>();
// 遍历每一层
Collection<Long> parentIds = ids;
for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下,存在死循环
// 查询当前层所有的子PPTX考点
List<ExamWpsPptx> pptxs = pptxMapper.selectListByParentId(parentIds);
// 1. 如果没有子PPTX考点则结束遍历
if (CollUtil.isEmpty(pptxs)) {
break;
}
// 2. 如果有子PPTX考点继续遍历
children.addAll(pptxs);
parentIds = convertSet(pptxs, ExamWpsPptx::getId);
}
return children;
}
@Override
public Set<Long> getChildPptxIdListFromCache(Long id) {
List<ExamWpsPptx> children = getChildPptxList(id);
return convertSet(children, ExamWpsPptx::getId);
}
@Override
public void validatePptxList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 获得科室信息
Map<Long, ExamWpsPptx> pptxMap = getPptxMap(ids);
// 校验
ids.forEach(id -> {
ExamWpsPptx pptx = pptxMap.get(id);
if (pptx == null) {
throw exception(PPTX_NOT_FOUND);
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(pptx.getStatus())) {
throw exception(PPTX_NOT_ENABLE, pptx.getName());
}
});
}
}