Commit 31aded61d8c05fc76162e2719f42fd824b641981

Authored by YunaiV
1 parent 6aa235e4

feat(pms): add project and knowledge management

Showing 320 changed files with 31954 additions and 1 deletions

Too many changes.

To preserve performance only 100 of 320 files are displayed.

... ... @@ -28,7 +28,8 @@
28 28 <!-- <module>yudao-module-wms</module>-->
29 29 <!-- <module>yudao-module-hrm</module>-->
30 30 <!-- <module>yudao-module-fms</module>-->
31   - <!-- <module>yudao-module-im</module>-->
  31 + <module>yudao-module-pms</module>
  32 +<!-- <module>yudao-module-im</module>-->
32 33 <!-- 请参考 https://doc.iocoder.cn/ai/build/ 文档,完成 AI 模块的启动!!! -->
33 34 <!-- <module>yudao-module-ai</module>-->
34 35 </modules>
... ...
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/StringListConvert.java
1 1 package cn.iocoder.yudao.framework.excel.core.convert;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
4 5 import cn.idev.excel.converters.Converter;
5 6 import cn.idev.excel.metadata.GlobalConfiguration;
  7 +import cn.idev.excel.metadata.data.ReadCellData;
6 8 import cn.idev.excel.metadata.data.WriteCellData;
7 9 import cn.idev.excel.metadata.property.ExcelContentProperty;
8 10  
  11 +import java.util.Collections;
