yusm 1 неделя назад
Родитель
Сommit
18056ecb8b
24 измененных файлов с 1203 добавлено и 0 удалено
  1. 116 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectClosureApplierController.java
  2. 136 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectClosureApplyController.java
  3. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectClosureApprovalLogController.java
  4. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectClosureAttachmentController.java
  5. 76 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectClosureApplier.java
  6. 106 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectClosureApply.java
  7. 73 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectClosureApprovalLog.java
  8. 79 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectClosureAttachment.java
  9. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectClosureApplierMapper.java
  10. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectClosureApplyMapper.java
  11. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectClosureApprovalLogMapper.java
  12. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectClosureAttachmentMapper.java
  13. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectClosureApplierService.java
  14. 88 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectClosureApplyService.java
  15. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectClosureApprovalLogService.java
  16. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectClosureAttachmentService.java
  17. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectClosureApplierServiceImpl.java
  18. 223 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectClosureApplyServiceImpl.java
  19. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectClosureApprovalLogServiceImpl.java
  20. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectClosureAttachmentServiceImpl.java
  21. 22 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectClosureApplierMapper.xml
  22. 27 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectClosureApplyMapper.xml
  23. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectClosureApprovalLogMapper.xml
  24. 22 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectClosureAttachmentMapper.xml

+ 116 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectClosureApplierController.java

