diff --git a/nl-common/src/main/java/org/nl/common/pojo/SelectListItem.java b/nl-common/src/main/java/org/nl/common/pojo/SelectListItem.java
new file mode 100644
index 0000000..055864a
--- /dev/null
+++ b/nl-common/src/main/java/org/nl/common/pojo/SelectListItem.java
@@ -0,0 +1,17 @@
+package org.nl.common.pojo;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 下拉框字段
+ * @Author: lyd
+ * @Date: 2025/11/11
+ */
+@Data
+@Builder
+public class SelectListItem {
+ public String label;
+
+ public String value;
+}
diff --git a/nl-common/src/main/java/org/nl/common/util/CommonTimeFormatUtil.java b/nl-common/src/main/java/org/nl/common/util/CommonTimeFormatUtil.java
index 3ad58db..d47fb79 100644
--- a/nl-common/src/main/java/org/nl/common/util/CommonTimeFormatUtil.java
+++ b/nl-common/src/main/java/org/nl/common/util/CommonTimeFormatUtil.java
@@ -17,6 +17,9 @@ import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
import java.util.Date;
/**
@@ -180,4 +183,17 @@ public class CommonTimeFormatUtil {
}
return result;
}
+
+ public static long calculateDaysBetween(Date startTime, Date endTime) {
+ // 将 Date 转换为 LocalDate
+ LocalDate startDate = startTime.toInstant()
+ .atZone(ZoneId.systemDefault())
+ .toLocalDate();
+ LocalDate endDate = endTime.toInstant()
+ .atZone(ZoneId.systemDefault())
+ .toLocalDate();
+
+ // 计算天数差
+ return ChronoUnit.DAYS.between(startDate, endDate);
+ }
}
diff --git a/nl-plugin-api/nl-plugin-pmm-api/pom.xml b/nl-plugin-api/nl-plugin-pmm-api/pom.xml
new file mode 100644
index 0000000..ceb5ff5
--- /dev/null
+++ b/nl-plugin-api/nl-plugin-pmm-api/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ org.nl
+ nl-plugin-api
+ 3.0.0
+
+
+ nl-plugin-pmm-api
+ jar
+ 项目管理插件api接口
+
+
+
+
+ org.nl
+ nl-common
+
+
+
diff --git a/nl-plugin-api/nl-plugin-pmm-api/src/main/java/org/nl/Main.java b/nl-plugin-api/nl-plugin-pmm-api/src/main/java/org/nl/Main.java
new file mode 100644
index 0000000..f5a7db3
--- /dev/null
+++ b/nl-plugin-api/nl-plugin-pmm-api/src/main/java/org/nl/Main.java
@@ -0,0 +1,11 @@
+package org.nl;
+
+/**
+ * @Author: lyd
+ * @Date: ${DATE}
+ */
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+}
diff --git a/nl-plugin-api/nl-plugin-sys-api/src/main/java/org/nl/sys/api/SysGroupApi.java b/nl-plugin-api/nl-plugin-sys-api/src/main/java/org/nl/sys/api/SysGroupApi.java
index 4529b50..533f9b8 100644
--- a/nl-plugin-api/nl-plugin-sys-api/src/main/java/org/nl/sys/api/SysGroupApi.java
+++ b/nl-plugin-api/nl-plugin-sys-api/src/main/java/org/nl/sys/api/SysGroupApi.java
@@ -14,6 +14,7 @@ package org.nl.sys.api;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.nl.sys.core.GroupUserVo;
import java.util.List;
@@ -48,4 +49,12 @@ public interface SysGroupApi {
* @date 2025/1/12 02:36
*/
Page groupSelector(String searchKey, int current, int size);
+
+
+ /**
+ * 获取用户组下的用户
+ * @param groupId
+ * @return List
+ */
+ List getAllUserInfoByGroupId(String groupId);
}
diff --git a/nl-plugin-api/nl-plugin-sys-api/src/main/java/org/nl/sys/core/GroupUserVo.java b/nl-plugin-api/nl-plugin-sys-api/src/main/java/org/nl/sys/core/GroupUserVo.java
new file mode 100644
index 0000000..30dc2b7
--- /dev/null
+++ b/nl-plugin-api/nl-plugin-sys-api/src/main/java/org/nl/sys/core/GroupUserVo.java
@@ -0,0 +1,16 @@
+package org.nl.sys.core;
+
+import lombok.Data;
+
+/**
+ * 组下用户
+ * @Author: lyd
+ * @Date: 2025/11/11
+ */
+@Data
+public class GroupUserVo {
+
+ private String userId;
+
+ private String name;
+}
diff --git a/nl-plugin-api/pom.xml b/nl-plugin-api/pom.xml
index d40b6c8..c1ffca6 100644
--- a/nl-plugin-api/pom.xml
+++ b/nl-plugin-api/pom.xml
@@ -35,5 +35,6 @@
nl-plugin-sys-api
+ nl-plugin-pmm-api
diff --git a/nl-plugin/nl-plugin-pmm/pom.xml b/nl-plugin/nl-plugin-pmm/pom.xml
new file mode 100644
index 0000000..f76b4dd
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/pom.xml
@@ -0,0 +1,36 @@
+
+
+ 4.0.0
+
+ org.nl
+ nl-plugin
+ 3.0.0
+
+
+ nl-plugin-pmm
+ jar
+ 项目管理插件
+
+
+
+
+ org.nl
+ nl-plugin-pmm-api
+
+
+
+
+ org.nl
+ nl-plugin-auth-api
+
+
+
+
+ org.nl
+ nl-plugin-sys-api
+
+
+
+
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/controller/ProjectController.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/controller/ProjectController.java
new file mode 100644
index 0000000..9916579
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/controller/ProjectController.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaIgnore;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.nl.common.pojo.SelectListItem;
+import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
+import org.nl.pmm.modular.project.entity.vo.ProjectVo;
+import org.nl.pmm.modular.project.param.*;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.nl.common.annotation.CommonLog;
+import org.nl.common.pojo.CommonResult;
+import org.nl.pmm.modular.project.entity.Project;
+import org.nl.pmm.modular.project.service.ProjectService;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 项目信息控制器
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+@Tag(name = "项目信息控制器")
+@RestController
+@Validated
+public class ProjectController {
+
+ @Resource
+ private ProjectService projectService;
+
+ /**
+ * 获取项目信息分页
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ @Operation(summary = "获取项目信息分页")
+ @SaCheckPermission("/pmm/project/page")
+ @GetMapping("/pmm/project/page")
+ public CommonResult> page(ProjectPageParam projectPageParam) {
+ return CommonResult.data(projectService.page(projectPageParam));
+ }
+
+ /**
+ * 添加项目信息
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ @Operation(summary = "添加项目信息")
+ @CommonLog("添加项目信息")
+ @SaCheckPermission("/pmm/project/add")
+ @PostMapping("/pmm/project/add")
+ public CommonResult add(@RequestBody @Valid ProjectAddParam projectAddParam) {
+ projectService.add(projectAddParam);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 编辑项目信息
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ @Operation(summary = "编辑项目信息")
+ @CommonLog("编辑项目信息")
+ @SaCheckPermission("/pmm/project/edit")
+ @PostMapping("/pmm/project/edit")
+ public CommonResult edit(@RequestBody @Valid ProjectEditParam projectEditParam) {
+ projectService.edit(projectEditParam);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 删除项目信息
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ @Operation(summary = "删除项目信息")
+ @CommonLog("删除项目信息")
+ @SaCheckPermission("/pmm/project/delete")
+ @PostMapping("/pmm/project/delete")
+ public CommonResult delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+ List projectIdParamList) {
+ projectService.delete(projectIdParamList);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 获取项目信息详情
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ @Operation(summary = "获取项目信息详情")
+ @SaCheckPermission("/pmm/project/detail")
+ @GetMapping("/pmm/project/detail")
+ public CommonResult detail(@Valid ProjectIdParam projectIdParam) {
+ return CommonResult.data(projectService.detail(projectIdParam));
+ }
+
+ /**
+ * 获取项目需求信息详情
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ @Operation(summary = "获取项目需求信息详情")
+// @SaCheckPermission("/pmm/project/request-detail")
+ @SaIgnore
+ @GetMapping("/pmm/project/requestDetail")
+ public CommonResult> requestDetail(@Valid ProjectIdParam projectIdParam) {
+ return CommonResult.data(projectService.requestDetail(projectIdParam));
+ }
+
+ /**
+ * 获取xx组用户
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ @Operation(summary = "获取xx组用户")
+// @SaCheckPermission("/pmm/project/detail")
+ @GetMapping("/pmm/project/dev-all-users")
+ public CommonResult> devUsers(GroupUserQueryParam param) {
+ return CommonResult.data(projectService.devUsers(param.getGroupId()));
+ }
+
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/Project.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/Project.java
new file mode 100644
index 0000000..5b75020
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/Project.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import org.nl.common.pojo.CommonEntity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目信息实体
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+@Getter
+@Setter
+@TableName("pmm_project")
+public class Project extends CommonEntity {
+
+ /** 项目id */
+ @TableId
+ @Schema(description = "项目id")
+ private String projectId;
+
+ /** 项目编码 */
+ @Schema(description = "项目编码")
+ private String projectCode;
+
+ /** 项目名称 */
+ @Schema(description = "项目名称")
+ private String projectName;
+
+ /** 创建时间 */
+ @Schema(description = "创建时间")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createTime;
+
+ /** 创建用户 */
+ @Schema(description = "创建用户")
+ @TableField(fill = FieldFill.INSERT)
+ private String createUser;
+
+ /** 修改时间 */
+ @Schema(description = "修改时间")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateTime;
+
+ /** 修改用户 */
+ @Schema(description = "修改用户")
+ @TableField(fill = FieldFill.UPDATE)
+ private String updateUser;
+
+ /** 删除标志 */
+ @Schema(description = "删除标志")
+ @TableLogic
+ @TableField(fill = FieldFill.INSERT)
+ private String deleteFlag;
+
+ /** 项目经理 */
+ @Schema(description = "项目经理")
+ private String projectManage;
+
+ /** 产品需求 */
+ @Schema(description = "产品需求")
+ private String requireManage;
+
+ /** 开发组 */
+ @Schema(description = "开发组")
+ private String devGroup;
+
+ /** 开始时间 */
+ @Schema(description = "开始时间")
+ private String startTime;
+
+ /** 结束时间 */
+ @Schema(description = "结束时间")
+ private String endTime;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/vo/ProjectRequestVo.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/vo/ProjectRequestVo.java
new file mode 100644
index 0000000..ff7b410
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/vo/ProjectRequestVo.java
@@ -0,0 +1,38 @@
+package org.nl.pmm.modular.project.entity.vo;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @Author: lyd
+ * @Date: 2025/11/17
+ */
+@Data
+@Builder
+public class ProjectRequestVo {
+ private String stageId;
+ private String stageName;
+ private String stageSeq;
+ private String projectId;
+ private List descriptions;
+ @Data
+ public static class RequestDescription {
+ private String detailId;
+ private String stageId;
+ private String parentDetailId;
+ private String seq;
+ private String modelName;
+ private String modelDetail;
+ private String modelDescription;
+ private String priority;
+ private String remark;
+ private Date startTime;
+ private Date endTime;
+ private long days;
+ private List children;
+ }
+}
+
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/vo/ProjectVo.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/vo/ProjectVo.java
new file mode 100644
index 0000000..5c99eb7
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/entity/vo/ProjectVo.java
@@ -0,0 +1,15 @@
+package org.nl.pmm.modular.project.entity.vo;
+
+import lombok.Data;
+import org.nl.pmm.modular.project.entity.Project;
+
+import java.util.List;
+
+/**
+ * @Author: lyd
+ * @Date: 2025/11/13
+ */
+@Data
+public class ProjectVo extends Project {
+ private List devGroups;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/enums/ProjectEnum.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/enums/ProjectEnum.java
new file mode 100644
index 0000000..7dc57b3
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/enums/ProjectEnum.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.enums;
+
+import lombok.Getter;
+
+/**
+ * 项目信息枚举
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+@Getter
+public enum ProjectEnum {
+
+ /** 测试 */
+ TEST("TEST");
+
+ private final String value;
+
+ ProjectEnum(String value) {
+ this.value = value;
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/mapper/ProjectMapper.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/mapper/ProjectMapper.java
new file mode 100644
index 0000000..81422af
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/mapper/ProjectMapper.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.nl.pmm.modular.project.entity.Project;
+
+/**
+ * 项目信息Mapper接口
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+public interface ProjectMapper extends BaseMapper {
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/mapper/mapping/ProjectMapper.xml b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/mapper/mapping/ProjectMapper.xml
new file mode 100644
index 0000000..b04e839
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/mapper/mapping/ProjectMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/GroupUserQueryParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/GroupUserQueryParam.java
new file mode 100644
index 0000000..c943606
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/GroupUserQueryParam.java
@@ -0,0 +1,13 @@
+package org.nl.pmm.modular.project.param;
+
+import lombok.Data;
+
+/**
+ * @Author: lyd
+ * @Date: 2025/11/12
+ */
+@Data
+public class GroupUserQueryParam {
+
+ private String groupId;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectAddParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectAddParam.java
new file mode 100644
index 0000000..adf8251
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectAddParam.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 项目信息添加参数
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+@Getter
+@Setter
+public class ProjectAddParam {
+
+ /** 项目编码 */
+ @Schema(description = "项目编码")
+ private String projectCode;
+
+ /** 项目名称 */
+ @Schema(description = "项目名称")
+ private String projectName;
+
+ /** 项目经理 */
+ @Schema(description = "项目经理")
+ private String projectManage;
+
+ /** 产品需求 */
+ @Schema(description = "产品需求")
+ private String requireManage;
+
+ /** 开发组 */
+ @Schema(description = "开发组")
+ private String devGroup;
+ private List devGroups;
+
+ /** 开始时间 */
+ @Schema(description = "开始时间")
+ private String startTime;
+
+ /** 结束时间 */
+ @Schema(description = "结束时间")
+ private String endTime;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectEditParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectEditParam.java
new file mode 100644
index 0000000..f0320b7
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectEditParam.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目信息编辑参数
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+@Getter
+@Setter
+public class ProjectEditParam {
+
+ /** 项目id */
+ @Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "projectId不能为空")
+ private String projectId;
+
+ /** 项目编码 */
+ @Schema(description = "项目编码")
+ private String projectCode;
+
+ /** 项目名称 */
+ @Schema(description = "项目名称")
+ private String projectName;
+
+ /** 项目经理 */
+ @Schema(description = "项目经理")
+ private String projectManage;
+
+ /** 产品需求 */
+ @Schema(description = "产品需求")
+ private String requireManage;
+
+ /** 开发组 */
+ @Schema(description = "开发组")
+ private String devGroup;
+
+ /** 开始时间 */
+ @Schema(description = "开始时间")
+ private String startTime;
+
+ /** 结束时间 */
+ @Schema(description = "结束时间")
+ private String endTime;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectIdParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectIdParam.java
new file mode 100644
index 0000000..9342de5
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectIdParam.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 项目信息Id参数
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+@Getter
+@Setter
+public class ProjectIdParam {
+
+ /** 项目id */
+ @Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "projectId不能为空")
+ private String projectId;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectPageParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectPageParam.java
new file mode 100644
index 0000000..001a9da
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/param/ProjectPageParam.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目信息查询参数
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+@Getter
+@Setter
+public class ProjectPageParam {
+
+ /** 当前页 */
+ @Schema(description = "当前页码")
+ private Integer current;
+
+ /** 每页条数 */
+ @Schema(description = "每页条数")
+ private Integer size;
+
+ /** 排序字段 */
+ @Schema(description = "排序字段,字段驼峰名称,如:userName")
+ private String sortField;
+
+ /** 排序方式 */
+ @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND")
+ private String sortOrder;
+
+ /** 关键词 */
+ @Schema(description = "关键词")
+ private String searchKey;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/service/ProjectService.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/service/ProjectService.java
new file mode 100644
index 0000000..7eec5c4
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/service/ProjectService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.nl.common.pojo.SelectListItem;
+import org.nl.pmm.modular.project.entity.Project;
+import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
+import org.nl.pmm.modular.project.entity.vo.ProjectVo;
+import org.nl.pmm.modular.project.param.ProjectAddParam;
+import org.nl.pmm.modular.project.param.ProjectEditParam;
+import org.nl.pmm.modular.project.param.ProjectIdParam;
+import org.nl.pmm.modular.project.param.ProjectPageParam;
+
+import java.util.List;
+
+/**
+ * 项目信息Service接口
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+public interface ProjectService extends IService {
+
+ /**
+ * 获取项目信息分页
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ Page page(ProjectPageParam projectPageParam);
+
+ /**
+ * 添加项目信息
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ void add(ProjectAddParam projectAddParam);
+
+ /**
+ * 编辑项目信息
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ void edit(ProjectEditParam projectEditParam);
+
+ /**
+ * 删除项目信息
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ void delete(List projectIdParamList);
+
+ /**
+ * 获取项目信息详情
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ */
+ Project detail(ProjectIdParam projectIdParam);
+
+ /**
+ * 获取项目信息详情
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+ Project queryEntity(String id);
+
+ /**
+ * 获取所有的开发组成员
+ * @return
+ */
+ List devUsers(String groupId);
+ /**
+ * 获取项目需求信息详情
+ * @param projectIdParam
+ * @return
+ */
+ List requestDetail(ProjectIdParam projectIdParam);
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/service/impl/ProjectServiceImpl.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/service/impl/ProjectServiceImpl.java
new file mode 100644
index 0000000..2b0256d
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/project/service/impl/ProjectServiceImpl.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.project.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.annotation.Resource;
+import org.nl.common.pojo.SelectListItem;
+import org.nl.common.util.CommonTimeFormatUtil;
+import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
+import org.nl.pmm.modular.project.entity.vo.ProjectVo;
+import org.nl.pmm.modular.projectstage.entity.ProjectStage;
+import org.nl.pmm.modular.projectstage.service.ProjectStageService;
+import org.nl.pmm.modular.stagedetail.entity.StageDetail;
+import org.nl.pmm.modular.stagedetail.service.StageDetailService;
+import org.nl.sys.api.SysGroupApi;
+import org.nl.sys.api.SysRelationApi;
+import org.nl.sys.core.GroupUserVo;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.nl.common.enums.CommonSortOrderEnum;
+import org.nl.common.exception.CommonException;
+import org.nl.common.page.CommonPageRequest;
+import org.nl.pmm.modular.project.entity.Project;
+import org.nl.pmm.modular.project.mapper.ProjectMapper;
+import org.nl.pmm.modular.project.param.ProjectAddParam;
+import org.nl.pmm.modular.project.param.ProjectEditParam;
+import org.nl.pmm.modular.project.param.ProjectIdParam;
+import org.nl.pmm.modular.project.param.ProjectPageParam;
+import org.nl.pmm.modular.project.service.ProjectService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 项目信息Service接口实现类
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+@Service
+public class ProjectServiceImpl extends ServiceImpl implements ProjectService {
+
+ @Resource
+ private SysGroupApi sysGroupApi;
+ @Resource
+ private ProjectStageService projectStageService;
+ @Resource
+ private StageDetailService stageDetailService;
+
+ @Override
+ public Page page(ProjectPageParam projectPageParam) {
+ QueryWrapper queryWrapper = new QueryWrapper().checkSqlInjection();
+ if(ObjectUtil.isAllNotEmpty(projectPageParam.getSortField(), projectPageParam.getSortOrder())) {
+ CommonSortOrderEnum.validate(projectPageParam.getSortOrder());
+ queryWrapper.orderBy(true, projectPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
+ StrUtil.toUnderlineCase(projectPageParam.getSortField()));
+ } else {
+ queryWrapper.lambda().orderByAsc(Project::getProjectId);
+ }
+ Page page = this.page(CommonPageRequest.defaultPage(), queryWrapper);
+ List projectList = new ArrayList<>(page.getRecords().size());
+ Page voPage = new Page<>(page.getCurrent(), page.getSize());
+ page.getRecords().forEach(p -> {
+ ProjectVo project = BeanUtil.toBean(p, ProjectVo.class);
+ project.setDevGroups(List.of(p.getDevGroup().split(",")));
+ projectList.add(project);
+ });
+ voPage.setRecords(projectList);
+ voPage.setTotal(page.getTotal());
+ return voPage;
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void add(ProjectAddParam projectAddParam) {
+ Project project = BeanUtil.toBean(projectAddParam, Project.class);
+ String collect = projectAddParam.getDevGroups().stream().collect(Collectors.joining(","));
+ project.setDevGroup(collect);
+ this.save(project);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void edit(ProjectEditParam projectEditParam) {
+ Project project = this.queryEntity(projectEditParam.getProjectId());
+ BeanUtil.copyProperties(projectEditParam, project);
+ this.updateById(project);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void delete(List projectIdParamList) {
+ // 执行删除
+ this.removeByIds(CollStreamUtil.toList(projectIdParamList, ProjectIdParam::getProjectId));
+ }
+
+ @Override
+ public Project detail(ProjectIdParam projectIdParam) {
+ return this.queryEntity(projectIdParam.getProjectId());
+ }
+
+ @Override
+ public Project queryEntity(String id) {
+ Project project = this.getById(id);
+ if(ObjectUtil.isEmpty(project)) {
+ throw new CommonException("项目信息不存在,id值为:{}", id);
+ }
+ return project;
+ }
+
+ @Override
+ public List devUsers(String groupId) {
+ List allUserInfoByGroupId = sysGroupApi.getAllUserInfoByGroupId(groupId);
+ if (allUserInfoByGroupId.size() == 0) {
+ return null;
+ }
+ List selectList = new ArrayList<>();
+ allUserInfoByGroupId.forEach(r ->
+ selectList.add(SelectListItem.builder().label(r.getName()).value(r.getName()).build())
+ );
+ return selectList;
+ }
+
+ @Override
+ public List requestDetail(ProjectIdParam projectIdParam) {
+ List projectStages = projectStageService.getByProjectId(projectIdParam.getProjectId());
+ // 构建阶段
+ List result = new ArrayList<>();
+ projectStages.stream().forEach(s -> result.add(ProjectRequestVo.builder()
+ .projectId(s.getProjectId())
+ .stageId(s.getStageId())
+ .stageName(s.getStageName())
+ .stageSeq(s.getStageSeq())
+ .descriptions(new ArrayList<>()).build()));
+ for (ProjectRequestVo res : result) {
+ // 获取阶段的所有明细
+ List details = stageDetailService.getByStageId(res.getStageId());
+ List requestDescriptions = new ArrayList<>();
+ for (StageDetail detail : details) {
+ ProjectRequestVo.RequestDescription req = new ProjectRequestVo.RequestDescription();
+ BeanUtil.copyProperties(detail, req);
+ req.setDays(CommonTimeFormatUtil.calculateDaysBetween(req.getStartTime(), req.getEndTime()));
+ if (ObjectUtil.isEmpty(req.getParentDetailId())) {
+ requestDescriptions.add(req);
+ }
+ for (StageDetail stageDetail : details) {
+ ProjectRequestVo.RequestDescription req2 = new ProjectRequestVo.RequestDescription();
+ BeanUtil.copyProperties(stageDetail, req2);
+ if (req.getDetailId().equals(req2.getParentDetailId())) {
+ if (ObjectUtil.isEmpty(req.getChildren())) {
+ req.setChildren(new ArrayList<>());
+ }
+ req.getChildren().add(req2);
+ }
+ }
+ }
+ res.setDescriptions(requestDescriptions);
+ }
+ return result;
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/controller/ProjectFileController.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/controller/ProjectFileController.java
new file mode 100644
index 0000000..356d3ee
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/controller/ProjectFileController.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.nl.common.annotation.CommonLog;
+import org.nl.common.pojo.CommonResult;
+import org.nl.pmm.modular.projectfile.entity.ProjectFile;
+import org.nl.pmm.modular.projectfile.param.ProjectFileAddParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFileEditParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFileIdParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFilePageParam;
+import org.nl.pmm.modular.projectfile.service.ProjectFileService;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 项目附件表控制器
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+@Tag(name = "项目附件表控制器")
+@RestController
+@Validated
+public class ProjectFileController {
+
+ @Resource
+ private ProjectFileService projectFileService;
+
+ /**
+ * 获取项目附件表分页
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ @Operation(summary = "获取项目附件表分页")
+ @SaCheckPermission("/pmm/projectfile/page")
+ @GetMapping("/pmm/projectfile/page")
+ public CommonResult> page(ProjectFilePageParam projectFilePageParam) {
+ return CommonResult.data(projectFileService.page(projectFilePageParam));
+ }
+
+ /**
+ * 添加项目附件表
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ @Operation(summary = "添加项目附件表")
+ @CommonLog("添加项目附件表")
+ @SaCheckPermission("/pmm/projectfile/add")
+ @PostMapping("/pmm/projectfile/add")
+ public CommonResult add(@RequestBody @Valid ProjectFileAddParam projectFileAddParam) {
+ projectFileService.add(projectFileAddParam);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 编辑项目附件表
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ @Operation(summary = "编辑项目附件表")
+ @CommonLog("编辑项目附件表")
+ @SaCheckPermission("/pmm/projectfile/edit")
+ @PostMapping("/pmm/projectfile/edit")
+ public CommonResult edit(@RequestBody @Valid ProjectFileEditParam projectFileEditParam) {
+ projectFileService.edit(projectFileEditParam);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 删除项目附件表
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ @Operation(summary = "删除项目附件表")
+ @CommonLog("删除项目附件表")
+ @SaCheckPermission("/pmm/projectfile/delete")
+ @PostMapping("/pmm/projectfile/delete")
+ public CommonResult delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+ List projectFileIdParamList) {
+ projectFileService.delete(projectFileIdParamList);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 获取项目附件表详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ @Operation(summary = "获取项目附件表详情")
+ @SaCheckPermission("/pmm/projectfile/detail")
+ @GetMapping("/pmm/projectfile/detail")
+ public CommonResult detail(@Valid ProjectFileIdParam projectFileIdParam) {
+ return CommonResult.data(projectFileService.detail(projectFileIdParam));
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/entity/ProjectFile.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/entity/ProjectFile.java
new file mode 100644
index 0000000..4ee0d04
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/entity/ProjectFile.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目附件表实体
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+@Getter
+@Setter
+@TableName("pmm_project_file")
+public class ProjectFile {
+
+ /** 附件id */
+ @TableId
+ @Schema(description = "附件id")
+ private String fjId;
+
+ /** 项目id */
+ @Schema(description = "项目id")
+ private String projectId;
+
+ /** 文件id */
+ @Schema(description = "文件id")
+ private String fileId;
+
+ /** 文件地址 */
+ @Schema(description = "文件地址")
+ private String fileAddress;
+
+ /** 创建时间 */
+ @Schema(description = "创建时间")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createTime;
+
+ /** 创建用户 */
+ @Schema(description = "创建用户")
+ @TableField(fill = FieldFill.INSERT)
+ private String createUser;
+
+ /** 修改时间 */
+ @Schema(description = "修改时间")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateTime;
+
+ /** 修改用户 */
+ @Schema(description = "修改用户")
+ @TableField(fill = FieldFill.UPDATE)
+ private String updateUser;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/enums/ProjectFileEnum.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/enums/ProjectFileEnum.java
new file mode 100644
index 0000000..9d8bf5c
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/enums/ProjectFileEnum.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.enums;
+
+import lombok.Getter;
+
+/**
+ * 项目附件表枚举
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+@Getter
+public enum ProjectFileEnum {
+
+ /** 测试 */
+ TEST("TEST");
+
+ private final String value;
+
+ ProjectFileEnum(String value) {
+ this.value = value;
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/mapper/ProjectFileMapper.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/mapper/ProjectFileMapper.java
new file mode 100644
index 0000000..26b6e03
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/mapper/ProjectFileMapper.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.nl.pmm.modular.projectfile.entity.ProjectFile;
+
+/**
+ * 项目附件表Mapper接口
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+public interface ProjectFileMapper extends BaseMapper {
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/mapper/mapping/ProjectFileMapper.xml b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/mapper/mapping/ProjectFileMapper.xml
new file mode 100644
index 0000000..464c9e6
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/mapper/mapping/ProjectFileMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileAddParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileAddParam.java
new file mode 100644
index 0000000..eafff12
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileAddParam.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目附件表添加参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+@Getter
+@Setter
+public class ProjectFileAddParam {
+
+ /** 项目id */
+ @Schema(description = "项目id")
+ private String projectId;
+
+ /** 文件id */
+ @Schema(description = "文件id")
+ private String fileId;
+
+ /** 文件地址 */
+ @Schema(description = "文件地址")
+ private String fileAddress;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileEditParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileEditParam.java
new file mode 100644
index 0000000..999b4a3
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileEditParam.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目附件表编辑参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+@Getter
+@Setter
+public class ProjectFileEditParam {
+
+ /** 附件id */
+ @Schema(description = "附件id", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "fjId不能为空")
+ private String fjId;
+
+ /** 项目id */
+ @Schema(description = "项目id")
+ private String projectId;
+
+ /** 文件id */
+ @Schema(description = "文件id")
+ private String fileId;
+
+ /** 文件地址 */
+ @Schema(description = "文件地址")
+ private String fileAddress;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileIdParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileIdParam.java
new file mode 100644
index 0000000..631e6f6
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFileIdParam.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 项目附件表Id参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+@Getter
+@Setter
+public class ProjectFileIdParam {
+
+ /** 附件id */
+ @Schema(description = "附件id", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "fjId不能为空")
+ private String fjId;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFilePageParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFilePageParam.java
new file mode 100644
index 0000000..bd62412
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/param/ProjectFilePageParam.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目附件表查询参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+@Getter
+@Setter
+public class ProjectFilePageParam {
+
+ /** 当前页 */
+ @Schema(description = "当前页码")
+ private Integer current;
+
+ /** 每页条数 */
+ @Schema(description = "每页条数")
+ private Integer size;
+
+ /** 排序字段 */
+ @Schema(description = "排序字段,字段驼峰名称,如:userName")
+ private String sortField;
+
+ /** 排序方式 */
+ @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND")
+ private String sortOrder;
+
+ /** 关键词 */
+ @Schema(description = "关键词")
+ private String searchKey;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/service/ProjectFileService.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/service/ProjectFileService.java
new file mode 100644
index 0000000..c71488d
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/service/ProjectFileService.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.nl.pmm.modular.projectfile.entity.ProjectFile;
+import org.nl.pmm.modular.projectfile.param.ProjectFileAddParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFileEditParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFileIdParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFilePageParam;
+
+import java.util.List;
+
+/**
+ * 项目附件表Service接口
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+public interface ProjectFileService extends IService {
+
+ /**
+ * 获取项目附件表分页
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ Page page(ProjectFilePageParam projectFilePageParam);
+
+ /**
+ * 添加项目附件表
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ void add(ProjectFileAddParam projectFileAddParam);
+
+ /**
+ * 编辑项目附件表
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ void edit(ProjectFileEditParam projectFileEditParam);
+
+ /**
+ * 删除项目附件表
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ void delete(List projectFileIdParamList);
+
+ /**
+ * 获取项目附件表详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ */
+ ProjectFile detail(ProjectFileIdParam projectFileIdParam);
+
+ /**
+ * 获取项目附件表详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+ ProjectFile queryEntity(String id);
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/service/impl/ProjectFileServiceImpl.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/service/impl/ProjectFileServiceImpl.java
new file mode 100644
index 0000000..1ef2271
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectfile/service/impl/ProjectFileServiceImpl.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectfile.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.nl.common.enums.CommonSortOrderEnum;
+import org.nl.common.exception.CommonException;
+import org.nl.common.page.CommonPageRequest;
+import org.nl.pmm.modular.projectfile.entity.ProjectFile;
+import org.nl.pmm.modular.projectfile.mapper.ProjectFileMapper;
+import org.nl.pmm.modular.projectfile.param.ProjectFileAddParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFileEditParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFileIdParam;
+import org.nl.pmm.modular.projectfile.param.ProjectFilePageParam;
+import org.nl.pmm.modular.projectfile.service.ProjectFileService;
+
+import java.util.List;
+
+/**
+ * 项目附件表Service接口实现类
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+@Service
+public class ProjectFileServiceImpl extends ServiceImpl implements ProjectFileService {
+
+ @Override
+ public Page page(ProjectFilePageParam projectFilePageParam) {
+ QueryWrapper queryWrapper = new QueryWrapper().checkSqlInjection();
+ if(ObjectUtil.isAllNotEmpty(projectFilePageParam.getSortField(), projectFilePageParam.getSortOrder())) {
+ CommonSortOrderEnum.validate(projectFilePageParam.getSortOrder());
+ queryWrapper.orderBy(true, projectFilePageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
+ StrUtil.toUnderlineCase(projectFilePageParam.getSortField()));
+ } else {
+ queryWrapper.lambda().orderByAsc(ProjectFile::getFjId);
+ }
+ return this.page(CommonPageRequest.defaultPage(), queryWrapper);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void add(ProjectFileAddParam projectFileAddParam) {
+ ProjectFile projectFile = BeanUtil.toBean(projectFileAddParam, ProjectFile.class);
+ this.save(projectFile);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void edit(ProjectFileEditParam projectFileEditParam) {
+ ProjectFile projectFile = this.queryEntity(projectFileEditParam.getFjId());
+ BeanUtil.copyProperties(projectFileEditParam, projectFile);
+ this.updateById(projectFile);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void delete(List projectFileIdParamList) {
+ // 执行删除
+ this.removeByIds(CollStreamUtil.toList(projectFileIdParamList, ProjectFileIdParam::getFjId));
+ }
+
+ @Override
+ public ProjectFile detail(ProjectFileIdParam projectFileIdParam) {
+ return this.queryEntity(projectFileIdParam.getFjId());
+ }
+
+ @Override
+ public ProjectFile queryEntity(String id) {
+ ProjectFile projectFile = this.getById(id);
+ if(ObjectUtil.isEmpty(projectFile)) {
+ throw new CommonException("项目附件表不存在,id值为:{}", id);
+ }
+ return projectFile;
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/controller/ProjectStageController.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/controller/ProjectStageController.java
new file mode 100644
index 0000000..84f9e9e
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/controller/ProjectStageController.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.nl.common.annotation.CommonLog;
+import org.nl.common.pojo.CommonResult;
+import org.nl.pmm.modular.projectstage.entity.ProjectStage;
+import org.nl.pmm.modular.projectstage.param.ProjectStageAddParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStageEditParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStageIdParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStagePageParam;
+import org.nl.pmm.modular.projectstage.service.ProjectStageService;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 项目阶段控制器
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+@Tag(name = "项目阶段控制器")
+@RestController
+@Validated
+public class ProjectStageController {
+
+ @Resource
+ private ProjectStageService projectStageService;
+
+ /**
+ * 获取项目阶段分页
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ @Operation(summary = "获取项目阶段分页")
+ @SaCheckPermission("/pmm/projectstage/page")
+ @GetMapping("/pmm/projectstage/page")
+ public CommonResult> page(ProjectStagePageParam projectStagePageParam) {
+ return CommonResult.data(projectStageService.page(projectStagePageParam));
+ }
+
+ /**
+ * 添加项目阶段
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ @Operation(summary = "添加项目阶段")
+ @CommonLog("添加项目阶段")
+ @SaCheckPermission("/pmm/projectstage/add")
+ @PostMapping("/pmm/projectstage/add")
+ public CommonResult add(@RequestBody @Valid ProjectStageAddParam projectStageAddParam) {
+ projectStageService.add(projectStageAddParam);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 编辑项目阶段
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ @Operation(summary = "编辑项目阶段")
+ @CommonLog("编辑项目阶段")
+ @SaCheckPermission("/pmm/projectstage/edit")
+ @PostMapping("/pmm/projectstage/edit")
+ public CommonResult edit(@RequestBody @Valid ProjectStageEditParam projectStageEditParam) {
+ projectStageService.edit(projectStageEditParam);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 删除项目阶段
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ @Operation(summary = "删除项目阶段")
+ @CommonLog("删除项目阶段")
+ @SaCheckPermission("/pmm/projectstage/delete")
+ @PostMapping("/pmm/projectstage/delete")
+ public CommonResult delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+ List projectStageIdParamList) {
+ projectStageService.delete(projectStageIdParamList);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 获取项目阶段详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ @Operation(summary = "获取项目阶段详情")
+ @SaCheckPermission("/pmm/projectstage/detail")
+ @GetMapping("/pmm/projectstage/detail")
+ public CommonResult detail(@Valid ProjectStageIdParam projectStageIdParam) {
+ return CommonResult.data(projectStageService.detail(projectStageIdParam));
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/entity/ProjectStage.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/entity/ProjectStage.java
new file mode 100644
index 0000000..7558a8f
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/entity/ProjectStage.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目阶段实体
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+@Getter
+@Setter
+@TableName("pmm_project_stage")
+public class ProjectStage {
+
+ /** 阶段id */
+ @TableId
+ @Schema(description = "阶段id")
+ private String stageId;
+
+ /** 阶段名称 */
+ @Schema(description = "阶段名称")
+ private String stageName;
+
+ /** 阶段序号 */
+ @Schema(description = "阶段序号")
+ private String stageSeq;
+
+ /** 项目id */
+ @Schema(description = "项目id")
+ private String projectId;
+
+ /** 创建时间 */
+ @Schema(description = "创建时间")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createTime;
+
+ /** 创建用户 */
+ @Schema(description = "创建用户")
+ @TableField(fill = FieldFill.INSERT)
+ private String createUser;
+
+ /** 修改时间 */
+ @Schema(description = "修改时间")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateTime;
+
+ /** 修改用户 */
+ @Schema(description = "修改用户")
+ @TableField(fill = FieldFill.UPDATE)
+ private String updateUser;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/enums/ProjectStageEnum.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/enums/ProjectStageEnum.java
new file mode 100644
index 0000000..c6dd98f
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/enums/ProjectStageEnum.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.enums;
+
+import lombok.Getter;
+
+/**
+ * 项目阶段枚举
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+@Getter
+public enum ProjectStageEnum {
+
+ /** 测试 */
+ TEST("TEST");
+
+ private final String value;
+
+ ProjectStageEnum(String value) {
+ this.value = value;
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/mapper/ProjectStageMapper.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/mapper/ProjectStageMapper.java
new file mode 100644
index 0000000..2b9043b
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/mapper/ProjectStageMapper.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.nl.pmm.modular.projectstage.entity.ProjectStage;
+
+/**
+ * 项目阶段Mapper接口
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+public interface ProjectStageMapper extends BaseMapper {
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/mapper/mapping/ProjectStageMapper.xml b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/mapper/mapping/ProjectStageMapper.xml
new file mode 100644
index 0000000..672638d
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/mapper/mapping/ProjectStageMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageAddParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageAddParam.java
new file mode 100644
index 0000000..8090dad
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageAddParam.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目阶段添加参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+@Getter
+@Setter
+public class ProjectStageAddParam {
+
+ /** 阶段名称 */
+ @Schema(description = "阶段名称")
+ private String stageName;
+
+ /** 阶段序号 */
+ @Schema(description = "阶段序号")
+ private String stageSeq;
+
+ /** 项目id */
+ @Schema(description = "项目id")
+ private String projectId;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageEditParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageEditParam.java
new file mode 100644
index 0000000..af5bd32
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageEditParam.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目阶段编辑参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+@Getter
+@Setter
+public class ProjectStageEditParam {
+
+ /** 阶段id */
+ @Schema(description = "阶段id", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "stageId不能为空")
+ private String stageId;
+
+ /** 阶段名称 */
+ @Schema(description = "阶段名称")
+ private String stageName;
+
+ /** 阶段序号 */
+ @Schema(description = "阶段序号")
+ private String stageSeq;
+
+ /** 项目id */
+ @Schema(description = "项目id")
+ private String projectId;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageIdParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageIdParam.java
new file mode 100644
index 0000000..3be5366
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStageIdParam.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 项目阶段Id参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+@Getter
+@Setter
+public class ProjectStageIdParam {
+
+ /** 阶段id */
+ @Schema(description = "阶段id", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "stageId不能为空")
+ private String stageId;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStagePageParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStagePageParam.java
new file mode 100644
index 0000000..386ebc6
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/param/ProjectStagePageParam.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目阶段查询参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+@Getter
+@Setter
+public class ProjectStagePageParam {
+
+ /** 当前页 */
+ @Schema(description = "当前页码")
+ private Integer current;
+
+ /** 每页条数 */
+ @Schema(description = "每页条数")
+ private Integer size;
+
+ /** 排序字段 */
+ @Schema(description = "排序字段,字段驼峰名称,如:userName")
+ private String sortField;
+
+ /** 排序方式 */
+ @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND")
+ private String sortOrder;
+
+ /** 关键词 */
+ @Schema(description = "关键词")
+ private String searchKey;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/service/ProjectStageService.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/service/ProjectStageService.java
new file mode 100644
index 0000000..fe6dca3
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/service/ProjectStageService.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.nl.pmm.modular.projectstage.entity.ProjectStage;
+import org.nl.pmm.modular.projectstage.param.ProjectStageAddParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStageEditParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStageIdParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStagePageParam;
+
+import java.util.List;
+
+/**
+ * 项目阶段Service接口
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+public interface ProjectStageService extends IService {
+
+ /**
+ * 获取项目阶段分页
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ Page page(ProjectStagePageParam projectStagePageParam);
+
+ /**
+ * 添加项目阶段
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ void add(ProjectStageAddParam projectStageAddParam);
+
+ /**
+ * 编辑项目阶段
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ void edit(ProjectStageEditParam projectStageEditParam);
+
+ /**
+ * 删除项目阶段
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ void delete(List projectStageIdParamList);
+
+ /**
+ * 获取项目阶段详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ */
+ ProjectStage detail(ProjectStageIdParam projectStageIdParam);
+
+ /**
+ * 获取项目阶段详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+ ProjectStage queryEntity(String id);
+
+ /**
+ * 根据项目id获取项目阶段数组
+ * @param projectId 项目id
+ * @return
+ */
+ List getByProjectId(String projectId);
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/service/impl/ProjectStageServiceImpl.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/service/impl/ProjectStageServiceImpl.java
new file mode 100644
index 0000000..a301e5d
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/projectstage/service/impl/ProjectStageServiceImpl.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.projectstage.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.nl.common.enums.CommonSortOrderEnum;
+import org.nl.common.exception.CommonException;
+import org.nl.common.page.CommonPageRequest;
+import org.nl.pmm.modular.projectstage.entity.ProjectStage;
+import org.nl.pmm.modular.projectstage.mapper.ProjectStageMapper;
+import org.nl.pmm.modular.projectstage.param.ProjectStageAddParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStageEditParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStageIdParam;
+import org.nl.pmm.modular.projectstage.param.ProjectStagePageParam;
+import org.nl.pmm.modular.projectstage.service.ProjectStageService;
+
+import java.util.List;
+
+/**
+ * 项目阶段Service接口实现类
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+@Service
+public class ProjectStageServiceImpl extends ServiceImpl implements ProjectStageService {
+
+ @Override
+ public Page page(ProjectStagePageParam projectStagePageParam) {
+ QueryWrapper queryWrapper = new QueryWrapper().checkSqlInjection();
+ if(ObjectUtil.isAllNotEmpty(projectStagePageParam.getSortField(), projectStagePageParam.getSortOrder())) {
+ CommonSortOrderEnum.validate(projectStagePageParam.getSortOrder());
+ queryWrapper.orderBy(true, projectStagePageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
+ StrUtil.toUnderlineCase(projectStagePageParam.getSortField()));
+ } else {
+ queryWrapper.lambda().orderByAsc(ProjectStage::getStageId);
+ }
+ return this.page(CommonPageRequest.defaultPage(), queryWrapper);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void add(ProjectStageAddParam projectStageAddParam) {
+ ProjectStage projectStage = BeanUtil.toBean(projectStageAddParam, ProjectStage.class);
+ this.save(projectStage);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void edit(ProjectStageEditParam projectStageEditParam) {
+ ProjectStage projectStage = this.queryEntity(projectStageEditParam.getStageId());
+ BeanUtil.copyProperties(projectStageEditParam, projectStage);
+ this.updateById(projectStage);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void delete(List projectStageIdParamList) {
+ // 执行删除
+ this.removeByIds(CollStreamUtil.toList(projectStageIdParamList, ProjectStageIdParam::getStageId));
+ }
+
+ @Override
+ public ProjectStage detail(ProjectStageIdParam projectStageIdParam) {
+ return this.queryEntity(projectStageIdParam.getStageId());
+ }
+
+ @Override
+ public ProjectStage queryEntity(String id) {
+ ProjectStage projectStage = this.getById(id);
+ if(ObjectUtil.isEmpty(projectStage)) {
+ throw new CommonException("项目阶段不存在,id值为:{}", id);
+ }
+ return projectStage;
+ }
+
+ @Override
+ public List getByProjectId(String projectId) {
+ LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper().lambda();
+ lambdaQueryWrapper.eq(ProjectStage::getProjectId, projectId)
+ .orderByAsc(ProjectStage::getStageSeq);
+ return list(lambdaQueryWrapper);
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/controller/StageDetailController.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/controller/StageDetailController.java
new file mode 100644
index 0000000..3c1e9f9
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/controller/StageDetailController.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.nl.common.annotation.CommonLog;
+import org.nl.common.pojo.CommonResult;
+import org.nl.pmm.modular.stagedetail.entity.StageDetail;
+import org.nl.pmm.modular.stagedetail.param.StageDetailAddParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailEditParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailIdParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailPageParam;
+import org.nl.pmm.modular.stagedetail.service.StageDetailService;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 项目阶段明细控制器
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+@Tag(name = "项目阶段明细控制器")
+@RestController
+@Validated
+public class StageDetailController {
+
+ @Resource
+ private StageDetailService stageDetailService;
+
+ /**
+ * 获取项目阶段明细分页
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ @Operation(summary = "获取项目阶段明细分页")
+ @SaCheckPermission("/pmm/stagedetail/page")
+ @GetMapping("/pmm/stagedetail/page")
+ public CommonResult> page(StageDetailPageParam stageDetailPageParam) {
+ return CommonResult.data(stageDetailService.page(stageDetailPageParam));
+ }
+
+ /**
+ * 添加项目阶段明细
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ @Operation(summary = "添加项目阶段明细")
+ @CommonLog("添加项目阶段明细")
+ @SaCheckPermission("/pmm/stagedetail/add")
+ @PostMapping("/pmm/stagedetail/add")
+ public CommonResult add(@RequestBody @Valid StageDetailAddParam stageDetailAddParam) {
+ stageDetailService.add(stageDetailAddParam);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 编辑项目阶段明细
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ @Operation(summary = "编辑项目阶段明细")
+ @CommonLog("编辑项目阶段明细")
+ @SaCheckPermission("/pmm/stagedetail/edit")
+ @PostMapping("/pmm/stagedetail/edit")
+ public CommonResult edit(@RequestBody @Valid StageDetailEditParam stageDetailEditParam) {
+ stageDetailService.edit(stageDetailEditParam);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 删除项目阶段明细
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ @Operation(summary = "删除项目阶段明细")
+ @CommonLog("删除项目阶段明细")
+ @SaCheckPermission("/pmm/stagedetail/delete")
+ @PostMapping("/pmm/stagedetail/delete")
+ public CommonResult delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+ List stageDetailIdParamList) {
+ stageDetailService.delete(stageDetailIdParamList);
+ return CommonResult.ok();
+ }
+
+ /**
+ * 获取项目阶段明细详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ @Operation(summary = "获取项目阶段明细详情")
+ @SaCheckPermission("/pmm/stagedetail/detail")
+ @GetMapping("/pmm/stagedetail/detail")
+ public CommonResult detail(@Valid StageDetailIdParam stageDetailIdParam) {
+ return CommonResult.data(stageDetailService.detail(stageDetailIdParam));
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/entity/StageDetail.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/entity/StageDetail.java
new file mode 100644
index 0000000..274fb94
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/entity/StageDetail.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目阶段明细实体
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+@Getter
+@Setter
+@TableName("pmm_stage_detail")
+public class StageDetail {
+
+ /** 明细id */
+ @TableId
+ @Schema(description = "明细id")
+ private String detailId;
+
+ /** 阶段id */
+ @Schema(description = "阶段id")
+ private String stageId;
+
+ /** 父明细id */
+ @Schema(description = "父明细id")
+ private String parentDetailId;
+
+ /** 序号 */
+ @Schema(description = "序号")
+ private String seq;
+
+ /** 功能模块 */
+ @Schema(description = "功能模块")
+ private String modelName;
+
+ /** 功能明细 */
+ @Schema(description = "功能明细")
+ private String modelDetail;
+
+ /** 功能描述 */
+ @Schema(description = "功能描述")
+ private String modelDescription;
+
+ /** 优先级 */
+ @Schema(description = "优先级")
+ private String priority;
+
+ /** 备注 */
+ @Schema(description = "备注")
+ private String remark;
+
+ /** 开始时间 */
+ @Schema(description = "开始时间")
+ private Date startTime;
+
+ /** 结束时间 */
+ @Schema(description = "结束时间")
+ private Date endTime;
+
+ /** 创建时间 */
+ @Schema(description = "创建时间")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createTime;
+
+ /** 创建用户 */
+ @Schema(description = "创建用户")
+ @TableField(fill = FieldFill.INSERT)
+ private String createUser;
+
+ /** 修改时间 */
+ @Schema(description = "修改时间")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateTime;
+
+ /** 修改用户 */
+ @Schema(description = "修改用户")
+ @TableField(fill = FieldFill.UPDATE)
+ private String updateUser;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/enums/StageDetailEnum.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/enums/StageDetailEnum.java
new file mode 100644
index 0000000..ca64bf3
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/enums/StageDetailEnum.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.enums;
+
+import lombok.Getter;
+
+/**
+ * 项目阶段明细枚举
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+@Getter
+public enum StageDetailEnum {
+
+ /** 测试 */
+ TEST("TEST");
+
+ private final String value;
+
+ StageDetailEnum(String value) {
+ this.value = value;
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/mapper/StageDetailMapper.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/mapper/StageDetailMapper.java
new file mode 100644
index 0000000..aa3e41f
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/mapper/StageDetailMapper.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.nl.pmm.modular.stagedetail.entity.StageDetail;
+
+/**
+ * 项目阶段明细Mapper接口
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+public interface StageDetailMapper extends BaseMapper {
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/mapper/mapping/StageDetailMapper.xml b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/mapper/mapping/StageDetailMapper.xml
new file mode 100644
index 0000000..914be47
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/mapper/mapping/StageDetailMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailAddParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailAddParam.java
new file mode 100644
index 0000000..c744f02
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailAddParam.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目阶段明细添加参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+@Getter
+@Setter
+public class StageDetailAddParam {
+
+ /** 阶段id */
+ @Schema(description = "阶段id")
+ private String stageId;
+
+ /** 父明细id */
+ @Schema(description = "父明细id")
+ private String parentDetailId;
+
+ /** 序号 */
+ @Schema(description = "序号")
+ private String seq;
+
+ /** 功能模块 */
+ @Schema(description = "功能模块")
+ private String modelName;
+
+ /** 功能明细 */
+ @Schema(description = "功能明细")
+ private String modelDetail;
+
+ /** 功能描述 */
+ @Schema(description = "功能描述")
+ private String modelDescription;
+
+ /** 优先级 */
+ @Schema(description = "优先级")
+ private String priority;
+
+ /** 备注 */
+ @Schema(description = "备注")
+ private String remark;
+
+ /** 开始时间 */
+ @Schema(description = "开始时间")
+ private Date startTime;
+
+ /** 结束时间 */
+ @Schema(description = "结束时间")
+ private Date endTime;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailEditParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailEditParam.java
new file mode 100644
index 0000000..a992738
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailEditParam.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目阶段明细编辑参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+@Getter
+@Setter
+public class StageDetailEditParam {
+
+ /** 明细id */
+ @Schema(description = "明细id", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "detailId不能为空")
+ private String detailId;
+
+ /** 阶段id */
+ @Schema(description = "阶段id")
+ private String stageId;
+
+ /** 父明细id */
+ @Schema(description = "父明细id")
+ private String parentDetailId;
+
+ /** 序号 */
+ @Schema(description = "序号")
+ private String seq;
+
+ /** 功能模块 */
+ @Schema(description = "功能模块")
+ private String modelName;
+
+ /** 功能明细 */
+ @Schema(description = "功能明细")
+ private String modelDetail;
+
+ /** 功能描述 */
+ @Schema(description = "功能描述")
+ private String modelDescription;
+
+ /** 优先级 */
+ @Schema(description = "优先级")
+ private String priority;
+
+ /** 备注 */
+ @Schema(description = "备注")
+ private String remark;
+
+ /** 开始时间 */
+ @Schema(description = "开始时间")
+ private Date startTime;
+
+ /** 结束时间 */
+ @Schema(description = "结束时间")
+ private Date endTime;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailIdParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailIdParam.java
new file mode 100644
index 0000000..5cdfa1c
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailIdParam.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 项目阶段明细Id参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+@Getter
+@Setter
+public class StageDetailIdParam {
+
+ /** 明细id */
+ @Schema(description = "明细id", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "detailId不能为空")
+ private String detailId;
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailPageParam.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailPageParam.java
new file mode 100644
index 0000000..ec0805f
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/param/StageDetailPageParam.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目阶段明细查询参数
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+@Getter
+@Setter
+public class StageDetailPageParam {
+
+ /** 当前页 */
+ @Schema(description = "当前页码")
+ private Integer current;
+
+ /** 每页条数 */
+ @Schema(description = "每页条数")
+ private Integer size;
+
+ /** 排序字段 */
+ @Schema(description = "排序字段,字段驼峰名称,如:userName")
+ private String sortField;
+
+ /** 排序方式 */
+ @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND")
+ private String sortOrder;
+
+ /** 关键词 */
+ @Schema(description = "关键词")
+ private String searchKey;
+
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/StageDetailService.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/StageDetailService.java
new file mode 100644
index 0000000..9c4e52a
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/StageDetailService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.nl.pmm.modular.stagedetail.entity.StageDetail;
+import org.nl.pmm.modular.stagedetail.param.StageDetailAddParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailEditParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailIdParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailPageParam;
+
+import java.util.List;
+
+/**
+ * 项目阶段明细Service接口
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+public interface StageDetailService extends IService {
+
+ /**
+ * 获取项目阶段明细分页
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ Page page(StageDetailPageParam stageDetailPageParam);
+
+ /**
+ * 添加项目阶段明细
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ void add(StageDetailAddParam stageDetailAddParam);
+
+ /**
+ * 编辑项目阶段明细
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ void edit(StageDetailEditParam stageDetailEditParam);
+
+ /**
+ * 删除项目阶段明细
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ void delete(List stageDetailIdParamList);
+
+ /**
+ * 获取项目阶段明细详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ */
+ StageDetail detail(StageDetailIdParam stageDetailIdParam);
+
+ /**
+ * 获取项目阶段明细详情
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+ StageDetail queryEntity(String id);
+
+ List getByStageId(String stageId);
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/impl/StageDetailServiceImpl.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/impl/StageDetailServiceImpl.java
new file mode 100644
index 0000000..45f6f00
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/impl/StageDetailServiceImpl.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package org.nl.pmm.modular.stagedetail.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.nl.common.enums.CommonSortOrderEnum;
+import org.nl.common.exception.CommonException;
+import org.nl.common.page.CommonPageRequest;
+import org.nl.pmm.modular.stagedetail.entity.StageDetail;
+import org.nl.pmm.modular.stagedetail.mapper.StageDetailMapper;
+import org.nl.pmm.modular.stagedetail.param.StageDetailAddParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailEditParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailIdParam;
+import org.nl.pmm.modular.stagedetail.param.StageDetailPageParam;
+import org.nl.pmm.modular.stagedetail.service.StageDetailService;
+
+import java.util.List;
+
+/**
+ * 项目阶段明细Service接口实现类
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+@Service
+public class StageDetailServiceImpl extends ServiceImpl implements StageDetailService {
+
+ @Override
+ public Page page(StageDetailPageParam stageDetailPageParam) {
+ QueryWrapper queryWrapper = new QueryWrapper().checkSqlInjection();
+ if(ObjectUtil.isAllNotEmpty(stageDetailPageParam.getSortField(), stageDetailPageParam.getSortOrder())) {
+ CommonSortOrderEnum.validate(stageDetailPageParam.getSortOrder());
+ queryWrapper.orderBy(true, stageDetailPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
+ StrUtil.toUnderlineCase(stageDetailPageParam.getSortField()));
+ } else {
+ queryWrapper.lambda().orderByAsc(StageDetail::getDetailId);
+ }
+ return this.page(CommonPageRequest.defaultPage(), queryWrapper);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void add(StageDetailAddParam stageDetailAddParam) {
+ StageDetail stageDetail = BeanUtil.toBean(stageDetailAddParam, StageDetail.class);
+ this.save(stageDetail);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void edit(StageDetailEditParam stageDetailEditParam) {
+ StageDetail stageDetail = this.queryEntity(stageDetailEditParam.getDetailId());
+ BeanUtil.copyProperties(stageDetailEditParam, stageDetail);
+ this.updateById(stageDetail);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public void delete(List stageDetailIdParamList) {
+ // 执行删除
+ this.removeByIds(CollStreamUtil.toList(stageDetailIdParamList, StageDetailIdParam::getDetailId));
+ }
+
+ @Override
+ public StageDetail detail(StageDetailIdParam stageDetailIdParam) {
+ return this.queryEntity(stageDetailIdParam.getDetailId());
+ }
+
+ @Override
+ public StageDetail queryEntity(String id) {
+ StageDetail stageDetail = this.getById(id);
+ if(ObjectUtil.isEmpty(stageDetail)) {
+ throw new CommonException("项目阶段明细不存在,id值为:{}", id);
+ }
+ return stageDetail;
+ }
+
+ @Override
+ public List getByStageId(String stageId) {
+ LambdaQueryWrapper lam = new QueryWrapper().lambda();
+ lam.eq(StageDetail::getStageId, stageId)
+ .orderByAsc(StageDetail::getSeq);
+ return list(lam);
+ }
+}
diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/package-info.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/package-info.java
new file mode 100644
index 0000000..cbec712
--- /dev/null
+++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 项目管理包
+ * @Author: lyd
+ * @Date: 2025/11/11
+ */
+package org.nl.pmm;
diff --git a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/mapper/SysGroupMapper.java b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/mapper/SysGroupMapper.java
index 19c9f85..43f390e 100644
--- a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/mapper/SysGroupMapper.java
+++ b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/mapper/SysGroupMapper.java
@@ -13,7 +13,12 @@
package org.nl.sys.modular.group.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+import org.nl.sys.core.GroupUserVo;
import org.nl.sys.modular.group.entity.SysGroup;
+import org.nl.sys.modular.group.param.SysGroupIdUserParam;
+
+import java.util.List;
/**
* 用户组Mapper接口
@@ -22,4 +27,5 @@ import org.nl.sys.modular.group.entity.SysGroup;
* @date 2024/12/21 01:25
**/
public interface SysGroupMapper extends BaseMapper {
+ List getAllUserInfoByGroupId(@Param("param") SysGroupIdUserParam param);
}
diff --git a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/mapper/mapping/SysGroupMapper.xml b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/mapper/mapping/SysGroupMapper.xml
index 6f285ac..f7e0247 100644
--- a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/mapper/mapping/SysGroupMapper.xml
+++ b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/mapper/mapping/SysGroupMapper.xml
@@ -2,4 +2,13 @@
+
diff --git a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/param/SysGroupIdUserParam.java b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/param/SysGroupIdUserParam.java
new file mode 100644
index 0000000..f30bf73
--- /dev/null
+++ b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/param/SysGroupIdUserParam.java
@@ -0,0 +1,22 @@
+package org.nl.sys.modular.group.param;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @Author: lyd
+ * @Date: 2025/11/11
+ */
+@Data
+@Builder
+public class SysGroupIdUserParam {
+ /**
+ * 用户组id
+ */
+ private String groupId;
+
+ /**
+ * 分类
+ */
+ private String category;
+}
diff --git a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/provider/SysGroupApiProvider.java b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/provider/SysGroupApiProvider.java
index 63cacb3..18419e9 100644
--- a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/provider/SysGroupApiProvider.java
+++ b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/provider/SysGroupApiProvider.java
@@ -16,6 +16,7 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
+import org.nl.sys.core.GroupUserVo;
import org.springframework.stereotype.Service;
import org.nl.sys.modular.group.service.SysGroupService;
import org.nl.sys.api.SysGroupApi;
@@ -61,4 +62,9 @@ public class SysGroupApiProvider implements SysGroupApi {
sysGroupSelectorParam.setSearchKey(searchKey);
return BeanUtil.toBean(sysGroupService.groupSelector(sysGroupSelectorParam), Page.class);
}
+
+ @Override
+ public List getAllUserInfoByGroupId(String groupId) {
+ return sysGroupService.getAllUserInfoByGroupId(groupId);
+ }
}
diff --git a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/service/SysGroupService.java b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/service/SysGroupService.java
index 4087a16..e372090 100644
--- a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/service/SysGroupService.java
+++ b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/service/SysGroupService.java
@@ -15,6 +15,7 @@ package org.nl.sys.modular.group.service;
import cn.hutool.core.lang.tree.Tree;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
+import org.nl.sys.core.GroupUserVo;
import org.nl.sys.modular.group.entity.SysGroup;
import org.nl.sys.modular.group.param.*;
import org.nl.sys.modular.group.param.*;
@@ -117,4 +118,11 @@ public interface SysGroupService extends IService {
* @date 2024/12/21 01:25
*/
Page groupSelector(SysGroupSelectorParam sysGroupSelectorParam);
+
+ /**
+ * 根据用户组id获取用户
+ * @param groupId
+ * @return
+ */
+ List getAllUserInfoByGroupId(String groupId);
}
diff --git a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/service/impl/SysGroupServiceImpl.java b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/service/impl/SysGroupServiceImpl.java
index 01394d6..fc7a1ef 100644
--- a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/service/impl/SysGroupServiceImpl.java
+++ b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/group/service/impl/SysGroupServiceImpl.java
@@ -24,6 +24,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
+import org.nl.sys.core.GroupUserVo;
import org.nl.sys.modular.group.param.*;
import org.nl.sys.modular.relation.entity.SysRelation;
import org.nl.sys.modular.relation.enums.SysRelationCategoryEnum;
@@ -187,4 +188,10 @@ public class SysGroupServiceImpl extends ServiceImpl i
}
return this.page(CommonPageRequest.defaultPage(), lambdaQueryWrapper);
}
+
+ @Override
+ public List getAllUserInfoByGroupId(String groupId) {
+ SysGroupIdUserParam param = SysGroupIdUserParam.builder().groupId(groupId).category(SysRelationCategoryEnum.SYS_USER_HAS_GROUP.getValue()).build();
+ return this.baseMapper.getAllUserInfoByGroupId(param);
+ }
}
diff --git a/nl-plugin/pom.xml b/nl-plugin/pom.xml
index c6d60a6..92d5fe3 100644
--- a/nl-plugin/pom.xml
+++ b/nl-plugin/pom.xml
@@ -35,5 +35,8 @@
nl-plugin-sys
+
+
+ nl-plugin-pmm
diff --git a/nl-vue/public/favicon.ico b/nl-vue/public/favicon.ico
deleted file mode 100644
index 9e3ed92..0000000
Binary files a/nl-vue/public/favicon.ico and /dev/null differ
diff --git a/nl-vue/public/img/logo.png b/nl-vue/public/img/logo.png
index 4d1a1df..a1ca19b 100644
Binary files a/nl-vue/public/img/logo.png and b/nl-vue/public/img/logo.png differ
diff --git a/nl-vue/public/img/logo2.png b/nl-vue/public/img/logo2.png
new file mode 100644
index 0000000..4d1a1df
Binary files /dev/null and b/nl-vue/public/img/logo2.png differ
diff --git a/nl-vue/src/api/pmm/projectApi.js b/nl-vue/src/api/pmm/projectApi.js
new file mode 100644
index 0000000..38edbe6
--- /dev/null
+++ b/nl-vue/src/api/pmm/projectApi.js
@@ -0,0 +1,36 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/pmm/project/` + url, ...arg)
+
+/**
+ * 项目信息Api接口管理器
+ *
+ * @author liyongde
+ * @date 2025/11/11 20:13
+ **/
+export default {
+ // 获取项目信息分页
+ projectPage(data) {
+ return request('page', data, 'get')
+ },
+ // 提交项目信息表单 edit为true时为编辑,默认为新增
+ projectSubmitForm(data, edit = false) {
+ return request(edit ? 'edit' : 'add', data)
+ },
+ // 删除项目信息
+ projectDelete(data) {
+ return request('delete', data)
+ },
+ // 获取项目信息详情
+ projectDetail(data) {
+ return request('detail', data, 'get')
+ },
+ // 获取项目需求信息详情
+ requestDetail(data) {
+ return request('requestDetail', data, 'get')
+ },
+ // 获取用户信息详情
+ devUsers(data) {
+ return request('dev-all-users', data, 'get')
+ }
+}
diff --git a/nl-vue/src/api/pmm/projectFileApi.js b/nl-vue/src/api/pmm/projectFileApi.js
new file mode 100644
index 0000000..58bd3a3
--- /dev/null
+++ b/nl-vue/src/api/pmm/projectFileApi.js
@@ -0,0 +1,28 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/pmm/projectfile/` + url, ...arg)
+
+/**
+ * 项目附件表Api接口管理器
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:24
+ **/
+export default {
+ // 获取项目附件表分页
+ projectFilePage(data) {
+ return request('page', data, 'get')
+ },
+ // 提交项目附件表表单 edit为true时为编辑,默认为新增
+ projectFileSubmitForm(data, edit = false) {
+ return request(edit ? 'edit' : 'add', data)
+ },
+ // 删除项目附件表
+ projectFileDelete(data) {
+ return request('delete', data)
+ },
+ // 获取项目附件表详情
+ projectFileDetail(data) {
+ return request('detail', data, 'get')
+ }
+}
diff --git a/nl-vue/src/api/pmm/projectStageApi.js b/nl-vue/src/api/pmm/projectStageApi.js
new file mode 100644
index 0000000..f0bba43
--- /dev/null
+++ b/nl-vue/src/api/pmm/projectStageApi.js
@@ -0,0 +1,28 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/pmm/projectstage/` + url, ...arg)
+
+/**
+ * 项目阶段Api接口管理器
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:32
+ **/
+export default {
+ // 获取项目阶段分页
+ projectStagePage(data) {
+ return request('page', data, 'get')
+ },
+ // 提交项目阶段表单 edit为true时为编辑,默认为新增
+ projectStageSubmitForm(data, edit = false) {
+ return request(edit ? 'edit' : 'add', data)
+ },
+ // 删除项目阶段
+ projectStageDelete(data) {
+ return request('delete', data)
+ },
+ // 获取项目阶段详情
+ projectStageDetail(data) {
+ return request('detail', data, 'get')
+ }
+}
diff --git a/nl-vue/src/api/pmm/stageDetailApi.js b/nl-vue/src/api/pmm/stageDetailApi.js
new file mode 100644
index 0000000..21c83f2
--- /dev/null
+++ b/nl-vue/src/api/pmm/stageDetailApi.js
@@ -0,0 +1,28 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/pmm/stagedetail/` + url, ...arg)
+
+/**
+ * 项目阶段明细Api接口管理器
+ *
+ * @author liyongde
+ * @date 2025/11/17 20:34
+ **/
+export default {
+ // 获取项目阶段明细分页
+ stageDetailPage(data) {
+ return request('page', data, 'get')
+ },
+ // 提交项目阶段明细表单 edit为true时为编辑,默认为新增
+ stageDetailSubmitForm(data, edit = false) {
+ return request(edit ? 'edit' : 'add', data)
+ },
+ // 删除项目阶段明细
+ stageDetailDelete(data) {
+ return request('delete', data)
+ },
+ // 获取项目阶段明细详情
+ stageDetailDetail(data) {
+ return request('detail', data, 'get')
+ }
+}
diff --git a/nl-vue/src/views/pmm/project/form.vue b/nl-vue/src/views/pmm/project/form.vue
new file mode 100644
index 0000000..3285b98
--- /dev/null
+++ b/nl-vue/src/views/pmm/project/form.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 关闭
+ 保存
+
+
+
+
+
diff --git a/nl-vue/src/views/pmm/project/index.vue b/nl-vue/src/views/pmm/project/index.vue
new file mode 100644
index 0000000..481899d
--- /dev/null
+++ b/nl-vue/src/views/pmm/project/index.vue
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
diff --git a/nl-vue/src/views/pmm/project/request/description.vue b/nl-vue/src/views/pmm/project/request/description.vue
new file mode 100644
index 0000000..15dfd00
--- /dev/null
+++ b/nl-vue/src/views/pmm/project/request/description.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nl-vue/src/views/pmm/project/request/index.vue b/nl-vue/src/views/pmm/project/request/index.vue
new file mode 100644
index 0000000..1f6f69f
--- /dev/null
+++ b/nl-vue/src/views/pmm/project/request/index.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+ {{ record.projectName }}
+
+
+
+
+
+
+
diff --git a/nl-web-app/pom.xml b/nl-web-app/pom.xml
index 096e114..6026856 100644
--- a/nl-web-app/pom.xml
+++ b/nl-web-app/pom.xml
@@ -117,6 +117,11 @@
org.nl
nl-plugin-sys
+
+
+ org.nl
+ nl-plugin-pmm
+
diff --git a/nl-web-app/src/main/resources/application.properties b/nl-web-app/src/main/resources/application.properties
index 10b2fe5..c3973a5 100644
--- a/nl-web-app/src/main/resources/application.properties
+++ b/nl-web-app/src/main/resources/application.properties
@@ -27,7 +27,7 @@ spring.servlet.multipart.max-file-size=100MB
# mysql
spring.datasource.dynamic.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
-spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/snowys?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true&rewriteBatchedStatements=true
+spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/nl_tool?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true&rewriteBatchedStatements=true
spring.datasource.dynamic.datasource.master.username=root
spring.datasource.dynamic.datasource.master.password=12356
spring.datasource.dynamic.strict=true
@@ -121,7 +121,7 @@ mybatis-plus.global-config.db-config.id-type=ASSIGN_ID
mybatis-plus.global-config.db-config.logic-delete-field=DELETE_FLAG
mybatis-plus.global-config.db-config.logic-delete-value=DELETED
mybatis-plus.global-config.db-config.logic-not-delete-value=NOT_DELETE
-mybatis-plus.mapper-locations=classpath*:vip/storm/**/mapping/*.xml
+mybatis-plus.mapper-locations=classpath*:org/nl/**/mapping/*.xml
mybatis-plus.type-handlers-package=org.nl.common.handler
#########################################
@@ -172,25 +172,25 @@ knife4j.setting.footerCustomContent=Apache License 2.0 | Copyright 2020-2024[SNO
springdoc.default-flat-param-object=true
# knife4j doc groups
-springdoc.group-configs[0].group=SNOWY-PLUGIN-AUTH
+springdoc.group-configs[0].group=NL-PLUGIN-AUTH
springdoc.group-configs[0].display-name=${springdoc.group-configs[0].group}
springdoc.group-configs[0].packages-to-scan=org.nl.auth
-springdoc.group-configs[1].group=SNOWY-PLUGIN-BIZ
+springdoc.group-configs[1].group=NL-PLUGIN-BIZ
springdoc.group-configs[1].display-name=${springdoc.group-configs[1].group}
springdoc.group-configs[1].packages-to-scan=org.nl.biz
-springdoc.group-configs[2].group=SNOWY-PLUGIN-CLIENT
+springdoc.group-configs[2].group=NL-PLUGIN-CLIENT
springdoc.group-configs[2].display-name=${springdoc.group-configs[2].group}
springdoc.group-configs[2].packages-to-scan=org.nl.client
-springdoc.group-configs[3].group=SNOWY-PLUGIN-DEV
+springdoc.group-configs[3].group=NL-PLUGIN-DEV
springdoc.group-configs[3].display-name=${springdoc.group-configs[3].group}
springdoc.group-configs[3].packages-to-scan=org.nl.dev
-springdoc.group-configs[4].group=SNOWY-PLUGIN-GEN
+springdoc.group-configs[4].group=NL-PLUGIN-GEN
springdoc.group-configs[4].display-name=${springdoc.group-configs[4].group}
springdoc.group-configs[4].packages-to-scan=org.nl.gen
-springdoc.group-configs[5].group=SNOWY-PLUGIN-MOBILE
+springdoc.group-configs[5].group=NL-PLUGIN-MOBILE
springdoc.group-configs[5].display-name=${springdoc.group-configs[5].group}
springdoc.group-configs[5].packages-to-scan=org.nl.mobile
-springdoc.group-configs[6].group=SNOWY-PLUGIN-SYS
+springdoc.group-configs[6].group=NL-PLUGIN-SYS
springdoc.group-configs[6].display-name=${springdoc.group-configs[6].group}
springdoc.group-configs[6].packages-to-scan=org.nl.sys
diff --git a/nl-web-app/src/main/resources/static/favicon.ico b/nl-web-app/src/main/resources/static/favicon.ico
index 9e3ed92..63a1585 100644
Binary files a/nl-web-app/src/main/resources/static/favicon.ico and b/nl-web-app/src/main/resources/static/favicon.ico differ
diff --git a/pom.xml b/pom.xml
index d58dbc9..af3f88d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,7 +41,17 @@
-
+
+
+ org.nl
+ nl-plugin-pmm
+ ${snowy.version}
+
+
+ org.nl
+ nl-plugin-pmm-api
+ ${snowy.version}
+
org.nl