9 12 import java.util.List;
10 13  
11 14 /**
... ... @@ -15,6 +18,25 @@ import java.util.List;
15 18 */
16 19 public class StringListConvert implements Converter<List<String>> {
17 20  
  21 + /**
  22 + * 将 Excel 单元格中的分隔字符串解析为字符串列表。
  23 + *
  24 + * @param value Excel 单元格数据
  25 + * @param contentProperty Excel 字段属性
  26 + * @param globalConfiguration Excel 全局配置
  27 + * @return 字符串列表
  28 + */
  29 + @Override
  30 + public List<String> convertToJavaData(ReadCellData<?> value, ExcelContentProperty contentProperty,
  31 + GlobalConfiguration globalConfiguration) {
  32 + String text = value == null ? null : value.getStringValue();
  33 + if (StrUtil.isBlank(text)) {
  34 + return Collections.emptyList();
  35 + }
  36 + // 兼容导入模板常用的英文逗号、中文逗号、顿号,以及导出时使用的斜杠。
  37 + return StrUtil.splitTrim(text.replace(',', ',').replace('、', ',').replace('/', ','), ',');
  38 + }
  39 +
18 40 @Override
19 41 public WriteCellData<String> convertToExcelData(List<String> value,
20 42 ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java
... ... @@ -50,6 +50,10 @@ public class BannerApplicationRunner implements ApplicationRunner {
50 50 if (isNotPresent("cn.iocoder.yudao.module.wms.framework.web.config.WmsWebConfiguration")) {
51 51 System.out.println("[WMS 仓库管理系统 yudao-module-wms - 已禁用][参考 https://doc.iocoder.cn/wms/build/ 开启]");
52 52 }
  53 + // PMS 系统
  54 + if (isNotPresent("cn.iocoder.yudao.module.pms.framework.web.config.PmsWebConfiguration")) {
  55 + System.out.println("[PMS 项目管理系统 yudao-module-pms - 已禁用][请在 yudao-server 中开启]");
  56 + }
53 57 // CRM 系统
54 58 if (isNotPresent("cn.iocoder.yudao.module.crm.framework.web.config.CrmWebConfiguration")) {
55 59 System.out.println("[CRM 系统 yudao-module-crm - 已禁用][参考 https://doc.iocoder.cn/crm/build/ 开启]");
... ...
yudao-module-pms/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <groupId>cn.iocoder.boot</groupId>
  7 + <artifactId>yudao</artifactId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 +
  12 + <artifactId>yudao-module-pms</artifactId>
  13 + <packaging>jar</packaging>
  14 +
  15 + <name>${project.artifactId}</name>
  16 + <description>
  17 + pms 包下,项目管理(Project Management System)
  18 + 例如说:项目、工作事项、迭代、看板、工时和知识库等等
  19 + </description>
  20 +
  21 + <dependencies>
  22 + <dependency>
  23 + <groupId>cn.iocoder.boot</groupId>
  24 + <artifactId>yudao-module-system</artifactId>
  25 + <version>${revision}</version>
  26 + </dependency>
  27 + <dependency>
  28 + <groupId>cn.iocoder.boot</groupId>
  29 + <artifactId>yudao-module-infra</artifactId>
  30 + <version>${revision}</version>
  31 + </dependency>
  32 +
  33 + <!-- Web 相关 -->
  34 + <dependency>
  35 + <groupId>cn.iocoder.boot</groupId>
  36 + <artifactId>yudao-spring-boot-starter-web</artifactId>
  37 + </dependency>
  38 + <dependency>
  39 + <groupId>cn.iocoder.boot</groupId>
  40 + <artifactId>yudao-spring-boot-starter-security</artifactId>
  41 + </dependency>
  42 +
  43 + <!-- DB 相关 -->
  44 + <dependency>
  45 + <groupId>cn.iocoder.boot</groupId>
  46 + <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
  47 + </dependency>
  48 +
  49 + <!-- Job 定时任务相关 -->
  50 + <dependency>
  51 + <groupId>cn.iocoder.boot</groupId>
  52 + <artifactId>yudao-spring-boot-starter-job</artifactId>
  53 + </dependency>
  54 +
  55 + <!-- Test 测试相关 -->
  56 + <dependency>
  57 + <groupId>cn.iocoder.boot</groupId>
  58 + <artifactId>yudao-spring-boot-starter-test</artifactId>
  59 + <scope>test</scope>
  60 + </dependency>
  61 + </dependencies>
  62 +
  63 +</project>
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/PmsKnowledgeContentPermissionController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.permission.PmsKnowledgeContentPermissionRespVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.permission.PmsKnowledgeContentPermissionUpdateReqVO;
  8 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.content.PmsKnowledgeContentPermissionDO;
  9 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.content.PmsKnowledgeContentPermissionMemberDO;
  10 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeContentPermissionService;
  11 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  12 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  13 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  14 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  15 +import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import jakarta.annotation.Resource;
  19 +import jakarta.validation.Valid;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.bind.annotation.GetMapping;
  23 +import org.springframework.web.bind.annotation.PutMapping;
  24 +import org.springframework.web.bind.annotation.RequestBody;
  25 +import org.springframework.web.bind.annotation.RequestMapping;
  26 +import org.springframework.web.bind.annotation.RequestParam;
  27 +import org.springframework.web.bind.annotation.RestController;
  28 +
  29 +import java.util.List;
  30 +import java.util.Map;
  31 +
  32 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  35 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  36 +
  37 +@Tag(name = "管理后台 - PMS 知识内容协作权限")
  38 +@RestController
  39 +@RequestMapping("/pms/kb/content-permission")
  40 +@Validated
  41 +public class PmsKnowledgeContentPermissionController {
  42 +
  43 + @Resource
  44 + private PmsKnowledgeContentPermissionService permissionService;
  45 +
  46 + @Resource
  47 + private AdminUserApi adminUserApi;
  48 + @Resource
  49 + private DeptApi deptApi;
  50 +
  51 + @GetMapping("/get")
  52 + @Operation(summary = "获得知识内容协作权限")
  53 + @Parameter(name = "id", description = "协作权限编号", required = true, example = "1024")
  54 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  55 + public CommonResult<PmsKnowledgeContentPermissionRespVO> getContentPermission(@RequestParam("id") Long id) {
  56 + Long userId = getLoginUserId();
  57 + PmsKnowledgeContentPermissionDO permission = permissionService.getContentPermission(id, userId);
  58 + List<PmsKnowledgeContentPermissionMemberDO> members = permissionService.getContentPermissionMemberList(id, userId);
  59 + Integer currentUserLevel = permissionService.getCurrentUserContentPermissionLevel(
  60 + id, permission.getLibraryId(), userId);
  61 + return success(buildPermissionRespVO(permission, members, currentUserLevel));
  62 + }
  63 +
  64 + @PutMapping("/update")
  65 + @Operation(summary = "更新知识内容协作权限")
  66 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  67 + public CommonResult<Boolean> updateContentPermission(@Valid @RequestBody PmsKnowledgeContentPermissionUpdateReqVO updateReqVO) {
  68 + permissionService.updateContentPermission(updateReqVO, getLoginUserId());
  69 + return success(true);
  70 + }
  71 +
  72 + // ==================== 拼接 VO ====================
  73 +
  74 + private PmsKnowledgeContentPermissionRespVO buildPermissionRespVO(PmsKnowledgeContentPermissionDO permission,
  75 + List<PmsKnowledgeContentPermissionMemberDO> members,
  76 + Integer currentUserLevel) {
  77 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  78 + convertSet(members, PmsKnowledgeContentPermissionMemberDO::getUserId));
  79 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
  80 + convertSet(members, PmsKnowledgeContentPermissionMemberDO::getDeptId));
  81 + // 拼接 VO 返回
  82 + PmsKnowledgeContentPermissionRespVO permissionVO = BeanUtils.toBean(permission, PmsKnowledgeContentPermissionRespVO.class)
  83 + .setCreatorUserId(NumberUtils.parseLong(permission.getCreator()))
  84 + .setCurrentUserLevel(currentUserLevel)
  85 + .setMembers(BeanUtils.toBean(members, PmsKnowledgeContentPermissionRespVO.Member.class));
  86 + permissionVO.getMembers().forEach(member -> {
  87 + findAndThen(userMap, member.getUserId(), user -> member.setUserName(user.getNickname()));
  88 + findAndThen(deptMap, member.getDeptId(), dept -> member.setDeptName(dept.getName()));
  89 + });
  90 + return permissionVO;
  91 + }
  92 +
  93 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/PmsKnowledgeDocumentController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document.PmsKnowledgeDocumentCreateReqVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document.PmsKnowledgeDocumentMoveReqVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document.PmsKnowledgeDocumentRespVO;
  11 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document.PmsKnowledgeDocumentSearchPageReqVO;
  12 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document.PmsKnowledgeDocumentUpdateReqVO;
  13 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.content.PmsKnowledgeDocumentDO;
  14 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.interaction.PmsKnowledgeDocumentLikeDO;
  15 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.library.PmsKnowledgeLibraryDO;
  16 +import cn.iocoder.yudao.module.pms.enums.kb.content.PmsKnowledgeContentLevelEnum;
  17 +import cn.iocoder.yudao.module.pms.enums.kb.content.PmsKnowledgeDocumentTypeEnum;
  18 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeContentPermissionService;
  19 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeDocumentService;
  20 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeDocumentLikeService;
  21 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeFavoriteService;
  22 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeViewRecordService;
  23 +import cn.iocoder.yudao.module.pms.service.kb.library.PmsKnowledgeLibraryService;
  24 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  25 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  26 +import io.swagger.v3.oas.annotations.Operation;
  27 +import io.swagger.v3.oas.annotations.Parameter;
  28 +import io.swagger.v3.oas.annotations.tags.Tag;
  29 +import jakarta.annotation.Resource;
  30 +import jakarta.validation.Valid;
  31 +import org.springframework.security.access.prepost.PreAuthorize;
  32 +import org.springframework.validation.annotation.Validated;
  33 +import org.springframework.web.bind.annotation.DeleteMapping;
  34 +import org.springframework.web.bind.annotation.GetMapping;
  35 +import org.springframework.web.bind.annotation.PostMapping;
  36 +import org.springframework.web.bind.annotation.PutMapping;
  37 +import org.springframework.web.bind.annotation.RequestBody;
  38 +import org.springframework.web.bind.annotation.RequestMapping;
  39 +import org.springframework.web.bind.annotation.RequestParam;
  40 +import org.springframework.web.bind.annotation.RestController;
  41 +
  42 +import java.util.Collections;
  43 +import java.util.List;
  44 +import java.util.Map;
  45 +import java.util.Set;
  46 +
  47 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  48 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  49 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  50 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  51 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  52 +
  53 +@Tag(name = "管理后台 - PMS 知识库文档")
  54 +@RestController
  55 +@RequestMapping("/pms/kb/document")
  56 +@Validated
  57 +public class PmsKnowledgeDocumentController {
  58 +
  59 + @Resource
  60 + private PmsKnowledgeDocumentService documentService;
  61 + @Resource
  62 + private PmsKnowledgeContentPermissionService contentPermissionService;
  63 + @Resource
  64 + private PmsKnowledgeFavoriteService favoriteService;
  65 + @Resource
  66 + private PmsKnowledgeDocumentLikeService documentLikeService;
  67 + @Resource
  68 + private PmsKnowledgeViewRecordService viewRecordService;
  69 + @Resource
  70 + private PmsKnowledgeLibraryService libraryService;
  71 +
  72 + @Resource
  73 + private AdminUserApi adminUserApi;
  74 +
  75 + @PostMapping("/create")
  76 + @Operation(summary = "创建文档")
  77 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  78 + public CommonResult<Long> createDocument(@Valid @RequestBody PmsKnowledgeDocumentCreateReqVO createReqVO) {
  79 + return success(documentService.createDocument(createReqVO, getLoginUserId()));
  80 + }
  81 +
  82 + @PutMapping("/update")
  83 + @Operation(summary = "更新文档内容")
  84 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  85 + public CommonResult<Boolean> updateDocument(@Valid @RequestBody PmsKnowledgeDocumentUpdateReqVO updateReqVO) {
  86 + documentService.updateDocument(updateReqVO, getLoginUserId());
  87 + return success(true);
  88 + }
  89 +
  90 + @DeleteMapping("/delete")
  91 + @Operation(summary = "将文档移入回收站")
  92 + @Parameter(name = "id", description = "文档编号", required = true, example = "1024")
  93 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  94 + public CommonResult<Boolean> deleteDocument(@RequestParam("id") Long id) {
  95 + documentService.deleteDocument(id, getLoginUserId());
  96 + return success(true);
  97 + }
  98 +
  99 + @PutMapping("/move")
  100 + @Operation(summary = "移动文档")
  101 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  102 + public CommonResult<Boolean> moveDocument(@Valid @RequestBody PmsKnowledgeDocumentMoveReqVO moveReqVO) {
  103 + documentService.moveDocument(moveReqVO, getLoginUserId());
  104 + return success(true);
  105 + }
  106 +
  107 + @GetMapping("/get")
  108 + @Operation(summary = "获得文档详情")
  109 + @Parameter(name = "id", description = "文档编号", required = true, example = "1024")
  110 + @Parameter(name = "view", description = "是否记录本次浏览", example = "true")
  111 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  112 + public CommonResult<PmsKnowledgeDocumentRespVO> getDocument(
  113 + @RequestParam("id") Long id,
  114 + @RequestParam(value = "view", defaultValue = "false") Boolean view) {
  115 + // 查询文档
  116 + Long userId = getLoginUserId();
  117 + PmsKnowledgeDocumentDO document = documentService.getDocument(id, userId);
  118 + // 记录浏览
  119 + if (Boolean.TRUE.equals(view)) {
  120 + viewRecordService.createViewRecord(document.getLibraryId(), document.getType(), document.getId(), userId);
  121 + }
  122 + // 构建文档 VO
  123 + return success(buildDocumentRespVO(document, userId));
  124 + }
  125 +
  126 + @GetMapping("/search-page")
  127 + @Operation(summary = "使用 MySQL 全局搜索文档")
  128 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  129 + public CommonResult<PageResult<PmsKnowledgeDocumentRespVO>> getDocumentSearchPage(
  130 + @Valid PmsKnowledgeDocumentSearchPageReqVO pageReqVO) {
  131 + PageResult<PmsKnowledgeDocumentDO> pageResult = documentService.getDocumentSearchPage(
  132 + pageReqVO, getLoginUserId());
  133 + return success(new PageResult<>(buildDocumentRespVOList(pageResult.getList()), pageResult.getTotal()));
  134 + }
  135 +
  136 + // ==================== 拼接 VO ====================
  137 +
  138 + private PmsKnowledgeDocumentRespVO buildDocumentRespVO(PmsKnowledgeDocumentDO document, Long userId) {
  139 + // 1.1 拼接当前用户的内容权限和互动状态
  140 + Integer currentUserLevel = contentPermissionService.getCurrentUserContentPermissionLevel(
  141 + document.getPermissionId(), document.getLibraryId(), userId);
  142 + Long creatorUserId = NumberUtils.parseLong(document.getCreator());
  143 + PmsKnowledgeDocumentRespVO documentVO = BeanUtils.toBean(document, PmsKnowledgeDocumentRespVO.class)
  144 + .setCreatorUserId(creatorUserId).setCurrentUserLevel(currentUserLevel)
  145 + .setDownloadStatus(PmsKnowledgeContentLevelEnum.canDownload(currentUserLevel))
  146 + .setPreviewUrl(document.getContent())
  147 + .setFavoriteStatus(favoriteService.isFavorite(document.getType(), document.getId(), userId));
  148 + // 1.2 文件不可下载时,隐藏实际文件地址
  149 + if (PmsKnowledgeDocumentTypeEnum.FILE.getType().equals(document.getType())
  150 + && Boolean.FALSE.equals(documentVO.getDownloadStatus())) {
  151 + documentVO.setContent(null);
  152 + }
  153 +
  154 + // 2. 一次查询并拼接创建人和点赞用户信息
  155 + List<PmsKnowledgeDocumentLikeDO> likes = documentLikeService.getDocumentLikeList(document.getId());
  156 + Set<Long> relatedUserIds = convertSet(likes, PmsKnowledgeDocumentLikeDO::getUserId);
  157 + relatedUserIds.add(creatorUserId);
  158 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(relatedUserIds);
  159 + findAndThen(userMap, creatorUserId, user -> documentVO.setCreatorUserName(user.getNickname()));
  160 + documentVO.setLikeStatus(CollUtil.anyMatch(likes, like -> userId.equals(like.getUserId())))
  161 + .setLikeUsers(convertList(likes, like -> {
  162 + PmsKnowledgeDocumentRespVO.LikeUser likeUser = new PmsKnowledgeDocumentRespVO.LikeUser()
  163 + .setId(like.getUserId());
  164 + findAndThen(userMap, like.getUserId(), user ->
  165 + likeUser.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
  166 + return likeUser;
  167 + }));
  168 + return documentVO;
  169 + }
  170 +
  171 + private List<PmsKnowledgeDocumentRespVO> buildDocumentRespVOList(List<PmsKnowledgeDocumentDO> documents) {
  172 + if (CollUtil.isEmpty(documents)) {
  173 + return Collections.emptyList();
  174 + }
  175 + Map<Long, PmsKnowledgeLibraryDO> libraryMap = libraryService.getLibraryMap(
  176 + convertSet(documents, PmsKnowledgeDocumentDO::getLibraryId));
  177 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(documents,
  178 + document -> NumberUtils.parseLong(document.getCreator())));
  179 + return convertList(documents, document -> {
  180 + PmsKnowledgeDocumentRespVO documentVO = BeanUtils.toBean(document, PmsKnowledgeDocumentRespVO.class)
  181 + .setContent(null).setContentSummary(buildContentSummary(document.getContent()))
  182 + .setCreatorUserId(NumberUtils.parseLong(document.getCreator()));
  183 + findAndThen(libraryMap, document.getLibraryId(),
  184 + library -> documentVO.setLibraryName(library.getName()));
  185 + findAndThen(userMap, documentVO.getCreatorUserId(),
  186 + user -> documentVO.setCreatorUserName(user.getNickname()));
  187 + return documentVO;
  188 + });
  189 + }
  190 +
  191 + /**
  192 + * 构造搜索结果摘要,避免直接返回完整正文
  193 + */
  194 + private String buildContentSummary(String content) {
  195 + if (content == null || content.trim().isEmpty()) {
  196 + return null;
  197 + }
  198 + String summary = content.replaceAll("<[^>]+>", " ").replaceAll("\\s+", " ").trim();
  199 + return summary.length() > 120 ? summary.substring(0, 120) + "…" : summary;
  200 + }
  201 +
  202 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/PmsKnowledgeDocumentLabelController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document.PmsKnowledgeDocumentRespVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.label.PmsKnowledgeDocumentLabelPageReqVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.label.PmsKnowledgeDocumentLabelRespVO;
  11 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.label.PmsKnowledgeDocumentLabelSaveReqVO;
  12 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.content.PmsKnowledgeDocumentDO;
  13 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.library.PmsKnowledgeLibraryDO;
  14 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeDocumentLabelService;
  15 +import cn.iocoder.yudao.module.pms.service.kb.library.PmsKnowledgeLibraryService;
  16 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  17 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  18 +import io.swagger.v3.oas.annotations.Operation;
  19 +import io.swagger.v3.oas.annotations.Parameter;
  20 +import io.swagger.v3.oas.annotations.tags.Tag;
  21 +import jakarta.annotation.Resource;
  22 +import jakarta.validation.Valid;
  23 +import org.springframework.security.access.prepost.PreAuthorize;
  24 +import org.springframework.validation.annotation.Validated;
  25 +import org.springframework.web.bind.annotation.DeleteMapping;
  26 +import org.springframework.web.bind.annotation.GetMapping;
  27 +import org.springframework.web.bind.annotation.PostMapping;
  28 +import org.springframework.web.bind.annotation.PutMapping;
  29 +import org.springframework.web.bind.annotation.RequestBody;
  30 +import org.springframework.web.bind.annotation.RequestMapping;
  31 +import org.springframework.web.bind.annotation.RequestParam;
  32 +import org.springframework.web.bind.annotation.RestController;
  33 +
  34 +import java.util.Collections;
  35 +import java.util.List;
  36 +import java.util.Map;
  37 +
  38 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  39 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  40 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  41 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  42 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  43 +
  44 +@Tag(name = "管理后台 - PMS 知识库文档标签")
  45 +@RestController
  46 +@RequestMapping("/pms/kb/document-label")
  47 +@Validated
  48 +public class PmsKnowledgeDocumentLabelController {
  49 +
  50 + @Resource
  51 + private PmsKnowledgeDocumentLabelService documentLabelService;
  52 + @Resource
  53 + private PmsKnowledgeLibraryService libraryService;
  54 +
  55 + @Resource
  56 + private AdminUserApi adminUserApi;
  57 +
  58 + @PostMapping("/create")
  59 + @Operation(summary = "创建文档标签")
  60 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  61 + public CommonResult<Long> createDocumentLabel(
  62 + @Valid @RequestBody PmsKnowledgeDocumentLabelSaveReqVO saveReqVO) {
  63 + return success(documentLabelService.createDocumentLabel(saveReqVO));
  64 + }
  65 +
  66 + @PutMapping("/update")
  67 + @Operation(summary = "修改文档标签")
  68 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  69 + public CommonResult<Boolean> updateDocumentLabel(
  70 + @Valid @RequestBody PmsKnowledgeDocumentLabelSaveReqVO saveReqVO) {
  71 + documentLabelService.updateDocumentLabel(saveReqVO);
  72 + return success(true);
  73 + }
  74 +
  75 + @DeleteMapping("/delete")
  76 + @Operation(summary = "删除文档标签")
  77 + @Parameter(name = "id", description = "文档标签编号", required = true, example = "1024")
  78 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  79 + public CommonResult<Boolean> deleteDocumentLabel(@RequestParam("id") Long id) {
  80 + documentLabelService.deleteDocumentLabel(id);
  81 + return success(true);
  82 + }
  83 +
  84 + @GetMapping("/get")
  85 + @Operation(summary = "获得文档标签详情")
  86 + @Parameter(name = "id", description = "文档标签编号", required = true, example = "1024")
  87 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  88 + public CommonResult<PmsKnowledgeDocumentLabelRespVO> getDocumentLabel(@RequestParam("id") Long id) {
  89 + return success(BeanUtils.toBean(documentLabelService.getDocumentLabel(id),
  90 + PmsKnowledgeDocumentLabelRespVO.class));
  91 + }
  92 +
  93 + @GetMapping("/list")
  94 + @Operation(summary = "获得文档标签列表")
  95 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  96 + public CommonResult<List<PmsKnowledgeDocumentLabelRespVO>> getDocumentLabelList() {
  97 + return success(BeanUtils.toBean(documentLabelService.getDocumentLabelList(),
  98 + PmsKnowledgeDocumentLabelRespVO.class));
  99 + }
  100 +
  101 + @GetMapping("/document-page")
  102 + @Operation(summary = "获得指定标签的文档分页")
  103 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  104 + public CommonResult<PageResult<PmsKnowledgeDocumentRespVO>> getDocumentPageByLabel(
  105 + @Valid PmsKnowledgeDocumentLabelPageReqVO pageReqVO) {
  106 + PageResult<PmsKnowledgeDocumentDO> pageResult = documentLabelService.getDocumentPageByLabel(
  107 + pageReqVO, getLoginUserId());
  108 + return success(new PageResult<>(buildDocumentRespVOList(pageResult.getList()), pageResult.getTotal()));
  109 + }
  110 +
  111 + // ==================== 拼接 VO ====================
  112 +
  113 + private List<PmsKnowledgeDocumentRespVO> buildDocumentRespVOList(List<PmsKnowledgeDocumentDO> documents) {
  114 + if (CollUtil.isEmpty(documents)) {
  115 + return Collections.emptyList();
  116 + }
  117 + Map<Long, PmsKnowledgeLibraryDO> libraryMap = libraryService.getLibraryMap(
  118 + convertSet(documents, PmsKnowledgeDocumentDO::getLibraryId));
  119 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(documents,
  120 + document -> NumberUtils.parseLong(document.getCreator())));
  121 + return convertList(documents, document -> {
  122 + PmsKnowledgeDocumentRespVO documentVO = BeanUtils.toBean(document, PmsKnowledgeDocumentRespVO.class)
  123 + .setContent(null).setCreatorUserId(NumberUtils.parseLong(document.getCreator()));
  124 + findAndThen(libraryMap, document.getLibraryId(),
  125 + library -> documentVO.setLibraryName(library.getName()));
  126 + findAndThen(userMap, documentVO.getCreatorUserId(),
  127 + user -> documentVO.setCreatorUserName(user.getNickname()));
  128 + return documentVO;
  129 + });
  130 + }
  131 +
  132 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/PmsKnowledgeFolderController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.folder.PmsKnowledgeFolderMoveReqVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.folder.PmsKnowledgeFolderRespVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.folder.PmsKnowledgeFolderSaveReqVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.folder.PmsKnowledgeTreeRespVO;
  10 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.content.PmsKnowledgeDocumentDO;
  11 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.content.PmsKnowledgeFolderDO;
  12 +import cn.iocoder.yudao.module.pms.enums.kb.PmsKnowledgeObjectTypeEnum;
  13 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeContentPermissionService;
  14 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeDocumentService;
  15 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeFolderService;
  16 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeFavoriteService;
  17 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeViewRecordService;
  18 +import cn.iocoder.yudao.module.pms.service.kb.library.PmsKnowledgeLibraryMemberService;
  19 +import io.swagger.v3.oas.annotations.Operation;
  20 +import io.swagger.v3.oas.annotations.Parameter;
  21 +import io.swagger.v3.oas.annotations.tags.Tag;
  22 +import jakarta.annotation.Resource;
  23 +import jakarta.validation.Valid;
  24 +import org.springframework.security.access.prepost.PreAuthorize;
  25 +import org.springframework.validation.annotation.Validated;
  26 +import org.springframework.web.bind.annotation.DeleteMapping;
  27 +import org.springframework.web.bind.annotation.GetMapping;
  28 +import org.springframework.web.bind.annotation.PostMapping;
  29 +import org.springframework.web.bind.annotation.PutMapping;
  30 +import org.springframework.web.bind.annotation.RequestBody;
  31 +import org.springframework.web.bind.annotation.RequestMapping;
  32 +import org.springframework.web.bind.annotation.RequestParam;
  33 +import org.springframework.web.bind.annotation.RestController;
  34 +
  35 +import java.util.ArrayList;
  36 +import java.util.List;
  37 +import java.util.Map;
  38 +import java.util.Set;
  39 +
  40 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  41 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  42 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  43 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  44 +
  45 +@Tag(name = "管理后台 - PMS 知识库文件夹")
  46 +@RestController
  47 +@RequestMapping("/pms/kb/folder")
  48 +@Validated
  49 +public class PmsKnowledgeFolderController {
  50 +
  51 + @Resource
  52 + private PmsKnowledgeFolderService folderService;
  53 + @Resource
  54 + private PmsKnowledgeDocumentService documentService;
  55 + @Resource
  56 + private PmsKnowledgeContentPermissionService contentPermissionService;
  57 + @Resource
  58 + private PmsKnowledgeLibraryMemberService libraryMemberService;
  59 + @Resource
  60 + private PmsKnowledgeFavoriteService favoriteService;
  61 + @Resource
  62 + private PmsKnowledgeViewRecordService viewRecordService;
  63 +
  64 + @PostMapping("/create")
  65 + @Operation(summary = "创建文件夹")
  66 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  67 + public CommonResult<Long> createFolder(@Valid @RequestBody PmsKnowledgeFolderSaveReqVO saveReqVO) {
  68 + return success(folderService.createFolder(saveReqVO, getLoginUserId()));
  69 + }
  70 +
  71 + @PutMapping("/update")
  72 + @Operation(summary = "更新文件夹")
  73 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  74 + public CommonResult<Boolean> updateFolder(@Valid @RequestBody PmsKnowledgeFolderSaveReqVO saveReqVO) {
  75 + folderService.updateFolder(saveReqVO, getLoginUserId());
  76 + return success(true);
  77 + }
  78 +
  79 + @DeleteMapping("/delete")
  80 + @Operation(summary = "将文件夹移入回收站")
  81 + @Parameter(name = "id", description = "文件夹编号", required = true, example = "1024")
  82 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  83 + public CommonResult<Boolean> deleteFolder(@RequestParam("id") Long id) {
  84 + folderService.deleteFolder(id, getLoginUserId());
  85 + return success(true);
  86 + }
  87 +
  88 + @PutMapping("/move")
  89 + @Operation(summary = "移动文件夹")
  90 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  91 + public CommonResult<Boolean> moveFolder(@Valid @RequestBody PmsKnowledgeFolderMoveReqVO moveReqVO) {
  92 + folderService.moveFolder(moveReqVO, getLoginUserId());
  93 + return success(true);
  94 + }
  95 +
  96 + @GetMapping("/get")
  97 + @Operation(summary = "获得文件夹详情")
  98 + @Parameter(name = "id", description = "文件夹编号", required = true, example = "1024")
  99 + @Parameter(name = "view", description = "是否记录本次浏览", example = "true")
  100 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  101 + public CommonResult<PmsKnowledgeFolderRespVO> getFolder(
  102 + @RequestParam("id") Long id,
  103 + @RequestParam(value = "view", defaultValue = "false") Boolean view) {
  104 + Long userId = getLoginUserId();
  105 + PmsKnowledgeFolderDO folder = folderService.getFolder(id, userId);
  106 + if (Boolean.TRUE.equals(view)) {
  107 + viewRecordService.createViewRecord(folder.getLibraryId(), PmsKnowledgeObjectTypeEnum.FOLDER.getType(),
  108 + folder.getId(), userId);
  109 + }
  110 + return success(buildFolderRespVO(folder, userId));
  111 + }
  112 +
  113 + @GetMapping("/tree")
  114 + @Operation(summary = "获得知识库目录树")
  115 + @Parameter(name = "libraryId", description = "知识库编号", required = true, example = "1024")
  116 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  117 + public CommonResult<PmsKnowledgeTreeRespVO> getFolderTree(@RequestParam("libraryId") Long libraryId) {
  118 + Long userId = getLoginUserId();
  119 + List<PmsKnowledgeFolderDO> folders = folderService.getFolderList(libraryId, userId);
  120 + List<PmsKnowledgeDocumentDO> documents = documentService.getDocumentList(libraryId, userId);
  121 + return success(buildTreeRespVO(libraryId, userId,
  122 + libraryMemberService.isLibraryWritable(libraryId, userId),
  123 + libraryMemberService.isLibraryAdmin(libraryId, userId), folders, documents));
  124 + }
  125 +
  126 + // ==================== 拼接 VO ====================
  127 +
  128 + private PmsKnowledgeFolderRespVO buildFolderRespVO(PmsKnowledgeFolderDO folder, Long userId) {
  129 + List<PmsKnowledgeFolderDO> folders = folderService.getFolderList(folder.getLibraryId(), userId);
  130 + List<PmsKnowledgeDocumentDO> documents = documentService.getDocumentList(folder.getLibraryId(), userId);
  131 + int childFolderCount = CollUtil.count(folders,
  132 + item -> folder.getId().equals(item.getParentId()));
  133 + int documentCount = (int) CollUtil.count(documents,
  134 + item -> folder.getId().equals(item.getFolderId())
  135 + && (item.getParentId() == null || item.getParentId() == 0L));
  136 + return BeanUtils.toBean(folder, PmsKnowledgeFolderRespVO.class)
  137 + .setChildFolderCount(childFolderCount).setDocumentCount(documentCount)
  138 + .setCurrentUserLevel(contentPermissionService.getCurrentUserContentPermissionLevel(folder.getPermissionId(), folder.getLibraryId(), userId))
  139 + .setFavoriteStatus(favoriteService.isFavorite(PmsKnowledgeObjectTypeEnum.FOLDER.getType(), folder.getId(), userId));
  140 + }
  141 +
  142 + private PmsKnowledgeTreeRespVO buildTreeRespVO(Long libraryId, Long userId, boolean writeStatus,
  143 + boolean manageStatus,
  144 + List<PmsKnowledgeFolderDO> folders, List<PmsKnowledgeDocumentDO> documents) {
  145 + // 1. 批量获得当前用户的内容权限等级
  146 + Set<Long> permissionIds = convertSet(folders, PmsKnowledgeFolderDO::getPermissionId);
  147 + permissionIds.addAll(convertSet(documents, PmsKnowledgeDocumentDO::getPermissionId));
  148 + Map<Long, Integer> levelMap = contentPermissionService.getCurrentUserContentPermissionLevelMap(permissionIds, libraryId, userId);
  149 +
  150 + // 2. 批量转换文件夹和文档节点
  151 + Map<Long, PmsKnowledgeTreeRespVO.FolderNode> folderNodeMap = convertMap(folders,
  152 + PmsKnowledgeFolderDO::getId, folder -> BeanUtils.toBean(folder, PmsKnowledgeTreeRespVO.FolderNode.class)
  153 + .setCurrentUserLevel(levelMap.get(folder.getPermissionId())).setChildren(new ArrayList<>()).setDocuments(new ArrayList<>()));
  154 + Map<Long, PmsKnowledgeTreeRespVO.DocumentNode> documentNodeMap = convertMap(documents,
  155 + PmsKnowledgeDocumentDO::getId, document -> BeanUtils.toBean(document, PmsKnowledgeTreeRespVO.DocumentNode.class)
  156 + .setCurrentUserLevel(levelMap.get(document.getPermissionId())).setChildren(new ArrayList<>()));
  157 +
  158 + // 3. 按父文档或所属文件夹组装文档树
  159 + List<PmsKnowledgeTreeRespVO.DocumentNode> rootDocuments = new ArrayList<>();
  160 + for (PmsKnowledgeDocumentDO document : documents) {
  161 + PmsKnowledgeTreeRespVO.DocumentNode node = documentNodeMap.get(document.getId());
  162 + PmsKnowledgeTreeRespVO.DocumentNode parentNode = documentNodeMap.get(document.getParentId());
  163 + PmsKnowledgeTreeRespVO.FolderNode folderNode = folderNodeMap.get(document.getFolderId());
  164 + if (parentNode != null) {
  165 + parentNode.getChildren().add(node);
  166 + } else if (folderNode != null) {
  167 + folderNode.getDocuments().add(node);
  168 + } else {
  169 + rootDocuments.add(node);
  170 + }
  171 + }
  172 +
  173 + // 4. 按父文件夹组装文件夹树
  174 + List<PmsKnowledgeTreeRespVO.FolderNode> rootFolders = new ArrayList<>();
  175 + for (PmsKnowledgeFolderDO folder : folders) {
  176 + PmsKnowledgeTreeRespVO.FolderNode node = folderNodeMap.get(folder.getId());
  177 + PmsKnowledgeTreeRespVO.FolderNode parentNode = folderNodeMap.get(folder.getParentId());
  178 + if (parentNode != null) {
  179 + parentNode.getChildren().add(node);
  180 + } else {
  181 + rootFolders.add(node);
  182 + }
  183 + }
  184 + return new PmsKnowledgeTreeRespVO().setLibraryId(libraryId).setWriteStatus(writeStatus)
  185 + .setManageStatus(manageStatus)
  186 + .setFolders(rootFolders).setDocuments(rootDocuments);
  187 + }
  188 +
  189 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/document/PmsKnowledgeDocumentCreateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.pms.enums.kb.content.PmsKnowledgeDocumentTypeEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.Max;
  7 +import jakarta.validation.constraints.Min;
  8 +import jakarta.validation.constraints.NotBlank;
  9 +import jakarta.validation.constraints.NotNull;
  10 +import jakarta.validation.constraints.Size;
  11 +import lombok.Data;
  12 +
  13 +@Schema(description = "管理后台 - PMS 知识库文档新增 Request VO")
  14 +@Data
  15 +public class PmsKnowledgeDocumentCreateReqVO {
  16 +
  17 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + @NotNull(message = "知识库编号不能为空")
  19 + private Long libraryId;
  20 +
  21 + @Schema(description = "文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  22 + @NotNull(message = "文件夹编号不能为空")
  23 + private Long folderId;
  24 +
  25 + @Schema(description = "父文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  26 + @NotNull(message = "父文档编号不能为空")
  27 + private Long parentId;
  28 +
  29 + @Schema(description = "文档标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品说明")
  30 + @NotBlank(message = "文档标题不能为空")
  31 + @Size(max = 255, message = "文档标题不能超过 255 个字符")
  32 + private String title;
  33 +
  34 + @Schema(description = "文档类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  35 + @NotNull(message = "文档类型不能为空")
  36 + @InEnum(PmsKnowledgeDocumentTypeEnum.class)
  37 + private Integer type;
  38 +
  39 + @Schema(description = "文档内容或文件地址")
  40 + private String content;
  41 +
  42 + @Schema(description = "文件类型", example = "pdf")
  43 + @Size(max = 50, message = "文件类型不能超过 50 个字符")
  44 + private String fileType;
  45 +
  46 + @Schema(description = "文件大小,单位:字节", example = "102400")
  47 + @Min(value = 0, message = "文件大小不能小于 0")
  48 + @Max(value = 104857600, message = "文件大小不能超过 100 MB")
  49 + private Long fileSize;
  50 +
  51 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/document/PmsKnowledgeDocumentMoveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - PMS 知识库文档移动 Request VO")
  8 +@Data
  9 +public class PmsKnowledgeDocumentMoveReqVO {
  10 +
  11 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + @NotNull(message = "文档编号不能为空")
  13 + private Long id;
  14 +
  15 + @Schema(description = "目标知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + @NotNull(message = "目标知识库编号不能为空")
  17 + private Long targetLibraryId;
  18 +
  19 + @Schema(description = "目标文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  20 + @NotNull(message = "目标文件夹编号不能为空")
  21 + private Long targetFolderId;
  22 +
  23 + @Schema(description = "目标父文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  24 + @NotNull(message = "目标父文档编号不能为空")
  25 + private Long targetParentId;
  26 +
  27 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/document/PmsKnowledgeDocumentRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库文档 Response VO")
  10 +@Data
  11 +public class PmsKnowledgeDocumentRespVO {
  12 +
  13 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + private Long libraryId;
  18 +
  19 + @Schema(description = "协作权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  20 + private Long permissionId;
  21 +
  22 + @Schema(description = "当前用户协作等级", example = "3")
  23 + private Integer currentUserLevel;
  24 +
  25 + @Schema(description = "当前用户是否可下载", example = "true")
  26 + private Boolean downloadStatus;
  27 +
  28 + @Schema(description = "知识库名称", example = "产品知识库")
  29 + private String libraryName;
  30 +
  31 + @Schema(description = "文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  32 + private Long folderId;
  33 +
  34 + @Schema(description = "父文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  35 + private Long parentId;
  36 +
  37 + @Schema(description = "文档标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品说明")
  38 + private String title;
  39 +
  40 + @Schema(description = "正文摘要")
  41 + private String contentSummary;
  42 +
  43 + @Schema(description = "文档内容或文件地址")
  44 + private String content;
  45 +
  46 + @Schema(description = "文件预览地址(下载权限不足时仍可用于在线预览)")
  47 + private String previewUrl;
  48 +
  49 + @Schema(description = "文档类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  50 + private Integer type;
  51 +
  52 + @Schema(description = "文件类型", example = "pdf")
  53 + private String fileType;
  54 +
  55 + @Schema(description = "文件大小,单位:字节", example = "102400")
  56 + private Long fileSize;
  57 +
  58 + @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  59 + private Integer status;
  60 +
  61 + @Schema(description = "标签编号列表", example = "[1024, 2048]")
  62 + private List<Long> labelIds;
  63 +
  64 + @Schema(description = "创建人用户编号", example = "1")
  65 + private Long creatorUserId;
  66 +
  67 + @Schema(description = "创建人姓名", example = "芋道")
  68 + private String creatorUserName;
  69 +
  70 + @Schema(description = "当前用户是否已关注")
  71 + private Boolean favoriteStatus;
  72 +
  73 + @Schema(description = "当前用户是否已点赞")
  74 + private Boolean likeStatus;
  75 +
  76 + @Schema(description = "点赞用户列表")
  77 + private List<LikeUser> likeUsers;
  78 +
  79 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  80 + private LocalDateTime createTime;
  81 +
  82 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  83 + private LocalDateTime updateTime;
  84 +
  85 + @Schema(description = "管理后台 - PMS 知识文档点赞用户 Response VO")
  86 + @Data
  87 + public static class LikeUser {
  88 +
  89 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  90 + private Long id;
  91 +
  92 + @Schema(description = "用户昵称", example = "芋道")
  93 + private String nickname;
  94 +
  95 + @Schema(description = "用户头像")
  96 + private String avatar;
  97 +
  98 + }
  99 +
  100 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/document/PmsKnowledgeDocumentSearchPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - PMS 知识库文档全局搜索分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class PmsKnowledgeDocumentSearchPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "搜索关键字", example = "产品方案")
  21 + private String keyword;
  22 +
  23 + @Schema(description = "知识库编号", example = "1024")
  24 + private Long libraryId;
  25 +
  26 + @Schema(description = "创建人用户编号", example = "1")
  27 + private Long creatorUserId;
  28 +
  29 + @Schema(description = "更新时间范围")
  30 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  31 + private LocalDateTime[] updateTime;
  32 +
  33 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/document/PmsKnowledgeDocumentUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.document;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.Max;
  5 +import jakarta.validation.constraints.Min;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import jakarta.validation.constraints.Size;
  8 +import lombok.Data;
  9 +
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - PMS 知识库文档修改 Request VO")
  13 +@Data
  14 +public class PmsKnowledgeDocumentUpdateReqVO {
  15 +
  16 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + @NotNull(message = "文档编号不能为空")
  18 + private Long id;
  19 +
  20 + @Schema(description = "文档标题", example = "产品说明")
  21 + @Size(max = 255, message = "文档标题不能超过 255 个字符")
  22 + private String title;
  23 +
  24 + @Schema(description = "文档内容或文件地址")
  25 + private String content;
  26 +
  27 + @Schema(description = "标签编号列表;传空集合表示清空标签")
  28 + private List<Long> labelIds;
  29 +
  30 + @Schema(description = "文件类型", example = "pdf")
  31 + @Size(max = 50, message = "文件类型不能超过 50 个字符")
  32 + private String fileType;
  33 +
  34 + @Schema(description = "文件大小,单位:字节", example = "102400")
  35 + @Min(value = 0, message = "文件大小不能小于 0")
  36 + @Max(value = 104857600, message = "文件大小不能超过 100 MB")
  37 + private Long fileSize;
  38 +
  39 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/folder/PmsKnowledgeFolderMoveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.folder;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - PMS 知识库文件夹移动 Request VO")
  8 +@Data
  9 +public class PmsKnowledgeFolderMoveReqVO {
  10 +
  11 + @Schema(description = "文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + @NotNull(message = "文件夹编号不能为空")
  13 + private Long id;
  14 +
  15 + @Schema(description = "目标知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + @NotNull(message = "目标知识库编号不能为空")
  17 + private Long targetLibraryId;
  18 +
  19 + @Schema(description = "目标父文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  20 + @NotNull(message = "目标父文件夹编号不能为空")
  21 + private Long targetParentId;
  22 +
  23 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/folder/PmsKnowledgeFolderRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.folder;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识库文件夹 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeFolderRespVO {
  11 +
  12 + @Schema(description = "文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long libraryId;
  17 +
  18 + @Schema(description = "协作权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + private Long permissionId;
  20 +
  21 + @Schema(description = "当前用户协作等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  22 + private Integer currentUserLevel;
  23 +
  24 + @Schema(description = "父文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  25 + private Long parentId;
  26 +
  27 + @Schema(description = "文件夹标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品文档")
  28 + private String title;
  29 +
  30 + @Schema(description = "直属子文件夹数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  31 + private Integer childFolderCount;
  32 +
  33 + @Schema(description = "直属文档数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
  34 + private Integer documentCount;
  35 +
  36 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  37 + private Integer status;
  38 +
  39 + @Schema(description = "当前用户是否已关注", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + private Boolean favoriteStatus;
  41 +
  42 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  43 + private LocalDateTime createTime;
  44 +
  45 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  46 + private LocalDateTime updateTime;
  47 +
  48 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/folder/PmsKnowledgeFolderSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.folder;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库文件夹新增/修改 Request VO")
  10 +@Data
  11 +public class PmsKnowledgeFolderSaveReqVO {
  12 +
  13 + @Schema(description = "文件夹编号", example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + @NotNull(message = "知识库编号不能为空")
  18 + private Long libraryId;
  19 +
  20 + @Schema(description = "父文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  21 + @NotNull(message = "父文件夹编号不能为空")
  22 + private Long parentId;
  23 +
  24 + @Schema(description = "文件夹标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品文档")
  25 + @NotBlank(message = "文件夹标题不能为空")
  26 + @Size(max = 255, message = "文件夹标题不能超过 255 个字符")
  27 + private String title;
  28 +
  29 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/folder/PmsKnowledgeTreeRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.folder;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.util.List;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识库目录树 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeTreeRespVO {
  11 +
  12 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long libraryId;
  14 +
  15 + @Schema(description = "当前用户是否可编辑", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  16 + private Boolean writeStatus;
  17 +
  18 + @Schema(description = "当前用户是否可管理目录结构", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  19 + private Boolean manageStatus;
  20 +
  21 + @Schema(description = "根文件夹列表", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private List<FolderNode> folders;
  23 +
  24 + @Schema(description = "根文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
  25 + private List<DocumentNode> documents;
  26 +
  27 + @Schema(description = "管理后台 - PMS 知识库文件夹树节点 Response VO")
  28 + @Data
  29 + public static class FolderNode {
  30 +
  31 + @Schema(description = "文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  32 + private Long id;
  33 +
  34 + @Schema(description = "协作权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  35 + private Long permissionId;
  36 +
  37 + @Schema(description = "当前用户协作等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  38 + private Integer currentUserLevel;
  39 +
  40 + @Schema(description = "父文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  41 + private Long parentId;
  42 +
  43 + @Schema(description = "文件夹标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品文档")
  44 + private String title;
  45 +
  46 + @Schema(description = "子文件夹列表", requiredMode = Schema.RequiredMode.REQUIRED)
  47 + private List<FolderNode> children;
  48 +
  49 + @Schema(description = "文件夹下的文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
  50 + private List<DocumentNode> documents;
  51 + }
  52 +
  53 + @Schema(description = "管理后台 - PMS 知识库文档树节点 Response VO")
  54 + @Data
  55 + public static class DocumentNode {
  56 +
  57 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  58 + private Long id;
  59 +
  60 + @Schema(description = "协作权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  61 + private Long permissionId;
  62 +
  63 + @Schema(description = "当前用户协作等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  64 + private Integer currentUserLevel;
  65 +
  66 + @Schema(description = "父文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  67 + private Long parentId;
  68 +
  69 + @Schema(description = "文档标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品说明")
  70 + private String title;
  71 +
  72 + @Schema(description = "文档类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  73 + private Integer type;
  74 +
  75 + @Schema(description = "文件类型", example = "pdf")
  76 + private String fileType;
  77 +
  78 + @Schema(description = "子文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
  79 + private List<DocumentNode> children;
  80 + }
  81 +
  82 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/label/PmsKnowledgeDocumentLabelPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.label;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +
  10 +@Schema(description = "管理后台 - PMS 按标签查询文档分页 Request VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +@ToString(callSuper = true)
  14 +public class PmsKnowledgeDocumentLabelPageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "文档标签编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + @NotNull(message = "文档标签编号不能为空")
  18 + private Long labelId;
  19 +
  20 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/label/PmsKnowledgeDocumentLabelRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.label;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识库文档标签 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeDocumentLabelRespVO {
  11 +
  12 + @Schema(description = "文档标签编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "标签名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品")
  16 + private String name;
  17 +
  18 + @Schema(description = "标签颜色", requiredMode = Schema.RequiredMode.REQUIRED, example = "#409EFF")
  19 + private String color;
  20 +
  21 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private LocalDateTime createTime;
  23 +
  24 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/label/PmsKnowledgeDocumentLabelSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.label;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.Size;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识库文档标签新增/修改 Request VO")
  9 +@Data
  10 +public class PmsKnowledgeDocumentLabelSaveReqVO {
  11 +
  12 + @Schema(description = "文档标签编号", example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "标签名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品")
  16 + @NotBlank(message = "标签名称不能为空")
  17 + @Size(max = 255, message = "标签名称不能超过 255 个字符")
  18 + private String name;
  19 +
  20 + @Schema(description = "标签颜色", requiredMode = Schema.RequiredMode.REQUIRED, example = "#409EFF")
  21 + @NotBlank(message = "标签颜色不能为空")
  22 + @Size(max = 20, message = "标签颜色不能超过 20 个字符")
  23 + private String color;
  24 +
  25 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/permission/PmsKnowledgeContentPermissionRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.permission;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.util.List;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识内容协作权限 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeContentPermissionRespVO {
  11 +
  12 + @Schema(description = "协作权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long libraryId;
  17 +
  18 + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  19 + private Boolean openStatus;
  20 +
  21 + @Schema(description = "公开协作等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  22 + private Integer openLevel;
  23 +
  24 + @Schema(description = "创建人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  25 + private Long creatorUserId;
  26 +
  27 + @Schema(description = "当前用户协作等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  28 + private Integer currentUserLevel;
  29 +
  30 + @Schema(description = "协作者列表", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + private List<Member> members;
  32 +
  33 + @Schema(description = "管理后台 - PMS 知识内容协作者 Response VO")
  34 + @Data
  35 + public static class Member {
  36 +
  37 + @Schema(description = "协作者编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  38 + private Long id;
  39 +
  40 + @Schema(description = "用户编号", example = "1")
  41 + private Long userId;
  42 +
  43 + @Schema(description = "用户昵称", example = "芋道")
  44 + private String userName;
  45 +
  46 + @Schema(description = "部门编号", example = "100")
  47 + private Long deptId;
  48 +
  49 + @Schema(description = "部门名称", example = "研发部")
  50 + private String deptName;
  51 +
  52 + @Schema(description = "协作等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  53 + private Integer level;
  54 +
  55 + }
  56 +
  57 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/content/vo/permission/PmsKnowledgeContentPermissionUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.content.vo.permission;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.pms.enums.kb.content.PmsKnowledgeContentLevelEnum;
  5 +import com.fasterxml.jackson.annotation.JsonIgnore;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import jakarta.validation.Valid;
  8 +import jakarta.validation.constraints.AssertTrue;
  9 +import jakarta.validation.constraints.NotNull;
  10 +import lombok.Data;
  11 +
  12 +import java.util.List;
  13 +
  14 +@Schema(description = "管理后台 - PMS 更新知识内容协作权限 Request VO")
  15 +@Data
  16 +public class PmsKnowledgeContentPermissionUpdateReqVO {
  17 +
  18 + @Schema(description = "协作权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @NotNull(message = "协作权限编号不能为空")
  20 + private Long id;
  21 +
  22 + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  23 + @NotNull(message = "是否公开不能为空")
  24 + private Boolean openStatus;
  25 +
  26 + @Schema(description = "公开协作等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  27 + @NotNull(message = "公开协作等级不能为空")
  28 + @InEnum(PmsKnowledgeContentLevelEnum.class)
  29 + private Integer openLevel;
  30 +
  31 + @Schema(description = "协作者列表", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + @Valid
  33 + @NotNull(message = "协作者列表不能为空")
  34 + private List<Member> members;
  35 +
  36 + @Schema(description = "管理后台 - PMS 知识内容协作者保存项")
  37 + @Data
  38 + public static class Member {
  39 +
  40 + @Schema(description = "用户编号", example = "1")
  41 + private Long userId;
  42 +
  43 + @Schema(description = "部门编号", example = "100")
  44 + private Long deptId;
  45 +
  46 + @Schema(description = "协作等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  47 + @NotNull(message = "协作等级不能为空")
  48 + @InEnum(PmsKnowledgeContentLevelEnum.class)
  49 + private Integer level;
  50 +
  51 + @AssertTrue(message = "协作者必须且只能选择用户或部门")
  52 + @JsonIgnore
  53 + public boolean isUserOrDeptValid() {
  54 + return (userId != null && deptId == null) || (userId == null && deptId != null);
  55 + }
  56 +
  57 + }
  58 +
  59 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/PmsKnowledgeDocumentCommentController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.comment.PmsKnowledgeDocumentCommentRespVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.comment.PmsKnowledgeDocumentCommentSaveReqVO;
  8 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.interaction.PmsKnowledgeDocumentCommentDO;
  9 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeDocumentCommentService;
  10 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  11 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.tags.Tag;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.DeleteMapping;
  20 +import org.springframework.web.bind.annotation.GetMapping;
  21 +import org.springframework.web.bind.annotation.PostMapping;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestParam;
  25 +import org.springframework.web.bind.annotation.RestController;
  26 +
  27 +import java.util.ArrayList;
  28 +import java.util.Collections;
  29 +import java.util.List;
  30 +import java.util.Map;
  31 +import java.util.stream.Stream;
  32 +
  33 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  37 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  38 +
  39 +@Tag(name = "管理后台 - PMS 知识库文档评论")
  40 +@RestController
  41 +@RequestMapping("/pms/kb/document-comment")
  42 +@Validated
  43 +public class PmsKnowledgeDocumentCommentController {
  44 +
  45 + @Resource
  46 + private PmsKnowledgeDocumentCommentService commentService;
  47 +
  48 + @Resource
  49 + private AdminUserApi adminUserApi;
  50 +
  51 + @PostMapping("/create")
  52 + @Operation(summary = "创建知识库文档评论")
  53 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  54 + public CommonResult<Long> createDocumentComment(@Valid @RequestBody PmsKnowledgeDocumentCommentSaveReqVO saveReqVO) {
  55 + return success(commentService.createDocumentComment(saveReqVO, getLoginUserId()));
  56 + }
  57 +
  58 + @DeleteMapping("/delete")
  59 + @Operation(summary = "删除知识库文档评论")
  60 + @Parameter(name = "id", description = "评论编号", required = true, example = "1024")
  61 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  62 + public CommonResult<Boolean> deleteDocumentComment(@RequestParam("id") Long id) {
  63 + commentService.deleteDocumentComment(id, getLoginUserId());
  64 + return success(true);
  65 + }
  66 +
  67 + @GetMapping("/list")
  68 + @Operation(summary = "获得知识库文档评论列表")
  69 + @Parameter(name = "documentId", description = "文档编号", required = true, example = "1024")
  70 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  71 + public CommonResult<List<PmsKnowledgeDocumentCommentRespVO>> getDocumentCommentList(
  72 + @RequestParam("documentId") Long documentId) {
  73 + List<PmsKnowledgeDocumentCommentDO> comments = commentService.getDocumentCommentList(documentId, getLoginUserId());
  74 + return success(buildCommentRespVOList(comments));
  75 + }
  76 +
  77 + // ==================== 拼接 VO ====================
  78 +
  79 + private List<PmsKnowledgeDocumentCommentRespVO> buildCommentRespVOList(
  80 + List<PmsKnowledgeDocumentCommentDO> comments) {
  81 + if (CollUtil.isEmpty(comments)) {
  82 + return Collections.emptyList();
  83 + }
  84 + // 1. 批量查询评论人和回复对象
  85 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(comments,
  86 + comment -> Stream.of(comment.getUserId(), comment.getReplyUserId())));
  87 +
  88 + // 2. 批量转换评论,避免回复组装依赖查询顺序
  89 + Map<Long, PmsKnowledgeDocumentCommentRespVO> commentMap = convertMap(comments,
  90 + PmsKnowledgeDocumentCommentDO::getId, comment -> {
  91 + PmsKnowledgeDocumentCommentRespVO commentVO = BeanUtils.toBean(comment,
  92 + PmsKnowledgeDocumentCommentRespVO.class).setChildren(new ArrayList<>());
  93 + findAndThen(userMap, comment.getUserId(), user -> commentVO.setUserName(user.getNickname()));
  94 + findAndThen(userMap, comment.getReplyUserId(),
  95 + user -> commentVO.setReplyUserName(user.getNickname()));
  96 + return commentVO;
  97 + });
  98 +
  99 + // 3. 按主评论组织回复列表
  100 + List<PmsKnowledgeDocumentCommentRespVO> result = new ArrayList<>();
  101 + for (PmsKnowledgeDocumentCommentDO comment : comments) {
  102 + PmsKnowledgeDocumentCommentRespVO commentVO = commentMap.get(comment.getId());
  103 + if (PmsKnowledgeDocumentCommentDO.MAIN_ID_ROOT.equals(comment.getMainId())) {
  104 + result.add(commentVO);
  105 + continue;
  106 + }
  107 + findAndThen(commentMap, comment.getMainId(), mainComment -> mainComment.getChildren().add(commentVO));
  108 + }
  109 + return result;
  110 + }
  111 +
  112 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/PmsKnowledgeDocumentLikeController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeDocumentLikeService;
  5 +import io.swagger.v3.oas.annotations.Operation;
  6 +import io.swagger.v3.oas.annotations.Parameter;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import jakarta.annotation.Resource;
  9 +import org.springframework.security.access.prepost.PreAuthorize;
  10 +import org.springframework.validation.annotation.Validated;
  11 +import org.springframework.web.bind.annotation.DeleteMapping;
  12 +import org.springframework.web.bind.annotation.PostMapping;
  13 +import org.springframework.web.bind.annotation.RequestMapping;
  14 +import org.springframework.web.bind.annotation.RequestParam;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  18 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  19 +
  20 +@Tag(name = "管理后台 - PMS 知识库文档点赞")
  21 +@RestController
  22 +@RequestMapping("/pms/kb/document-like")
  23 +@Validated
  24 +public class PmsKnowledgeDocumentLikeController {
  25 +
  26 + @Resource
  27 + private PmsKnowledgeDocumentLikeService documentLikeService;
  28 +
  29 + @PostMapping("/create")
  30 + @Operation(summary = "点赞文档")
  31 + @Parameter(name = "documentId", description = "文档编号", required = true, example = "1024")
  32 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  33 + public CommonResult<Boolean> createDocumentLike(@RequestParam("documentId") Long documentId) {
  34 + documentLikeService.createDocumentLike(documentId, getLoginUserId());
  35 + return success(true);
  36 + }
  37 +
  38 + @DeleteMapping("/delete")
  39 + @Operation(summary = "取消点赞文档")
  40 + @Parameter(name = "documentId", description = "文档编号", required = true, example = "1024")
  41 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  42 + public CommonResult<Boolean> deleteDocumentLike(@RequestParam("documentId") Long documentId) {
  43 + documentLikeService.deleteDocumentLike(documentId, getLoginUserId());
  44 + return success(true);
  45 + }
  46 +
  47 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/PmsKnowledgeDocumentShareController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
  6 +import cn.iocoder.yudao.module.infra.api.file.FileApi;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.share.PmsKnowledgeDocumentSharePublicRespVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.share.PmsKnowledgeDocumentShareRespVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.share.PmsKnowledgeDocumentShareSaveReqVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.share.PmsKnowledgeDocumentShareUpdateMemberListReqVO;
  11 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.content.PmsKnowledgeDocumentDO;
  12 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.interaction.PmsKnowledgeDocumentShareDO;
  13 +import cn.iocoder.yudao.module.pms.enums.kb.content.PmsKnowledgeDocumentTypeEnum;
  14 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeDocumentShareService;
  15 +import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import jakarta.annotation.Resource;
  19 +import jakarta.annotation.security.PermitAll;
  20 +import jakarta.validation.Valid;
  21 +import jakarta.validation.constraints.NotBlank;
  22 +import org.springframework.security.access.prepost.PreAuthorize;
  23 +import org.springframework.validation.annotation.Validated;
  24 +import org.springframework.web.bind.annotation.GetMapping;
  25 +import org.springframework.web.bind.annotation.PostMapping;
  26 +import org.springframework.web.bind.annotation.PutMapping;
  27 +import org.springframework.web.bind.annotation.RequestBody;
  28 +import org.springframework.web.bind.annotation.RequestMapping;
  29 +import org.springframework.web.bind.annotation.RequestParam;
  30 +import org.springframework.web.bind.annotation.RestController;
  31 +
  32 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  33 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  34 +
  35 +@Tag(name = "管理后台 - PMS 知识库文档分享")
  36 +@RestController
  37 +@RequestMapping("/pms/kb/document-share")
  38 +@Validated
  39 +public class PmsKnowledgeDocumentShareController {
  40 +
  41 + @Resource
  42 + private PmsKnowledgeDocumentShareService documentShareService;
  43 +
  44 + @Resource
  45 + private FileApi fileApi;
  46 +
  47 + @PostMapping("/open")
  48 + @Operation(summary = "开启文档分享")
  49 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  50 + public CommonResult<PmsKnowledgeDocumentShareRespVO> openShare(
  51 + @Valid @RequestBody PmsKnowledgeDocumentShareSaveReqVO reqVO) {
  52 + PmsKnowledgeDocumentShareDO share = documentShareService.openShare(reqVO.getDocumentId(),
  53 + reqVO.getShareUserIds(), getLoginUserId());
  54 + return success(BeanUtils.toBean(share, PmsKnowledgeDocumentShareRespVO.class));
  55 + }
  56 +
  57 + @PutMapping("/update-member-list")
  58 + @Operation(summary = "更新文档内部分享成员")
  59 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  60 + public CommonResult<Boolean> updateShareMemberList(
  61 + @Valid @RequestBody PmsKnowledgeDocumentShareUpdateMemberListReqVO reqVO) {
  62 + documentShareService.updateShareMemberList(reqVO.getDocumentId(), reqVO.getShareUserIds(), getLoginUserId());
  63 + return success(true);
  64 + }
  65 +
  66 + @PutMapping("/close")
  67 + @Operation(summary = "关闭文档分享")
  68 + @Parameter(name = "documentId", description = "文档编号", required = true, example = "1024")
  69 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  70 + public CommonResult<Boolean> closeShare(@RequestParam("documentId") Long documentId) {
  71 + documentShareService.closeShare(documentId, getLoginUserId());
  72 + return success(true);
  73 + }
  74 +
  75 + @GetMapping("/get")
  76 + @Operation(summary = "获得文档分享配置")
  77 + @Parameter(name = "documentId", description = "文档编号", required = true, example = "1024")
  78 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  79 + public CommonResult<PmsKnowledgeDocumentShareRespVO> getDocumentShare(@RequestParam("documentId") Long documentId) {
  80 + PmsKnowledgeDocumentShareDO share = documentShareService.getActiveDocumentShare(documentId, getLoginUserId());
  81 + return success(BeanUtils.toBean(share, PmsKnowledgeDocumentShareRespVO.class));
  82 + }
  83 +
  84 + @GetMapping("/get-by-token")
  85 + @Operation(summary = "通过分享令牌获得文档")
  86 + @Parameter(name = "token", description = "分享令牌", required = true)
  87 + @PermitAll
  88 + @TenantIgnore
  89 + public CommonResult<PmsKnowledgeDocumentSharePublicRespVO> getDocumentByToken(
  90 + @RequestParam("token") @NotBlank(message = "分享令牌不能为空") String token) {
  91 + PmsKnowledgeDocumentDO document = documentShareService.getDocumentByShareToken(token);
  92 + PmsKnowledgeDocumentSharePublicRespVO documentVO = BeanUtils.toBean(
  93 + document, PmsKnowledgeDocumentSharePublicRespVO.class);
  94 + if (PmsKnowledgeDocumentTypeEnum.FILE.getType().equals(document.getType())) {
  95 + documentVO.setContent(null).setPreviewUrl(
  96 + fileApi.presignGetUrl(document.getContent(), null));
  97 + }
  98 + return success(documentVO);
  99 + }
  100 +
  101 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/PmsKnowledgeFavoriteController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.PmsKnowledgeInteractionItemRespVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.favorite.PmsKnowledgeFavoritePageReqVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.favorite.PmsKnowledgeFavoriteSaveReqVO;
  9 +import cn.iocoder.yudao.module.pms.enums.kb.PmsKnowledgeObjectTypeEnum;
  10 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeFavoriteService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.DeleteMapping;
  19 +import org.springframework.web.bind.annotation.GetMapping;
  20 +import org.springframework.web.bind.annotation.PostMapping;
  21 +import org.springframework.web.bind.annotation.RequestBody;
  22 +import org.springframework.web.bind.annotation.RequestMapping;
  23 +import org.springframework.web.bind.annotation.RequestParam;
  24 +import org.springframework.web.bind.annotation.RestController;
  25 +
  26 +import java.util.List;
  27 +
  28 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  29 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  30 +
  31 +@Tag(name = "管理后台 - PMS 知识关注")
  32 +@RestController
  33 +@RequestMapping("/pms/kb/favorite")
  34 +@Validated
  35 +public class PmsKnowledgeFavoriteController {
  36 +
  37 + @Resource
  38 + private PmsKnowledgeFavoriteService favoriteService;
  39 +
  40 + @PostMapping("/create")
  41 + @Operation(summary = "关注知识对象")
  42 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  43 + public CommonResult<Boolean> createFavorite(@Valid @RequestBody PmsKnowledgeFavoriteSaveReqVO saveReqVO) {
  44 + favoriteService.createFavorite(saveReqVO, getLoginUserId());
  45 + return success(true);
  46 + }
  47 +
  48 + @DeleteMapping("/delete")
  49 + @Operation(summary = "取消关注知识对象")
  50 + @Parameter(name = "type", description = "对象类型", required = true, example = "3")
  51 + @Parameter(name = "entityId", description = "对象编号", required = true, example = "1024")
  52 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  53 + public CommonResult<Boolean> deleteFavorite(
  54 + @RequestParam("type") @InEnum(PmsKnowledgeObjectTypeEnum.class) Integer type,
  55 + @RequestParam("entityId") Long entityId) {
  56 + favoriteService.deleteFavorite(type, entityId, getLoginUserId());
  57 + return success(true);
  58 + }
  59 +
  60 + @GetMapping("/page")
  61 + @Operation(summary = "获得我的关注分页")
  62 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  63 + public CommonResult<PageResult<PmsKnowledgeInteractionItemRespVO>> getFavoritePage(
  64 + @Valid PmsKnowledgeFavoritePageReqVO pageReqVO) {
  65 + return success(favoriteService.getFavoritePage(pageReqVO, getLoginUserId()));
  66 + }
  67 +
  68 + @GetMapping("/list")
  69 + @Operation(summary = "获得知识库内我的关注列表")
  70 + @Parameter(name = "libraryId", description = "知识库编号", required = true, example = "1024")
  71 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  72 + public CommonResult<List<PmsKnowledgeInteractionItemRespVO>> getFavoriteListByLibraryId(
  73 + @RequestParam("libraryId") Long libraryId) {
  74 + return success(favoriteService.getFavoriteListByLibraryId(libraryId, getLoginUserId()));
  75 + }
  76 +
  77 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/PmsKnowledgeViewRecordController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.viewrecord.PmsKnowledgeRecentListRespVO;
  5 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeViewRecordService;
  6 +import io.swagger.v3.oas.annotations.Operation;
  7 +import io.swagger.v3.oas.annotations.Parameter;
  8 +import io.swagger.v3.oas.annotations.tags.Tag;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.security.access.prepost.PreAuthorize;
  11 +import org.springframework.validation.annotation.Validated;
  12 +import org.springframework.web.bind.annotation.GetMapping;
  13 +import org.springframework.web.bind.annotation.RequestMapping;
  14 +import org.springframework.web.bind.annotation.RequestParam;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  18 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  19 +
  20 +@Tag(name = "管理后台 - PMS 知识最近浏览")
  21 +@RestController
  22 +@RequestMapping("/pms/kb/view-record")
  23 +@Validated
  24 +public class PmsKnowledgeViewRecordController {
  25 +
  26 + @Resource
  27 + private PmsKnowledgeViewRecordService viewRecordService;
  28 +
  29 + @GetMapping("/recent-list")
  30 + @Operation(summary = "获得最近浏览列表")
  31 + @Parameter(name = "libraryId", description = "知识库编号;不传时查询全部可读知识库", example = "1024")
  32 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  33 + public CommonResult<PmsKnowledgeRecentListRespVO> getRecentViewRecordList(
  34 + @RequestParam(value = "libraryId", required = false) Long libraryId) {
  35 + return success(viewRecordService.getRecentViewRecordList(libraryId, getLoginUserId()));
  36 + }
  37 +
  38 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/PmsKnowledgeInteractionItemRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识交互对象 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeInteractionItemRespVO {
  11 +
  12 + @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  16 + private Integer type;
  17 +
  18 + @Schema(description = "对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + private Long entityId;
  20 +
  21 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  22 + private Long libraryId;
  23 +
  24 + @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品知识库")
  25 + private String libraryName;
  26 +
  27 + @Schema(description = "对象名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品说明")
  28 + private String name;
  29 +
  30 + @Schema(description = "知识库简介")
  31 + private String description;
  32 +
  33 + @Schema(description = "文件夹编号", example = "1024")
  34 + private Long folderId;
  35 +
  36 + @Schema(description = "文档编号", example = "1024")
  37 + private Long documentId;
  38 +
  39 + @Schema(description = "文件类型", example = "pdf")
  40 + private String fileType;
  41 +
  42 + @Schema(description = "文件大小,单位:字节")
  43 + private Long fileSize;
  44 +
  45 + @Schema(description = "对象更新时间")
  46 + private LocalDateTime targetUpdateTime;
  47 +
  48 + @Schema(description = "记录时间", requiredMode = Schema.RequiredMode.REQUIRED)
  49 + private LocalDateTime createTime;
  50 +
  51 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/comment/PmsKnowledgeDocumentCommentRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.comment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库文档评论 Response VO")
  10 +@Data
  11 +public class PmsKnowledgeDocumentCommentRespVO {
  12 +
  13 + @Schema(description = "评论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + private Long documentId;
  18 +
  19 + @Schema(description = "评论人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  20 + private Long userId;
  21 +
  22 + @Schema(description = "评论人昵称", example = "芋道")
  23 + private String userName;
  24 +
  25 + @Schema(description = "主评论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  26 + private Long mainId;
  27 +
  28 + @Schema(description = "回复对象用户编号", example = "2")
  29 + private Long replyUserId;
  30 +
  31 + @Schema(description = "回复对象昵称", example = "源码")
  32 + private String replyUserName;
  33 +
  34 + @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + private String content;
  36 +
  37 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + private LocalDateTime createTime;
  39 +
  40 + @Schema(description = "回复列表", requiredMode = Schema.RequiredMode.REQUIRED)
  41 + private List<PmsKnowledgeDocumentCommentRespVO> children;
  42 +
  43 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/comment/PmsKnowledgeDocumentCommentSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.comment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库文档评论保存 Request VO")
  10 +@Data
  11 +public class PmsKnowledgeDocumentCommentSaveReqVO {
  12 +
  13 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + @NotNull(message = "文档编号不能为空")
  15 + private Long documentId;
  16 +
  17 + @Schema(description = "主评论编号", example = "0")
  18 + private Long mainId;
  19 +
  20 + @Schema(description = "回复对象用户编号", example = "1")
  21 + private Long replyUserId;
  22 +
  23 + @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "文档内容很清楚")
  24 + @NotBlank(message = "评论内容不能为空")
  25 + @Size(max = 2000, message = "评论内容不能超过 2000 个字符")
  26 + private String content;
  27 +
  28 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/favorite/PmsKnowledgeFavoritePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.favorite;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.pms.enums.kb.PmsKnowledgeObjectTypeEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +
  10 +@Schema(description = "管理后台 - PMS 知识收藏(关注)分页 Request VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +public class PmsKnowledgeFavoritePageReqVO extends PageParam {
  14 +
  15 + @Schema(description = "对象类型", example = "3")
  16 + @InEnum(value = PmsKnowledgeObjectTypeEnum.class, message = "对象类型必须是 {value}")
  17 + private Integer type;
  18 +
  19 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/favorite/PmsKnowledgeFavoriteSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.favorite;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.pms.enums.kb.PmsKnowledgeObjectTypeEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识收藏(关注)保存 Request VO")
  10 +@Data
  11 +public class PmsKnowledgeFavoriteSaveReqVO {
  12 +
  13 + @Schema(description = "对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  14 + @NotNull(message = "对象类型不能为空")
  15 + @InEnum(value = PmsKnowledgeObjectTypeEnum.class, message = "对象类型必须是 {value}")
  16 + private Integer type;
  17 +
  18 + @Schema(description = "对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @NotNull(message = "对象编号不能为空")
  20 + private Long entityId;
  21 +
  22 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/share/PmsKnowledgeDocumentSharePublicRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.share;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "PMS 知识库文档公开分享 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeDocumentSharePublicRespVO {
  11 +
  12 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "文档标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品说明")
  16 + private String title;
  17 +
  18 + @Schema(description = "文档内容或文件地址")
  19 + private String content;
  20 +
  21 + @Schema(description = "文件临时预览地址")
  22 + private String previewUrl;
  23 +
  24 + @Schema(description = "文档类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  25 + private Integer type;
  26 +
  27 + @Schema(description = "文件类型", example = "pdf")
  28 + private String fileType;
  29 +
  30 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + private LocalDateTime updateTime;
  32 +
  33 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/share/PmsKnowledgeDocumentShareRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.share;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库文档分享 Response VO")
  10 +@Data
  11 +public class PmsKnowledgeDocumentShareRespVO {
  12 +
  13 + @Schema(description = "分享编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + private Long documentId;
  18 +
  19 + @Schema(description = "内部分享成员用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED)
  20 + private List<Long> shareUserIds;
  21 +
  22 + @Schema(description = "外部查看令牌", requiredMode = Schema.RequiredMode.REQUIRED)
  23 + private String token;
  24 +
  25 + @Schema(description = "分享状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  26 + private Integer status;
  27 +
  28 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + private LocalDateTime createTime;
  30 +
  31 + @Schema(description = "关闭人用户编号")
  32 + private Long closeUserId;
  33 +
  34 + @Schema(description = "关闭时间")
  35 + private LocalDateTime closeTime;
  36 +
  37 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/share/PmsKnowledgeDocumentShareSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.share;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库文档分享保存 Request VO")
  10 +@Data
  11 +public class PmsKnowledgeDocumentShareSaveReqVO {
  12 +
  13 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + @NotNull(message = "文档编号不能为空")
  15 + private Long documentId;
  16 +
  17 + @Schema(description = "内部分享成员用户编号列表")
  18 + private List<@NotNull(message = "分享成员用户编号不能为空") Long> shareUserIds;
  19 +
  20 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/share/PmsKnowledgeDocumentShareUpdateMemberListReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.share;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库文档分享成员列表更新 Request VO")
  10 +@Data
  11 +public class PmsKnowledgeDocumentShareUpdateMemberListReqVO {
  12 +
  13 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + @NotNull(message = "文档编号不能为空")
  15 + private Long documentId;
  16 +
  17 + @Schema(description = "内部分享成员用户编号列表")
  18 + private List<@NotNull(message = "分享成员用户编号不能为空") Long> shareUserIds;
  19 +
  20 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/interaction/vo/viewrecord/PmsKnowledgeRecentListRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.viewrecord;
  2 +
  3 +import cn.iocoder.yudao.module.pms.controller.admin.kb.interaction.vo.PmsKnowledgeInteractionItemRespVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识最近浏览 Response VO")
  10 +@Data
  11 +public class PmsKnowledgeRecentListRespVO {
  12 +
  13 + @Schema(description = "今天浏览列表", requiredMode = Schema.RequiredMode.REQUIRED)
  14 + private List<PmsKnowledgeInteractionItemRespVO> todayItems;
  15 +
  16 + @Schema(description = "昨天浏览列表", requiredMode = Schema.RequiredMode.REQUIRED)
  17 + private List<PmsKnowledgeInteractionItemRespVO> yesterdayItems;
  18 +
  19 + @Schema(description = "更早 30 天浏览列表", requiredMode = Schema.RequiredMode.REQUIRED)
  20 + private List<PmsKnowledgeInteractionItemRespVO> recent30DayItems;
  21 +
  22 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/PmsKnowledgeGroupController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.group.PmsKnowledgeGroupRespVO;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.group.PmsKnowledgeGroupSaveReqVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.group.PmsKnowledgeGroupSortReqVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.group.PmsKnowledgeLibraryMoveGroupReqVO;
  9 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.library.PmsKnowledgeGroupDO;
  10 +import cn.iocoder.yudao.module.pms.service.kb.library.PmsKnowledgeGroupService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.DeleteMapping;
  19 +import org.springframework.web.bind.annotation.GetMapping;
  20 +import org.springframework.web.bind.annotation.PostMapping;
  21 +import org.springframework.web.bind.annotation.PutMapping;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestParam;
  25 +import org.springframework.web.bind.annotation.RestController;
  26 +
  27 +import java.util.List;
  28 +import java.util.Map;
  29 +
  30 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  33 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  34 +
  35 +@Tag(name = "管理后台 - PMS 知识库分组")
  36 +@RestController
  37 +@RequestMapping("/pms/kb/group")
  38 +@Validated
  39 +public class PmsKnowledgeGroupController {
  40 +
  41 + @Resource
  42 + private PmsKnowledgeGroupService knowledgeGroupService;
  43 +
  44 + @PostMapping("/create")
  45 + @Operation(summary = "创建知识库分组")
  46 + @PreAuthorize("@ss.hasPermission('pms:kb:library:create')")
  47 + public CommonResult<Long> createGroup(@Valid @RequestBody PmsKnowledgeGroupSaveReqVO saveReqVO) {
  48 + return success(knowledgeGroupService.createGroup(saveReqVO, getLoginUserId()));
  49 + }
  50 +
  51 + @PutMapping("/update")
  52 + @Operation(summary = "修改知识库分组")
  53 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  54 + public CommonResult<Boolean> updateGroup(@Valid @RequestBody PmsKnowledgeGroupSaveReqVO saveReqVO) {
  55 + knowledgeGroupService.updateGroup(saveReqVO, getLoginUserId());
  56 + return success(true);
  57 + }
  58 +
  59 + @PutMapping("/update-sort")
  60 + @Operation(summary = "修改知识库分组排序")
  61 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  62 + public CommonResult<Boolean> updateGroupSort(
  63 + @Valid @RequestBody PmsKnowledgeGroupSortReqVO sortReqVO) {
  64 + knowledgeGroupService.updateGroupSort(sortReqVO, getLoginUserId());
  65 + return success(true);
  66 + }
  67 +
  68 + @DeleteMapping("/delete")
  69 + @Operation(summary = "删除知识库分组")
  70 + @Parameter(name = "id", description = "知识库分组编号", required = true, example = "1024")
  71 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  72 + public CommonResult<Boolean> deleteGroup(@RequestParam("id") Long id) {
  73 + knowledgeGroupService.deleteGroup(id, getLoginUserId());
  74 + return success(true);
  75 + }
  76 +
  77 + @GetMapping("/list")
  78 + @Operation(summary = "获得知识库分组列表")
  79 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  80 + public CommonResult<List<PmsKnowledgeGroupRespVO>> getGroupList() {
  81 + Long userId = getLoginUserId();
  82 + List<PmsKnowledgeGroupDO> groups = knowledgeGroupService.getGroupList(userId);
  83 + Map<Long, Integer> groupCountMap = knowledgeGroupService.getGroupLibraryCountMap(userId,
  84 + convertSet(groups, PmsKnowledgeGroupDO::getId));
  85 + return success(convertList(groups, group -> BeanUtils.toBean(group, PmsKnowledgeGroupRespVO.class)
  86 + .setLibraryCount(groupCountMap.getOrDefault(group.getId(), 0))));
  87 + }
  88 +
  89 + @GetMapping("/get")
  90 + @Operation(summary = "获得知识库分组")
  91 + @Parameter(name = "id", description = "知识库分组编号", required = true, example = "1024")
  92 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  93 + public CommonResult<PmsKnowledgeGroupRespVO> getGroup(@RequestParam("id") Long id) {
  94 + return success(BeanUtils.toBean(knowledgeGroupService.getGroup(id, getLoginUserId()),
  95 + PmsKnowledgeGroupRespVO.class));
  96 + }
  97 +
  98 + @PutMapping("/move")
  99 + @Operation(summary = "移动知识库到个人分组")
  100 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  101 + public CommonResult<Boolean> moveLibraryToGroup(
  102 + @Valid @RequestBody PmsKnowledgeLibraryMoveGroupReqVO moveReqVO) {
  103 + knowledgeGroupService.moveLibraryToGroup(moveReqVO, getLoginUserId());
  104 + return success(true);
  105 + }
  106 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/PmsKnowledgeLibraryController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.library.PmsKnowledgeLibraryPageReqVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.library.PmsKnowledgeLibraryRespVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.library.PmsKnowledgeLibrarySaveReqVO;
  11 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.library.PmsKnowledgeLibraryDO;
  12 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.library.PmsKnowledgeLibraryMemberDO;
  13 +import cn.iocoder.yudao.module.pms.enums.kb.PmsKnowledgeObjectTypeEnum;
  14 +import cn.iocoder.yudao.module.pms.enums.kb.content.PmsKnowledgeDocumentTypeEnum;
  15 +import cn.iocoder.yudao.module.pms.enums.kb.library.PmsKnowledgeLibraryMemberLevelEnum;
  16 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeDocumentService;
  17 +import cn.iocoder.yudao.module.pms.service.kb.interaction.PmsKnowledgeFavoriteService;
  18 +import cn.iocoder.yudao.module.pms.service.kb.library.PmsKnowledgeLibraryMemberService;
  19 +import cn.iocoder.yudao.module.pms.service.kb.library.PmsKnowledgeLibraryService;
  20 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  21 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  22 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  23 +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
  24 +import io.swagger.v3.oas.annotations.Operation;
  25 +import io.swagger.v3.oas.annotations.Parameter;
  26 +import io.swagger.v3.oas.annotations.tags.Tag;
  27 +import jakarta.annotation.Resource;
  28 +import jakarta.validation.Valid;
  29 +import org.springframework.security.access.prepost.PreAuthorize;
  30 +import org.springframework.validation.annotation.Validated;
  31 +import org.springframework.web.bind.annotation.DeleteMapping;
  32 +import org.springframework.web.bind.annotation.GetMapping;
  33 +import org.springframework.web.bind.annotation.PostMapping;
  34 +import org.springframework.web.bind.annotation.PutMapping;
  35 +import org.springframework.web.bind.annotation.RequestBody;
  36 +import org.springframework.web.bind.annotation.RequestMapping;
  37 +import org.springframework.web.bind.annotation.RequestParam;
  38 +import org.springframework.web.bind.annotation.RestController;
  39 +
  40 +import java.util.Collections;
  41 +import java.util.List;
  42 +import java.util.Map;
  43 +import java.util.Set;
  44 +
  45 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  46 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  47 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  48 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  49 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  50 +
  51 +@Tag(name = "管理后台 - PMS 知识库")
  52 +@RestController
  53 +@RequestMapping("/pms/kb/library")
  54 +@Validated
  55 +public class PmsKnowledgeLibraryController {
  56 +
  57 + @Resource
  58 + private PmsKnowledgeLibraryService libraryService;
  59 + @Resource
  60 + private PmsKnowledgeLibraryMemberService memberService;
  61 + @Resource
  62 + private PmsKnowledgeDocumentService documentService;
  63 + @Resource
  64 + private PmsKnowledgeFavoriteService favoriteService;
  65 +
  66 + @Resource
  67 + private AdminUserApi adminUserApi;
  68 + @Resource
  69 + private PermissionApi permissionApi;
  70 +
  71 + @PostMapping("/create")
  72 + @Operation(summary = "创建知识库")
  73 + @PreAuthorize("@ss.hasPermission('pms:kb:library:create')")
  74 + public CommonResult<Long> createLibrary(@Valid @RequestBody PmsKnowledgeLibrarySaveReqVO saveReqVO) {
  75 + return success(libraryService.createLibrary(saveReqVO, getLoginUserId()));
  76 + }
  77 +
  78 + @PutMapping("/update")
  79 + @Operation(summary = "更新知识库")
  80 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  81 + public CommonResult<Boolean> updateLibrary(@Valid @RequestBody PmsKnowledgeLibrarySaveReqVO saveReqVO) {
  82 + libraryService.updateLibrary(saveReqVO, getLoginUserId());
  83 + return success(true);
  84 + }
  85 +
  86 + @DeleteMapping("/delete")
  87 + @Operation(summary = "删除知识库")
  88 + @Parameter(name = "id", description = "知识库编号", required = true, example = "1024")
  89 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  90 + public CommonResult<Boolean> deleteLibrary(@RequestParam("id") Long id) {
  91 + libraryService.deleteLibrary(id, getLoginUserId());
  92 + return success(true);
  93 + }
  94 +
  95 + @GetMapping("/get")
  96 + @Operation(summary = "获得知识库详情")
  97 + @Parameter(name = "id", description = "知识库编号", required = true, example = "1024")
  98 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  99 + public CommonResult<PmsKnowledgeLibraryRespVO> getLibrary(@RequestParam("id") Long id) {
  100 + Long userId = getLoginUserId();
  101 + PmsKnowledgeLibraryDO library = memberService.validateLibraryReadable(id, userId);
  102 + return success(CollUtil.getFirst(buildLibraryRespVOList(Collections.singletonList(library), userId)));
  103 + }
  104 +
  105 + @GetMapping("/page")
  106 + @Operation(summary = "获得知识库分页")
  107 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  108 + public CommonResult<PageResult<PmsKnowledgeLibraryRespVO>> getLibraryPage(
  109 + @Valid PmsKnowledgeLibraryPageReqVO pageReqVO) {
  110 + Long userId = getLoginUserId();
  111 + PageResult<PmsKnowledgeLibraryDO> pageResult = libraryService.getLibraryPage(pageReqVO, userId);
  112 + return success(new PageResult<>(buildLibraryRespVOList(pageResult.getList(), userId), pageResult.getTotal()));
  113 + }
  114 +
  115 + // ==================== 拼接 VO ====================
  116 +
  117 + private List<PmsKnowledgeLibraryRespVO> buildLibraryRespVOList(
  118 + List<PmsKnowledgeLibraryDO> libraries, Long userId) {
  119 + if (CollUtil.isEmpty(libraries)) {
  120 + return Collections.emptyList();
  121 + }
  122 +
  123 + // 1. 批量查询知识库成员、内容数量、创建人和当前用户权限信息
  124 + Set<Long> libraryIds = convertSet(libraries, PmsKnowledgeLibraryDO::getId);
  125 + Map<Long, List<PmsKnowledgeLibraryMemberDO>> memberListMap = memberService.getLibraryMemberListMap(libraryIds);
  126 + Map<Long, Map<Integer, Long>> documentTypeCountMap = documentService.getDocumentTypeCountMap(libraryIds);
  127 + Set<Long> userIds = convertSet(libraries, library -> NumberUtils.parseLong(library.getCreator()));
  128 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
  129 + AdminUserRespDTO currentUser = adminUserApi.getUser(userId);
  130 + Long deptId = currentUser != null ? currentUser.getDeptId() : null;
  131 + boolean superAdmin = permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode());
  132 + Set<Long> favoriteLibraryIds = favoriteService.getFavoriteEntityIdSet(
  133 + PmsKnowledgeObjectTypeEnum.LIBRARY.getType(), libraryIds, userId);
  134 +
  135 + // 2. 拼接每个知识库的成员权限、统计数量、收藏状态和创建人名称
  136 + return convertList(libraries, library -> {
  137 + List<PmsKnowledgeLibraryMemberDO> members = memberListMap.getOrDefault(library.getId(), Collections.emptyList());
  138 + PmsKnowledgeLibraryMemberDO directMember = CollUtil.findOne(members, member -> userId.equals(member.getUserId()));
  139 + PmsKnowledgeLibraryMemberDO currentMember = directMember;
  140 + if (currentMember == null && deptId != null) {
  141 + currentMember = CollUtil.findOne(members, member -> deptId.equals(member.getDeptId()));
  142 + }
  143 + boolean adminStatus = superAdmin || currentMember != null
  144 + && (PmsKnowledgeLibraryMemberLevelEnum.CREATOR.getLevel().equals(currentMember.getLevel())
  145 + || PmsKnowledgeLibraryMemberLevelEnum.ADMIN.getLevel().equals(currentMember.getLevel()));
  146 + PmsKnowledgeLibraryRespVO libraryVO = BeanUtils.toBean(library, PmsKnowledgeLibraryRespVO.class)
  147 + .setCreatorUserId(NumberUtils.parseLong(library.getCreator())).setMemberCount(members.size())
  148 + .setWriteStatus(superAdmin || currentMember != null).setAdminStatus(adminStatus)
  149 + .setExitStatus(directMember != null && !PmsKnowledgeLibraryMemberLevelEnum.CREATOR.getLevel().equals(directMember.getLevel()))
  150 + .setFavoriteStatus(favoriteLibraryIds.contains(library.getId()));
  151 + Map<Integer, Long> typeCountMap = documentTypeCountMap.getOrDefault(library.getId(), Collections.emptyMap());
  152 + libraryVO.setDocumentCount(typeCountMap.getOrDefault(PmsKnowledgeDocumentTypeEnum.RICH_TEXT.getType(), 0L))
  153 + .setFileCount(typeCountMap.getOrDefault(PmsKnowledgeDocumentTypeEnum.FILE.getType(), 0L));
  154 + findAndThen(userMap, libraryVO.getCreatorUserId(), user -> libraryVO.setCreatorUserName(user.getNickname()));
  155 + return libraryVO;
  156 + });
  157 + }
  158 +
  159 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/PmsKnowledgeLibraryMemberController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.member.PmsKnowledgeLibraryMemberRespVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.member.PmsKnowledgeLibraryUpdateMemberListReqVO;
  8 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.library.PmsKnowledgeLibraryMemberDO;
  9 +import cn.iocoder.yudao.module.pms.service.kb.library.PmsKnowledgeLibraryMemberService;
  10 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  11 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  12 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  13 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  14 +import io.swagger.v3.oas.annotations.Operation;
  15 +import io.swagger.v3.oas.annotations.Parameter;
  16 +import io.swagger.v3.oas.annotations.tags.Tag;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.validation.Valid;
  19 +import org.springframework.security.access.prepost.PreAuthorize;
  20 +import org.springframework.validation.annotation.Validated;
  21 +import org.springframework.web.bind.annotation.DeleteMapping;
  22 +import org.springframework.web.bind.annotation.GetMapping;
  23 +import org.springframework.web.bind.annotation.PutMapping;
  24 +import org.springframework.web.bind.annotation.RequestBody;
  25 +import org.springframework.web.bind.annotation.RequestMapping;
  26 +import org.springframework.web.bind.annotation.RequestParam;
  27 +import org.springframework.web.bind.annotation.RestController;
  28 +
  29 +import java.util.Collections;
  30 +import java.util.List;
  31 +import java.util.Map;
  32 +
  33 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  37 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  38 +
  39 +@Tag(name = "管理后台 - PMS 知识库成员")
  40 +@RestController
  41 +@RequestMapping("/pms/kb/library-member")
  42 +@Validated
  43 +public class PmsKnowledgeLibraryMemberController {
  44 +
  45 + @Resource
  46 + private PmsKnowledgeLibraryMemberService memberService;
  47 +
  48 + @Resource
  49 + private AdminUserApi adminUserApi;
  50 + @Resource
  51 + private DeptApi deptApi;
  52 +
  53 + @GetMapping("/list")
  54 + @Operation(summary = "获得知识库成员列表")
  55 + @Parameter(name = "libraryId", description = "知识库编号", required = true, example = "1024")
  56 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  57 + public CommonResult<List<PmsKnowledgeLibraryMemberRespVO>> getLibraryMemberList(@RequestParam("libraryId") Long libraryId) {
  58 + List<PmsKnowledgeLibraryMemberDO> members = memberService.getLibraryMemberList(libraryId, getLoginUserId());
  59 + return success(buildLibraryMemberRespVOList(members));
  60 + }
  61 +
  62 + @PutMapping("/update-list")
  63 + @Operation(summary = "更新知识库成员")
  64 + @PreAuthorize("@ss.hasPermission('pms:kb:library:update')")
  65 + public CommonResult<Boolean> updateLibraryMemberList(@Valid @RequestBody PmsKnowledgeLibraryUpdateMemberListReqVO updateReqVO) {
  66 + memberService.updateLibraryMemberList(updateReqVO, getLoginUserId());
  67 + return success(true);
  68 + }
  69 +
  70 + @DeleteMapping("/exit")
  71 + @Operation(summary = "主动退出知识库")
  72 + @Parameter(name = "libraryId", description = "知识库编号", required = true, example = "1024")
  73 + @PreAuthorize("@ss.hasPermission('pms:kb:library:query')")
  74 + public CommonResult<Boolean> exitLibrary(@RequestParam("libraryId") Long libraryId) {
  75 + memberService.exitLibrary(libraryId, getLoginUserId());
  76 + return success(true);
  77 + }
  78 +
  79 + // ==================== 拼接 VO ====================
  80 +
  81 + private List<PmsKnowledgeLibraryMemberRespVO> buildLibraryMemberRespVOList(
  82 + List<PmsKnowledgeLibraryMemberDO> members) {
  83 + if (CollUtil.isEmpty(members)) {
  84 + return Collections.emptyList();
  85 + }
  86 +
  87 + // 1. 批量查询用户和部门
  88 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  89 + convertSet(members, PmsKnowledgeLibraryMemberDO::getUserId));
  90 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
  91 + convertSet(members, PmsKnowledgeLibraryMemberDO::getDeptId));
  92 + Map<Long, DeptRespDTO> parentDeptMap = deptApi.getDeptMap(convertSet(deptMap.values(), DeptRespDTO::getParentId));
  93 + // 2. 转换成员响应,并拼接用户和部门名称
  94 + return convertList(members, member -> {
  95 + PmsKnowledgeLibraryMemberRespVO memberVO = BeanUtils.toBean(member, PmsKnowledgeLibraryMemberRespVO.class);
  96 + findAndThen(userMap, member.getUserId(), user ->
  97 + memberVO.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
  98 + findAndThen(deptMap, member.getDeptId(), dept -> {
  99 + memberVO.setDeptName(dept.getName()).setParentDeptId(dept.getParentId());
  100 + findAndThen(parentDeptMap, dept.getParentId(), parent -> memberVO.setParentDeptName(parent.getName()));
  101 + });
  102 + return memberVO;
  103 + });
  104 + }
  105 +
  106 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/PmsKnowledgeLibraryTemplateController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.template.PmsKnowledgeLibraryTemplatePageReqVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.template.PmsKnowledgeLibraryTemplateRespVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.template.PmsKnowledgeLibraryTemplateSaveReqVO;
  10 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.library.PmsKnowledgeLibraryTemplateDO;
  11 +import cn.iocoder.yudao.module.pms.service.kb.library.PmsKnowledgeLibraryTemplateService;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.tags.Tag;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.DeleteMapping;
  20 +import org.springframework.web.bind.annotation.GetMapping;
  21 +import org.springframework.web.bind.annotation.PostMapping;
  22 +import org.springframework.web.bind.annotation.PutMapping;
  23 +import org.springframework.web.bind.annotation.RequestBody;
  24 +import org.springframework.web.bind.annotation.RequestMapping;
  25 +import org.springframework.web.bind.annotation.RequestParam;
  26 +import org.springframework.web.bind.annotation.RestController;
  27 +
  28 +import java.util.List;
  29 +
  30 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  32 +
  33 +/**
  34 + * 管理后台 - PMS 知识库模板 Controller
  35 + *
  36 + * @author 芋道源码
  37 + */
  38 +@Tag(name = "管理后台 - PMS 知识库模板")
  39 +@RestController
  40 +@RequestMapping("/pms/kb/library-template")
  41 +@Validated
  42 +public class PmsKnowledgeLibraryTemplateController {
  43 +
  44 + @Resource
  45 + private PmsKnowledgeLibraryTemplateService libraryTemplateService;
  46 +
  47 + @PostMapping("/create")
  48 + @Operation(summary = "创建知识库模板")
  49 + @PreAuthorize("@ss.hasPermission('pms:kb:library-template:create')")
  50 + public CommonResult<Long> createLibraryTemplate(
  51 + @Valid @RequestBody PmsKnowledgeLibraryTemplateSaveReqVO createReqVO) {
  52 + return success(libraryTemplateService.createLibraryTemplate(createReqVO));
  53 + }
  54 +
  55 + @PutMapping("/update")
  56 + @Operation(summary = "更新知识库模板")
  57 + @PreAuthorize("@ss.hasPermission('pms:kb:library-template:update')")
  58 + public CommonResult<Boolean> updateLibraryTemplate(
  59 + @Valid @RequestBody PmsKnowledgeLibraryTemplateSaveReqVO updateReqVO) {
  60 + libraryTemplateService.updateLibraryTemplate(updateReqVO);
  61 + return success(true);
  62 + }
  63 +
  64 + @DeleteMapping("/delete")
  65 + @Operation(summary = "删除知识库模板")
  66 + @Parameter(name = "id", description = "模板编号", required = true, example = "1024")
  67 + @PreAuthorize("@ss.hasPermission('pms:kb:library-template:delete')")
  68 + public CommonResult<Boolean> deleteLibraryTemplate(@RequestParam("id") Long id) {
  69 + libraryTemplateService.deleteLibraryTemplate(id);
  70 + return success(true);
  71 + }
  72 +
  73 + @GetMapping("/get")
  74 + @Operation(summary = "获得知识库模板")
  75 + @Parameter(name = "id", description = "模板编号", required = true, example = "1024")
  76 + @PreAuthorize("@ss.hasPermission('pms:kb:library-template:query')")
  77 + public CommonResult<PmsKnowledgeLibraryTemplateRespVO> getLibraryTemplate(@RequestParam("id") Long id) {
  78 + PmsKnowledgeLibraryTemplateDO template = libraryTemplateService.getLibraryTemplate(id);
  79 + return success(BeanUtils.toBean(template, PmsKnowledgeLibraryTemplateRespVO.class));
  80 + }
  81 +
  82 + @GetMapping("/simple-list")
  83 + @Operation(summary = "获取知识库模板全列表", description = "只包含开启状态模板,返回模板选择所需的基本信息和文档标题")
  84 + public CommonResult<List<PmsKnowledgeLibraryTemplateRespVO>> getSimpleLibraryTemplateList() {
  85 + return success(convertList(libraryTemplateService.getLibraryTemplateList(CommonStatusEnum.ENABLE.getStatus()),
  86 + template -> new PmsKnowledgeLibraryTemplateRespVO().setId(template.getId())
  87 + .setName(template.getName()).setCoverUrl(template.getCoverUrl()).setDescription(template.getDescription())
  88 + .setDocuments(convertList(template.getDocuments(), document ->
  89 + new PmsKnowledgeLibraryTemplateSaveReqVO.Document().setTitle(document.getTitle())))));
  90 + }
  91 +
  92 + @GetMapping("/page")
  93 + @Operation(summary = "获得知识库模板分页")
  94 + @PreAuthorize("@ss.hasPermission('pms:kb:library-template:query')")
  95 + public CommonResult<PageResult<PmsKnowledgeLibraryTemplateRespVO>> getLibraryTemplatePage(
  96 + @Valid PmsKnowledgeLibraryTemplatePageReqVO pageReqVO) {
  97 + PageResult<PmsKnowledgeLibraryTemplateDO> pageResult = libraryTemplateService
  98 + .getLibraryTemplatePage(pageReqVO);
  99 + return success(new PageResult<>(BeanUtils.toBean(pageResult.getList(), PmsKnowledgeLibraryTemplateRespVO.class),
  100 + pageResult.getTotal()));
  101 + }
  102 +
  103 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/group/PmsKnowledgeGroupRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识库分组 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeGroupRespVO {
  11 +
  12 + @Schema(description = "知识库分组编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "分组名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品知识库")
  16 + private String name;
  17 +
  18 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  19 + private Integer sort;
  20 +
  21 + @Schema(description = "分组类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  22 + private Integer type;
  23 +
  24 + @Schema(description = "分组内知识库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  25 + private Integer libraryCount;
  26 +
  27 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + private LocalDateTime createTime;
  29 +
  30 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/group/PmsKnowledgeGroupSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.Size;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识库分组新增/修改 Request VO")
  9 +@Data
  10 +public class PmsKnowledgeGroupSaveReqVO {
  11 +
  12 + @Schema(description = "知识库分组编号", example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "分组名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品知识库")
  16 + @NotBlank(message = "分组名称不能为空")
  17 + @Size(max = 100, message = "分组名称不能超过 100 个字符")
  18 + private String name;
  19 +
  20 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/group/PmsKnowledgeGroupSortReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.Valid;
  5 +import jakarta.validation.constraints.Min;
  6 +import jakarta.validation.constraints.NotEmpty;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
  9 +
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - PMS 知识库分组排序 Request VO")
  13 +@Data
  14 +public class PmsKnowledgeGroupSortReqVO {
  15 +
  16 + @Schema(description = "知识库分组排序项", requiredMode = Schema.RequiredMode.REQUIRED)
  17 + @NotEmpty(message = "知识库分组排序项不能为空")
  18 + private List<@Valid Item> items;
  19 +
  20 + @Schema(description = "管理后台 - PMS 知识库分组排序项 Request VO")
  21 + @Data
  22 + public static class Item {
  23 +
  24 + @Schema(description = "知识库分组编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  25 + @NotNull(message = "知识库分组编号不能为空")
  26 + private Long id;
  27 +
  28 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  29 + @NotNull(message = "显示顺序不能为空")
  30 + @Min(value = 0, message = "显示顺序不能小于 0")
  31 + private Integer sort;
  32 +
  33 + }
  34 +
  35 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/group/PmsKnowledgeLibraryMoveGroupReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - PMS 知识库移动分组 Request VO")
  8 +@Data
  9 +public class PmsKnowledgeLibraryMoveGroupReqVO {
  10 +
  11 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + @NotNull(message = "知识库编号不能为空")
  13 + private Long libraryId;
  14 +
  15 + @Schema(description = "目标分组编号;为空表示移出分组", example = "2048")
  16 + private Long groupId;
  17 +
  18 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/library/PmsKnowledgeLibraryPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.library;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库分页 Request VO")
  10 +@Data
  11 +@EqualsAndHashCode(callSuper = true)
  12 +@ToString(callSuper = true)
  13 +public class PmsKnowledgeLibraryPageReqVO extends PageParam {
  14 +
  15 + @Schema(description = "知识库名称", example = "产品")
  16 + private String name;
  17 +
  18 + @Schema(description = "个人知识库分组编号", example = "1024")
  19 + private Long groupId;
  20 +
  21 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/library/PmsKnowledgeLibraryRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.library;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识库 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeLibraryRespVO {
  11 +
  12 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品知识库")
  16 + private String name;
  17 +
  18 + @Schema(description = "知识库简介")
  19 + private String description;
  20 +
  21 + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private Boolean openStatus;
  23 +
  24 + @Schema(description = "知识库封面")
  25 + private String coverUrl;
  26 +
  27 + @Schema(description = "创建人用户编号", example = "1")
  28 + private Long creatorUserId;
  29 +
  30 + @Schema(description = "创建人姓名", example = "芋道源码")
  31 + private String creatorUserName;
  32 +
  33 + @Schema(description = "成员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  34 + private Integer memberCount;
  35 +
  36 + @Schema(description = "文档数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
  37 + private Long documentCount;
  38 +
  39 + @Schema(description = "文件数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4")
  40 + private Long fileCount;
  41 +
  42 + @Schema(description = "当前用户是否可以编辑内容", requiredMode = Schema.RequiredMode.REQUIRED)
  43 + private Boolean writeStatus;
  44 +
  45 + @Schema(description = "当前用户是否为知识库管理员", requiredMode = Schema.RequiredMode.REQUIRED)
  46 + private Boolean adminStatus;
  47 +
  48 + @Schema(description = "当前用户是否可以主动退出知识库", requiredMode = Schema.RequiredMode.REQUIRED)
  49 + private Boolean exitStatus;
  50 +
  51 + @Schema(description = "当前用户是否已关注", requiredMode = Schema.RequiredMode.REQUIRED)
  52 + private Boolean favoriteStatus;
  53 +
  54 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  55 + private LocalDateTime createTime;
  56 +
  57 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/library/PmsKnowledgeLibrarySaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.library;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Positive;
  7 +import jakarta.validation.constraints.Size;
  8 +import lombok.Data;
  9 +
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - PMS 知识库新增/修改 Request VO")
  13 +@Data
  14 +public class PmsKnowledgeLibrarySaveReqVO {
  15 +
  16 + @Schema(description = "知识库编号", example = "1024")
  17 + private Long id;
  18 +
  19 + @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品知识库")
  20 + @NotBlank(message = "知识库名称不能为空")
  21 + @Size(max = 50, message = "知识库名称不能超过 50 个字符")
  22 + private String name;
  23 +
  24 + @Schema(description = "知识库简介", example = "沉淀产品文档")
  25 + @Size(max = 300, message = "知识库简介不能超过 300 个字符")
  26 + private String description;
  27 +
  28 + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  29 + @NotNull(message = "知识库可见范围不能为空")
  30 + private Boolean openStatus;
  31 +
  32 + @Schema(description = "知识库封面")
  33 + @Size(max = 255, message = "知识库封面地址不能超过 255 个字符")
  34 + private String coverUrl;
  35 +
  36 + @Schema(description = "创建时的初始管理员用户编号列表", example = "[1, 2]")
  37 + private List<Long> adminUserIds;
  38 +
  39 + @Schema(description = "创建时的初始普通成员用户编号列表", example = "[3, 4]")
  40 + private List<Long> memberUserIds;
  41 +
  42 + @Schema(description = "模板编号,仅创建时有效", example = "1")
  43 + @Positive(message = "模板编号必须大于 0")
  44 + private Long templateId;
  45 +
  46 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/member/PmsKnowledgeLibraryMemberRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.member;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - PMS 知识库成员 Response VO")
  7 +@Data
  8 +public class PmsKnowledgeLibraryMemberRespVO {
  9 +
  10 + @Schema(description = "成员编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  11 + private Long id;
  12 +
  13 + @Schema(description = "用户编号", example = "1")
  14 + private Long userId;
  15 +
  16 + @Schema(description = "用户姓名", example = "芋道源码")
  17 + private String nickname;
  18 +
  19 + @Schema(description = "用户头像")
  20 + private String avatar;
  21 +
  22 + @Schema(description = "部门编号", example = "100")
  23 + private Long deptId;
  24 +
  25 + @Schema(description = "部门名称", example = "研发部")
  26 + private String deptName;
  27 +
  28 + @Schema(description = "父部门编号")
  29 + private Long parentDeptId;
  30 +
  31 + @Schema(description = "父部门名称")
  32 + private String parentDeptName;
  33 +
  34 + @Schema(description = "成员等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  35 + private Integer level;
  36 +
  37 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/member/PmsKnowledgeLibraryUpdateMemberListReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.member;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.pms.enums.kb.library.PmsKnowledgeLibraryMemberLevelEnum;
  5 +import com.fasterxml.jackson.annotation.JsonIgnore;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import jakarta.validation.Valid;
  8 +import jakarta.validation.constraints.AssertTrue;
  9 +import jakarta.validation.constraints.NotNull;
  10 +import lombok.Data;
  11 +
  12 +import java.util.List;
  13 +
  14 +@Schema(description = "管理后台 - PMS 更新知识库成员列表 Request VO")
  15 +@Data
  16 +public class PmsKnowledgeLibraryUpdateMemberListReqVO {
  17 +
  18 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @NotNull(message = "知识库编号不能为空")
  20 + private Long libraryId;
  21 +
  22 + @Schema(description = "成员列表", requiredMode = Schema.RequiredMode.REQUIRED)
  23 + @Valid
  24 + @NotNull(message = "成员列表不能为空")
  25 + private List<Member> members;
  26 +
  27 + @Schema(description = "管理后台 - PMS 知识库成员更新项")
  28 + @Data
  29 + public static class Member {
  30 +
  31 + @Schema(description = "用户编号", example = "1")
  32 + private Long userId;
  33 +
  34 + @Schema(description = "部门编号", example = "100")
  35 + private Long deptId;
  36 +
  37 + @Schema(description = "成员等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  38 + @NotNull(message = "成员等级不能为空")
  39 + @InEnum(PmsKnowledgeLibraryMemberLevelEnum.class)
  40 + private Integer level;
  41 +
  42 + @AssertTrue(message = "知识库成员必须且只能选择用户或部门")
  43 + @JsonIgnore
  44 + public boolean isUserOrDeptValid() {
  45 + return (userId != null && deptId == null) || (userId == null && deptId != null);
  46 + }
  47 +
  48 + }
  49 +
  50 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/template/PmsKnowledgeLibraryTemplatePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.template;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +
  10 +@Schema(description = "管理后台 - PMS 知识库模板分页 Request VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +public class PmsKnowledgeLibraryTemplatePageReqVO extends PageParam {
  14 +
  15 + @Schema(description = "模板名称,模糊匹配", example = "产品研发")
  16 + private String name;
  17 +
  18 + @Schema(description = "模板状态", example = "0")
  19 + @InEnum(CommonStatusEnum.class)
  20 + private Integer status;
  21 +
  22 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/template/PmsKnowledgeLibraryTemplateRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.template;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库模板 Response VO")
  10 +@Data
  11 +public class PmsKnowledgeLibraryTemplateRespVO {
  12 +
  13 + @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品研发")
  17 + private String name;
  18 +
  19 + @Schema(description = "模板简介", example = "提供完善的产品流程文档")
  20 + private String description;
  21 +
  22 + @Schema(description = "模板封面地址", example = "/static/pms/kb/template/product.png")
  23 + private String coverUrl;
  24 +
  25 + @Schema(description = "模板状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  26 + private Integer status;
  27 +
  28 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  29 + private Integer sort;
  30 +
  31 + @Schema(description = "模板文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + private List<PmsKnowledgeLibraryTemplateSaveReqVO.Document> documents;
  33 +
  34 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + private LocalDateTime createTime;
  36 +
  37 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/library/vo/template/PmsKnowledgeLibraryTemplateSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.library.vo.template;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.Valid;
  7 +import jakarta.validation.constraints.NotBlank;
  8 +import jakarta.validation.constraints.NotEmpty;
  9 +import jakarta.validation.constraints.NotNull;
  10 +import jakarta.validation.constraints.PositiveOrZero;
  11 +import jakarta.validation.constraints.Size;
  12 +import lombok.Data;
  13 +
  14 +import java.util.List;
  15 +
  16 +@Schema(description = "管理后台 - PMS 知识库模板新增/修改 Request VO")
  17 +@Data
  18 +public class PmsKnowledgeLibraryTemplateSaveReqVO {
  19 +
  20 + @Schema(description = "模板编号", example = "1024")
  21 + private Long id;
  22 +
  23 + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品研发")
  24 + @NotBlank(message = "模板名称不能为空")
  25 + @Size(max = 100, message = "模板名称不能超过 100 个字符")
  26 + private String name;
  27 +
  28 + @Schema(description = "模板简介", example = "提供完善的产品流程文档")
  29 + @Size(max = 500, message = "模板简介不能超过 500 个字符")
  30 + private String description;
  31 +
  32 + @Schema(description = "模板封面地址", example = "/static/pms/kb/template/product.png")
  33 + @Size(max = 255, message = "模板封面地址不能超过 255 个字符")
  34 + private String coverUrl;
  35 +
  36 + @Schema(description = "模板状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  37 + @NotNull(message = "模板状态不能为空")
  38 + @InEnum(CommonStatusEnum.class)
  39 + private Integer status;
  40 +
  41 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  42 + @NotNull(message = "显示顺序不能为空")
  43 + @PositiveOrZero(message = "显示顺序不能小于 0")
  44 + private Integer sort;
  45 +
  46 + @Schema(description = "模板文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
  47 + @NotEmpty(message = "模板文档不能为空")
  48 + @Valid
  49 + private List<Document> documents;
  50 +
  51 + @Schema(description = "模板文档")
  52 + @Data
  53 + public static class Document {
  54 +
  55 + @Schema(description = "文档标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品需求文档")
  56 + @NotBlank(message = "文档标题不能为空")
  57 + @Size(max = 255, message = "文档标题不能超过 255 个字符")
  58 + private String title;
  59 +
  60 + @Schema(description = "文档内容", requiredMode = Schema.RequiredMode.REQUIRED)
  61 + @NotBlank(message = "文档内容不能为空")
  62 + private String content;
  63 +
  64 + }
  65 +
  66 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/recycle/PmsKnowledgeRecycleController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.recycle;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.pms.controller.admin.kb.recycle.vo.PmsKnowledgeRecycleDetailRespVO;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.kb.recycle.vo.PmsKnowledgeRecyclePreviewRespVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.kb.recycle.vo.PmsKnowledgeRecycleRespVO;
  8 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.content.PmsKnowledgeDocumentDO;
  9 +import cn.iocoder.yudao.module.pms.dal.dataobject.kb.recycle.PmsKnowledgeRecycleRecordDO;
  10 +import cn.iocoder.yudao.module.pms.enums.kb.PmsKnowledgeObjectTypeEnum;
  11 +import cn.iocoder.yudao.module.pms.service.kb.content.PmsKnowledgeDocumentService;
  12 +import cn.iocoder.yudao.module.pms.service.kb.recycle.PmsKnowledgeRecycleService;
  13 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  14 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  15 +import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import jakarta.annotation.Resource;
  19 +import org.springframework.security.access.prepost.PreAuthorize;
  20 +import org.springframework.validation.annotation.Validated;
  21 +import org.springframework.web.bind.annotation.DeleteMapping;
  22 +import org.springframework.web.bind.annotation.GetMapping;
  23 +import org.springframework.web.bind.annotation.PutMapping;
  24 +import org.springframework.web.bind.annotation.RequestMapping;
  25 +import org.springframework.web.bind.annotation.RequestParam;
  26 +import org.springframework.web.bind.annotation.RestController;
  27 +
  28 +import java.util.List;
  29 +import java.util.Map;
  30 +
  31 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  35 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  36 +
  37 +@Tag(name = "管理后台 - PMS 知识库回收站")
  38 +@RestController
  39 +@RequestMapping("/pms/kb/recycle")
  40 +@Validated
  41 +public class PmsKnowledgeRecycleController {
  42 +
  43 + @Resource
  44 + private PmsKnowledgeRecycleService recycleService;
  45 +
  46 + @Resource
  47 + private AdminUserApi adminUserApi;
  48 +
  49 + @Resource
  50 + private PmsKnowledgeDocumentService documentService;
  51 +
  52 + @GetMapping("/library-list")
  53 + @Operation(summary = "获得已删除知识库列表")
  54 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  55 + public CommonResult<List<PmsKnowledgeRecycleRespVO>> getLibraryRecycleList() {
  56 + List<PmsKnowledgeRecycleRecordDO> records = recycleService.getLibraryRecycleList(getLoginUserId());
  57 + return success(buildRecycleRespVOList(records));
  58 + }
  59 +
  60 + @GetMapping("/content-list")
  61 + @Operation(summary = "获得知识库最近删除内容")
  62 + @Parameter(name = "libraryId", description = "知识库编号", required = true, example = "1024")
  63 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  64 + public CommonResult<List<PmsKnowledgeRecycleRespVO>> getContentRecycleList(
  65 + @RequestParam("libraryId") Long libraryId) {
  66 + List<PmsKnowledgeRecycleRecordDO> records = recycleService.getContentRecycleList(
  67 + libraryId, getLoginUserId());
  68 + return success(buildRecycleRespVOList(records));
  69 + }
  70 +
  71 + @GetMapping("/content-detail")
  72 + @Operation(summary = "获得知识库最近删除详情")
  73 + @Parameter(name = "id", description = "回收站记录编号", required = true, example = "1024")
  74 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  75 + public CommonResult<PmsKnowledgeRecycleDetailRespVO> getContentRecycleDetail(
  76 + @RequestParam("id") Long id) {
  77 + return success(recycleService.getContentRecycleDetail(id, getLoginUserId()));
  78 + }
  79 +
  80 + @GetMapping("/content-preview")
  81 + @Operation(summary = "预览知识库最近删除内容")
  82 + @Parameter(name = "id", description = "回收站记录编号", required = true, example = "1024")
  83 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  84 + public CommonResult<PmsKnowledgeRecyclePreviewRespVO> getContentRecyclePreview(
  85 + @RequestParam("id") Long id,
  86 + @RequestParam(value = "entityId", required = false) Long entityId) {
  87 + PmsKnowledgeDocumentDO document = recycleService.getContentRecyclePreview(id, entityId, getLoginUserId());
  88 + return success(BeanUtils.toBean(document, PmsKnowledgeRecyclePreviewRespVO.class,
  89 + o -> o.setName(document.getTitle())));
  90 + }
  91 +
  92 + @PutMapping("/restore")
  93 + @Operation(summary = "恢复回收站对象")
  94 + @Parameter(name = "id", description = "回收站记录编号", required = true, example = "1024")
  95 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  96 + public CommonResult<Boolean> restoreContentRecycle(@RequestParam("id") Long id) {
  97 + recycleService.restoreContentRecycle(id, getLoginUserId());
  98 + return success(true);
  99 + }
  100 +
  101 + @DeleteMapping("/permanent-delete")
  102 + @Operation(summary = "彻底删除回收站对象")
  103 + @Parameter(name = "id", description = "回收站记录编号", required = true, example = "1024")
  104 + @PreAuthorize("@ss.hasPermission('pms:kb:library:delete')")
  105 + public CommonResult<Boolean> deleteContentRecycle(@RequestParam("id") Long id) {
  106 + recycleService.deleteContentRecycle(id, getLoginUserId());
  107 + return success(true);
  108 + }
  109 +
  110 + // ==================== 拼接 VO ====================
  111 +
  112 + private List<PmsKnowledgeRecycleRespVO> buildRecycleRespVOList(List<PmsKnowledgeRecycleRecordDO> records) {
  113 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  114 + convertSet(records, PmsKnowledgeRecycleRecordDO::getDeleteUserId));
  115 + Map<Long, PmsKnowledgeDocumentDO> documentMap = documentService.getDocumentMap(convertSet(records, record ->
  116 + PmsKnowledgeObjectTypeEnum.FILE.getType().equals(record.getType()) ? record.getEntityId() : null));
  117 + return convertList(records, record -> {
  118 + PmsKnowledgeRecycleRespVO recordVO = BeanUtils.toBean(record, PmsKnowledgeRecycleRespVO.class);
  119 + findAndThen(documentMap, record.getEntityId(), document -> recordVO.setFileSize(document.getFileSize()));
  120 + findAndThen(userMap, record.getDeleteUserId(), user -> recordVO.setDeleteUserName(user.getNickname()));
  121 + return recordVO;
  122 + });
  123 + }
  124 +
  125 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/recycle/vo/PmsKnowledgeRecycleDetailRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.recycle.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 知识库回收站详情 Response VO")
  10 +@Data
  11 +public class PmsKnowledgeRecycleDetailRespVO {
  12 +
  13 + @Schema(description = "根回收站记录")
  14 + private PmsKnowledgeRecycleRespVO root;
  15 +
  16 + @Schema(description = "级联删除内容")
  17 + private List<Item> children;
  18 +
  19 + @Data
  20 + public static class Item {
  21 +
  22 + @Schema(description = "内容编号", requiredMode = Schema.RequiredMode.REQUIRED)
  23 + private Long id;
  24 +
  25 + @Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED)
  26 + private Integer type;
  27 +
  28 + @Schema(description = "内容名称", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + private String name;
  30 +
  31 + @Schema(description = "父文档或父文件夹编号")
  32 + private Long parentId;
  33 +
  34 + @Schema(description = "所属文件夹编号")
  35 + private Long folderId;
  36 +
  37 + @Schema(description = "删除时间")
  38 + private LocalDateTime deleteTime;
  39 +
  40 + }
  41 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/recycle/vo/PmsKnowledgeRecyclePreviewRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.recycle.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - PMS 知识库回收站内容预览 Response VO")
  7 +@Data
  8 +public class PmsKnowledgeRecyclePreviewRespVO {
  9 +
  10 + @Schema(description = "内容编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  11 + private Long id;
  12 +
  13 + @Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  14 + private Integer type;
  15 +
  16 + @Schema(description = "内容名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品说明")
  17 + private String name;
  18 +
  19 + @Schema(description = "文档正文或文件地址")
  20 + private String content;
  21 +
  22 + @Schema(description = "文件类型", example = "pdf")
  23 + private String fileType;
  24 +
  25 + @Schema(description = "文件大小,单位:字节", example = "102400")
  26 + private Long fileSize;
  27 +
  28 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/kb/recycle/vo/PmsKnowledgeRecycleRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.kb.recycle.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 知识库回收站记录 Response VO")
  9 +@Data
  10 +public class PmsKnowledgeRecycleRespVO {
  11 +
  12 + @Schema(description = "回收站记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long libraryId;
  17 +
  18 + @Schema(description = "对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  19 + private Integer type;
  20 +
  21 + @Schema(description = "对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  22 + private Long entityId;
  23 +
  24 + @Schema(description = "对象名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品说明")
  25 + private String name;
  26 +
  27 + @Schema(description = "文件大小,单位:字节")
  28 + private Long fileSize;
  29 +
  30 + @Schema(description = "删除人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  31 + private Long deleteUserId;
  32 +
  33 + @Schema(description = "删除人姓名", example = "芋道")
  34 + private String deleteUserName;
  35 +
  36 + @Schema(description = "删除时间", requiredMode = Schema.RequiredMode.REQUIRED)
  37 + private LocalDateTime deleteTime;
  38 +
  39 + @Schema(description = "父对象编号,回收站详情中用于组装层级", example = "2048")
  40 + private Long parentId;
  41 +
  42 + @Schema(description = "所属文件夹编号,回收站详情中用于组装层级", example = "2048")
  43 + private Long folderId;
  44 +
  45 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/iteration/PmsIterationController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.iteration;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo.PmsIterationOverviewRespVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo.PmsIterationPageReqVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo.PmsIterationRespVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo.PmsIterationSaveReqVO;
  11 +import cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo.PmsIterationStartReqVO;
  12 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.iteration.PmsIterationDO;
  13 +import cn.iocoder.yudao.module.pms.service.pm.iteration.PmsIterationService;
  14 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  15 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  16 +import io.swagger.v3.oas.annotations.Operation;
  17 +import io.swagger.v3.oas.annotations.Parameter;
  18 +import io.swagger.v3.oas.annotations.tags.Tag;
  19 +import jakarta.annotation.Resource;
  20 +import jakarta.validation.Valid;
  21 +import org.springframework.security.access.prepost.PreAuthorize;
  22 +import org.springframework.validation.annotation.Validated;
  23 +import org.springframework.web.bind.annotation.DeleteMapping;
  24 +import org.springframework.web.bind.annotation.GetMapping;
  25 +import org.springframework.web.bind.annotation.PostMapping;
  26 +import org.springframework.web.bind.annotation.PutMapping;
  27 +import org.springframework.web.bind.annotation.RequestBody;
  28 +import org.springframework.web.bind.annotation.RequestMapping;
  29 +import org.springframework.web.bind.annotation.RequestParam;
  30 +import org.springframework.web.bind.annotation.RestController;
  31 +
  32 +import java.util.Collections;
  33 +import java.util.List;
  34 +import java.util.Map;
  35 +
  36 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  37 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  38 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  39 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  40 +
  41 +@Tag(name = "管理后台 - PMS 项目迭代")
  42 +@RestController
  43 +@RequestMapping("/pms/pm/iteration")
  44 +@Validated
  45 +public class PmsIterationController {
  46 +
  47 + @Resource
  48 + private PmsIterationService iterationService;
  49 +
  50 + @Resource
  51 + private AdminUserApi adminUserApi;
  52 +
  53 + @PostMapping("/create")
  54 + @Operation(summary = "创建项目迭代")
  55 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:create')")
  56 + public CommonResult<Long> createIteration(@Valid @RequestBody PmsIterationSaveReqVO saveReqVO) {
  57 + return success(iterationService.createIteration(saveReqVO, getLoginUserId()));
  58 + }
  59 +
  60 + @PutMapping("/update")
  61 + @Operation(summary = "更新项目迭代")
  62 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:update')")
  63 + public CommonResult<Boolean> updateIteration(@Valid @RequestBody PmsIterationSaveReqVO saveReqVO) {
  64 + iterationService.updateIteration(saveReqVO, getLoginUserId());
  65 + return success(true);
  66 + }
  67 +
  68 + @PutMapping("/start")
  69 + @Operation(summary = "开始项目迭代")
  70 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:update')")
  71 + public CommonResult<Boolean> startIteration(@Valid @RequestBody PmsIterationStartReqVO startReqVO) {
  72 + iterationService.startIteration(startReqVO, getLoginUserId());
  73 + return success(true);
  74 + }
  75 +
  76 + @PutMapping("/complete")
  77 + @Operation(summary = "完成项目迭代")
  78 + @Parameter(name = "id", description = "迭代编号", required = true, example = "1024")
  79 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:update')")
  80 + public CommonResult<Boolean> completeIteration(@RequestParam("id") Long id) {
  81 + iterationService.completeIteration(id, getLoginUserId());
  82 + return success(true);
  83 + }
  84 +
  85 + @DeleteMapping("/delete")
  86 + @Operation(summary = "删除项目迭代")
  87 + @Parameter(name = "id", description = "迭代编号", required = true, example = "1024")
  88 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:delete')")
  89 + public CommonResult<Boolean> deleteIteration(@RequestParam("id") Long id) {
  90 + iterationService.deleteIteration(id, getLoginUserId());
  91 + return success(true);
  92 + }
  93 +
  94 + @GetMapping("/get")
  95 + @Operation(summary = "获得项目迭代详情")
  96 + @Parameter(name = "id", description = "迭代编号", required = true, example = "1024")
  97 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:query')")
  98 + public CommonResult<PmsIterationRespVO> getIteration(@RequestParam("id") Long id) {
  99 + PmsIterationDO iteration = iterationService.getIteration(id, getLoginUserId());
  100 + PmsIterationRespVO iterationVO = buildIterationRespVO(iteration);
  101 + return success(iterationVO);
  102 + }
  103 +
  104 + @GetMapping("/page")
  105 + @Operation(summary = "获得项目迭代分页")
  106 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:query')")
  107 + public CommonResult<PageResult<PmsIterationRespVO>> getIterationPage(
  108 + @Valid PmsIterationPageReqVO pageReqVO) {
  109 + PageResult<PmsIterationDO> pageResult = iterationService.getIterationPage(pageReqVO, getLoginUserId());
  110 + List<PmsIterationRespVO> iterationVOList = buildIterationRespVOList(pageResult.getList());
  111 + Map<Long, Integer> progressMap = iterationService.getIterationProgressMap(
  112 + convertSet(pageResult.getList(), PmsIterationDO::getId));
  113 + iterationVOList.forEach(iteration -> iteration.setProgress(progressMap.getOrDefault(iteration.getId(), 0)));
  114 + return success(new PageResult<>(iterationVOList, pageResult.getTotal()));
  115 + }
  116 +
  117 + @GetMapping("/overview")
  118 + @Operation(summary = "获得迭代概览统计")
  119 + @Parameter(name = "id", description = "迭代编号", required = true, example = "1024")
  120 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:query')")
  121 + public CommonResult<PmsIterationOverviewRespVO> getIterationOverview(@RequestParam("id") Long id) {
  122 + PmsIterationOverviewRespVO overview = iterationService.getIterationOverview(id, getLoginUserId());
  123 + List<PmsIterationOverviewRespVO.ActivityItem> activities = overview.getRecentActivities();
  124 + Map<Long, AdminUserRespDTO> activityUserMap = adminUserApi.getUserMap(
  125 + convertSet(activities, PmsIterationOverviewRespVO.ActivityItem::getOperatorUserId));
  126 + activities.forEach(activity -> findAndThen(activityUserMap, activity.getOperatorUserId(),
  127 + user -> activity.setOperatorUserName(user.getNickname())));
  128 + return success(overview.setRecentActivities(activities));
  129 + }
  130 +
  131 + // ==================== 拼接 VO ====================
  132 +
  133 + private PmsIterationRespVO buildIterationRespVO(PmsIterationDO iteration) {
  134 + return CollUtil.getFirst(buildIterationRespVOList(Collections.singletonList(iteration)));
  135 + }
  136 +
  137 + private List<PmsIterationRespVO> buildIterationRespVOList(List<PmsIterationDO> iterations) {
  138 + if (CollUtil.isEmpty(iterations)) {
  139 + return Collections.emptyList();
  140 + }
  141 +
  142 + // 1. 批量查询负责人
  143 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  144 + convertSet(iterations, PmsIterationDO::getOwnerUserId));
  145 +
  146 + // 2. 转换迭代响应,并拼接负责人姓名
  147 + return BeanUtils.toBean(iterations, PmsIterationRespVO.class, iterationVO ->
  148 + findAndThen(userMap, iterationVO.getOwnerUserId(),
  149 + user -> iterationVO.setOwnerUserName(user.getNickname())));
  150 + }
  151 +
  152 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/iteration/vo/PmsIterationOverviewRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +import java.util.Map;
  9 +
  10 +@Schema(description = "管理后台 - PMS 迭代概览 Response VO")
  11 +@Data
  12 +public class PmsIterationOverviewRespVO {
  13 +
  14 + @Schema(description = "工作项总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  15 + private Integer totalCount;
  16 +
  17 + @Schema(description = "未开始工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
  18 + private Integer pendingCount;
  19 +
  20 + @Schema(description = "进行中工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "30")
  21 + private Integer processingCount;
  22 +
  23 + @Schema(description = "已完成工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
  24 + private Integer completedCount;
  25 +
  26 + @Schema(description = "完成进度百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
  27 + private Integer progress;
  28 +
  29 + @Schema(description = "工作项类型数量统计", requiredMode = Schema.RequiredMode.REQUIRED, example = "{\"1\": 60, \"2\": 40}")
  30 + private Map<Integer, Integer> typeCountMap;
  31 +
  32 + @Schema(description = "工作项类型和状态数量统计", requiredMode = Schema.RequiredMode.REQUIRED)
  33 + private Map<Integer, Map<Integer, Integer>> typeStatusCountMap;
  34 +
  35 + @Schema(description = "状态趋势", requiredMode = Schema.RequiredMode.REQUIRED)
  36 + private List<TrendItem> statusTrends;
  37 +
  38 + @Schema(description = "燃尽图数据", requiredMode = Schema.RequiredMode.REQUIRED)
  39 + private List<BurnDownItem> burnDowns;
  40 +
  41 + @Schema(description = "最近动态列表", requiredMode = Schema.RequiredMode.REQUIRED)
  42 + private List<ActivityItem> recentActivities;
  43 +
  44 + @Schema(description = "管理后台 - PMS 迭代概览状态趋势项 VO")
  45 + @Data
  46 + public static class TrendItem {
  47 +
  48 + @Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-01-01")
  49 + private String date;
  50 +
  51 + @Schema(description = "当日未开始工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  52 + private Integer pendingCount;
  53 +
  54 + @Schema(description = "当日进行中工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  55 + private Integer processingCount;
  56 +
  57 + @Schema(description = "当日已完成工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  58 + private Integer completedCount;
  59 +
  60 + }
  61 +
  62 + @Schema(description = "管理后台 - PMS 迭代概览燃尽图项 VO")
  63 + @Data
  64 + public static class BurnDownItem {
  65 +
  66 + @Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-01-01")
  67 + private String date;
  68 +
  69 + @Schema(description = "理想剩余工时,单位:小时", requiredMode = Schema.RequiredMode.REQUIRED, example = "40")
  70 + private Integer idealRemaining;
  71 +
  72 + @Schema(description = "实际剩余工时,单位:小时", requiredMode = Schema.RequiredMode.REQUIRED, example = "38")
  73 + private Integer actualRemaining;
  74 +
  75 + }
  76 +
  77 + @Schema(description = "管理后台 - PMS 迭代概览动态项 VO")
  78 + @Data
  79 + public static class ActivityItem {
  80 +
  81 + @Schema(description = "动态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  82 + private Long id;
  83 +
  84 + @Schema(description = "工作项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  85 + private Long workItemId;
  86 +
  87 + @Schema(description = "工作项序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  88 + private Integer workItemSerialNumber;
  89 +
  90 + @Schema(description = "工作项名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "优化登录页")
  91 + private String workItemName;
  92 +
  93 + @Schema(description = "操作人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  94 + private Long operatorUserId;
  95 +
  96 + @Schema(description = "操作人昵称", example = "芋道")
  97 + private String operatorUserName;
  98 +
  99 + @Schema(description = "动态内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "将状态更新为「已完成」")
  100 + private String content;
  101 +
  102 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  103 + private LocalDateTime createTime;
  104 +
  105 + }
  106 +
  107 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/iteration/vo/PmsIterationPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.pms.enums.pm.iteration.PmsIterationStatusEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +
  12 +@Schema(description = "管理后台 - PMS 项目迭代分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class PmsIterationPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @NotNull(message = "项目编号不能为空")
  20 + private Long projectId;
  21 +
  22 + @Schema(description = "迭代名称", example = "第一期")
  23 + private String name;
  24 +
  25 + @Schema(description = "迭代状态", example = "1")
  26 + @InEnum(PmsIterationStatusEnum.class)
  27 + private Integer status;
  28 +
  29 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/iteration/vo/PmsIterationRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 项目迭代 Response VO")
  9 +@Data
  10 +public class PmsIterationRespVO {
  11 +
  12 + @Schema(description = "迭代编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long projectId;
  17 +
  18 + @Schema(description = "迭代名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "第一期")
  19 + private String name;
  20 +
  21 + @Schema(description = "负责人用户编号", example = "1")
  22 + private Long ownerUserId;
  23 +
  24 + @Schema(description = "迭代状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  25 + private Integer status;
  26 +
  27 + @Schema(description = "负责人姓名", example = "芋道源码")
  28 + private String ownerUserName;
  29 +
  30 + @Schema(description = "开始时间")
  31 + private LocalDateTime startTime;
  32 +
  33 + @Schema(description = "结束时间")
  34 + private LocalDateTime endTime;
  35 +
  36 + @Schema(description = "迭代目标", example = "完成核心流程")
  37 + private String target;
  38 +
  39 + @Schema(description = "迭代描述")
  40 + private String description;
  41 +
  42 + @Schema(description = "完成时间")
  43 + private LocalDateTime finishTime;
  44 +
  45 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  46 + private Integer sort;
  47 +
  48 + @Schema(description = "完成进度百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
  49 + private Integer progress;
  50 +
  51 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  52 + private LocalDateTime createTime;
  53 +
  54 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/iteration/vo/PmsIterationSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonIgnore;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.AssertTrue;
  6 +import jakarta.validation.constraints.NotBlank;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import jakarta.validation.constraints.Size;
  9 +import lombok.Data;
  10 +
  11 +import java.time.LocalDateTime;
  12 +
  13 +@Schema(description = "管理后台 - PMS 项目迭代新增/修改 Request VO")
  14 +@Data
  15 +public class PmsIterationSaveReqVO {
  16 +
  17 + @Schema(description = "迭代编号", example = "1024")
  18 + private Long id;
  19 +
  20 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  21 + @NotNull(message = "项目编号不能为空")
  22 + private Long projectId;
  23 +
  24 + @Schema(description = "迭代名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "第一期")
  25 + @NotBlank(message = "迭代名称不能为空")
  26 + @Size(max = 100, message = "迭代名称长度不能超过 100 个字符")
  27 + private String name;
  28 +
  29 + @Schema(description = "负责人用户编号", example = "1")
  30 + private Long ownerUserId;
  31 +
  32 + @Schema(description = "开始时间")
  33 + private LocalDateTime startTime;
  34 +
  35 + @Schema(description = "结束时间")
  36 + private LocalDateTime endTime;
  37 +
  38 + @Schema(description = "迭代目标", example = "完成核心流程")
  39 + @Size(max = 255, message = "迭代目标长度不能超过 255 个字符")
  40 + private String target;
  41 +
  42 + @Schema(description = "迭代描述")
  43 + private String description;
  44 +
  45 + @Schema(hidden = true)
  46 + @AssertTrue(message = "迭代结束时间必须晚于开始时间,且开始、结束时间需要同时填写")
  47 + @JsonIgnore
  48 + public boolean isTimeRangeValid() {
  49 + return startTime == null && endTime == null
  50 + || startTime != null && endTime != null && endTime.isAfter(startTime);
  51 + }
  52 +
  53 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/iteration/vo/PmsIterationStartReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.iteration.vo;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonIgnore;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.AssertTrue;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +@Schema(description = "管理后台 - PMS 项目迭代开始 Request VO")
  12 +@Data
  13 +public class PmsIterationStartReqVO {
  14 +
  15 + @Schema(description = "迭代编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + @NotNull(message = "迭代编号不能为空")
  17 + private Long id;
  18 +
  19 + @Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
  20 + @NotNull(message = "迭代开始时间不能为空")
  21 + private LocalDateTime startTime;
  22 +
  23 + @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
  24 + @NotNull(message = "迭代结束时间不能为空")
  25 + private LocalDateTime endTime;
  26 +
  27 + @Schema(hidden = true)
  28 + @AssertTrue(message = "迭代结束时间必须晚于开始时间")
  29 + @JsonIgnore
  30 + public boolean isTimeRangeValid() {
  31 + return startTime == null || endTime == null || endTime.isAfter(startTime);
  32 + }
  33 +
  34 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/PmsProjectAnnouncementController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.announcement.PmsProjectAnnouncementRespVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.announcement.PmsProjectAnnouncementSaveReqVO;
  9 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.project.PmsProjectAnnouncementDO;
  10 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectAnnouncementService;
  11 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  12 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  13 +import io.swagger.v3.oas.annotations.Operation;
  14 +import io.swagger.v3.oas.annotations.Parameter;
  15 +import io.swagger.v3.oas.annotations.tags.Tag;
  16 +import jakarta.annotation.Resource;
  17 +import jakarta.validation.Valid;
  18 +import org.springframework.security.access.prepost.PreAuthorize;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import org.springframework.web.bind.annotation.DeleteMapping;
  21 +import org.springframework.web.bind.annotation.GetMapping;
  22 +import org.springframework.web.bind.annotation.PostMapping;
  23 +import org.springframework.web.bind.annotation.PutMapping;
  24 +import org.springframework.web.bind.annotation.RequestBody;
  25 +import org.springframework.web.bind.annotation.RequestMapping;
  26 +import org.springframework.web.bind.annotation.RequestParam;
  27 +import org.springframework.web.bind.annotation.RestController;
  28 +
  29 +import java.util.Collections;
  30 +import java.util.List;
  31 +import java.util.Map;
  32 +
  33 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  37 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  38 +
  39 +@Tag(name = "管理后台 - PMS 项目公告")
  40 +@RestController
  41 +@RequestMapping("/pms/pm/project-announcement")
  42 +@Validated
  43 +public class PmsProjectAnnouncementController {
  44 +
  45 + @Resource
  46 + private PmsProjectAnnouncementService projectAnnouncementService;
  47 + @Resource
  48 + private AdminUserApi adminUserApi;
  49 +
  50 + @PostMapping("/create")
  51 + @Operation(summary = "创建项目公告")
  52 + @PreAuthorize("@ss.hasPermission('pms:pm:project:update')")
  53 + public CommonResult<Long> createProjectAnnouncement(@Valid @RequestBody PmsProjectAnnouncementSaveReqVO saveReqVO) {
  54 + return success(projectAnnouncementService.createProjectAnnouncement(saveReqVO, getLoginUserId()));
  55 + }
  56 +
  57 + @PutMapping("/update")
  58 + @Operation(summary = "更新项目公告")
  59 + @PreAuthorize("@ss.hasPermission('pms:pm:project:update')")
  60 + public CommonResult<Boolean> updateProjectAnnouncement(
  61 + @Valid @RequestBody PmsProjectAnnouncementSaveReqVO saveReqVO) {
  62 + projectAnnouncementService.updateProjectAnnouncement(saveReqVO, getLoginUserId());
  63 + return success(true);
  64 + }
  65 +
  66 + @DeleteMapping("/delete")
  67 + @Operation(summary = "删除项目公告")
  68 + @Parameter(name = "id", description = "公告编号", required = true, example = "1024")
  69 + @PreAuthorize("@ss.hasPermission('pms:pm:project:update')")
  70 + public CommonResult<Boolean> deleteProjectAnnouncement(@RequestParam("id") Long id) {
  71 + projectAnnouncementService.deleteProjectAnnouncement(id, getLoginUserId());
  72 + return success(true);
  73 + }
  74 +
  75 + @GetMapping("/get")
  76 + @Operation(summary = "获得项目公告详情")
  77 + @Parameter(name = "id", description = "公告编号", required = true, example = "1024")
  78 + @PreAuthorize("@ss.hasPermission('pms:pm:project:query')")
  79 + public CommonResult<PmsProjectAnnouncementRespVO> getProjectAnnouncement(@RequestParam("id") Long id) {
  80 + PmsProjectAnnouncementDO announcement = projectAnnouncementService
  81 + .getProjectAnnouncement(id, getLoginUserId());
  82 + return success(buildProjectAnnouncementRespVO(announcement));
  83 + }
  84 +
  85 + @GetMapping("/list")
  86 + @Operation(summary = "获得项目公告列表")
  87 + @Parameter(name = "projectId", description = "项目编号", required = true, example = "1024")
  88 + @PreAuthorize("@ss.hasPermission('pms:pm:project:query')")
  89 + public CommonResult<List<PmsProjectAnnouncementRespVO>> getProjectAnnouncementList(
  90 + @RequestParam("projectId") Long projectId) {
  91 + List<PmsProjectAnnouncementDO> announcements = projectAnnouncementService
  92 + .getProjectAnnouncementList(projectId, getLoginUserId());
  93 + List<PmsProjectAnnouncementRespVO> announcementVOList = buildProjectAnnouncementRespVOList(announcements);
  94 + return success(announcementVOList);
  95 + }
  96 +
  97 + // ==================== 拼接 VO ====================
  98 +
  99 + /**
  100 + * 构建项目公告响应
  101 + *
  102 + * @param announcement 项目公告
  103 + * @return 项目公告响应
  104 + */
  105 + private PmsProjectAnnouncementRespVO buildProjectAnnouncementRespVO(PmsProjectAnnouncementDO announcement) {
  106 + return CollUtil.getFirst(buildProjectAnnouncementRespVOList(Collections.singletonList(announcement)));
  107 + }
  108 +
  109 + /**
  110 + * 构建项目公告响应列表
  111 + *
  112 + * @param announcements 项目公告列表
  113 + * @return 项目公告响应列表
  114 + */
  115 + private List<PmsProjectAnnouncementRespVO> buildProjectAnnouncementRespVOList(
  116 + List<PmsProjectAnnouncementDO> announcements) {
  117 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(announcements,
  118 + announcement -> NumberUtils.parseLong(announcement.getCreator())));
  119 + return convertList(announcements, announcement -> {
  120 + PmsProjectAnnouncementRespVO announcementVO = BeanUtils.toBean(
  121 + announcement, PmsProjectAnnouncementRespVO.class)
  122 + .setCreatorUserId(NumberUtils.parseLong(announcement.getCreator()));
  123 + findAndThen(userMap, announcementVO.getCreatorUserId(),
  124 + user -> announcementVO.setCreatorUserName(user.getNickname()));
  125 + return announcementVO;
  126 + });
  127 + }
  128 +
  129 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/PmsProjectController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.project.PmsProjectOverviewRespVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.project.PmsProjectPageReqVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.project.PmsProjectRespVO;
  11 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.project.PmsProjectSaveReqVO;
  12 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.project.PmsProjectDO;
  13 +import cn.iocoder.yudao.module.pms.enums.pm.workitem.PmsWorkItemStatusTypeEnum;
  14 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectFavoriteService;
  15 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectMemberService;
  16 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectService;
  17 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemService;
  18 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  19 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  20 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  21 +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
  22 +import io.swagger.v3.oas.annotations.Operation;
  23 +import io.swagger.v3.oas.annotations.Parameter;
  24 +import io.swagger.v3.oas.annotations.tags.Tag;
  25 +import jakarta.annotation.Resource;
  26 +import jakarta.validation.Valid;
  27 +import org.springframework.security.access.prepost.PreAuthorize;
  28 +import org.springframework.validation.annotation.Validated;
  29 +import org.springframework.web.bind.annotation.DeleteMapping;
  30 +import org.springframework.web.bind.annotation.GetMapping;
  31 +import org.springframework.web.bind.annotation.PostMapping;
  32 +import org.springframework.web.bind.annotation.PutMapping;
  33 +import org.springframework.web.bind.annotation.RequestBody;
  34 +import org.springframework.web.bind.annotation.RequestMapping;
  35 +import org.springframework.web.bind.annotation.RequestParam;
  36 +import org.springframework.web.bind.annotation.RestController;
  37 +
  38 +import java.util.ArrayList;
  39 +import java.util.Collections;
  40 +import java.util.List;
  41 +import java.util.Map;
  42 +import java.util.Set;
  43 +
  44 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  45 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  46 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  47 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  48 +
  49 +@Tag(name = "管理后台 - PMS 项目")
  50 +@RestController
  51 +@RequestMapping("/pms/pm/project")
  52 +@Validated
  53 +public class PmsProjectController {
  54 +
  55 + @Resource
  56 + private PmsProjectService projectService;
  57 + @Resource
  58 + private PmsProjectMemberService projectMemberService;
  59 + @Resource
  60 + private PmsProjectFavoriteService projectFavoriteService;
  61 + @Resource
  62 + private PmsWorkItemService workItemService;
  63 + @Resource
  64 + private AdminUserApi adminUserApi;
  65 + @Resource
  66 + private PermissionApi permissionApi;
  67 +
  68 + @PostMapping("/create")
  69 + @Operation(summary = "创建项目")
  70 + @PreAuthorize("@ss.hasPermission('pms:pm:project:create')")
  71 + public CommonResult<Long> createProject(@Valid @RequestBody PmsProjectSaveReqVO saveReqVO) {
  72 + return success(projectService.createProject(saveReqVO, getLoginUserId()));
  73 + }
  74 +
  75 + @PutMapping("/update")
  76 + @Operation(summary = "更新项目")
  77 + @PreAuthorize("@ss.hasPermission('pms:pm:project:update')")
  78 + public CommonResult<Boolean> updateProject(@Valid @RequestBody PmsProjectSaveReqVO saveReqVO) {
  79 + projectService.updateProject(saveReqVO, getLoginUserId());
  80 + return success(true);
  81 + }
  82 +
  83 + @GetMapping("/get")
  84 + @Operation(summary = "获得项目详情")
  85 + @Parameter(name = "id", description = "项目编号", required = true, example = "1024")
  86 + @PreAuthorize("@ss.hasPermission('pms:pm:project:query')")
  87 + public CommonResult<PmsProjectRespVO> getProject(@RequestParam("id") Long id) {
  88 + Long userId = getLoginUserId();
  89 + PmsProjectDO project = projectService.getProjectAndUpdateVisitTime(id, userId);
  90 + return success(buildProjectRespVO(project, userId));
  91 + }
  92 +
  93 + @GetMapping("/page")
  94 + @Operation(summary = "获得项目分页")
  95 + @PreAuthorize("@ss.hasPermission('pms:pm:project:query')")
  96 + public CommonResult<PageResult<PmsProjectRespVO>> getProjectPage(@Valid PmsProjectPageReqVO pageReqVO) {
  97 + Long userId = getLoginUserId();
  98 + PageResult<PmsProjectDO> pageResult = projectService.getProjectPage(pageReqVO, userId);
  99 + return success(new PageResult<>(buildProjectRespVOList(pageResult.getList(), userId), pageResult.getTotal()));
  100 + }
  101 +
  102 + @GetMapping("/favorite-list")
  103 + @Operation(summary = "获得星标项目列表")
  104 + @PreAuthorize("@ss.hasPermission('pms:pm:project:query')")
  105 + public CommonResult<List<PmsProjectRespVO>> getFavoriteProjectList() {
  106 + Long userId = getLoginUserId();
  107 + return success(buildProjectRespVOList(projectService.getFavoriteProjectList(userId), userId));
  108 + }
  109 +
  110 + @GetMapping("/overview")
  111 + @Operation(summary = "获得项目概况")
  112 + @Parameter(name = "projectId", description = "项目编号", required = true, example = "1024")
  113 + @PreAuthorize("@ss.hasPermission('pms:pm:project:query')")
  114 + public CommonResult<PmsProjectOverviewRespVO> getProjectOverview(@RequestParam("projectId") Long projectId) {
  115 + return success(projectService.getProjectOverview(projectId, getLoginUserId()));
  116 + }
  117 +
  118 + @PutMapping("/archive")
  119 + @Operation(summary = "归档项目")
  120 + @Parameter(name = "id", description = "项目编号", required = true, example = "1024")
  121 + @PreAuthorize("@ss.hasPermission('pms:pm:project:update')")
  122 + public CommonResult<Boolean> archiveProject(@RequestParam("id") Long id) {
  123 + projectService.archiveProject(id, getLoginUserId());
  124 + return success(true);
  125 + }
  126 +
  127 + @PutMapping("/recycle")
  128 + @Operation(summary = "将项目移入回收站")
  129 + @Parameter(name = "id", description = "项目编号", required = true, example = "1024")
  130 + @PreAuthorize("@ss.hasPermission('pms:pm:project:update')")
  131 + public CommonResult<Boolean> recycleProject(@RequestParam("id") Long id) {
  132 + projectService.recycleProject(id, getLoginUserId());
  133 + return success(true);
  134 + }
  135 +
  136 + @PutMapping("/restore")
  137 + @Operation(summary = "恢复项目")
  138 + @Parameter(name = "id", description = "项目编号", required = true, example = "1024")
  139 + @PreAuthorize("@ss.hasPermission('pms:pm:project:update')")
  140 + public CommonResult<Boolean> restoreProject(@RequestParam("id") Long id) {
  141 + projectService.restoreProject(id, getLoginUserId());
  142 + return success(true);
  143 + }
  144 +
  145 + @DeleteMapping("/delete")
  146 + @Operation(summary = "彻底删除回收站项目")
  147 + @Parameter(name = "id", description = "项目编号", required = true, example = "1024")
  148 + @PreAuthorize("@ss.hasPermission('pms:pm:project:delete')")
  149 + public CommonResult<Boolean> deleteProject(@RequestParam("id") Long id) {
  150 + projectService.deleteProject(id, getLoginUserId());
  151 + return success(true);
  152 + }
  153 +
  154 + // ==================== 拼接 VO ====================
  155 +
  156 + private PmsProjectRespVO buildProjectRespVO(PmsProjectDO project, Long userId) {
  157 + if (project == null) {
  158 + return null;
  159 + }
  160 + return CollUtil.getFirst(buildProjectRespVOList(Collections.singletonList(project), userId));
  161 + }
  162 +
  163 + private List<PmsProjectRespVO> buildProjectRespVOList(List<PmsProjectDO> projects, Long userId) {
  164 + if (CollUtil.isEmpty(projects)) {
  165 + return Collections.emptyList();
  166 + }
  167 + // 1. 批量查询创建人、项目管理员、成员数量和工作项完成度
  168 + Set<Long> projectIds = convertSet(projects, PmsProjectDO::getId);
  169 + Map<Long, List<Long>> managerUserIdListMap = projectMemberService.getProjectManagerUserIdListMap(projectIds);
  170 + Map<Long, Integer> memberCountMap = projectMemberService.getProjectMemberCountMap(projectIds);
  171 + Map<Long, Map<Integer, Long>> projectStatusCountMap = workItemService
  172 + .getProjectWorkItemStatusCountMap(projectIds);
  173 + Set<Long> userIds = convertSet(projects, project ->
  174 + NumberUtils.parseLong(project.getCreator()));
  175 + managerUserIdListMap.values().forEach(userIds::addAll);
  176 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
  177 + Set<Long> ownerProjectIds = convertSet(projectMemberService.getOwnerProjectIdListByUserId(userId));
  178 + Set<Long> managedProjectIds = convertSet(projectMemberService.getManagedProjectIdListByUserId(userId));
  179 + Set<Long> memberProjectIds = convertSet(projectMemberService.getProjectIdListByUserId(userId));
  180 + Set<Long> writableProjectIds = convertSet(projectMemberService.getWritableProjectIdListByUserId(userId));
  181 + Set<Long> favoriteProjectIds = convertSet(projectFavoriteService.getFavoriteProjectIdListByUserId(userId));
  182 + favoriteProjectIds.retainAll(projectIds);
  183 + boolean superAdmin = permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode());
  184 + // 2. 转换项目响应,并拼接展示字段
  185 + return BeanUtils.toBean(projects, PmsProjectRespVO.class, projectVO -> {
  186 + findAndThen(userMap, NumberUtils.parseLong(projectVO.getCreator()),
  187 + creator -> projectVO.setCreatorName(creator.getNickname()));
  188 + List<String> adminNames = new ArrayList<>();
  189 + for (Long adminUserId : managerUserIdListMap.getOrDefault(projectVO.getId(), Collections.emptyList())) {
  190 + findAndThen(userMap, adminUserId, admin -> adminNames.add(admin.getNickname()));
  191 + }
  192 + Map<Integer, Long> statusCountMap = projectStatusCountMap.getOrDefault(
  193 + projectVO.getId(), Collections.emptyMap());
  194 + projectVO.setAdminNames(adminNames).setMemberCount(memberCountMap.getOrDefault(projectVO.getId(), 0))
  195 + .setPendingWorkItemCount(statusCountMap.getOrDefault(PmsWorkItemStatusTypeEnum.PENDING.getType(), 0L))
  196 + .setProcessingWorkItemCount(statusCountMap.getOrDefault(PmsWorkItemStatusTypeEnum.PROCESSING.getType(), 0L))
  197 + .setCompletedWorkItemCount(statusCountMap.getOrDefault(PmsWorkItemStatusTypeEnum.COMPLETED.getType(), 0L))
  198 + .setOwnerStatus(superAdmin || ownerProjectIds.contains(projectVO.getId()))
  199 + .setAdminStatus(superAdmin || managedProjectIds.contains(projectVO.getId()))
  200 + .setMemberStatus(memberProjectIds.contains(projectVO.getId()))
  201 + .setExitStatus(memberProjectIds.contains(projectVO.getId()) && !ownerProjectIds.contains(projectVO.getId()))
  202 + .setWriteStatus(superAdmin || writableProjectIds.contains(projectVO.getId()))
  203 + .setFavoriteStatus(favoriteProjectIds.contains(projectVO.getId()));
  204 + });
  205 + }
  206 +
  207 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/PmsProjectFavoriteController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectFavoriteService;
  5 +import io.swagger.v3.oas.annotations.Operation;
  6 +import io.swagger.v3.oas.annotations.Parameter;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import jakarta.annotation.Resource;
  9 +import org.springframework.security.access.prepost.PreAuthorize;
  10 +import org.springframework.validation.annotation.Validated;
  11 +import org.springframework.web.bind.annotation.DeleteMapping;
  12 +import org.springframework.web.bind.annotation.PostMapping;
  13 +import org.springframework.web.bind.annotation.RequestMapping;
  14 +import org.springframework.web.bind.annotation.RequestParam;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  18 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  19 +
  20 +@Tag(name = "管理后台 - PMS 项目收藏")
  21 +@RestController
  22 +@RequestMapping("/pms/pm/project-favorite")
  23 +@Validated
  24 +public class PmsProjectFavoriteController {
  25 +
  26 + @Resource
  27 + private PmsProjectFavoriteService projectFavoriteService;
  28 +
  29 + @PostMapping("/create")
  30 + @Operation(summary = "收藏项目")
  31 + @Parameter(name = "projectId", description = "项目编号", required = true, example = "1024")
  32 + @PreAuthorize("@ss.hasPermission('pms:pm:project:query')")
  33 + public CommonResult<Boolean> createProjectFavorite(@RequestParam("projectId") Long projectId) {
  34 + projectFavoriteService.createProjectFavorite(projectId, getLoginUserId());
  35 + return success(true);
  36 + }
  37 +
  38 + @DeleteMapping("/delete")
  39 + @Operation(summary = "取消收藏项目")
  40 + @Parameter(name = "projectId", description = "项目编号", required = true, example = "1024")
  41 + @PreAuthorize("@ss.hasPermission('pms:pm:project:query')")
  42 + public CommonResult<Boolean> deleteProjectFavorite(@RequestParam("projectId") Long projectId) {
  43 + projectFavoriteService.deleteProjectFavorite(projectId, getLoginUserId());
  44 + return success(true);
  45 + }
  46 +
  47 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/PmsProjectGroupController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.group.PmsProjectGroupMoveReqVO;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.group.PmsProjectGroupRespVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.group.PmsProjectGroupSaveReqVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.group.PmsProjectGroupSortReqVO;
  9 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.project.PmsProjectGroupDO;
  10 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectGroupService;
  11 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectMemberService;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.tags.Tag;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.DeleteMapping;
  20 +import org.springframework.web.bind.annotation.GetMapping;
  21 +import org.springframework.web.bind.annotation.PostMapping;
  22 +import org.springframework.web.bind.annotation.PutMapping;
  23 +import org.springframework.web.bind.annotation.RequestBody;
  24 +import org.springframework.web.bind.annotation.RequestMapping;
  25 +import org.springframework.web.bind.annotation.RequestParam;
  26 +import org.springframework.web.bind.annotation.RestController;
  27 +
  28 +import java.util.List;
  29 +import java.util.Map;
  30 +
  31 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  32 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  33 +
  34 +@Tag(name = "管理后台 - PMS 项目分组")
  35 +@RestController
  36 +@RequestMapping("/pms/pm/project-group")
  37 +@Validated
  38 +public class PmsProjectGroupController {
  39 +
  40 + @Resource
  41 + private PmsProjectGroupService projectGroupService;
  42 + @Resource
  43 + private PmsProjectMemberService projectMemberService;
  44 +
  45 + @PostMapping("/create")
  46 + @Operation(summary = "创建项目分组")
  47 + @PreAuthorize("@ss.hasPermission('pms:pm:project-group:create')")
  48 + public CommonResult<Long> createProjectGroup(@Valid @RequestBody PmsProjectGroupSaveReqVO createReqVO) {
  49 + return success(projectGroupService.createProjectGroup(createReqVO, getLoginUserId()));
  50 + }
  51 +
  52 + @PutMapping("/update")
  53 + @Operation(summary = "修改项目分组")
  54 + @PreAuthorize("@ss.hasPermission('pms:pm:project-group:update')")
  55 + public CommonResult<Boolean> updateProjectGroup(@Valid @RequestBody PmsProjectGroupSaveReqVO updateReqVO) {
  56 + projectGroupService.updateProjectGroup(updateReqVO, getLoginUserId());
  57 + return success(true);
  58 + }
  59 +
  60 + @PutMapping("/update-sort")
  61 + @Operation(summary = "修改项目分组排序")
  62 + @PreAuthorize("@ss.hasPermission('pms:pm:project-group:update')")
  63 + public CommonResult<Boolean> updateProjectGroupSort(@Valid @RequestBody PmsProjectGroupSortReqVO sortReqVO) {
  64 + projectGroupService.updateProjectGroupSort(sortReqVO, getLoginUserId());
  65 + return success(true);
  66 + }
  67 +
  68 + @DeleteMapping("/delete")
  69 + @Operation(summary = "删除项目分组")
  70 + @Parameter(name = "id", description = "项目分组编号", required = true, example = "1024")
  71 + @PreAuthorize("@ss.hasPermission('pms:pm:project-group:delete')")
  72 + public CommonResult<Boolean> deleteProjectGroup(@RequestParam("id") Long id) {
  73 + projectGroupService.deleteProjectGroup(id, getLoginUserId());
  74 + return success(true);
  75 + }
  76 +
  77 + @GetMapping("/list")
  78 + @Operation(summary = "获得项目分组列表")
  79 + @PreAuthorize("@ss.hasPermission('pms:pm:project-group:query')")
  80 + public CommonResult<List<PmsProjectGroupRespVO>> getProjectGroupList() {
  81 + Long userId = getLoginUserId();
  82 + List<PmsProjectGroupDO> groups = projectGroupService.getProjectGroupList(userId);
  83 + List<Long> projectIds = projectMemberService.getActiveProjectIdListByUserId(userId);
  84 + Map<Long, Integer> groupCountMap = projectGroupService.getProjectGroupCountMap(userId, groups, projectIds);
  85 + return success(BeanUtils.toBean(groups, PmsProjectGroupRespVO.class,
  86 + group -> group.setProjectCount(groupCountMap.getOrDefault(group.getId(), 0))));
  87 + }
  88 +
  89 + @PutMapping("/move-project")
  90 + @Operation(summary = "移动项目到个人分组")
  91 + @PreAuthorize("@ss.hasPermission('pms:pm:project-group:update')")
  92 + public CommonResult<Boolean> moveProjectToGroup(@Valid @RequestBody PmsProjectGroupMoveReqVO moveReqVO) {
  93 + projectGroupService.moveProjectToGroup(moveReqVO, getLoginUserId());
  94 + return success(true);
  95 + }
  96 +
  97 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/PmsProjectMemberController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.member.PmsProjectMemberRespVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.member.PmsProjectMemberSaveReqVO;
  8 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectMemberService;
  9 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  10 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.Parameters;
  14 +import io.swagger.v3.oas.annotations.tags.Tag;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.DeleteMapping;
  20 +import org.springframework.web.bind.annotation.GetMapping;
  21 +import org.springframework.web.bind.annotation.PutMapping;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestParam;
  25 +import org.springframework.web.bind.annotation.RestController;
  26 +
  27 +import java.util.Collections;
  28 +import java.util.List;
  29 +import java.util.Map;
  30 +
  31 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  33 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  34 +
  35 +@Tag(name = "管理后台 - PMS 项目成员")
  36 +@RestController
  37 +@RequestMapping("/pms/pm/project-member")
  38 +@Validated
  39 +public class PmsProjectMemberController {
  40 +
  41 + @Resource
  42 + private PmsProjectMemberService projectMemberService;
  43 +
  44 + @Resource
  45 + private AdminUserApi adminUserApi;
  46 +
  47 + @GetMapping("/list")
  48 + @Operation(summary = "获得项目成员列表")
  49 + @Parameter(name = "projectId", description = "项目编号", required = true, example = "1024")
  50 + @PreAuthorize("@ss.hasPermission('pms:pm:project-member:query')")
  51 + public CommonResult<List<PmsProjectMemberRespVO>> getProjectMemberList(
  52 + @RequestParam("projectId") Long projectId) {
  53 + List<PmsProjectMemberRespVO> members = projectMemberService.getProjectMemberList(projectId, getLoginUserId());
  54 + return success(fillProjectMemberRespVOList(members));
  55 + }
  56 +
  57 + @PutMapping("/update-list")
  58 + @Operation(summary = "保存项目成员及其权限级别")
  59 + @PreAuthorize("@ss.hasPermission('pms:pm:project-member:update')")
  60 + public CommonResult<Boolean> updateProjectMemberList(@Valid @RequestBody PmsProjectMemberSaveReqVO saveReqVO) {
  61 + projectMemberService.updateProjectMemberList(saveReqVO, getLoginUserId());
  62 + return success(true);
  63 + }
  64 +
  65 + @DeleteMapping("/delete")
  66 + @Operation(summary = "移除项目成员")
  67 + @Parameters({
  68 + @Parameter(name = "projectId", description = "项目编号", required = true, example = "1024"),
  69 + @Parameter(name = "userId", description = "后台用户编号", required = true, example = "1")
  70 + })
  71 + @PreAuthorize("@ss.hasPermission('pms:pm:project-member:update')")
  72 + public CommonResult<Boolean> deleteProjectMember(@RequestParam("projectId") Long projectId,
  73 + @RequestParam("userId") Long userId) {
  74 + projectMemberService.deleteProjectMember(projectId, userId, getLoginUserId());
  75 + return success(true);
  76 + }
  77 +
  78 + @DeleteMapping("/exit")
  79 + @Operation(summary = "主动退出项目")
  80 + @Parameter(name = "projectId", description = "项目编号", required = true, example = "1024")
  81 + @PreAuthorize("@ss.hasPermission('pms:pm:project-member:query')")
  82 + public CommonResult<Boolean> exitProject(@RequestParam("projectId") Long projectId) {
  83 + projectMemberService.exitProject(projectId, getLoginUserId());
  84 + return success(true);
  85 + }
  86 +
  87 + // ==================== 拼接 VO ====================
  88 +
  89 + private List<PmsProjectMemberRespVO> fillProjectMemberRespVOList(List<PmsProjectMemberRespVO> members) {
  90 + if (CollUtil.isEmpty(members)) {
  91 + return Collections.emptyList();
  92 + }
  93 + // 1. 批量查询用户
  94 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  95 + convertSet(members, PmsProjectMemberRespVO::getUserId));
  96 + // 2. 拼接用户信息
  97 + for (PmsProjectMemberRespVO memberVO : members) {
  98 + MapUtils.findAndThen(userMap, memberVO.getUserId(),
  99 + user -> memberVO.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
  100 + }
  101 + return members;
  102 + }
  103 +
  104 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/PmsProjectTemplateController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.template.PmsProjectTemplatePageReqVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.template.PmsProjectTemplateRespVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.template.PmsProjectTemplateSaveReqVO;
  9 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.project.PmsProjectTemplateDO;
  10 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectTemplateService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.DeleteMapping;
  19 +import org.springframework.web.bind.annotation.GetMapping;
  20 +import org.springframework.web.bind.annotation.PostMapping;
  21 +import org.springframework.web.bind.annotation.PutMapping;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestParam;
  25 +import org.springframework.web.bind.annotation.RestController;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  29 +
  30 +@Tag(name = "管理后台 - PMS 项目模板")
  31 +@RestController
  32 +@RequestMapping("/pms/pm/project-template")
  33 +@Validated
  34 +public class PmsProjectTemplateController {
  35 +
  36 + @Resource
  37 + private PmsProjectTemplateService projectTemplateService;
  38 +
  39 + @PostMapping("/create")
  40 + @Operation(summary = "创建项目模板")
  41 + @PreAuthorize("@ss.hasPermission('pms:pm:project-template:create')")
  42 + public CommonResult<Long> createProjectTemplate(@Valid @RequestBody PmsProjectTemplateSaveReqVO createReqVO) {
  43 + return success(projectTemplateService.createProjectTemplate(createReqVO));
  44 + }
  45 +
  46 + @PutMapping("/update")
  47 + @Operation(summary = "更新项目模板")
  48 + @PreAuthorize("@ss.hasPermission('pms:pm:project-template:update')")
  49 + public CommonResult<Boolean> updateProjectTemplate(@Valid @RequestBody PmsProjectTemplateSaveReqVO updateReqVO) {
  50 + projectTemplateService.updateProjectTemplate(updateReqVO);
  51 + return success(true);
  52 + }
  53 +
  54 + @DeleteMapping("/delete")
  55 + @Operation(summary = "删除项目模板")
  56 + @Parameter(name = "id", description = "模板编号", required = true, example = "1024")
  57 + @PreAuthorize("@ss.hasPermission('pms:pm:project-template:delete')")
  58 + public CommonResult<Boolean> deleteProjectTemplate(@RequestParam("id") Long id) {
  59 + projectTemplateService.deleteProjectTemplate(id);
  60 + return success(true);
  61 + }
  62 +
  63 + @GetMapping("/get")
  64 + @Operation(summary = "获得项目模板")
  65 + @Parameter(name = "id", description = "模板编号", required = true, example = "1024")
  66 + @PreAuthorize("@ss.hasPermission('pms:pm:project-template:query')")
  67 + public CommonResult<PmsProjectTemplateRespVO> getProjectTemplate(@RequestParam("id") Long id) {
  68 + PmsProjectTemplateDO template = projectTemplateService.getProjectTemplate(id);
  69 + return success(BeanUtils.toBean(template, PmsProjectTemplateRespVO.class));
  70 + }
  71 +
  72 + @GetMapping("/page")
  73 + @Operation(summary = "获得项目模板分页")
  74 + @PreAuthorize("@ss.hasPermission('pms:pm:project-template:query')")
  75 + public CommonResult<PageResult<PmsProjectTemplateRespVO>> getProjectTemplatePage(
  76 + @Valid PmsProjectTemplatePageReqVO pageReqVO) {
  77 + PageResult<PmsProjectTemplateDO> pageResult = projectTemplateService.getProjectTemplatePage(pageReqVO);
  78 + return success(BeanUtils.toBean(pageResult, PmsProjectTemplateRespVO.class));
  79 + }
  80 +
  81 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/announcement/PmsProjectAnnouncementRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.announcement;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 项目公告 Response VO")
  10 +@Data
  11 +public class PmsProjectAnnouncementRespVO {
  12 +
  13 + @Schema(description = "公告编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  17 + private Long projectId;
  18 +
  19 + @Schema(description = "公告内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "项目启动")
  20 + private String content;
  21 +
  22 + @Schema(description = "附件地址列表", example = "[\"https://example.com/a.pdf\"]")
  23 + private List<String> fileUrls;
  24 +
  25 + @Schema(description = "创建者用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  26 + private Long creatorUserId;
  27 +
  28 + @Schema(description = "创建者用户昵称", example = "芋道")
  29 + private String creatorUserName;
  30 +
  31 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + private LocalDateTime createTime;
  33 +
  34 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + private LocalDateTime updateTime;
  36 +
  37 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/announcement/PmsProjectAnnouncementSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.announcement;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +
  9 +import java.util.List;
  10 +
  11 +@Schema(description = "管理后台 - PMS 项目公告保存 Request VO")
  12 +@Data
  13 +public class PmsProjectAnnouncementSaveReqVO {
  14 +
  15 + @Schema(description = "公告编号", example = "1024")
  16 + private Long id;
  17 +
  18 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @NotNull(message = "项目编号不能为空")
  20 + private Long projectId;
  21 +
  22 + @Schema(description = "公告内容", requiredMode = Schema.RequiredMode.REQUIRED)
  23 + @NotBlank(message = "公告内容不能为空")
  24 + @Size(max = 5000, message = "公告内容不能超过 5000 个字符")
  25 + private String content;
  26 +
  27 + @Schema(description = "附件地址列表")
  28 + private List<String> fileUrls;
  29 +
  30 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/group/PmsProjectGroupMoveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - PMS 项目移动分组 Request VO")
  8 +@Data
  9 +public class PmsProjectGroupMoveReqVO {
  10 +
  11 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + @NotNull(message = "项目编号不能为空")
  13 + private Long projectId;
  14 +
  15 + @Schema(description = "目标项目分组编号;不传表示移动到未分组", example = "2048")
  16 + private Long groupId;
  17 +
  18 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/group/PmsProjectGroupRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 项目分组 Response VO")
  9 +@Data
  10 +public class PmsProjectGroupRespVO {
  11 +
  12 + @Schema(description = "项目分组编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "分组名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "重点项目")
  16 + private String name;
  17 +
  18 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  19 + private Integer sort;
  20 +
  21 + @Schema(description = "分组类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  22 + private Integer type;
  23 +
  24 + @Schema(description = "创建时间")
  25 + private LocalDateTime createTime;
  26 +
  27 + @Schema(description = "项目数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  28 + private Integer projectCount;
  29 +
  30 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/group/PmsProjectGroupSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.Size;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - PMS 项目分组新增/修改 Request VO")
  9 +@Data
  10 +public class PmsProjectGroupSaveReqVO {
  11 +
  12 + @Schema(description = "项目分组编号", example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "分组名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "重点项目")
  16 + @NotBlank(message = "分组名称不能为空")
  17 + @Size(max = 100, message = "分组名称不能超过 100 个字符")
  18 + private String name;
  19 +
  20 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/group/PmsProjectGroupSortReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.Valid;
  5 +import jakarta.validation.constraints.Min;
  6 +import jakarta.validation.constraints.NotEmpty;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
  9 +
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - PMS 项目分组排序 Request VO")
  13 +@Data
  14 +public class PmsProjectGroupSortReqVO {
  15 +
  16 + @Schema(description = "项目分组排序项", requiredMode = Schema.RequiredMode.REQUIRED)
  17 + @Valid
  18 + @NotEmpty(message = "项目分组排序项不能为空")
  19 + private List<Item> items;
  20 +
  21 + @Schema(description = "管理后台 - PMS 项目分组排序项")
  22 + @Data
  23 + public static class Item {
  24 +
  25 + @Schema(description = "项目分组编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  26 + @NotNull(message = "项目分组编号不能为空")
  27 + private Long id;
  28 +
  29 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  30 + @NotNull(message = "显示顺序不能为空")
  31 + @Min(value = 0, message = "显示顺序不能小于 0")
  32 + private Integer sort;
  33 +
  34 + }
  35 +
  36 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/member/PmsProjectMemberRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.member;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - PMS 项目成员 Response VO")
  7 +@Data
  8 +public class PmsProjectMemberRespVO {
  9 +
  10 + @Schema(description = "后台用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  11 + private Long userId;
  12 +
  13 + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
  14 + private String nickname;
  15 +
  16 + @Schema(description = "用户头像")
  17 + private String avatar;
  18 +
  19 + @Schema(description = "成员权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  20 + private Integer level;
  21 +
  22 + @Schema(description = "是否项目创建人", example = "true")
  23 + private Boolean creatorStatus;
  24 +
  25 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/member/PmsProjectMemberSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.member;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.pms.enums.pm.project.PmsProjectMemberLevelEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.Valid;
  7 +import jakarta.validation.constraints.NotEmpty;
  8 +import jakarta.validation.constraints.NotNull;
  9 +import lombok.Data;
  10 +
  11 +import java.util.List;
  12 +
  13 +@Schema(description = "管理后台 - PMS 项目成员保存 Request VO")
  14 +@Data
  15 +public class PmsProjectMemberSaveReqVO {
  16 +
  17 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + @NotNull(message = "项目编号不能为空")
  19 + private Long projectId;
  20 +
  21 + @Schema(description = "项目成员列表", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + @NotEmpty(message = "项目成员不能为空")
  23 + @Valid
  24 + private List<Member> members;
  25 +
  26 + @Schema(description = "管理后台 - PMS 项目成员保存项")
  27 + @Data
  28 + public static class Member {
  29 +
  30 + @Schema(description = "后台用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  31 + @NotNull(message = "后台用户编号不能为空")
  32 + private Long userId;
  33 +
  34 + @Schema(description = "成员权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  35 + @NotNull(message = "成员权限级别不能为空")
  36 + @InEnum(PmsProjectMemberLevelEnum.class)
  37 + private Integer level;
  38 +
  39 + }
  40 +
  41 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/project/PmsProjectOverviewRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.project;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +import java.util.Map;
  9 +
  10 +@Schema(description = "管理后台 - PMS 项目概况 Response VO")
  11 +@Data
  12 +public class PmsProjectOverviewRespVO {
  13 +
  14 + @Schema(description = "工作项总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
  15 + private Long totalCount;
  16 +
  17 + @Schema(description = "未开始工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8")
  18 + private Long pendingCount;
  19 +
  20 + @Schema(description = "进行中工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "6")
  21 + private Long processingCount;
  22 +
  23 + @Schema(description = "已完成工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "6")
  24 + private Long completedCount;
  25 +
  26 + @Schema(description = "按工作项类型统计")
  27 + private Map<Integer, Long> typeCountMap;
  28 +
  29 + @Schema(description = "近十四日完成趋势")
  30 + private List<TrendPoint> completedTrends;
  31 +
  32 + @Schema(description = "分配给当前用户的未完成工作项")
  33 + private List<AssignedWorkItem> assignedWorkItems;
  34 +
  35 + @Schema(description = "PMS 项目概况 - 趋势点")
  36 + @Data
  37 + public static class TrendPoint {
  38 +
  39 + @Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + private String date;
  41 +
  42 + @Schema(description = "当日完成数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  43 + private Long count;
  44 +
  45 + }
  46 +
  47 + @Schema(description = "PMS 项目概况 - 分配给我的工作项")
  48 + @Data
  49 + public static class AssignedWorkItem {
  50 +
  51 + @Schema(description = "工作项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  52 + private Long id;
  53 +
  54 + @Schema(description = "项目内序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
  55 + private Integer serialNumber;
  56 +
  57 + @Schema(description = "工作项类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  58 + private Integer type;
  59 +
  60 + @Schema(description = "工作项标题", requiredMode = Schema.RequiredMode.REQUIRED)
  61 + private String name;
  62 +
  63 + @Schema(description = "语义状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  64 + private Integer status;
  65 +
  66 + @Schema(description = "截止时间")
  67 + private LocalDateTime endTime;
  68 +
  69 + @Schema(description = "完成进度", requiredMode = Schema.RequiredMode.REQUIRED, example = "60")
  70 + private Integer progress;
  71 +
  72 + }
  73 +
  74 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/project/PmsProjectPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.project;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.pms.enums.pm.project.PmsProjectSceneTypeEnum;
  6 +import cn.iocoder.yudao.module.pms.enums.pm.project.PmsProjectSortTypeEnum;
  7 +import cn.iocoder.yudao.module.pms.enums.pm.project.PmsProjectStatusEnum;
  8 +import io.swagger.v3.oas.annotations.media.Schema;
  9 +import lombok.Data;
  10 +import lombok.EqualsAndHashCode;
  11 +import lombok.ToString;
  12 +
  13 +@Schema(description = "管理后台 - PMS 项目分页 Request VO")
  14 +@Data
  15 +@EqualsAndHashCode(callSuper = true)
  16 +@ToString(callSuper = true)
  17 +public class PmsProjectPageReqVO extends PageParam {
  18 +
  19 + @Schema(description = "项目名称", example = "官网")
  20 + private String name;
  21 +
  22 + @Schema(description = "列表场景", example = "1")
  23 + @InEnum(PmsProjectSceneTypeEnum.class)
  24 + private Integer sceneType = PmsProjectSceneTypeEnum.ALL.getType();
  25 +
  26 + @Schema(description = "个人项目分组编号", example = "1024")
  27 + private Long groupId;
  28 +
  29 + @Schema(description = "项目状态", example = "1")
  30 + @InEnum(PmsProjectStatusEnum.class)
  31 + private Integer status = PmsProjectStatusEnum.ACTIVE.getStatus();
  32 +
  33 + @Schema(description = "排序类型", example = "1")
  34 + @InEnum(PmsProjectSortTypeEnum.class)
  35 + private Integer sortType = PmsProjectSortTypeEnum.ACCESS_TIME.getType();
  36 +
  37 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/project/PmsProjectRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.project;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 项目 Response VO")
  10 +@Data
  11 +public class PmsProjectRespVO {
  12 +
  13 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "官网重构")
  17 + private String name;
  18 +
  19 + @Schema(description = "项目状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  20 + private Integer status;
  21 +
  22 + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  23 + private Integer type;
  24 +
  25 + @Schema(description = "项目优先级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  26 + private Integer level;
  27 +
  28 + @Schema(description = "项目描述", example = "完成官网前后端重构")
  29 + private String description;
  30 +
  31 + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  32 + private Boolean openStatus;
  33 +
  34 + @Schema(description = "项目图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "ep:folder")
  35 + private String icon;
  36 +
  37 + @Schema(description = "开始时间")
  38 + private LocalDateTime startTime;
  39 +
  40 + @Schema(description = "截止时间")
  41 + private LocalDateTime endTime;
  42 +
  43 + @Schema(description = "归档时间")
  44 + private LocalDateTime archiveTime;
  45 +
  46 + @Schema(description = "移入回收站时间")
  47 + private LocalDateTime recycleTime;
  48 +
  49 + @Schema(description = "最近访问时间")
  50 + private LocalDateTime accessTime;
  51 +
  52 + @Schema(description = "创建时间")
  53 + private LocalDateTime createTime;
  54 +
  55 + @Schema(description = "创建人编号", example = "1")
  56 + private String creator;
  57 +
  58 + @Schema(description = "创建人姓名", example = "芋道源码")
  59 + private String creatorName;
  60 +
  61 + @Schema(description = "项目管理员姓名列表")
  62 + private List<String> adminNames;
  63 +
  64 + @Schema(description = "项目成员数量", example = "5")
  65 + private Integer memberCount;
  66 +
  67 + @Schema(description = "未开始工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  68 + private Long pendingWorkItemCount;
  69 +
  70 + @Schema(description = "进行中工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  71 + private Long processingWorkItemCount;
  72 +
  73 + @Schema(description = "已完成工作项数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  74 + private Long completedWorkItemCount;
  75 +
  76 + @Schema(description = "当前用户是否为项目管理员", example = "true")
  77 + private Boolean adminStatus;
  78 +
  79 + @Schema(description = "当前用户是否拥有项目所有者权限", example = "true")
  80 + private Boolean ownerStatus;
  81 +
  82 + @Schema(description = "当前用户是否为项目成员", example = "true")
  83 + private Boolean memberStatus;
  84 +
  85 + @Schema(description = "当前用户是否可以主动退出项目", requiredMode = Schema.RequiredMode.REQUIRED)
  86 + private Boolean exitStatus;
  87 +
  88 + @Schema(description = "当前用户是否可以编辑项目业务数据", requiredMode = Schema.RequiredMode.REQUIRED)
  89 + private Boolean writeStatus;
  90 +
  91 + @Schema(description = "当前用户是否已收藏", requiredMode = Schema.RequiredMode.REQUIRED)
  92 + private Boolean favoriteStatus;
  93 +
  94 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/project/PmsProjectSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.project;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.pms.enums.pm.project.PmsProjectLevelEnum;
  5 +import cn.iocoder.yudao.module.pms.enums.pm.project.PmsProjectTypeEnum;
  6 +import com.fasterxml.jackson.annotation.JsonIgnore;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import jakarta.validation.constraints.AssertTrue;
  9 +import jakarta.validation.constraints.NotBlank;
  10 +import jakarta.validation.constraints.NotNull;
  11 +import jakarta.validation.constraints.Size;
  12 +import lombok.Data;
  13 +import org.springframework.format.annotation.DateTimeFormat;
  14 +
  15 +import java.time.LocalDateTime;
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  19 +
  20 +@Schema(description = "管理后台 - PMS 项目新增/修改 Request VO")
  21 +@Data
  22 +public class PmsProjectSaveReqVO {
  23 +
  24 + @Schema(description = "项目编号", example = "1024")
  25 + private Long id;
  26 +
  27 + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "官网重构")
  28 + @NotBlank(message = "项目名称不能为空")
  29 + @Size(max = 31, message = "项目名称不能超过 31 个字符")
  30 + private String name;
  31 +
  32 + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  33 + @NotNull(message = "项目类型不能为空")
  34 + @InEnum(PmsProjectTypeEnum.class)
  35 + private Integer type;
  36 +
  37 + @Schema(description = "项目优先级", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  38 + @NotNull(message = "项目优先级不能为空")
  39 + @InEnum(PmsProjectLevelEnum.class)
  40 + private Integer level;
  41 +
  42 + @Schema(description = "项目描述", example = "完成官网前后端重构")
  43 + @Size(max = 500, message = "项目描述不能超过 500 个字符")
  44 + private String description;
  45 +
  46 + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  47 + @NotNull(message = "项目可见范围不能为空")
  48 + private Boolean openStatus;
  49 +
  50 + @Schema(description = "项目图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "ep:folder")
  51 + @NotBlank(message = "项目图标不能为空")
  52 + private String icon;
  53 +
  54 + @Schema(description = "开始时间", example = "2026-08-01 00:00:00")
  55 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  56 + private LocalDateTime startTime;
  57 +
  58 + @Schema(description = "截止时间", example = "2026-12-31 23:59:59")
  59 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  60 + private LocalDateTime endTime;
  61 +
  62 + @Schema(description = "初始成员编号列表", example = "[1, 2]")
  63 + private List<Long> memberUserIds;
  64 +
  65 + @JsonIgnore
  66 + @Schema(hidden = true)
  67 + @AssertTrue(message = "项目截止时间必须晚于开始时间,且开始、截止时间需要同时填写")
  68 + public boolean isTimeValid() {
  69 + return startTime == null && endTime == null
  70 + || startTime != null && endTime != null && startTime.isBefore(endTime);
  71 + }
  72 +
  73 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/template/PmsProjectTemplatePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.template;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.pms.enums.pm.project.PmsProjectTypeEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +
  10 +@Schema(description = "管理后台 - PMS 项目模板分页 Request VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +public class PmsProjectTemplatePageReqVO extends PageParam {
  14 +
  15 + @Schema(description = "模板名称,模糊匹配", example = "敏捷研发模板")
  16 + private String name;
  17 +
  18 + @Schema(description = "项目类型", example = "2")
  19 + @InEnum(PmsProjectTypeEnum.class)
  20 + private Integer projectType;
  21 +
  22 + @Schema(description = "模板状态", example = "0")
  23 + private Integer status;
  24 +
  25 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/template/PmsProjectTemplateRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.template;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 项目模板 Response VO")
  10 +@Data
  11 +public class PmsProjectTemplateRespVO {
  12 +
  13 + @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "敏捷研发模板")
  17 + private String name;
  18 +
  19 + @Schema(description = "模板描述", example = "适用于标准敏捷研发项目")
  20 + private String description;
  21 +
  22 + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  23 + private Integer projectType;
  24 +
  25 + @Schema(description = "模板状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  26 + private Integer status;
  27 +
  28 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  29 + private Integer sort;
  30 +
  31 + @Schema(description = "启用的工作项类型列表", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + private List<Integer> itemTypes;
  33 +
  34 + @Schema(description = "工作项状态模板列表", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + private List<PmsProjectTemplateSaveReqVO.StatusTemplate> statuses;
  36 +
  37 + @Schema(description = "看板列模板列表", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + private List<PmsProjectTemplateSaveReqVO.BoardTemplate> boards;
  39 +
  40 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  41 + private LocalDateTime createTime;
  42 +
  43 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/project/vo/template/PmsProjectTemplateSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.project.vo.template;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.pms.enums.pm.project.PmsProjectTypeEnum;
  5 +import cn.iocoder.yudao.module.pms.enums.pm.workitem.PmsWorkItemStatusTypeEnum;
  6 +import cn.iocoder.yudao.module.pms.enums.pm.workitem.PmsWorkItemTypeEnum;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import jakarta.validation.Valid;
  9 +import jakarta.validation.constraints.NotBlank;
  10 +import jakarta.validation.constraints.NotEmpty;
  11 +import jakarta.validation.constraints.NotNull;
  12 +import jakarta.validation.constraints.Size;
  13 +import lombok.Data;
  14 +
  15 +import java.util.List;
  16 +
  17 +@Schema(description = "管理后台 - PMS 项目模板新增/修改 Request VO")
  18 +@Data
  19 +public class PmsProjectTemplateSaveReqVO {
  20 +
  21 + @Schema(description = "模板编号", example = "1024")
  22 + private Long id;
  23 +
  24 + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "敏捷研发模板")
  25 + @NotBlank(message = "模板名称不能为空")
  26 + @Size(max = 100, message = "模板名称不能超过 100 个字符")
  27 + private String name;
  28 +
  29 + @Schema(description = "模板描述", example = "适用于标准敏捷研发项目")
  30 + @Size(max = 500, message = "模板描述不能超过 500 个字符")
  31 + private String description;
  32 +
  33 + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  34 + @NotNull(message = "项目类型不能为空")
  35 + @InEnum(PmsProjectTypeEnum.class)
  36 + private Integer projectType;
  37 +
  38 + @Schema(description = "模板状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  39 + @NotNull(message = "模板状态不能为空")
  40 + private Integer status;
  41 +
  42 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  43 + @NotNull(message = "显示顺序不能为空")
  44 + private Integer sort;
  45 +
  46 + @Schema(description = "启用的工作项类型列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2, 3, 4]")
  47 + @NotEmpty(message = "工作项类型不能为空")
  48 + private List<Integer> itemTypes;
  49 +
  50 + @Schema(description = "工作项状态模板列表", requiredMode = Schema.RequiredMode.REQUIRED)
  51 + @Valid
  52 + @NotEmpty(message = "工作项状态不能为空")
  53 + private List<StatusTemplate> statuses;
  54 +
  55 + @Schema(description = "看板列模板列表", requiredMode = Schema.RequiredMode.REQUIRED)
  56 + @Valid
  57 + @NotEmpty(message = "看板列不能为空")
  58 + private List<BoardTemplate> boards;
  59 +
  60 + @Schema(description = "工作项状态模板")
  61 + @Data
  62 + public static class StatusTemplate {
  63 +
  64 + @Schema(description = "模板内稳定编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "task_todo")
  65 + @NotBlank(message = "状态编码不能为空")
  66 + @Size(max = 50, message = "状态编码不能超过 50 个字符")
  67 + private String code;
  68 +
  69 + @Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "待处理")
  70 + @NotBlank(message = "状态名称不能为空")
  71 + @Size(max = 50, message = "状态名称不能超过 50 个字符")
  72 + private String name;
  73 +
  74 + @Schema(description = "工作项类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  75 + @NotNull(message = "状态工作项类型不能为空")
  76 + @InEnum(PmsWorkItemTypeEnum.class)
  77 + private Integer workItemType;
  78 +
  79 + @Schema(description = "语义状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  80 + @NotNull(message = "语义状态不能为空")
  81 + @InEnum(PmsWorkItemStatusTypeEnum.class)
  82 + private Integer statusType;
  83 +
  84 + @Schema(description = "是否为初始状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  85 + @NotNull(message = "是否为初始状态不能为空")
  86 + private Boolean defaultStatus;
  87 +
  88 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  89 + @NotNull(message = "状态显示顺序不能为空")
  90 + private Integer sort;
  91 +
  92 + @Schema(description = "所属看板列编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "todo")
  93 + @NotBlank(message = "状态所属看板列不能为空")
  94 + private String boardCode;
  95 + }
  96 +
  97 + @Schema(description = "看板列模板")
  98 + @Data
  99 + public static class BoardTemplate {
  100 +
  101 + @Schema(description = "模板内稳定编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "todo")
  102 + @NotBlank(message = "看板列编码不能为空")
  103 + @Size(max = 50, message = "看板列编码不能超过 50 个字符")
  104 + private String code;
  105 +
  106 + @Schema(description = "看板列名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "待处理")
  107 + @NotBlank(message = "看板列名称不能为空")
  108 + @Size(max = 50, message = "看板列名称不能超过 50 个字符")
  109 + private String name;
  110 +
  111 + @Schema(description = "工作项类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  112 + @NotNull(message = "看板列工作项类型不能为空")
  113 + @InEnum(PmsWorkItemTypeEnum.class)
  114 + private Integer workItemType;
  115 +
  116 + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  117 + @NotNull(message = "看板列显示顺序不能为空")
  118 + private Integer sort;
  119 +
  120 + @Schema(description = "关联的状态编码列表", requiredMode = Schema.RequiredMode.REQUIRED,
  121 + example = "[\"task_todo\"]")
  122 + @NotEmpty(message = "看板列关联状态不能为空")
  123 + private List<String> statusCodes;
  124 + }
  125 +
  126 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workbench/PmsWorkbenchController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workbench;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workbench.vo.PmsWorkbenchCountRespVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workbench.vo.PmsWorkbenchIterationRespVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workbench.vo.PmsWorkbenchPageReqVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workbench.vo.PmsWorkbenchWorkItemRespVO;
  11 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.iteration.PmsIterationDO;
  12 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.project.PmsProjectDO;
  13 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemDO;
  14 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemStatusDO;
  15 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectMemberService;
  16 +import cn.iocoder.yudao.module.pms.service.pm.project.PmsProjectService;
  17 +import cn.iocoder.yudao.module.pms.service.pm.workbench.PmsWorkbenchService;
  18 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemStatusService;
  19 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  20 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  21 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  22 +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
  23 +import io.swagger.v3.oas.annotations.Operation;
  24 +import io.swagger.v3.oas.annotations.tags.Tag;
  25 +import jakarta.annotation.Resource;
  26 +import jakarta.validation.Valid;
  27 +import org.springframework.security.access.prepost.PreAuthorize;
  28 +import org.springframework.validation.annotation.Validated;
  29 +import org.springframework.web.bind.annotation.GetMapping;
  30 +import org.springframework.web.bind.annotation.RequestMapping;
  31 +import org.springframework.web.bind.annotation.RestController;
  32 +
  33 +import java.util.List;
  34 +import java.util.Map;
  35 +import java.util.Set;
  36 +
  37 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  38 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  39 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  40 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  41 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  42 +
  43 +@Tag(name = "管理后台 - PMS 工作台")
  44 +@RestController
  45 +@RequestMapping("/pms/pm/workbench")
  46 +@Validated
  47 +public class PmsWorkbenchController {
  48 +
  49 + @Resource
  50 + private PmsWorkbenchService workbenchService;
  51 + @Resource
  52 + private PmsProjectService projectService;
  53 + @Resource
  54 + private PmsProjectMemberService projectMemberService;
  55 + @Resource
  56 + private PmsWorkItemStatusService workItemStatusService;
  57 + @Resource
  58 + private PermissionApi permissionApi;
  59 + @Resource
  60 + private AdminUserApi adminUserApi;
  61 +
  62 + @GetMapping("/work-item-page")
  63 + @Operation(summary = "获得我负责的工作项分页")
  64 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  65 + public CommonResult<PageResult<PmsWorkbenchWorkItemRespVO>> getWorkbenchWorkItemPage(
  66 + @Valid PmsWorkbenchPageReqVO pageReqVO) {
  67 + // 1. 查询当前用户负责的工作项分页
  68 + Long userId = getLoginUserId();
  69 + PageResult<PmsWorkItemDO> pageResult = workbenchService.getWorkbenchWorkItemPage(pageReqVO, userId);
  70 + // 2.1 批量查询项目、状态及项目写权限
  71 + Map<Long, PmsProjectDO> projectMap = projectService.getProjectMap(
  72 + convertSet(pageResult.getList(), PmsWorkItemDO::getProjectId));
  73 + Map<Long, PmsWorkItemStatusDO> statusMap = workItemStatusService.getWorkItemStatusMap(
  74 + convertSet(pageResult.getList(), PmsWorkItemDO::getStatusId));
  75 + Set<Long> writableProjectIds = convertSet(projectMemberService.getWritableProjectIdListByUserId(userId));
  76 + boolean superAdmin = permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode());
  77 + // 2.2 批量查询负责人和创建人
  78 + Set<Long> userIds = convertSet(pageResult.getList(), PmsWorkItemDO::getAssigneeUserId);
  79 + userIds.addAll(convertSet(pageResult.getList(), workItem -> NumberUtils.parseLong(workItem.getCreator())));
  80 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
  81 + Map<Long, Long> creatorUserIdMap = convertMap(pageResult.getList(), PmsWorkItemDO::getId,
  82 + workItem -> NumberUtils.parseLong(workItem.getCreator()));
  83 + // 3. 转换响应 VO,并拼接项目、状态、用户及写权限
  84 + List<PmsWorkbenchWorkItemRespVO> workItemVOList = BeanUtils.toBean(pageResult.getList(),
  85 + PmsWorkbenchWorkItemRespVO.class, itemVO -> {
  86 + findAndThen(projectMap, itemVO.getProjectId(), project -> itemVO
  87 + .setProjectName(project.getName()).setProjectType(project.getType()));
  88 + findAndThen(statusMap, itemVO.getStatusId(), status -> itemVO.setStatusName(status.getName()));
  89 + findAndThen(userMap, itemVO.getAssigneeUserId(),
  90 + user -> itemVO.setAssigneeUserName(user.getNickname()));
  91 + itemVO.setCreatorUserId(creatorUserIdMap.get(itemVO.getId()));
  92 + findAndThen(userMap, itemVO.getCreatorUserId(),
  93 + user -> itemVO.setCreatorUserName(user.getNickname()));
  94 + itemVO.setWriteStatus(superAdmin || writableProjectIds.contains(itemVO.getProjectId()));
  95 + });
  96 + return success(new PageResult<>(workItemVOList, pageResult.getTotal()));
  97 + }
  98 +
  99 + @GetMapping("/iteration-page")
  100 + @Operation(summary = "获得我负责的迭代分页")
  101 + @PreAuthorize("@ss.hasPermission('pms:pm:iteration:query')")
  102 + public CommonResult<PageResult<PmsWorkbenchIterationRespVO>> getWorkbenchIterationPage(
  103 + @Valid PmsWorkbenchPageReqVO pageReqVO) {
  104 + // 1.1 查询当前用户负责的迭代分页
  105 + PageResult<PmsIterationDO> pageResult = workbenchService.getWorkbenchIterationPage(pageReqVO, getLoginUserId());
  106 + // 1.2 批量查询关联项目
  107 + Map<Long, PmsProjectDO> projectMap = projectService.getProjectMap(
  108 + convertSet(pageResult.getList(), PmsIterationDO::getProjectId));
  109 + // 2. 转换响应 VO,并拼接项目展示字段
  110 + List<PmsWorkbenchIterationRespVO> iterationVOList = BeanUtils.toBean(pageResult.getList(),
  111 + PmsWorkbenchIterationRespVO.class, itemVO ->
  112 + findAndThen(projectMap, itemVO.getProjectId(), project -> itemVO
  113 + .setProjectName(project.getName()).setProjectType(project.getType())));
  114 + return success(new PageResult<>(iterationVOList, pageResult.getTotal()));
  115 + }
  116 +
  117 + @GetMapping("/count")
  118 + @Operation(summary = "获得我的未完成事项数量")
  119 + @PreAuthorize("@ss.hasAnyPermissions('pms:pm:work-item:query', 'pms:pm:iteration:query')")
  120 + public CommonResult<PmsWorkbenchCountRespVO> getWorkbenchCount(PmsWorkbenchPageReqVO pageReqVO) {
  121 + return success(workbenchService.getWorkbenchCount(pageReqVO, getLoginUserId()));
  122 + }
  123 +
  124 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workbench/vo/PmsWorkbenchCountRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workbench.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.experimental.Accessors;
  6 +
  7 +@Schema(description = "管理后台 - PMS 工作台数量响应 VO")
  8 +@Data
  9 +@Accessors(chain = true)
  10 +public class PmsWorkbenchCountRespVO {
  11 +
  12 + @Schema(description = "需求数量", requiredMode = Schema.RequiredMode.REQUIRED)
  13 + private Integer requirementCount;
  14 +
  15 + @Schema(description = "任务数量", requiredMode = Schema.RequiredMode.REQUIRED)
  16 + private Integer taskCount;
  17 +
  18 + @Schema(description = "缺陷数量", requiredMode = Schema.RequiredMode.REQUIRED)
  19 + private Integer defectCount;
  20 +
  21 + @Schema(description = "迭代数量", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private Integer iterationCount;
  23 +
  24 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workbench/vo/PmsWorkbenchIterationRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workbench.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 工作台迭代 Response VO")
  9 +@Data
  10 +public class PmsWorkbenchIterationRespVO {
  11 +
  12 + @Schema(description = "迭代编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  16 + private Long projectId;
  17 +
  18 + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "商城项目")
  19 + private String projectName;
  20 +
  21 + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  22 + private Integer projectType;
  23 +
  24 + @Schema(description = "迭代名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "第一个迭代")
  25 + private String name;
  26 +
  27 + @Schema(description = "迭代状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  28 + private Integer status;
  29 +
  30 + @Schema(description = "开始时间", example = "2026-01-01 00:00:00")
  31 + private LocalDateTime startTime;
  32 +
  33 + @Schema(description = "结束时间", example = "2026-01-31 23:59:59")
  34 + private LocalDateTime endTime;
  35 +
  36 + @Schema(description = "迭代目标", example = "完成登录模块")
  37 + private String target;
  38 +
  39 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workbench/vo/PmsWorkbenchPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workbench.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.pms.enums.pm.workitem.PmsWorkItemPriorityEnum;
  6 +import cn.iocoder.yudao.module.pms.enums.pm.workitem.PmsWorkItemTypeEnum;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +import org.springframework.format.annotation.DateTimeFormat;
  12 +
  13 +import java.time.LocalDateTime;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  16 +
  17 +@Schema(description = "管理后台 - PMS 工作台分页 Request VO")
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +public class PmsWorkbenchPageReqVO extends PageParam {
  22 +
  23 + @Schema(description = "项目编号", example = "1024")
  24 + private Long projectId;
  25 +
  26 + @Schema(description = "工作项类型", example = "3")
  27 + @InEnum(PmsWorkItemTypeEnum.class)
  28 + private Integer type;
  29 +
  30 + @Schema(description = "名称", example = "登录")
  31 + private String name;
  32 +
  33 + @Schema(description = "状态,工作项为语义状态,迭代为迭代状态", example = "2")
  34 + private Integer status;
  35 +
  36 + @Schema(description = "优先级", example = "2")
  37 + @InEnum(PmsWorkItemPriorityEnum.class)
  38 + private Integer priority;
  39 +
  40 + @Schema(description = "迭代编号", example = "1024")
  41 + private Long iterationId;
  42 +
  43 + @Schema(description = "截止时间范围")
  44 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  45 + private LocalDateTime[] endTime;
  46 +
  47 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workbench/vo/PmsWorkbenchWorkItemRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workbench.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 工作台工作项 Response VO")
  9 +@Data
  10 +public class PmsWorkbenchWorkItemRespVO {
  11 +
  12 + @Schema(description = "工作项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  16 + private Long projectId;
  17 +
  18 + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "商城项目")
  19 + private String projectName;
  20 +
  21 + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  22 + private Integer projectType;
  23 +
  24 + @Schema(description = "工作项类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  25 + private Integer type;
  26 +
  27 + @Schema(description = "工作项序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  28 + private Integer serialNumber;
  29 +
  30 + @Schema(description = "工作项名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "优化登录页")
  31 + private String name;
  32 +
  33 + @Schema(description = "优先级", example = "2")
  34 + private Integer priority;
  35 +
  36 + @Schema(description = "负责人用户编号", example = "1024")
  37 + private Long assigneeUserId;
  38 +
  39 + @Schema(description = "负责人姓名", example = "芋道源码")
  40 + private String assigneeUserName;
  41 +
  42 + @Schema(description = "创建人用户编号", example = "2048")
  43 + private Long creatorUserId;
  44 +
  45 + @Schema(description = "创建人姓名", example = "源码")
  46 + private String creatorUserName;
  47 +
  48 + @Schema(description = "状态编号", example = "1")
  49 + private Long statusId;
  50 +
  51 + @Schema(description = "状态名称", example = "进行中")
  52 + private String statusName;
  53 +
  54 + @Schema(description = "语义状态", example = "2")
  55 + private Integer status;
  56 +
  57 + @Schema(description = "进度", example = "60")
  58 + private Integer progress;
  59 +
  60 + @Schema(description = "开始时间", example = "2026-01-01 10:00:00")
  61 + private LocalDateTime startTime;
  62 +
  63 + @Schema(description = "结束时间", example = "2026-01-31 18:00:00")
  64 + private LocalDateTime endTime;
  65 +
  66 + @Schema(description = "是否可编辑", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  67 + private Boolean writeStatus;
  68 +
  69 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  70 + private LocalDateTime createTime;
  71 +
  72 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/PmsWorkItemActivityController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.activity.PmsWorkItemActivityRespVO;
  6 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemActivityDO;
  7 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemActivityService;
  8 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemService;
  9 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  10 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.validation.annotation.Validated;
  17 +import org.springframework.web.bind.annotation.GetMapping;
  18 +import org.springframework.web.bind.annotation.RequestMapping;
  19 +import org.springframework.web.bind.annotation.RequestParam;
  20 +import org.springframework.web.bind.annotation.RestController;
  21 +
  22 +import java.util.List;
  23 +import java.util.Map;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  28 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  29 +
  30 +@Tag(name = "管理后台 - PMS 工作项动态")
  31 +@RestController
  32 +@RequestMapping("/pms/pm/work-item-activity")
  33 +@Validated
  34 +public class PmsWorkItemActivityController {
  35 +
  36 + @Resource
  37 + private PmsWorkItemActivityService activityService;
  38 + @Resource
  39 + private PmsWorkItemService workItemService;
  40 +
  41 + @Resource
  42 + private AdminUserApi adminUserApi;
  43 +
  44 + @GetMapping("/list")
  45 + @Operation(summary = "获得工作项动态列表")
  46 + @Parameter(name = "workItemId", description = "工作项编号", required = true, example = "1024")
  47 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  48 + public CommonResult<List<PmsWorkItemActivityRespVO>> getWorkItemActivityList(
  49 + @RequestParam("workItemId") Long workItemId) {
  50 + workItemService.getWorkItem(workItemId, getLoginUserId());
  51 + List<PmsWorkItemActivityDO> activities = activityService.getWorkItemActivityList(workItemId);
  52 + // 拼接 VO:批量查询操作人,并补充用户展示字段
  53 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  54 + convertSet(activities, PmsWorkItemActivityDO::getOperatorUserId));
  55 + List<PmsWorkItemActivityRespVO> activityVOList = BeanUtils.toBean(
  56 + activities, PmsWorkItemActivityRespVO.class, activityVO ->
  57 + findAndThen(userMap, activityVO.getOperatorUserId(), user -> activityVO
  58 + .setOperatorUserName(user.getNickname()).setOperatorUserAvatar(user.getAvatar())));
  59 + return success(activityVOList);
  60 + }
  61 +
  62 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/PmsWorkItemCommentController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.comment.PmsWorkItemCommentRespVO;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.comment.PmsWorkItemCommentSaveReqVO;
  7 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemCommentDO;
  8 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemCommentService;
  9 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  10 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.DeleteMapping;
  19 +import org.springframework.web.bind.annotation.GetMapping;
  20 +import org.springframework.web.bind.annotation.PostMapping;
  21 +import org.springframework.web.bind.annotation.PutMapping;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestParam;
  25 +import org.springframework.web.bind.annotation.RestController;
  26 +
  27 +import java.util.ArrayList;
  28 +import java.util.LinkedHashMap;
  29 +import java.util.List;
  30 +import java.util.Map;
  31 +import java.util.Set;
  32 +
  33 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  36 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  37 +
  38 +@Tag(name = "管理后台 - PMS 工作项评论")
  39 +@RestController
  40 +@RequestMapping("/pms/pm/work-item-comment")
  41 +@Validated
  42 +public class PmsWorkItemCommentController {
  43 +
  44 + @Resource
  45 + private PmsWorkItemCommentService commentService;
  46 +
  47 + @Resource
  48 + private AdminUserApi adminUserApi;
  49 +
  50 + @PostMapping("/create")
  51 + @Operation(summary = "创建工作项评论")
  52 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  53 + public CommonResult<Long> createWorkItemComment(@Valid @RequestBody PmsWorkItemCommentSaveReqVO saveReqVO) {
  54 + return success(commentService.createWorkItemComment(saveReqVO, getLoginUserId()));
  55 + }
  56 +
  57 + @PutMapping("/update")
  58 + @Operation(summary = "修改工作项评论")
  59 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  60 + public CommonResult<Boolean> updateWorkItemComment(@Valid @RequestBody PmsWorkItemCommentSaveReqVO saveReqVO) {
  61 + commentService.updateWorkItemComment(saveReqVO, getLoginUserId());
  62 + return success(true);
  63 + }
  64 +
  65 + @DeleteMapping("/delete")
  66 + @Operation(summary = "删除工作项评论")
  67 + @Parameter(name = "id", description = "评论编号", required = true, example = "1024")
  68 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  69 + public CommonResult<Boolean> deleteWorkItemComment(@RequestParam("id") Long id) {
  70 + commentService.deleteWorkItemComment(id, getLoginUserId());
  71 + return success(true);
  72 + }
  73 +
  74 + @GetMapping("/list")
  75 + @Operation(summary = "获得工作项评论列表")
  76 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  77 + public CommonResult<List<PmsWorkItemCommentRespVO>> getWorkItemCommentList(
  78 + @RequestParam("workItemId") Long workItemId) {
  79 + List<PmsWorkItemCommentDO> comments = commentService.getWorkItemCommentList(workItemId, getLoginUserId());
  80 + // 拼接 VO:批量查询评论人和回复对象
  81 + Set<Long> userIds = convertSet(comments, PmsWorkItemCommentDO::getUserId);
  82 + userIds.addAll(convertSet(comments, PmsWorkItemCommentDO::getReplyUserId));
  83 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
  84 + // 拼接 VO:转换评论,并按主评论组织回复列表
  85 + Map<Long, PmsWorkItemCommentRespVO> mainCommentMap = new LinkedHashMap<>();
  86 + List<PmsWorkItemCommentRespVO> mainComments = new ArrayList<>();
  87 + for (PmsWorkItemCommentDO comment : comments) {
  88 + PmsWorkItemCommentRespVO commentVO = BeanUtils.toBean(comment, PmsWorkItemCommentRespVO.class).setChildren(new ArrayList<>());
  89 + findAndThen(userMap, comment.getUserId(), user -> commentVO.setUserName(user.getNickname()));
  90 + findAndThen(userMap, comment.getReplyUserId(), user -> commentVO.setReplyUserName(user.getNickname()));
  91 + if (PmsWorkItemCommentDO.MAIN_ID_ROOT.equals(comment.getMainId())) {
  92 + mainCommentMap.put(comment.getId(), commentVO);
  93 + mainComments.add(commentVO);
  94 + } else {
  95 + PmsWorkItemCommentRespVO mainComment = mainCommentMap.get(comment.getMainId());
  96 + if (mainComment != null) {
  97 + mainComment.getChildren().add(commentVO);
  98 + }
  99 + }
  100 + }
  101 + return success(mainComments);
  102 + }
  103 +
  104 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/PmsWorkItemController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.label.PmsWorkItemLabelRespVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.status.PmsWorkItemStatusUpdateReqVO;
  11 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemBoardReqVO;
  12 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemBoardRespVO;
  13 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemImportExcelVO;
  14 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemImportRespVO;
  15 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemIterationUpdateReqVO;
  16 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemNameUpdateReqVO;
  17 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemPageReqVO;
  18 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemPlanningSortReqVO;
  19 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemRespVO;
  20 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemSaveReqVO;
  21 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.workitem.PmsWorkItemSortReqVO;
  22 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.iteration.PmsIterationDO;
  23 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemDO;
  24 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemLabelDO;
  25 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemStatusDO;
  26 +import cn.iocoder.yudao.module.pms.enums.pm.workitem.PmsWorkItemPriorityEnum;
  27 +import cn.iocoder.yudao.module.pms.service.pm.iteration.PmsIterationService;
  28 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemLabelService;
  29 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemService;
  30 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemStatusService;
  31 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  32 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  33 +import io.swagger.v3.oas.annotations.Operation;
  34 +import io.swagger.v3.oas.annotations.Parameter;
  35 +import io.swagger.v3.oas.annotations.Parameters;
  36 +import io.swagger.v3.oas.annotations.tags.Tag;
  37 +import jakarta.annotation.Resource;
  38 +import jakarta.servlet.http.HttpServletResponse;
  39 +import jakarta.validation.Valid;
  40 +import org.springframework.security.access.prepost.PreAuthorize;
  41 +import org.springframework.validation.annotation.Validated;
  42 +import org.springframework.web.bind.annotation.DeleteMapping;
  43 +import org.springframework.web.bind.annotation.GetMapping;
  44 +import org.springframework.web.bind.annotation.PostMapping;
  45 +import org.springframework.web.bind.annotation.PutMapping;
  46 +import org.springframework.web.bind.annotation.RequestBody;
  47 +import org.springframework.web.bind.annotation.RequestMapping;
  48 +import org.springframework.web.bind.annotation.RequestParam;
  49 +import org.springframework.web.bind.annotation.RestController;
  50 +import org.springframework.web.multipart.MultipartFile;
  51 +
  52 +import java.io.IOException;
  53 +import java.time.LocalDateTime;
  54 +import java.util.Collections;
  55 +import java.util.List;
  56 +import java.util.Map;
  57 +import java.util.Set;
  58 +
  59 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  60 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  61 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
  62 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  63 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
  64 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  65 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  66 +
  67 +@Tag(name = "管理后台 - PMS 工作项")
  68 +@RestController
  69 +@RequestMapping("/pms/pm/work-item")
  70 +@Validated
  71 +public class PmsWorkItemController {
  72 +
  73 + @Resource
  74 + private PmsWorkItemService workItemService;
  75 + @Resource
  76 + private PmsWorkItemLabelService workItemLabelService;
  77 + @Resource
  78 + private PmsWorkItemStatusService workItemStatusService;
  79 + @Resource
  80 + private PmsIterationService iterationService;
  81 +
  82 + @Resource
  83 + private AdminUserApi adminUserApi;
  84 +
  85 + @PostMapping("/create")
  86 + @Operation(summary = "创建工作项")
  87 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:create')")
  88 + public CommonResult<Long> createWorkItem(@Valid @RequestBody PmsWorkItemSaveReqVO saveReqVO) {
  89 + return success(workItemService.createWorkItem(saveReqVO, getLoginUserId()));
  90 + }
  91 +
  92 + @PutMapping("/update")
  93 + @Operation(summary = "更新工作项")
  94 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  95 + public CommonResult<Boolean> updateWorkItem(@Valid @RequestBody PmsWorkItemSaveReqVO saveReqVO) {
  96 + workItemService.updateWorkItem(saveReqVO, getLoginUserId());
  97 + return success(true);
  98 + }
  99 +
  100 + @PutMapping("/update-name")
  101 + @Operation(summary = "更新工作项名称")
  102 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  103 + public CommonResult<Boolean> updateWorkItemName(@Valid @RequestBody PmsWorkItemNameUpdateReqVO updateReqVO) {
  104 + workItemService.updateWorkItemName(updateReqVO, getLoginUserId());
  105 + return success(true);
  106 + }
  107 +
  108 + @PutMapping("/update-status")
  109 + @Operation(summary = "更新工作项状态")
  110 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  111 + public CommonResult<Boolean> updateWorkItemStatus(@Valid @RequestBody PmsWorkItemStatusUpdateReqVO updateReqVO) {
  112 + workItemService.updateWorkItemStatus(updateReqVO, getLoginUserId());
  113 + return success(true);
  114 + }
  115 +
  116 + @PutMapping("/update-iteration")
  117 + @Operation(summary = "更新工作项所属迭代")
  118 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  119 + public CommonResult<Boolean> updateWorkItemIteration(@Valid @RequestBody PmsWorkItemIterationUpdateReqVO updateReqVO) {
  120 + workItemService.updateWorkItemIteration(updateReqVO, getLoginUserId());
  121 + return success(true);
  122 + }
  123 +
  124 + @PutMapping("/update-sort")
  125 + @Operation(summary = "更新看板列内工作项顺序")
  126 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  127 + public CommonResult<Boolean> updateWorkItemSort(@Valid @RequestBody PmsWorkItemSortReqVO sortReqVO) {
  128 + workItemService.updateWorkItemSort(sortReqVO, getLoginUserId());
  129 + return success(true);
  130 + }
  131 +
  132 + @PutMapping("/update-planning-sort")
  133 + @Operation(summary = "更新待规划或迭代内工作项顺序")
  134 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  135 + public CommonResult<Boolean> updateWorkItemPlanningSort(@Valid @RequestBody PmsWorkItemPlanningSortReqVO sortReqVO) {
  136 + workItemService.updateWorkItemPlanningSort(sortReqVO, getLoginUserId());
  137 + return success(true);
  138 + }
  139 +
  140 + @PutMapping("/archive")
  141 + @Operation(summary = "归档工作项")
  142 + @Parameter(name = "id", description = "工作项编号", required = true, example = "1024")
  143 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  144 + public CommonResult<Boolean> archiveWorkItem(@RequestParam("id") Long id) {
  145 + workItemService.archiveWorkItem(id, getLoginUserId());
  146 + return success(true);
  147 + }
  148 +
  149 + @PutMapping("/recycle")
  150 + @Operation(summary = "将工作项移入回收站")
  151 + @Parameter(name = "id", description = "工作项编号", required = true, example = "1024")
  152 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  153 + public CommonResult<Boolean> recycleWorkItem(@RequestParam("id") Long id) {
  154 + workItemService.recycleWorkItem(id, getLoginUserId());
  155 + return success(true);
  156 + }
  157 +
  158 + @PutMapping("/restore")
  159 + @Operation(summary = "恢复工作项")
  160 + @Parameter(name = "id", description = "工作项编号", required = true, example = "1024")
  161 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  162 + public CommonResult<Boolean> restoreWorkItem(@RequestParam("id") Long id) {
  163 + workItemService.restoreWorkItem(id, getLoginUserId());
  164 + return success(true);
  165 + }
  166 +
  167 + @DeleteMapping("/delete")
  168 + @Operation(summary = "彻底删除回收站中的工作项")
  169 + @Parameter(name = "id", description = "工作项编号", required = true, example = "1024")
  170 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:delete')")
  171 + public CommonResult<Boolean> deleteWorkItem(@RequestParam("id") Long id) {
  172 + workItemService.deleteWorkItem(id, getLoginUserId());
  173 + return success(true);
  174 + }
  175 +
  176 + @GetMapping("/get")
  177 + @Operation(summary = "获得工作项详情")
  178 + @Parameter(name = "id", description = "工作项编号", required = true, example = "1024")
  179 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  180 + public CommonResult<PmsWorkItemRespVO> getWorkItem(@RequestParam("id") Long id) {
  181 + PmsWorkItemDO workItem = workItemService.getWorkItem(id, getLoginUserId());
  182 + PmsWorkItemRespVO workItemVO = buildWorkItemRespVO(workItem);
  183 + return success(workItemVO);
  184 + }
  185 +
  186 + @GetMapping("/page")
  187 + @Operation(summary = "获得工作项分页")
  188 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  189 + public CommonResult<PageResult<PmsWorkItemRespVO>> getWorkItemPage(@Valid PmsWorkItemPageReqVO pageReqVO) {
  190 + PageResult<PmsWorkItemDO> pageResult = workItemService.getWorkItemPage(pageReqVO, getLoginUserId());
  191 + return success(new PageResult<>(buildWorkItemRespVOList(pageResult.getList()), pageResult.getTotal()));
  192 + }
  193 +
  194 + @GetMapping("/export-excel")
  195 + @Operation(summary = "导出工作项")
  196 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:export')")
  197 + public void exportWorkItemList(@Valid PmsWorkItemPageReqVO pageReqVO,
  198 + HttpServletResponse response) throws IOException {
  199 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  200 + List<PmsWorkItemDO> list = workItemService.getWorkItemList(pageReqVO, getLoginUserId());
  201 + List<PmsWorkItemRespVO> workItemVOList = buildWorkItemRespVOList(list);
  202 + ExcelUtils.write(response, "工作项.xlsx", "工作项", PmsWorkItemRespVO.class, workItemVOList);
  203 + }
  204 +
  205 + @GetMapping("/get-import-template")
  206 + @Operation(summary = "下载工作项导入模板")
  207 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:import')")
  208 + public void getWorkItemImportTemplate(HttpServletResponse response) throws IOException {
  209 + LocalDateTime now = LocalDateTime.now();
  210 + LocalDateTime deadline = now.plusDays(3);
  211 + PmsWorkItemImportExcelVO example = PmsWorkItemImportExcelVO.builder()
  212 + .name("登录页面开发").description("实现账号密码登录").statusName("")
  213 + .priority(PmsWorkItemPriorityEnum.MEDIUM.getPriority())
  214 + .startTime(now).endTime(deadline)
  215 + .labels(Collections.emptyList()).progress(0).estimatedHours(16)
  216 + .build();
  217 + ExcelUtils.write(response, "工作项导入模板.xlsx", "工作项", PmsWorkItemImportExcelVO.class,
  218 + Collections.singletonList(example));
  219 + }
  220 +
  221 + @PostMapping("/import")
  222 + @Operation(summary = "导入工作项")
  223 + @Parameters({
  224 + @Parameter(name = "projectId", description = "项目编号", required = true, example = "1024"),
  225 + @Parameter(name = "type", description = "工作项类型(需求、任务、缺陷)", required = true, example = "1"),
  226 + @Parameter(name = "file", description = "Excel 文件", required = true)
  227 + })
  228 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:import')")
  229 + public CommonResult<PmsWorkItemImportRespVO> importWorkItemList(
  230 + @RequestParam("projectId") Long projectId,
  231 + @RequestParam("type") Integer workItemType,
  232 + @RequestParam("file") MultipartFile file) throws Exception {
  233 + return success(workItemService.importWorkItemList(projectId, workItemType,
  234 + ExcelUtils.read(file, PmsWorkItemImportExcelVO.class), getLoginUserId()));
  235 + }
  236 +
  237 + @GetMapping("/board")
  238 + @Operation(summary = "获得工作项看板")
  239 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  240 + public CommonResult<List<PmsWorkItemBoardRespVO>> getWorkItemBoard(@Valid PmsWorkItemBoardReqVO queryReqVO) {
  241 + List<PmsWorkItemBoardRespVO> columns = workItemService.getWorkItemBoard(queryReqVO, getLoginUserId());
  242 + fillWorkItemRespVOList(convertListByFlatMap(columns, column -> column.getItems().stream()));
  243 + return success(columns);
  244 + }
  245 +
  246 + // ==================== 拼接 VO ====================
  247 +
  248 + private PmsWorkItemRespVO buildWorkItemRespVO(PmsWorkItemDO workItem) {
  249 + return CollUtil.getFirst(buildWorkItemRespVOList(Collections.singletonList(workItem)));
  250 + }
  251 +
  252 + private List<PmsWorkItemRespVO> buildWorkItemRespVOList(List<PmsWorkItemDO> workItems) {
  253 + if (CollUtil.isEmpty(workItems)) {
  254 + return Collections.emptyList();
  255 + }
  256 + return fillWorkItemRespVOList(BeanUtils.toBean(workItems, PmsWorkItemRespVO.class));
  257 + }
  258 +
  259 + private List<PmsWorkItemRespVO> fillWorkItemRespVOList(List<PmsWorkItemRespVO> workItems) {
  260 + if (CollUtil.isEmpty(workItems)) {
  261 + return Collections.emptyList();
  262 + }
  263 +
  264 + // 1.1 批量查询状态、迭代、关联需求和参与人
  265 + Map<Long, PmsWorkItemStatusDO> statusMap = workItemStatusService.getWorkItemStatusMap(
  266 + convertSet(workItems, PmsWorkItemRespVO::getStatusId));
  267 + Map<Long, PmsIterationDO> iterationMap = iterationService.getIterationMap(
  268 + convertSet(workItems, PmsWorkItemRespVO::getIterationId));
  269 + Map<Long, PmsWorkItemDO> requirementMap = workItemService.getWorkItemMap(
  270 + convertSet(workItems, PmsWorkItemRespVO::getRelatedRequirementId));
  271 + Map<Long, List<Long>> memberUserIdListMap = workItemService.getWorkItemMemberUserIdListMap(
  272 + convertSet(workItems, PmsWorkItemRespVO::getId));
  273 + Map<Long, PmsWorkItemLabelDO> labelMap = workItemLabelService.getWorkItemLabelMap(
  274 + convertSetByFlatMap(workItems, PmsWorkItemRespVO::getLabelIds, List::stream));
  275 + // 1.2 批量查询负责人和参与人用户
  276 + Set<Long> userIds = convertSetByFlatMap(workItems, workItem -> java.util.stream.Stream.concat(
  277 + java.util.stream.Stream.of(workItem.getAssigneeUserId()),
  278 + memberUserIdListMap.getOrDefault(workItem.getId(), Collections.emptyList()).stream()));
  279 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
  280 + // 2. 拼接工作项展示字段
  281 + for (PmsWorkItemRespVO workItemVO : workItems) {
  282 + findAndThen(statusMap, workItemVO.getStatusId(), status -> workItemVO.setStatusName(status.getName()));
  283 + findAndThen(iterationMap, workItemVO.getIterationId(),
  284 + iteration -> workItemVO.setIterationName(iteration.getName()));
  285 + findAndThen(requirementMap, workItemVO.getRelatedRequirementId(),
  286 + requirement -> workItemVO.setRelatedRequirementName(requirement.getName()));
  287 + findAndThen(userMap, workItemVO.getAssigneeUserId(),
  288 + assignee -> workItemVO.setAssigneeUserName(assignee.getNickname()));
  289 + List<Long> memberUserIds = memberUserIdListMap.getOrDefault(workItemVO.getId(), Collections.emptyList());
  290 + List<String> memberUserNames = convertList(convertList(memberUserIds, userMap::get),
  291 + AdminUserRespDTO::getNickname);
  292 + workItemVO.setMemberUserIds(memberUserIds).setMemberUserNames(memberUserNames);
  293 + workItemVO.setLabels(BeanUtils.toBean(convertList(workItemVO.getLabelIds(), labelMap::get),
  294 + PmsWorkItemLabelRespVO.class));
  295 + }
  296 + return workItems;
  297 + }
  298 +
  299 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/PmsWorkItemLabelController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.label.PmsWorkItemLabelRespVO;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.label.PmsWorkItemLabelSaveReqVO;
  7 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemLabelDO;
  8 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemLabelService;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +import io.swagger.v3.oas.annotations.Parameter;
  11 +import io.swagger.v3.oas.annotations.tags.Tag;
  12 +import jakarta.annotation.Resource;
  13 +import jakarta.validation.Valid;
  14 +import org.springframework.security.access.prepost.PreAuthorize;
  15 +import org.springframework.validation.annotation.Validated;
  16 +import org.springframework.web.bind.annotation.DeleteMapping;
  17 +import org.springframework.web.bind.annotation.GetMapping;
  18 +import org.springframework.web.bind.annotation.PostMapping;
  19 +import org.springframework.web.bind.annotation.PutMapping;
  20 +import org.springframework.web.bind.annotation.RequestBody;
  21 +import org.springframework.web.bind.annotation.RequestMapping;
  22 +import org.springframework.web.bind.annotation.RequestParam;
  23 +import org.springframework.web.bind.annotation.RestController;
  24 +
  25 +import java.util.List;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +
  29 +@Tag(name = "管理后台 - PMS 工作项标签")
  30 +@RestController
  31 +@RequestMapping("/pms/pm/work-item-label")
  32 +@Validated
  33 +public class PmsWorkItemLabelController {
  34 +
  35 + @Resource
  36 + private PmsWorkItemLabelService labelService;
  37 +
  38 + @PostMapping("/create")
  39 + @Operation(summary = "创建工作项标签")
  40 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  41 + public CommonResult<Long> createWorkItemLabel(@Valid @RequestBody PmsWorkItemLabelSaveReqVO saveReqVO) {
  42 + return success(labelService.createWorkItemLabel(saveReqVO));
  43 + }
  44 +
  45 + @PutMapping("/update")
  46 + @Operation(summary = "修改工作项标签")
  47 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  48 + public CommonResult<Boolean> updateWorkItemLabel(@Valid @RequestBody PmsWorkItemLabelSaveReqVO saveReqVO) {
  49 + labelService.updateWorkItemLabel(saveReqVO);
  50 + return success(true);
  51 + }
  52 +
  53 + @DeleteMapping("/delete")
  54 + @Operation(summary = "删除工作项标签")
  55 + @Parameter(name = "id", description = "标签编号", required = true, example = "1024")
  56 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  57 + public CommonResult<Boolean> deleteWorkItemLabel(@RequestParam("id") Long id) {
  58 + labelService.deleteWorkItemLabel(id);
  59 + return success(true);
  60 + }
  61 +
  62 + @GetMapping("/list")
  63 + @Operation(summary = "获得工作项标签列表")
  64 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  65 + public CommonResult<List<PmsWorkItemLabelRespVO>> getWorkItemLabelList(
  66 + @RequestParam(value = "name", required = false) String name) {
  67 + List<PmsWorkItemLabelDO> labels = labelService.getWorkItemLabelList(name);
  68 + return success(BeanUtils.toBean(labels, PmsWorkItemLabelRespVO.class));
  69 + }
  70 +
  71 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/PmsWorkItemStatusController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.status.PmsWorkItemBoardConfigRespVO;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.status.PmsWorkItemBoardConfigSaveReqVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.status.PmsWorkItemStatusConfigUpdateReqVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.status.PmsWorkItemStatusCreateReqVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.status.PmsWorkItemStatusDeleteReqVO;
  11 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.status.PmsWorkItemStatusRespVO;
  12 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.status.PmsWorkItemStatusSortReqVO;
  13 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemBoardDO;
  14 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemStatusDO;
  15 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemStatusService;
  16 +import io.swagger.v3.oas.annotations.Operation;
  17 +import io.swagger.v3.oas.annotations.Parameter;
  18 +import io.swagger.v3.oas.annotations.tags.Tag;
  19 +import jakarta.annotation.Resource;
  20 +import jakarta.validation.Valid;
  21 +import org.springframework.security.access.prepost.PreAuthorize;
  22 +import org.springframework.validation.annotation.Validated;
  23 +import org.springframework.web.bind.annotation.DeleteMapping;
  24 +import org.springframework.web.bind.annotation.GetMapping;
  25 +import org.springframework.web.bind.annotation.PostMapping;
  26 +import org.springframework.web.bind.annotation.PutMapping;
  27 +import org.springframework.web.bind.annotation.RequestBody;
  28 +import org.springframework.web.bind.annotation.RequestMapping;
  29 +import org.springframework.web.bind.annotation.RequestParam;
  30 +import org.springframework.web.bind.annotation.RestController;
  31 +
  32 +import java.util.ArrayList;
  33 +import java.util.LinkedHashMap;
  34 +import java.util.LinkedHashSet;
  35 +import java.util.List;
  36 +import java.util.Map;
  37 +import java.util.Set;
  38 +
  39 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  40 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  41 +
  42 +@Tag(name = "管理后台 - PMS 工作项状态")
  43 +@RestController
  44 +@RequestMapping("/pms/pm/work-item-status")
  45 +@Validated
  46 +public class PmsWorkItemStatusController {
  47 +
  48 + @Resource
  49 + private PmsWorkItemStatusService workItemStatusService;
  50 +
  51 + @PostMapping("/create")
  52 + @Operation(summary = "创建工作项状态")
  53 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  54 + public CommonResult<Long> createWorkItemStatus(
  55 + @Valid @RequestBody PmsWorkItemStatusCreateReqVO createReqVO) {
  56 + return success(workItemStatusService.createWorkItemStatus(createReqVO, getLoginUserId()));
  57 + }
  58 +
  59 + @PutMapping("/update")
  60 + @Operation(summary = "更新工作项状态")
  61 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  62 + public CommonResult<Boolean> updateWorkItemStatusConfig(
  63 + @Valid @RequestBody PmsWorkItemStatusConfigUpdateReqVO updateReqVO) {
  64 + workItemStatusService.updateWorkItemStatusConfig(updateReqVO, getLoginUserId());
  65 + return success(true);
  66 + }
  67 +
  68 + @PutMapping("/update-default")
  69 + @Operation(summary = "更新工作项初始状态")
  70 + @Parameter(name = "id", description = "状态编号", required = true, example = "1024")
  71 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  72 + public CommonResult<Boolean> updateDefaultWorkItemStatus(@RequestParam("id") Long id) {
  73 + workItemStatusService.updateDefaultWorkItemStatus(id, getLoginUserId());
  74 + return success(true);
  75 + }
  76 +
  77 + @PutMapping("/update-sort")
  78 + @Operation(summary = "更新工作项状态顺序")
  79 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  80 + public CommonResult<Boolean> updateWorkItemStatusSort(
  81 + @Valid @RequestBody PmsWorkItemStatusSortReqVO sortReqVO) {
  82 + workItemStatusService.updateWorkItemStatusSort(sortReqVO, getLoginUserId());
  83 + return success(true);
  84 + }
  85 +
  86 + @PutMapping("/update-board-config")
  87 + @Operation(summary = "更新工作项看板列和状态映射")
  88 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  89 + public CommonResult<Boolean> updateWorkItemBoardConfig(
  90 + @Valid @RequestBody PmsWorkItemBoardConfigSaveReqVO saveReqVO) {
  91 + workItemStatusService.updateWorkItemBoardConfig(saveReqVO, getLoginUserId());
  92 + return success(true);
  93 + }
  94 +
  95 + @DeleteMapping("/delete")
  96 + @Operation(summary = "删除工作项状态并迁移工作项")
  97 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  98 + public CommonResult<Boolean> deleteWorkItemStatus(
  99 + @Valid @RequestBody PmsWorkItemStatusDeleteReqVO deleteReqVO) {
  100 + workItemStatusService.deleteWorkItemStatus(deleteReqVO, getLoginUserId());
  101 + return success(true);
  102 + }
  103 +
  104 + @GetMapping("/get")
  105 + @Operation(summary = "获得工作项状态详情")
  106 + @Parameter(name = "id", description = "状态编号", required = true, example = "1024")
  107 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  108 + public CommonResult<PmsWorkItemStatusRespVO> getWorkItemStatus(@RequestParam("id") Long id) {
  109 + PmsWorkItemStatusDO status = workItemStatusService.getWorkItemStatus(id, getLoginUserId());
  110 + return success(BeanUtils.toBean(status, PmsWorkItemStatusRespVO.class));
  111 + }
  112 +
  113 + @GetMapping("/list")
  114 + @Operation(summary = "获得工作项看板状态列表")
  115 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  116 + public CommonResult<List<PmsWorkItemStatusRespVO>> getWorkItemStatusList(
  117 + @RequestParam("projectId") Long projectId, @RequestParam("type") Integer workItemType) {
  118 + List<PmsWorkItemStatusDO> statuses = workItemStatusService.getWorkItemStatusList(projectId, workItemType,
  119 + getLoginUserId());
  120 + return success(BeanUtils.toBean(statuses, PmsWorkItemStatusRespVO.class));
  121 + }
  122 +
  123 + @GetMapping("/get-board-config")
  124 + @Operation(summary = "获得工作项看板列和状态映射")
  125 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  126 + public CommonResult<PmsWorkItemBoardConfigRespVO> getWorkItemBoardConfig(
  127 + @RequestParam("projectId") Long projectId, @RequestParam("type") Integer workItemType) {
  128 + // 1. 校验项目可读并查询已保存的看板列、状态
  129 + List<PmsWorkItemStatusDO> statuses = workItemStatusService
  130 + .getWorkItemStatusList(projectId, workItemType, getLoginUserId());
  131 + List<PmsWorkItemBoardDO> boards = workItemStatusService.getWorkItemBoardList(projectId, workItemType);
  132 +
  133 + // 2.1 按看板列名称聚合状态编号
  134 + Map<String, List<Long>> statusIdMap = new LinkedHashMap<>();
  135 + for (PmsWorkItemStatusDO status : statuses) {
  136 + if (StrUtil.isNotBlank(status.getBoardName())) {
  137 + statusIdMap.computeIfAbsent(status.getBoardName(), key -> new ArrayList<>()).add(status.getId());
  138 + }
  139 + }
  140 + // 2.2 按看板列顺序组装响应,并保留空列
  141 + Set<Long> assignedStatusIds = new LinkedHashSet<>();
  142 + List<PmsWorkItemBoardConfigRespVO.Board> boardVOs = new ArrayList<>();
  143 + for (PmsWorkItemBoardDO board : boards) {
  144 + List<Long> statusIds = statusIdMap.getOrDefault(board.getName(), new ArrayList<>());
  145 + assignedStatusIds.addAll(statusIds);
  146 + boardVOs.add(new PmsWorkItemBoardConfigRespVO.Board().setId(board.getId())
  147 + .setName(board.getName()).setStatusIds(statusIds));
  148 + }
  149 + // 2.3 收集未分配到看板列的状态
  150 + List<Long> unassignedStatusIds = new ArrayList<>();
  151 + for (PmsWorkItemStatusDO status : statuses) {
  152 + if (!assignedStatusIds.contains(status.getId())) {
  153 + unassignedStatusIds.add(status.getId());
  154 + }
  155 + }
  156 + return success(new PmsWorkItemBoardConfigRespVO().setBoards(boardVOs).setUnassignedStatusIds(unassignedStatusIds));
  157 + }
  158 +
  159 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/PmsWorkItemWorkLogController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.worklog.PmsProjectWorkLogReportReqVO;
  8 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.worklog.PmsProjectWorkLogReportRespVO;
  9 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.worklog.PmsWorkItemWorkLogRespVO;
  10 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.worklog.PmsWorkItemWorkLogSaveReqVO;
  11 +import cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.worklog.PmsWorkItemWorkLogSummaryRespVO;
  12 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemDO;
  13 +import cn.iocoder.yudao.module.pms.dal.dataobject.pm.workitem.PmsWorkItemWorkLogDO;
  14 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemService;
  15 +import cn.iocoder.yudao.module.pms.service.pm.workitem.PmsWorkItemWorkLogService;
  16 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  17 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  18 +import io.swagger.v3.oas.annotations.Operation;
  19 +import io.swagger.v3.oas.annotations.Parameter;
  20 +import io.swagger.v3.oas.annotations.tags.Tag;
  21 +import jakarta.annotation.Resource;
  22 +import jakarta.validation.Valid;
  23 +import org.springframework.security.access.prepost.PreAuthorize;
  24 +import org.springframework.validation.annotation.Validated;
  25 +import org.springframework.web.bind.annotation.GetMapping;
  26 +import org.springframework.web.bind.annotation.PostMapping;
  27 +import org.springframework.web.bind.annotation.PutMapping;
  28 +import org.springframework.web.bind.annotation.RequestBody;
  29 +import org.springframework.web.bind.annotation.RequestMapping;
  30 +import org.springframework.web.bind.annotation.RequestParam;
  31 +import org.springframework.web.bind.annotation.RestController;
  32 +
  33 +import java.util.Collections;
  34 +import java.util.List;
  35 +import java.util.Map;
  36 +import java.util.Set;
  37 +
  38 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  39 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  40 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  41 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  42 +
  43 +@Tag(name = "管理后台 - PMS 工作项工时")
  44 +@RestController
  45 +@RequestMapping("/pms/pm/work-item-work-log")
  46 +@Validated
  47 +public class PmsWorkItemWorkLogController {
  48 +
  49 + @Resource
  50 + private PmsWorkItemWorkLogService workLogService;
  51 + @Resource
  52 + private PmsWorkItemService workItemService;
  53 +
  54 + @Resource
  55 + private AdminUserApi adminUserApi;
  56 +
  57 + @PostMapping("/create")
  58 + @Operation(summary = "创建工作项工时记录")
  59 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  60 + public CommonResult<Long> createWorkItemWorkLog(@Valid @RequestBody PmsWorkItemWorkLogSaveReqVO saveReqVO) {
  61 + return success(workLogService.createWorkItemWorkLog(saveReqVO, getLoginUserId()));
  62 + }
  63 +
  64 + @PutMapping("/update")
  65 + @Operation(summary = "更新工作项工时记录")
  66 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:update')")
  67 + public CommonResult<Boolean> updateWorkItemWorkLog(@Valid @RequestBody PmsWorkItemWorkLogSaveReqVO saveReqVO) {
  68 + workLogService.updateWorkItemWorkLog(saveReqVO, getLoginUserId());
  69 + return success(true);
  70 + }
  71 +
  72 + @GetMapping("/get")
  73 + @Operation(summary = "获得工作项工时记录")
  74 + @Parameter(name = "id", description = "工时记录编号", required = true, example = "1024")
  75 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  76 + public CommonResult<PmsWorkItemWorkLogRespVO> getWorkItemWorkLog(@RequestParam("id") Long id) {
  77 + PmsWorkItemWorkLogDO workLog = workLogService.getWorkItemWorkLog(id, getLoginUserId());
  78 + PmsWorkItemWorkLogRespVO respVO = BeanUtils.toBean(workLog, PmsWorkItemWorkLogRespVO.class)
  79 + .setCreatorUserId(NumberUtils.parseLong(workLog.getCreator()));
  80 + fillWorkItemWorkLogRespVOList(Collections.singletonList(respVO));
  81 + return success(respVO);
  82 + }
  83 +
  84 + @GetMapping("/summary")
  85 + @Operation(summary = "获得工作项工时汇总")
  86 + @Parameter(name = "workItemId", description = "工作项编号", required = true, example = "1024")
  87 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  88 + public CommonResult<PmsWorkItemWorkLogSummaryRespVO> getWorkItemWorkLogSummary(
  89 + @RequestParam("workItemId") Long workItemId) {
  90 + PmsWorkItemWorkLogSummaryRespVO summary = workLogService.getWorkItemWorkLogSummary(workItemId, getLoginUserId());
  91 + fillWorkItemWorkLogRespVOList(summary.getRecords());
  92 + return success(summary);
  93 + }
  94 +
  95 + @GetMapping("/project-report")
  96 + @Operation(summary = "获得项目工时报表")
  97 + @PreAuthorize("@ss.hasPermission('pms:pm:work-item:query')")
  98 + public CommonResult<PmsProjectWorkLogReportRespVO> getProjectWorkItemWorkLogReport(
  99 + @Valid PmsProjectWorkLogReportReqVO reqVO) {
  100 + PmsProjectWorkLogReportRespVO report = workLogService.getProjectWorkItemWorkLogReport(
  101 + reqVO, getLoginUserId());
  102 + return success(report);
  103 + }
  104 +
  105 + // ==================== 拼接 VO ====================
  106 +
  107 + private List<PmsWorkItemWorkLogRespVO> fillWorkItemWorkLogRespVOList(List<PmsWorkItemWorkLogRespVO> workLogs) {
  108 + if (CollUtil.isEmpty(workLogs)) {
  109 + return Collections.emptyList();
  110 + }
  111 + // 1. 批量查询创建人和工作项
  112 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(
  113 + workLogs, PmsWorkItemWorkLogRespVO::getCreatorUserId));
  114 + Map<Long, PmsWorkItemDO> workItemMap = workItemService.getWorkItemMap(
  115 + convertSet(workLogs, PmsWorkItemWorkLogRespVO::getWorkItemId));
  116 + // 2. 拼接创建人和工作项名称
  117 + for (PmsWorkItemWorkLogRespVO workLog : workLogs) {
  118 + findAndThen(userMap, workLog.getCreatorUserId(), user -> workLog.setCreatorUserName(user.getNickname()));
  119 + findAndThen(workItemMap, workLog.getWorkItemId(), item -> workLog.setWorkItemName(item.getName()));
  120 + }
  121 + return workLogs;
  122 + }
  123 +
  124 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/vo/activity/PmsWorkItemActivityRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.activity;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.experimental.Accessors;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +@Schema(description = "管理后台 - PMS 工作项动态响应 VO")
  10 +@Data
  11 +@Accessors(chain = true)
  12 +public class PmsWorkItemActivityRespVO {
  13 +
  14 + @Schema(description = "动态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  15 + private Long id;
  16 +
  17 + @Schema(description = "工作项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  18 + private Long workItemId;
  19 +
  20 + @Schema(description = "操作人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  21 + private Long operatorUserId;
  22 +
  23 + @Schema(description = "操作人姓名", example = "芋道")
  24 + private String operatorUserName;
  25 +
  26 + @Schema(description = "操作人头像")
  27 + private String operatorUserAvatar;
  28 +
  29 + @Schema(description = "动态内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "更新了工作项")
  30 + private String content;
  31 +
  32 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  33 + private LocalDateTime createTime;
  34 +
  35 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/vo/comment/PmsWorkItemCommentRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.comment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - PMS 工作项评论 Response VO")
  10 +@Data
  11 +public class PmsWorkItemCommentRespVO {
  12 +
  13 + @Schema(description = "评论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "工作项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + private Long workItemId;
  18 +
  19 + @Schema(description = "评论人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  20 + private Long userId;
  21 +
  22 + @Schema(description = "评论人姓名", example = "芋道源码")
  23 + private String userName;
  24 +
  25 + @Schema(description = "主评论编号", example = "1024")
  26 + private Long mainId;
  27 +
  28 + @Schema(description = "回复对象用户编号", example = "2048")
  29 + private Long replyUserId;
  30 +
  31 + @Schema(description = "回复对象姓名", example = "源码")
  32 + private String replyUserName;
  33 +
  34 + @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + private String content;
  36 +
  37 + @Schema(description = "回复列表")
  38 + private List<PmsWorkItemCommentRespVO> children;
  39 +
  40 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  41 + private LocalDateTime createTime;
  42 +
  43 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/vo/comment/PmsWorkItemCommentSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.comment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +
  9 +@Schema(description = "管理后台 - PMS 工作项评论新增/修改 Request VO")
  10 +@Data
  11 +public class PmsWorkItemCommentSaveReqVO {
  12 +
  13 + @Schema(description = "评论编号", example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "工作项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + @NotNull(message = "工作项编号不能为空")
  18 + private Long workItemId;
  19 +
  20 + @Schema(description = "主评论编号,回复时传入", example = "1024")
  21 + private Long mainId;
  22 +
  23 + @Schema(description = "回复对象用户编号", example = "2048")
  24 + private Long replyUserId;
  25 +
  26 + @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "已处理")
  27 + @NotBlank(message = "评论内容不能为空")
  28 + @Size(max = 2000, message = "评论内容不能超过 2000 个字符")
  29 + private String content;
  30 +
  31 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/vo/label/PmsWorkItemLabelRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.label;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - PMS 工作项标签 Response VO")
  9 +@Data
  10 +public class PmsWorkItemLabelRespVO {
  11 +
  12 + @Schema(description = "标签编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "标签名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "紧急")
  16 + private String name;
  17 +
  18 + @Schema(description = "标签颜色", requiredMode = Schema.RequiredMode.REQUIRED, example = "#409EFF")
  19 + private String color;
  20 +
  21 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private LocalDateTime createTime;
  23 +
  24 +}
... ...
yudao-module-pms/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/pm/workitem/vo/label/PmsWorkItemLabelSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pms.controller.admin.pm.workitem.vo.label;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.Size;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - PMS 工作项标签新增/修改 Request VO")
  9 +@Data
  10 +public class PmsWorkItemLabelSaveReqVO {
  11 +
  12 + @Schema(description = "标签编号", example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "标签名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "紧急")
  16 + @NotBlank(message = "标签名称不能为空")
  17 + @Size(max = 50, message = "标签名称不能超过 50 个字符")
  18 + private String name;
  19 +
  20 + @Schema(description = "标签颜色", requiredMode = Schema.RequiredMode.REQUIRED, example = "#409EFF")
  21 + @NotBlank(message = "标签颜色不能为空")
  22 + @Size(max = 20, message = "标签颜色不能超过 20 个字符")
  23 + private String color;
  24 +
  25 +}
... ...