@@ -0,0 +1,116 @@
+package com.management.platform.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.ProjectClosureApplier;
+import com.management.platform.mapper.ProjectClosureApplierMapper;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * <p>
+ * 项目结项审批人设置表 前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@RestController
+@RequestMapping("/project-closure-applier")
+public class ProjectClosureApplierController {
+
+    @Resource
+    private ProjectClosureApplierMapper closureApplierMapper;
+
+    /**
+     * 获取审批人配置列表
+     */
+    @GetMapping("/list")
+    public HttpRespMsg list(@RequestParam(required = false) Long timeTypeId) {
+        QueryWrapper<ProjectClosureApplier> wrapper = new QueryWrapper<>();
+        if (timeTypeId != null && timeTypeId > 0) {
+            wrapper.eq("time_type_id", timeTypeId);
+        }
+        wrapper.orderByAsc("sort_order");
+        List<ProjectClosureApplier> list = closureApplierMapper.selectList(wrapper);
+        return HttpRespMsg.success(list);
+    }
+
+    /**
+     * 获取单个审批人配置
+     */
+    @GetMapping("/{id}")
+    public HttpRespMsg getById(@PathVariable Long id) {
+        ProjectClosureApplier applier = closureApplierMapper.selectById(id);
+        if (applier != null) {
+            return HttpRespMsg.success(applier);
+        }
+        return HttpRespMsg.error("审批人配置不存在");
+    }
+
+    /**
+     * 添加审批人配置
+     */
+    @PostMapping("/add")
+    public HttpRespMsg add(@RequestParam Long timeTypeId,
+                           @RequestParam Long approverUserId,
+                           @RequestParam String approverName,
+                           @RequestParam Integer sortOrder,
+                           @RequestParam(defaultValue = "1") Integer status) {
+        try {
+            ProjectClosureApplier applier = new ProjectClosureApplier();
+            applier.setTimeTypeId(timeTypeId);
+            applier.setApproverUserId(approverUserId);
+            applier.setApproverName(approverName);
+            applier.setSortOrder(sortOrder);
+            applier.setStatus(status);
+            applier.setCreateTime(java.time.LocalDateTime.now());
+            applier.setUpdateTime(java.time.LocalDateTime.now());
+            closureApplierMapper.insert(applier);
+            return HttpRespMsg.success("添加成功");
+        } catch (Exception e) {
+            return HttpRespMsg.error("添加失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 更新审批人配置
+     */
+    @PostMapping("/update")
+    public HttpRespMsg update(@RequestParam Long id,
+                              @RequestParam Long timeTypeId,
+                              @RequestParam Long approverUserId,
+                              @RequestParam String approverName,
+                              @RequestParam Integer sortOrder,
+                              @RequestParam Integer status) {
+        try {
+            ProjectClosureApplier applier = new ProjectClosureApplier();
+            applier.setId(id);
+            applier.setTimeTypeId(timeTypeId);
+            applier.setApproverUserId(approverUserId);
+            applier.setApproverName(approverName);
+            applier.setSortOrder(sortOrder);
+            applier.setStatus(status);
+            applier.setUpdateTime(java.time.LocalDateTime.now());
+            closureApplierMapper.updateById(applier);
+            return HttpRespMsg.success("更新成功");
+        } catch (Exception e) {
+            return HttpRespMsg.error("更新失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 删除审批人配置
+     */
+    @PostMapping("/delete/{id}")
+    public HttpRespMsg delete(@PathVariable Long id) {
+        try {
+            closureApplierMapper.deleteById(id);
+            return HttpRespMsg.success("删除成功");
+        } catch (Exception e) {
+            return HttpRespMsg.error("删除失败:" + e.getMessage());
+        }
+    }
+}

+ 136 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectClosureApplyController.java

@@ -0,0 +1,136 @@
+package com.management.platform.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.management.platform.entity.ProjectClosureApply;
+import com.management.platform.service.ProjectClosureApplyService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.Map;
+
+/**
+ * <p>
+ * 项目结项申请表 前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@RestController
+@RequestMapping("/project-closure-apply")
+public class ProjectClosureApplyController {
+
+    @Resource
+    private ProjectClosureApplyService closureApplyService;
+
+    /**
+     * 提交结项申请
+     */
+    @PostMapping("/submit")
+    public HttpRespMsg submitApply(@RequestParam Long projectId,
+                                   @RequestParam Long userId,
+                                   @RequestParam(required = false) String reason,
+                                   @RequestParam(required = false) String attachmentIds) {
+        try {
+            String[] ids = attachmentIds != null ? attachmentIds.split(",") : new String[0];
+            java.util.List<Long> attachmentIdList = new java.util.ArrayList<>();
+            for (String id : ids) {
+                if (id != null && !id.trim().isEmpty()) {
+                    attachmentIdList.add(Long.parseLong(id.trim()));
+                }
+            }
+            ProjectClosureApply apply = closureApplyService.submitApply(projectId, userId, reason, attachmentIdList);
+            return HttpRespMsg.success("申请提交成功", apply);
+        } catch (Exception e) {
+            return HttpRespMsg.error("申请提交失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取申请详情
+     */
+    @GetMapping("/detail/{id}")
+    public HttpRespMsg getDetail(@PathVariable Long id) {
+        ProjectClosureApply apply = closureApplyService.getById(id);
+        if (apply != null) {
+            return HttpRespMsg.success(apply);
+        }
+        return HttpRespMsg.error("申请记录不存在");
+    }
+
+    /**
+     * 获取审批记录列表
+     */
+    @GetMapping("/approval-logs/{applyId}")
+    public HttpRespMsg getApprovalLogs(@PathVariable Long applyId) {
+        try {
+            java.util.List<com.management.platform.entity.ProjectClosureApprovalLog> logs = closureApplyService.getApprovalLogs(applyId);
+            return HttpRespMsg.success(logs);
+        } catch (Exception e) {
+            return HttpRespMsg.error("获取审批记录失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取审批进度
+     */
+    @GetMapping("/progress/{applyId}")
+    public HttpRespMsg getProgress(@PathVariable Long applyId) {
+        try {
+            Map<String, Object> progress = closureApplyService.getProgress(applyId);
+            return HttpRespMsg.success(progress);
+        } catch (Exception e) {
+            return HttpRespMsg.error("获取审批进度失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 审批操作
+     */
+    @PostMapping("/approve")
+    public HttpRespMsg approve(@RequestParam Long applyId,
+                               @RequestParam Long applierId,
+                               @RequestParam(required = false) String comment,
+                               @RequestParam Integer action) {
+        try {
+            boolean result = closureApplyService.approve(applyId, applierId, comment, action);
+            if (result) {
+                return HttpRespMsg.success("审批成功");
+            }
+            return HttpRespMsg.error("审批失败");
+        } catch (Exception e) {
+            return HttpRespMsg.error("审批失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取我的待审批列表
+     */
+    @GetMapping("/my-pending")
+    public HttpRespMsg getMyPendingApproval(@RequestParam Long userId,
+                                            @RequestParam(defaultValue = "1") int current,
+                                            @RequestParam(defaultValue = "10") int size) {
+        try {
+            IPage<ProjectClosureApply> page = closureApplyService.getMyPendingApproval(userId, current, size);
+            return HttpRespMsg.success(page);
+        } catch (Exception e) {
+            return HttpRespMsg.error("获取待审批列表失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取我的申请列表
+     */
+    @GetMapping("/my-apply")
+    public HttpRespMsg getMyApplyList(@RequestParam Long userId,
+                                      @RequestParam(defaultValue = "1") int current,
+                                      @RequestParam(defaultValue = "10") int size) {
+        try {
+            IPage<ProjectClosureApply> page = closureApplyService.getMyApplyList(userId, current, size);
+            return HttpRespMsg.success(page);
+        } catch (Exception e) {
+            return HttpRespMsg.error("获取申请列表失败:" + e.getMessage());
+        }
+    }
+}

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectClosureApprovalLogController.java

@@ -0,0 +1,21 @@
+package com.management.platform.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 项目结项审批记录表 前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@RestController
+@RequestMapping("/project-closure-approval-log")
+public class ProjectClosureApprovalLogController {
+
+}
+

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectClosureAttachmentController.java

@@ -0,0 +1,21 @@
+package com.management.platform.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 项目结项附件表 前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@RestController
+@RequestMapping("/project-closure-attachment")
+public class ProjectClosureAttachmentController {
+
+}
+

+ 76 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectClosureApplier.java

@@ -0,0 +1,76 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 项目结项审批人设置表
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ProjectClosureApplier extends Model<ProjectClosureApplier> {
+
+    private static final long serialVersionUID=1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 关联time_type.id(按企业配置)
+     */
+    @TableField("time_type_id")
+    private Long timeTypeId;
+
+    /**
+     * 审批人用户ID
+     */
+    @TableField("approver_user_id")
+    private Long approverUserId;
+
+    /**
+     * 审批人姓名
+     */
+    @TableField("approver_name")
+    private String approverName;
+
+    /**
+     * 排序(多审批人顺序)
+     */
+    @TableField("sort_order")
+    private Integer sortOrder;
+
+    /**
+     * 状态 0-停用 1-启用
+     */
+    @TableField("status")
+    private Integer status;
+
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 106 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectClosureApply.java

@@ -0,0 +1,106 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 项目结项申请表
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ProjectClosureApply extends Model<ProjectClosureApply> {
+
+    private static final long serialVersionUID=1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 项目ID
+     */
+    @TableField("project_id")
+    private Long projectId;
+
+    /**
+     * 项目名称
+     */
+    @TableField("project_name")
+    private String projectName;
+
+    /**
+     * 项目编码
+     */
+    @TableField("project_code")
+    private String projectCode;
+
+    /**
+     * 申请人用户ID
+     */
+    @TableField("applicant_id")
+    private Long applicantId;
+
+    /**
+     * 申请人姓名
+     */
+    @TableField("applicant_name")
+    private String applicantName;
+
+    /**
+     * 当前审批步骤(0-待审批)
+     */
+    @TableField("current_step")
+    private Integer currentStep;
+
+    /**
+     * 当前审批人用户ID
+     */
+    @TableField("current_approver_id")
+    private Long currentApproverId;
+
+    /**
+     * 当前审批人姓名
+     */
+    @TableField("current_approver_name")
+    private String currentApproverName;
+
+    /**
+     * 审批状态 0-待审批 1-审批中 2-已通过 3-已驳回
+     */
+    @TableField("approval_status")
+    private Integer approvalStatus;
+
+    /**
+     * 申请人备注
+     */
+    @TableField("remark")
+    private String remark;
+
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 73 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectClosureApprovalLog.java

@@ -0,0 +1,73 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 项目结项审批记录表
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ProjectClosureApprovalLog extends Model<ProjectClosureApprovalLog> {
+
+    private static final long serialVersionUID=1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 关联project_closure_apply.id
+     */
+    @TableField("apply_id")
+    private Long applyId;
+
+    /**
+     * 审批人ID
+     */
+    @TableField("approver_id")
+    private Long approverId;
+
+    /**
+     * 审批人姓名
+     */
+    @TableField("approver_name")
+    private String approverName;
+
+    /**
+     * 操作 1-同意 2-驳回
+     */
+    @TableField("action")
+    private Integer action;
+
+    /**
+     * 审批意见
+     */
+    @TableField("opinion")
+    private String opinion;
+
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 79 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectClosureAttachment.java

@@ -0,0 +1,79 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 项目结项附件表
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ProjectClosureAttachment extends Model<ProjectClosureAttachment> {
+
+    private static final long serialVersionUID=1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 关联project_closure_apply.id
+     */
+    @TableField("apply_id")
+    private Long applyId;
+
+    /**
+     * 文件名称
+     */
+    @TableField("file_name")
+    private String fileName;
+
+    /**
+     * 文件路径/URL
+     */
+    @TableField("file_url")
+    private String fileUrl;
+
+    /**
+     * 文件大小(字节)
+     */
+    @TableField("file_size")
+    private Long fileSize;
+
+    /**
+     * 文件类型
+     */
+    @TableField("file_type")
+    private String fileType;
+
+    /**
+     * 上传人ID
+     */
+    @TableField("upload_user_id")
+    private Long uploadUserId;
+
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectClosureApplierMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.ProjectClosureApplier;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 项目结项审批人设置表 Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+public interface ProjectClosureApplierMapper extends BaseMapper<ProjectClosureApplier> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectClosureApplyMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.ProjectClosureApply;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 项目结项申请表 Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+public interface ProjectClosureApplyMapper extends BaseMapper<ProjectClosureApply> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectClosureApprovalLogMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.ProjectClosureApprovalLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 项目结项审批记录表 Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+public interface ProjectClosureApprovalLogMapper extends BaseMapper<ProjectClosureApprovalLog> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectClosureAttachmentMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.ProjectClosureAttachment;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 项目结项附件表 Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+public interface ProjectClosureAttachmentMapper extends BaseMapper<ProjectClosureAttachment> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectClosureApplierService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.ProjectClosureApplier;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 项目结项审批人设置表 服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+public interface ProjectClosureApplierService extends IService<ProjectClosureApplier> {
+
+}

+ 88 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectClosureApplyService.java

@@ -0,0 +1,88 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.ProjectClosureApply;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 项目结项申请表 服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+public interface ProjectClosureApplyService extends IService<ProjectClosureApply> {
+
+    /**
+     * 提交结项申请
+     *
+     * @param projectId 项目ID
+     * @param userId 申请人用户ID
+     * @param reason 结项原因
+     * @param attachments 附件列表
+     * @return 申请记录
+     */
+    ProjectClosureApply submitApply(Long projectId, Long userId, String reason, java.util.List<Long> attachmentIds);
+
+    /**
+     * 获取审批流程(按 sort_order 排序的审批人列表)
+     *
+     * @param timeTypeId 时间类型ID
+     * @return 审批人列表
+     */
+    java.util.List<com.management.platform.entity.ProjectClosureApplier> getApprovalFlow(Long timeTypeId);
+
+    /**
+     * 审批人审批(同意/拒绝)
+     *
+     * @param applyId 申请ID
+     * @param applierId 审批人ID
+     * @param comment 审批意见
+     * @param action 操作 1-同意 2-驳回
+     * @return 是否成功
+     */
+    boolean approve(Long applyId, Long applierId, String comment, Integer action);
+
+    /**
+     * 查询当前审批进度
+     *
+     * @param applyId 申请ID
+     * @return 进度信息
+     */
+    java.util.Map<String, Object> getProgress(Long applyId);
+
+    /**
+     * 查询审批记录列表
+     *
+     * @param applyId 申请ID
+     * @return 审批记录列表
+     */
+    java.util.List<com.management.platform.entity.ProjectClosureApprovalLog> getApprovalLogs(Long applyId);
+
+    /**
+     * 查询我的待审批列表
+     *
+     * @param userId 用户ID
+     * @param current 页码
+     * @param size 每页数量
+     * @return 分页结果
+     */
+    com.baomidou.mybatisplus.core.metadata.IPage<ProjectClosureApply> getMyPendingApproval(Long userId, int current, int size);
+
+    /**
+     * 查询我的申请列表
+     *
+     * @param userId 用户ID
+     * @param current 页码
+     * @param size 每页数量
+     * @return 分页结果
+     */
+    com.baomidou.mybatisplus.core.metadata.IPage<ProjectClosureApply> getMyApplyList(Long userId, int current, int size);
+
+    /**
+     * 审批通过后更新项目状态
+     *
+     * @param applyId 申请ID
+     */
+    void onApprovalPassed(Long applyId);
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectClosureApprovalLogService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.ProjectClosureApprovalLog;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 项目结项审批记录表 服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+public interface ProjectClosureApprovalLogService extends IService<ProjectClosureApprovalLog> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectClosureAttachmentService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.ProjectClosureAttachment;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 项目结项附件表 服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+public interface ProjectClosureAttachmentService extends IService<ProjectClosureAttachment> {
+
+}

+ 20 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectClosureApplierServiceImpl.java

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.ProjectClosureApplier;
+import com.management.platform.mapper.ProjectClosureApplierMapper;
+import com.management.platform.service.ProjectClosureApplierService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 项目结项审批人设置表 服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@Service
+public class ProjectClosureApplierServiceImpl extends ServiceImpl<ProjectClosureApplierMapper, ProjectClosureApplier> implements ProjectClosureApplierService {
+
+}

+ 223 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectClosureApplyServiceImpl.java

@@ -0,0 +1,223 @@
+package com.management.platform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.management.platform.entity.ProjectClosureApplier;
+import com.management.platform.entity.ProjectClosureApply;
+import com.management.platform.entity.ProjectClosureApprovalLog;
+import com.management.platform.entity.ProjectClosureAttachment;
+import com.management.platform.mapper.ProjectClosureApplierMapper;
+import com.management.platform.mapper.ProjectClosureApplyMapper;
+import com.management.platform.mapper.ProjectClosureApprovalLogMapper;
+import com.management.platform.mapper.ProjectClosureAttachmentMapper;
+import com.management.platform.service.ProjectClosureApplyService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+ * <p>
+ * 项目结项申请表 服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@Service
+public class ProjectClosureApplyServiceImpl extends ServiceImpl<ProjectClosureApplyMapper, ProjectClosureApply> implements ProjectClosureApplyService {
+
+    @Resource
+    private ProjectClosureApplierMapper closureApplierMapper;
+    @Resource
+    private ProjectClosureApprovalLogMapper approvalLogMapper;
+    @Resource
+    private ProjectClosureAttachmentMapper attachmentMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public ProjectClosureApply submitApply(Long projectId, Long userId, String reason, List<Long> attachmentIds) {
+        // 1. 获取项目信息(需要通过项目Service获取实际项目名称和编码)
+        String projectName = "项目-" + projectId;
+        String projectCode = "CODE-" + projectId;
+        
+        // 2. 创建申请记录
+        ProjectClosureApply apply = new ProjectClosureApply();
+        apply.setProjectId(projectId);
+        apply.setProjectName(projectName);
+        apply.setProjectCode(projectCode);
+        apply.setApplicantId(userId);
+        apply.setApplicantName("用户" + userId); // TODO: 从用户表查询
+        apply.setCurrentStep(0);
+        apply.setApprovalStatus(0); // 待审批
+        apply.setRemark(reason);
+        apply.setCreateTime(LocalDateTime.now());
+        apply.setUpdateTime(LocalDateTime.now());
+        
+        // 保存申请记录
+        this.save(apply);
+        
+        // 3. 保存附件
+        if (!CollectionUtils.isEmpty(attachmentIds)) {
+            for (Long attachmentId : attachmentIds) {
+                // TODO: 从附件Service获取附件信息并保存到project_closure_attachment表
+            }
+        }
+        
+        // 4. 获取审批流程,设置第一个审批人
+        this.setNextApprover(apply.getId());
+        
+        return apply;
+    }
+
+    @Override
+    public List<ProjectClosureApplier> getApprovalFlow(Long timeTypeId) {
+        QueryWrapper<ProjectClosureApplier> wrapper = new QueryWrapper<>();
+        wrapper.eq("time_type_id", timeTypeId)
+               .eq("status", 1) // 启用状态
+               .orderByAsc("sort_order");
+        return closureApplierMapper.selectList(wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean approve(Long applyId, Long applierId, String comment, Integer action) {
+        // 1. 获取申请记录
+        ProjectClosureApply apply = this.getById(applyId);
+        if (apply == null) {
+            return false;
+        }
+        
+        // 2. 检查是否是当前审批人
+        if (!applierId.equals(apply.getCurrentApproverId())) {
+            throw new RuntimeException("您不是当前审批人");
+        }
+        
+        // 3. 创建审批记录
+        ProjectClosureApprovalLog log = new ProjectClosureApprovalLog();
+        log.setApplyId(applyId);
+        log.setApproverId(applierId);
+        log.setApproverName("用户" + applierId); // TODO: 从用户表查询
+        log.setAction(action);
+        log.setOpinion(comment);
+        log.setCreateTime(LocalDateTime.now());
+        approvalLogMapper.insert(log);
+        
+        // 4. 根据审批结果处理
+        if (action == 1) { // 同意
+            apply.setCurrentStep(apply.getCurrentStep() + 1);
+            
+            // 检查是否还有下一个审批人
+            ProjectClosureApplier nextApplier = this.getNextApprover(apply.getProjectId(), apply.getCurrentStep());
+            if (nextApplier != null) {
+                // 还有下一个审批人
+                apply.setCurrentApproverId(nextApplier.getApproverUserId());
+                apply.setCurrentApproverName(nextApplier.getApproverName());
+                apply.setApprovalStatus(1); // 审批中
+                this.updateById(apply);
+            } else {
+                // 所有审批人都同意
+                apply.setApprovalStatus(2); // 已通过
+                this.updateById(apply);
+                // 调用审批通过回调
+                this.onApprovalPassed(applyId);
+            }
+        } else { // 驳回
+            apply.setApprovalStatus(3); // 已驳回
+            this.updateById(apply);
+        }
+        
+        return true;
+    }
+
+    @Override
+    public Map<String, Object> getProgress(Long applyId) {
+        ProjectClosureApply apply = this.getById(applyId);
+        if (apply == null) {
+            return new HashMap<>();
+        }
+        
+        Map<String, Object> progress = new HashMap<>();
+        progress.put("apply", apply);
+        progress.put("logs", approvalLogMapper.selectList(
+            new QueryWrapper<ProjectClosureApprovalLog>()
+                .eq("apply_id", applyId)
+                .orderByDesc("create_time")
+        ));
+        
+        // 获取审批流程
+        ProjectClosureApply actualApply = this.getById(applyId);
+        // TODO: 需要通过项目ID获取timeTypeId,然后获取审批流程
+        List<ProjectClosureApplier> flow = new ArrayList<>();
+        progress.put("flow", flow);
+        
+        return progress;
+    }
+
+    @Override
+    public List<ProjectClosureApprovalLog> getApprovalLogs(Long applyId) {
+        return approvalLogMapper.selectList(
+            new QueryWrapper<ProjectClosureApprovalLog>()
+                .eq("apply_id", applyId)
+                .orderByDesc("create_time")
+        );
+    }
+
+    @Override
+    public IPage<ProjectClosureApply> getMyPendingApproval(Long userId, int current, int size) {
+        Page<ProjectClosureApply> page = new Page<>(current, size);
+        QueryWrapper<ProjectClosureApply> wrapper = new QueryWrapper<>();
+        wrapper.eq("current_approver_id", userId)
+               .in("approval_status", 0, 1) // 待审批或审批中
+               .orderByDesc("create_time");
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    public IPage<ProjectClosureApply> getMyApplyList(Long userId, int current, int size) {
+        Page<ProjectClosureApply> page = new Page<>(current, size);
+        QueryWrapper<ProjectClosureApply> wrapper = new QueryWrapper<>();
+        wrapper.eq("applicant_id", userId)
+               .orderByDesc("create_time");
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void onApprovalPassed(Long applyId) {
+        // 审批通过后,更新项目状态
+        ProjectClosureApply apply = this.getById(applyId);
+        if (apply != null) {
+            // TODO: 调用项目Service更新项目状态为"已结项"
+            // projectService.updateStatus(apply.getProjectId(), ProjectStatus.CLOSED);
+        }
+    }
+    
+    /**
+     * 获取下一个审批人
+     */
+    private ProjectClosureApplier getNextApprover(Long projectId, int currentStep) {
+        // TODO: 需要通过项目获取timeTypeId,然后查询审批人
+        return null;
+    }
+    
+    /**
+     * 设置下一个审批人
+     */
+    private void setNextApprover(Long applyId) {
+        ProjectClosureApply apply = this.getById(applyId);
+        if (apply != null) {
+            ProjectClosureApplier firstApprover = this.getNextApprover(apply.getProjectId(), 0);
+            if (firstApprover != null) {
+                apply.setCurrentApproverId(firstApprover.getApproverUserId());
+                apply.setCurrentApproverName(firstApprover.getApproverName());
+                this.updateById(apply);
+            }
+        }
+    }
+}

+ 20 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectClosureApprovalLogServiceImpl.java

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.ProjectClosureApprovalLog;
+import com.management.platform.mapper.ProjectClosureApprovalLogMapper;
+import com.management.platform.service.ProjectClosureApprovalLogService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 项目结项审批记录表 服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@Service
+public class ProjectClosureApprovalLogServiceImpl extends ServiceImpl<ProjectClosureApprovalLogMapper, ProjectClosureApprovalLog> implements ProjectClosureApprovalLogService {
+
+}

+ 20 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectClosureAttachmentServiceImpl.java

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.ProjectClosureAttachment;
+import com.management.platform.mapper.ProjectClosureAttachmentMapper;
+import com.management.platform.service.ProjectClosureAttachmentService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 项目结项附件表 服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-12
+ */
+@Service
+public class ProjectClosureAttachmentServiceImpl extends ServiceImpl<ProjectClosureAttachmentMapper, ProjectClosureAttachment> implements ProjectClosureAttachmentService {
+
+}

+ 22 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectClosureApplierMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.ProjectClosureApplierMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ProjectClosureApplier">
+        <id column="id" property="id" />
+        <result column="time_type_id" property="timeTypeId" />
+        <result column="approver_user_id" property="approverUserId" />
+        <result column="approver_name" property="approverName" />
+        <result column="sort_order" property="sortOrder" />
+        <result column="status" property="status" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, time_type_id, approver_user_id, approver_name, sort_order, status, create_time, update_time
+    </sql>
+
+</mapper>

+ 27 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectClosureApplyMapper.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.ProjectClosureApplyMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ProjectClosureApply">
+        <id column="id" property="id" />
+        <result column="project_id" property="projectId" />
+        <result column="project_name" property="projectName" />
+        <result column="project_code" property="projectCode" />
+        <result column="applicant_id" property="applicantId" />
+        <result column="applicant_name" property="applicantName" />
+        <result column="current_step" property="currentStep" />
+        <result column="current_approver_id" property="currentApproverId" />
+        <result column="current_approver_name" property="currentApproverName" />
+        <result column="approval_status" property="approvalStatus" />
+        <result column="remark" property="remark" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, project_id, project_name, project_code, applicant_id, applicant_name, current_step, current_approver_id, current_approver_name, approval_status, remark, create_time, update_time
+    </sql>
+
+</mapper>

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectClosureApprovalLogMapper.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.ProjectClosureApprovalLogMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ProjectClosureApprovalLog">
+        <id column="id" property="id" />
+        <result column="apply_id" property="applyId" />
+        <result column="approver_id" property="approverId" />
+        <result column="approver_name" property="approverName" />
+        <result column="action" property="action" />
+        <result column="opinion" property="opinion" />
+        <result column="create_time" property="createTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, apply_id, approver_id, approver_name, action, opinion, create_time
+    </sql>
+
+</mapper>

+ 22 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectClosureAttachmentMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.ProjectClosureAttachmentMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ProjectClosureAttachment">
+        <id column="id" property="id" />
+        <result column="apply_id" property="applyId" />
+        <result column="file_name" property="fileName" />
+        <result column="file_url" property="fileUrl" />
+        <result column="file_size" property="fileSize" />
+        <result column="file_type" property="fileType" />
+        <result column="upload_user_id" property="uploadUserId" />
+        <result column="create_time" property="createTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, apply_id, file_name, file_url, file_size, file_type, upload_user_id, create_time
+    </sql>
+
+</mapper>