Huanyi 3 месяцев назад
Родитель
Сommit
d675f00f81
34 измененных файлов с 870 добавлено и 240 удалено
  1. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
  2. 9 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/constant/QcTaskStatusConst.java
  3. 0 9
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/constant/QualityControlTaskStatusConst.java
  4. 22 17
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/DocumentQcTaskController.java
  5. 36 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/DocumentQcTaskDetailController.java
  6. 6 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/ProjectController.java
  7. 2 3
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/DocumentQcTask.java
  8. 67 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/DocumentQcTaskDetail.java
  9. 0 2
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentAuditBo.java
  10. 0 4
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentFilingBo.java
  11. 7 5
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentQcTaskBo.java
  12. 62 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentQcTaskDetailBo.java
  13. 15 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentQcTaskDetailCreateBo.java
  14. 10 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/ProjectListOnQcBo.java
  15. 16 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/ProjectMemberEditMemberBo.java
  16. 23 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentQcGenerateVo.java
  17. 96 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentQcTaskDetailVo.java
  18. 8 9
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentQcTaskVo.java
  19. 20 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/ProjectListOnQcVo.java
  20. 20 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/enumeration/DocumentQcStatusEnum.java
  21. 48 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/enumeration/MailTemplateEnum.java
  22. 3 5
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/enumeration/ProjectStatusEnum.java
  23. 15 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/mapper/DocumentQcTaskDetailMapper.java
  24. 15 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/mapper/DocumentQcTaskMapper.java
  25. 0 15
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/mapper/DocumentQualityControlTaskMapper.java
  26. 16 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/IDocumentQcTaskDetailService.java
  27. 8 8
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/IDocumentQcTaskService.java
  28. 23 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/CommonDocumentService.java
  29. 88 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentQcTaskDetailServiceImpl.java
  30. 204 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentQcTaskServiceImpl.java
  31. 0 145
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentQualityControlTaskServiceImpl.java
  32. 1 4
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentServiceImpl.java
  33. 27 13
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/ProjectServiceImpl.java
  34. 2 0
      script/sql/business/create.sql

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java

@@ -219,7 +219,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
             return List.of();
         }
         return baseMapper.selectVoList(new LambdaQueryWrapper<SysUser>()
-            .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName)
+//            .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName)
             .eq(SysUser::getStatus, SystemConstants.NORMAL)
             .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds));
     }

+ 9 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/constant/QcTaskStatusConst.java

@@ -0,0 +1,9 @@
+package com.yingpaipay.business.constant;
+
+public interface QcTaskStatusConst {
+
+    Integer UNFINISHED = 0;
+
+    Integer COMPLETE = 1;
+
+}

+ 0 - 9
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/constant/QualityControlTaskStatusConst.java

@@ -1,9 +0,0 @@
-package com.yingpaipay.business.constant;
-
-public interface QualityControlTaskStatusConst {
-
-    Integer IN_EXECUTION = 0;
-
-    Integer COMPLETE = 1;
-
-}

+ 22 - 17
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/DocumentQualityControlTaskController.java → ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/DocumentQcTaskController.java

@@ -2,6 +2,11 @@ package com.yingpaipay.business.controller;
 
 import java.util.List;
 
+import com.yingpaipay.business.domain.bo.DocumentQcTaskBo;
+import com.yingpaipay.business.domain.bo.ProjectListOnQcBo;
+import com.yingpaipay.business.domain.vo.ProjectListOnQcVo;
+import com.yingpaipay.business.domain.vo.ProjectVo;
+import com.yingpaipay.business.service.IProjectService;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
@@ -17,9 +22,8 @@ import org.dromara.common.core.validate.AddGroup;
 import org.dromara.common.core.validate.EditGroup;
 import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.excel.utils.ExcelUtil;
-import com.yingpaipay.business.domain.vo.DocumentQualityControlTaskVo;
-import com.yingpaipay.business.domain.bo.DocumentQualityControlTaskBo;
-import com.yingpaipay.business.service.IDocumentQualityControlTaskService;
+import com.yingpaipay.business.domain.vo.DocumentQcTaskVo;
+import com.yingpaipay.business.service.IDocumentQcTaskService;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 
 /**
@@ -32,17 +36,18 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
 @RequiredArgsConstructor
 @RestController
 @RequestMapping("/qc/task")
-public class DocumentQualityControlTaskController extends BaseController {
+public class DocumentQcTaskController extends BaseController {
 
-    private final IDocumentQualityControlTaskService documentQualityControlTaskService;
+    private final IDocumentQcTaskService documentQcTaskDetailService;
+    private final IProjectService projectService;
 
     /**
      * 查询文档质控任务列表
      */
     @SaCheckPermission("qc:task:list")
     @GetMapping("/list")
-    public TableDataInfo<DocumentQualityControlTaskVo> list(DocumentQualityControlTaskBo bo, PageQuery pageQuery) {
-        return documentQualityControlTaskService.queryPageList(bo, pageQuery);
+    public TableDataInfo<DocumentQcTaskVo> list(DocumentQcTaskBo bo, PageQuery pageQuery) {
+        return documentQcTaskDetailService.queryPageList(bo, pageQuery);
     }
 
     /**
@@ -51,9 +56,9 @@ public class DocumentQualityControlTaskController extends BaseController {
     @SaCheckPermission("qc:task:export")
     @Log(title = "文档质控任务", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
-    public void export(DocumentQualityControlTaskBo bo, HttpServletResponse response) {
-        List<DocumentQualityControlTaskVo> list = documentQualityControlTaskService.queryList(bo);
-        ExcelUtil.exportExcel(list, "文档质控任务", DocumentQualityControlTaskVo.class, response);
+    public void export(DocumentQcTaskBo bo, HttpServletResponse response) {
+        List<DocumentQcTaskVo> list = documentQcTaskDetailService.queryList(bo);
+        ExcelUtil.exportExcel(list, "文档质控任务", DocumentQcTaskVo.class, response);
     }
 
     /**
@@ -63,9 +68,9 @@ public class DocumentQualityControlTaskController extends BaseController {
      */
     @SaCheckPermission("qc:task:query")
     @GetMapping("/{id}")
-    public R<DocumentQualityControlTaskVo> getInfo(@NotNull(message = "主键不能为空")
+    public R<DocumentQcTaskVo> getInfo(@NotNull(message = "主键不能为空")
                                      @PathVariable Long id) {
-        return R.ok(documentQualityControlTaskService.queryById(id));
+        return R.ok(documentQcTaskDetailService.queryById(id));
     }
 
     /**
@@ -75,8 +80,8 @@ public class DocumentQualityControlTaskController extends BaseController {
     @Log(title = "文档质控任务", businessType = BusinessType.INSERT)
     @RepeatSubmit()
     @PostMapping()
-    public R<Void> add(@Validated(AddGroup.class) @RequestBody DocumentQualityControlTaskBo bo) {
-        return toAjax(documentQualityControlTaskService.insertByBo(bo));
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody DocumentQcTaskBo bo) {
+        return toAjax(documentQcTaskDetailService.insertByBo(bo));
     }
 
     /**
@@ -86,8 +91,8 @@ public class DocumentQualityControlTaskController extends BaseController {
     @Log(title = "文档质控任务", businessType = BusinessType.UPDATE)
     @RepeatSubmit()
     @PutMapping()
-    public R<Void> edit(@Validated(EditGroup.class) @RequestBody DocumentQualityControlTaskBo bo) {
-        return toAjax(documentQualityControlTaskService.updateByBo(bo));
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody DocumentQcTaskBo bo) {
+        return toAjax(documentQcTaskDetailService.updateByBo(bo));
     }
 
     /**
@@ -100,6 +105,6 @@ public class DocumentQualityControlTaskController extends BaseController {
     @DeleteMapping("/{ids}")
     public R<Void> remove(@NotEmpty(message = "主键不能为空")
                           @PathVariable Long[] ids) {
-        return toAjax(documentQualityControlTaskService.deleteWithValidByIds(List.of(ids), true));
+        return toAjax(documentQcTaskDetailService.deleteWithValidByIds(List.of(ids), true));
     }
 }

+ 36 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/DocumentQcTaskDetailController.java

@@ -0,0 +1,36 @@
+package com.yingpaipay.business.controller;
+
+
+import com.yingpaipay.business.domain.vo.DocumentQcGenerateVo;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.web.core.BaseController;
+import com.yingpaipay.business.service.IDocumentQcTaskDetailService;
+
+import java.util.List;
+
+/**
+ * 文档质控细节
+ *
+ * @author Huanyi
+ * @date 2026-01-08
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/qcTask/detail")
+public class DocumentQcTaskDetailController extends BaseController {
+
+    private final IDocumentQcTaskDetailService documentQcTaskDetailService;
+
+    /**
+     * 已废弃
+     */
+    @GetMapping("/generate")
+    public R<List<DocumentQcGenerateVo>> generate(@RequestParam Long projectId) {
+        return R.ok(documentQcTaskDetailService.generate(projectId));
+    }
+
+}

+ 6 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/ProjectController.java

@@ -166,4 +166,10 @@ public class ProjectController extends BaseController {
         return folderService.queryCenterMember(bo, pageQuery);
     }
 
+    @SaCheckPermission("project:management:queryProjectMemberEditMember")
+    @PutMapping("/queryProjectMemberEditMember")
+    public R<Void> queryProjectMemberEditMember(@RequestBody ProjectMemberEditMemberBo bo) {
+        return R.ok();
+    }
+
 }

+ 2 - 3
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/DocumentQualityControlTask.java → ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/DocumentQcTask.java

@@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.annotation.*;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import java.util.Date;
-import com.fasterxml.jackson.annotation.JsonFormat;
 
 import java.io.Serial;
 
@@ -17,8 +16,8 @@ import java.io.Serial;
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
-@TableName("document_quality_control_task")
-public class DocumentQualityControlTask extends TenantEntity {
+@TableName("document_qc_task")
+public class DocumentQcTask extends TenantEntity {
 
     @Serial
     private static final long serialVersionUID = 1L;

+ 67 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/DocumentQcTaskDetail.java

@@ -0,0 +1,67 @@
+package com.yingpaipay.business.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 文档质控细节对象 document_quality_control_task_detail
+ *
+ * @author Huanyi
+ * @date 2026-01-08
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("document_qc_task_detail")
+public class DocumentQcTaskDetail extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 序号
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 所属任务
+     */
+    private Long taskId;
+
+    /**
+     * 质控文件
+     */
+    private Long documentId;
+
+    /**
+     * 计划执行人
+     */
+    private Long executor;
+
+    /**
+     * 状态
+     */
+    private Integer status;
+
+    /**
+     * 备注
+     */
+    private String note;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 方便查询的冗余字段
+     */
+    private Long projectId;
+
+
+}

+ 0 - 2
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentAuditBo.java

@@ -11,6 +11,4 @@ public class DocumentAuditBo {
 
     private String rejectReason;
 
-    private Long ossId;
-
 }

+ 0 - 4
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentFilingBo.java

@@ -7,8 +7,4 @@ public class DocumentFilingBo {
 
     private Long documentId;
 
-    private Long folderId;
-
-    private Long projectId;
-
 }

+ 7 - 5
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentQualityControlTaskBo.java → ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentQcTaskBo.java

@@ -1,6 +1,6 @@
 package com.yingpaipay.business.domain.bo;
 
-import com.yingpaipay.business.domain.DocumentQualityControlTask;
+import com.yingpaipay.business.domain.DocumentQcTask;
 import org.dromara.common.mybatis.core.domain.BaseEntity;
 import org.dromara.common.core.validate.AddGroup;
 import org.dromara.common.core.validate.EditGroup;
@@ -9,7 +9,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import jakarta.validation.constraints.*;
 import java.util.Date;
-import com.fasterxml.jackson.annotation.JsonFormat;
+import java.util.List;
 
 /**
  * 文档质控任务业务对象 document_quality_control_task
@@ -19,8 +19,8 @@ import com.fasterxml.jackson.annotation.JsonFormat;
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
-@AutoMapper(target = DocumentQualityControlTask.class, reverseConvertGenerate = false)
-public class DocumentQualityControlTaskBo extends BaseEntity {
+@AutoMapper(target = DocumentQcTask.class, reverseConvertGenerate = false)
+public class DocumentQcTaskBo extends BaseEntity {
 
     /**
      * 序号
@@ -61,7 +61,7 @@ public class DocumentQualityControlTaskBo extends BaseEntity {
     /**
      * 状态
      */
-    @NotNull(message = "状态不能为空", groups = { AddGroup.class, EditGroup.class })
+//    @NotNull(message = "状态不能为空", groups = { AddGroup.class, EditGroup.class })
     private Integer status;
 
     /**
@@ -69,5 +69,7 @@ public class DocumentQualityControlTaskBo extends BaseEntity {
      */
     private String note;
 
+    @NotEmpty(message = "指控任务不能为空")
+    private List<DocumentQcTaskDetailBo> details;
 
 }

+ 62 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentQcTaskDetailBo.java

@@ -0,0 +1,62 @@
+package com.yingpaipay.business.domain.bo;
+
+import com.yingpaipay.business.domain.DocumentQcTaskDetail;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 文档质控细节业务对象 document_quality_control_task_detail
+ *
+ * @author Huanyi
+ * @date 2026-01-08
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = DocumentQcTaskDetail.class, reverseConvertGenerate = false)
+public class DocumentQcTaskDetailBo extends BaseEntity {
+
+    /**
+     * 序号
+     */
+    @NotNull(message = "序号不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 所属任务
+     */
+    @NotNull(message = "所属任务不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long taskId;
+
+    /**
+     * 质控文件
+     */
+    @NotNull(message = "质控文件不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long documentId;
+
+    /**
+     * 计划执行人
+     */
+    @NotNull(message = "计划执行人不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long executor;
+
+    @NotNull(message = "指控项目不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long projectId;
+
+    /**
+     * 状态
+     */
+//    @NotNull(message = "状态不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Integer status;
+
+    /**
+     * 备注
+     */
+    private String note;
+
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/DocumentQcTaskDetailCreateBo.java

@@ -0,0 +1,15 @@
+package com.yingpaipay.business.domain.bo;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class DocumentQcTaskDetailCreateBo {
+
+    @NotNull(message = "指控文档不能为空")
+    private Long document;
+
+    @NotNull(message = "质控人不能为空")
+    private Long executor;
+
+}

+ 10 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/ProjectListOnQcBo.java

@@ -0,0 +1,10 @@
+package com.yingpaipay.business.domain.bo;
+
+import lombok.Data;
+
+@Data
+public class ProjectListOnQcBo {
+
+    private String name;
+
+}

+ 16 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/ProjectMemberEditMemberBo.java

@@ -0,0 +1,16 @@
+package com.yingpaipay.business.domain.bo;
+
+import lombok.Data;
+
+@Data
+public class ProjectMemberEditMemberBo {
+
+    private Long id;
+
+    private String nickname;
+
+    private String phoneNumber;
+
+    private String email;
+
+}

+ 23 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentQcGenerateVo.java

@@ -0,0 +1,23 @@
+package com.yingpaipay.business.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+public class DocumentQcGenerateVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long documentId;
+
+    private String documentName;
+
+    private String executorName;
+    private Long executor;
+
+    private String executorStatus;
+
+}

+ 96 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentQcTaskDetailVo.java

@@ -0,0 +1,96 @@
+package com.yingpaipay.business.domain.vo;
+
+import com.yingpaipay.business.domain.DocumentQcTaskDetail;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 文档质控细节视图对象 document_quality_control_task_detail
+ *
+ * @author Huanyi
+ * @date 2026-01-08
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = DocumentQcTaskDetail.class)
+public class DocumentQcTaskDetailVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 序号
+     */
+    @ExcelProperty(value = "序号")
+    private Long id;
+
+    /**
+     * 所属任务
+     */
+    @ExcelProperty(value = "所属任务")
+    private Long taskId;
+
+    /**
+     * 质控文件
+     */
+    @ExcelProperty(value = "质控文件")
+    private String documentName;
+    private Long documentId;
+
+    /**
+     * 计划执行人
+     */
+    @ExcelProperty(value = "计划执行人")
+    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "executor")
+    private String executorName;
+    private Integer executorStatus;
+    private Long executor;
+
+    /**
+     * 状态
+     */
+    @ExcelProperty(value = "状态")
+    private Integer status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String note;
+
+    /**
+     * 创建者
+     */
+    @ExcelProperty(value = "创建者")
+    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
+    private Long createBy;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+    /**
+     * 更新者
+     */
+    @ExcelProperty(value = "更新者")
+    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "updateBy")
+    private Long updateBy;
+
+    /**
+     * 更新时间
+     */
+    @ExcelProperty(value = "更新时间")
+    private Date updateTime;
+
+
+}

+ 8 - 9
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentQualityControlTaskVo.java → ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentQcTaskVo.java

@@ -1,12 +1,10 @@
 package com.yingpaipay.business.domain.vo;
 
 import java.util.Date;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.yingpaipay.business.domain.DocumentQualityControlTask;
+
+import com.yingpaipay.business.domain.DocumentQcTask;
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;
-import org.dromara.common.excel.annotation.ExcelDictFormat;
-import org.dromara.common.excel.convert.ExcelDictConvert;
 import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
 import org.dromara.common.translation.annotation.Translation;
@@ -14,8 +12,7 @@ import org.dromara.common.translation.constant.TransConstant;
 
 import java.io.Serial;
 import java.io.Serializable;
-import java.util.Date;
-
+import java.util.List;
 
 
 /**
@@ -26,8 +23,8 @@ import java.util.Date;
  */
 @Data
 @ExcelIgnoreUnannotated
-@AutoMapper(target = DocumentQualityControlTask.class)
-public class DocumentQualityControlTaskVo implements Serializable {
+@AutoMapper(target = DocumentQcTask.class)
+public class DocumentQcTaskVo implements Serializable {
 
     @Serial
     private static final long serialVersionUID = 1L;
@@ -48,13 +45,14 @@ public class DocumentQualityControlTaskVo implements Serializable {
      * 发起人
      */
     @ExcelProperty(value = "发起人")
-    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "initiator  ")
+    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "initiator")
     private Long initiator;
 
     /**
      * 质控项目
      */
     @ExcelProperty(value = "质控项目")
+    private String projectName;
     private Long projectId;
 
     /**
@@ -107,5 +105,6 @@ public class DocumentQualityControlTaskVo implements Serializable {
     @ExcelProperty(value = "更新时间")
     private Date updateTime;
 
+    private List<DocumentQcTaskDetailVo> details;
 
 }

+ 20 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/ProjectListOnQcVo.java

@@ -0,0 +1,20 @@
+package com.yingpaipay.business.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+public class ProjectListOnQcVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    private String code;
+
+    private String name;
+
+}

+ 20 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/enumeration/DocumentQcStatusEnum.java

@@ -0,0 +1,20 @@
+package com.yingpaipay.business.enumeration;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum DocumentQcStatusEnum {
+
+    UN_AUDIT(0, "未审核", "Unreviewed"),
+    PASS(1, "通过", "Pass"),
+    REJECT(2, "驳回", "Reject"),
+    ;
+
+    private final Integer code;
+    private final String textZh;
+    private final String textEn;
+
+}

+ 48 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/enumeration/MailTemplateEnum.java

@@ -0,0 +1,48 @@
+package com.yingpaipay.business.enumeration;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum MailTemplateEnum {
+
+    TO_SUBMIT(
+        """
+
+        """,
+        """
+
+        """
+    ),
+    TO_AUDIT(
+        """
+
+        """,
+        """
+
+        """
+    ),
+    AUDIT_REJECT(
+        """
+
+        """,
+        """
+
+        """
+    ),
+    AUDIT_PASS(
+        """
+
+        """,
+        """
+
+        """
+    )
+    ;
+
+    private final String zhContent;
+    private final String enContent;
+
+}

+ 3 - 5
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/enumeration/ProjectStatusEnum.java

@@ -1,5 +1,7 @@
 package com.yingpaipay.business.enumeration;
 
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
 import lombok.Getter;
 
 /**
@@ -10,6 +12,7 @@ import lombok.Getter;
  */
 
 @Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
 public enum ProjectStatusEnum {
 
     UNPLAYED(0, "未开始"),
@@ -21,11 +24,6 @@ public enum ProjectStatusEnum {
     private final Integer value;
     private final String label;
 
-    ProjectStatusEnum(Integer value, String label) {
-        this.value = value;
-        this.label = label;
-    }
-
     public static String getLabel(Integer value) {
         for (ProjectStatusEnum status : values()) {
             if (value.equals(status.value)) {

+ 15 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/mapper/DocumentQcTaskDetailMapper.java

@@ -0,0 +1,15 @@
+package com.yingpaipay.business.mapper;
+
+import com.yingpaipay.business.domain.DocumentQcTaskDetail;
+import com.yingpaipay.business.domain.vo.DocumentQcTaskDetailVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 文档质控细节Mapper接口
+ *
+ * @author Huanyi
+ * @date 2026-01-08
+ */
+public interface DocumentQcTaskDetailMapper extends BaseMapperPlus<DocumentQcTaskDetail, DocumentQcTaskDetailVo> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/mapper/DocumentQcTaskMapper.java

@@ -0,0 +1,15 @@
+package com.yingpaipay.business.mapper;
+
+import com.yingpaipay.business.domain.DocumentQcTask;
+import com.yingpaipay.business.domain.vo.DocumentQcTaskVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 文档质控任务Mapper接口
+ *
+ * @author Huanyi
+ * @date 2026-01-06
+ */
+public interface DocumentQcTaskMapper extends BaseMapperPlus<DocumentQcTask, DocumentQcTaskVo> {
+
+}

+ 0 - 15
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/mapper/DocumentQualityControlTaskMapper.java

@@ -1,15 +0,0 @@
-package com.yingpaipay.business.mapper;
-
-import com.yingpaipay.business.domain.DocumentQualityControlTask;
-import com.yingpaipay.business.domain.vo.DocumentQualityControlTaskVo;
-import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
-
-/**
- * 文档质控任务Mapper接口
- *
- * @author Huanyi
- * @date 2026-01-06
- */
-public interface DocumentQualityControlTaskMapper extends BaseMapperPlus<DocumentQualityControlTask, DocumentQualityControlTaskVo> {
-
-}

+ 16 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/IDocumentQcTaskDetailService.java

@@ -0,0 +1,16 @@
+package com.yingpaipay.business.service;
+
+import com.yingpaipay.business.domain.vo.DocumentQcGenerateVo;
+
+import java.util.List;
+
+/**
+ * 文档质控细节Service接口
+ *
+ * @author Huanyi
+ * @date 2026-01-08
+ */
+public interface IDocumentQcTaskDetailService {
+
+    List<DocumentQcGenerateVo> generate(Long projectId);
+}

+ 8 - 8
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/IDocumentQualityControlTaskService.java → ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/IDocumentQcTaskService.java

@@ -1,7 +1,7 @@
 package com.yingpaipay.business.service;
 
-import com.yingpaipay.business.domain.vo.DocumentQualityControlTaskVo;
-import com.yingpaipay.business.domain.bo.DocumentQualityControlTaskBo;
+import com.yingpaipay.business.domain.bo.DocumentQcTaskBo;
+import com.yingpaipay.business.domain.vo.DocumentQcTaskVo;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
 
@@ -14,7 +14,7 @@ import java.util.List;
  * @author Huanyi
  * @date 2026-01-06
  */
-public interface IDocumentQualityControlTaskService {
+public interface IDocumentQcTaskService {
 
     /**
      * 查询文档质控任务
@@ -22,7 +22,7 @@ public interface IDocumentQualityControlTaskService {
      * @param id 主键
      * @return 文档质控任务
      */
-    DocumentQualityControlTaskVo queryById(Long id);
+    DocumentQcTaskVo queryById(Long id);
 
     /**
      * 分页查询文档质控任务列表
@@ -31,7 +31,7 @@ public interface IDocumentQualityControlTaskService {
      * @param pageQuery 分页参数
      * @return 文档质控任务分页列表
      */
-    TableDataInfo<DocumentQualityControlTaskVo> queryPageList(DocumentQualityControlTaskBo bo, PageQuery pageQuery);
+    TableDataInfo<DocumentQcTaskVo> queryPageList(DocumentQcTaskBo bo, PageQuery pageQuery);
 
     /**
      * 查询符合条件的文档质控任务列表
@@ -39,7 +39,7 @@ public interface IDocumentQualityControlTaskService {
      * @param bo 查询条件
      * @return 文档质控任务列表
      */
-    List<DocumentQualityControlTaskVo> queryList(DocumentQualityControlTaskBo bo);
+    List<DocumentQcTaskVo> queryList(DocumentQcTaskBo bo);
 
     /**
      * 新增文档质控任务
@@ -47,7 +47,7 @@ public interface IDocumentQualityControlTaskService {
      * @param bo 文档质控任务
      * @return 是否新增成功
      */
-    Boolean insertByBo(DocumentQualityControlTaskBo bo);
+    Boolean insertByBo(DocumentQcTaskBo bo);
 
     /**
      * 修改文档质控任务
@@ -55,7 +55,7 @@ public interface IDocumentQualityControlTaskService {
      * @param bo 文档质控任务
      * @return 是否修改成功
      */
-    Boolean updateByBo(DocumentQualityControlTaskBo bo);
+    Boolean updateByBo(DocumentQcTaskBo bo);
 
     /**
      * 校验并批量删除文档质控任务信息

+ 23 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/CommonDocumentService.java

@@ -1,11 +1,13 @@
 package com.yingpaipay.business.service.impl;
 
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.yingpaipay.business.constant.DocumentStatusConst;
 import com.yingpaipay.business.domain.Document;
 import com.yingpaipay.business.mapper.DocumentMapper;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 
+import java.util.Collections;
 import java.util.List;
 
 @Service
@@ -35,4 +37,25 @@ public class CommonDocumentService {
                 .eq(Document::getOssId, ossId)
         );
     }
+
+    public List<Document> selectByOutofIds(List<Long> documentIds) {
+        if (documentIds.isEmpty()) {
+            return baseMapper.selectList();
+        }
+        return baseMapper.selectList(
+            Wrappers.lambdaQuery(Document.class)
+                .eq(Document::getStatus, DocumentStatusConst.FILING)
+                .notIn(Document::getId, documentIds)
+        );
+    }
+
+    public List<Document> getByIds(List<Long> ids) {
+        if (ids.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return baseMapper.selectList(
+            Wrappers.lambdaQuery(Document.class)
+                .in(Document::getId, ids)
+        );
+    }
 }

+ 88 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentQcTaskDetailServiceImpl.java

@@ -0,0 +1,88 @@
+package com.yingpaipay.business.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.yingpaipay.business.domain.Document;
+import com.yingpaipay.business.domain.DocumentQcTaskDetail;
+import com.yingpaipay.business.domain.vo.DocumentQcGenerateVo;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.BusinessException;
+import org.dromara.system.domain.vo.SysUserVo;
+import org.dromara.system.service.ISysUserService;
+import org.springframework.stereotype.Service;
+import com.yingpaipay.business.mapper.DocumentQcTaskDetailMapper;
+import com.yingpaipay.business.service.IDocumentQcTaskDetailService;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+/**
+ * 文档质控细节Service业务层处理
+ *
+ * @author Huanyi
+ * @date 2026-01-08
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class DocumentQcTaskDetailServiceImpl implements IDocumentQcTaskDetailService {
+
+    private final DocumentQcTaskDetailMapper baseMapper;
+
+    private final CommonDocumentService documentService;
+    private final ISysUserService userService;
+
+    /**
+     * FIXME 需要考虑到并发场景下重复生成同一个文件的质控任务
+     * 以目前的场景,可暂时不考虑
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public List<DocumentQcGenerateVo> generate(Long projectId) {
+
+        List<Long> documentIds = new ArrayList<>();
+        baseMapper.selectList(
+            Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
+                .eq(DocumentQcTaskDetail::getProjectId, projectId)
+        ).forEach(e -> documentIds.add(e.getDocumentId()));
+        List<Document> documents = documentService.selectByOutofIds(documentIds);
+
+        // FIXME 先写死,后续走配置
+        int count = 5;
+
+        int selectCount = Math.min(count, documents.size());
+
+        List<Document> shuffled = new ArrayList<>(documents);
+        Collections.shuffle(shuffled);
+
+        List<Document> selected = shuffled.subList(0, selectCount);
+
+        List<Long> selectedUserIds = new ArrayList<>();
+        Map<Long, SysUserVo> selectedUserMap = new HashMap<>();
+        selected.forEach(e -> selectedUserIds.add(e.getCreateBy()));
+        userService.selectUserByIds(selectedUserIds)
+            .forEach(e -> selectedUserMap.put(e.getUserId(), e));
+
+        List<DocumentQcGenerateVo> result = new ArrayList<>();
+        for (Document document : selected) {
+            DocumentQcGenerateVo vo = new DocumentQcGenerateVo();
+            vo.setDocumentId(document.getId());
+            vo.setDocumentName(document.getName());
+            SysUserVo user = selectedUserMap.get(document.getCreateBy());
+            if (user != null) {
+                vo.setExecutor(user.getUserId());
+                vo.setExecutorName(user.getNickName());
+                vo.setExecutorStatus(user.getStatus());
+            }
+            result.add(vo);
+        }
+
+        // TODO 不能创建空的质控任务
+        if (result.isEmpty()) {
+            throw new BusinessException("当前没有文档需要进行质控");
+        }
+
+        return result;
+
+    }
+}

+ 204 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentQcTaskServiceImpl.java

@@ -0,0 +1,204 @@
+package com.yingpaipay.business.service.impl;
+
+import com.yingpaipay.business.domain.Document;
+import com.yingpaipay.business.domain.DocumentQcTask;
+import com.yingpaipay.business.domain.DocumentQcTaskDetail;
+import com.yingpaipay.business.domain.bo.DocumentQcTaskBo;
+import com.yingpaipay.business.domain.bo.DocumentQcTaskDetailBo;
+import com.yingpaipay.business.domain.vo.DocumentQcTaskDetailVo;
+import com.yingpaipay.business.domain.vo.DocumentQcTaskVo;
+import com.yingpaipay.business.mapper.DocumentQcTaskDetailMapper;
+import org.dromara.common.core.exception.BusinessException;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.system.domain.vo.SysUserVo;
+import org.dromara.system.service.ISysUserService;
+import org.springframework.stereotype.Service;
+import com.yingpaipay.business.mapper.DocumentQcTaskMapper;
+import com.yingpaipay.business.service.IDocumentQcTaskService;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+/**
+ * 文档质控任务Service业务层处理
+ *
+ * @author Huanyi
+ * @date 2026-01-06
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class DocumentQcTaskServiceImpl implements IDocumentQcTaskService {
+
+    private final DocumentQcTaskMapper baseMapper;
+    private final DocumentQcTaskDetailMapper detailMapper;
+
+    private final CommonProjectService projectService;
+    private final CommonDocumentService documentService;
+    private final ISysUserService userService;
+
+    /**
+     * 查询文档质控任务
+     *
+     * @param id 主键
+     * @return 文档质控任务
+     */
+    @Override
+    public DocumentQcTaskVo queryById(Long id){
+        DocumentQcTaskVo vo = baseMapper.selectVoById(id);
+        List<DocumentQcTaskDetailVo> voList = detailMapper.selectVoList(
+            Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
+                .eq(DocumentQcTaskDetail::getTaskId, vo.getId())
+        );
+        List<Long> documentIds = new ArrayList<>();
+        List<Long> userIds = new ArrayList<>();
+        Map<Long, String> documentMap = new HashMap<>();
+        Map<Long, SysUserVo> userMap = new HashMap<>();
+        voList.forEach(e -> {
+            documentIds.add(e.getDocumentId());
+            userIds.add(e.getExecutor());
+        });
+        documentService.getByIds(documentIds).forEach(e -> documentMap.put(e.getId(), e.getName()));
+        userService.selectUserByIds(userIds).forEach(e -> userMap.put(e.getUserId(), e));
+        voList.forEach(e -> {
+            e.setDocumentName(documentMap.get(e.getDocumentId()));
+            SysUserVo executor = userMap.get(e.getExecutor());
+            e.setExecutorName(executor.getNickName());
+            e.setExecutorStatus(e.getStatus());
+        });
+        vo.setDetails(voList);
+        return vo;
+    }
+
+    /**
+     * 分页查询文档质控任务列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 文档质控任务分页列表
+     */
+    @Override
+    public TableDataInfo<DocumentQcTaskVo> queryPageList(DocumentQcTaskBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<DocumentQcTask> lqw = buildQueryWrapper(bo);
+        Page<DocumentQcTaskVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        buildVo(result.getRecords());
+        return TableDataInfo.build(result);
+    }
+
+    private void buildVo(List<DocumentQcTaskVo> result) {
+        List<Long> projectIds = new ArrayList<>();
+        result.forEach(e -> projectIds.add(e.getProjectId()));
+        Map<Long, String> projectMap = new HashMap<>();
+        projectService.queryByIds(projectIds).forEach(e -> projectMap.put(e.getId(), e.getName()));
+        result.forEach(e -> e.setProjectName(projectMap.get(e.getProjectId())));
+    }
+
+    /**
+     * 查询符合条件的文档质控任务列表
+     *
+     * @param bo 查询条件
+     * @return 文档质控任务列表
+     */
+    @Override
+    public List<DocumentQcTaskVo> queryList(DocumentQcTaskBo bo) {
+        LambdaQueryWrapper<DocumentQcTask> lqw = buildQueryWrapper(bo);
+        List<DocumentQcTaskVo> vos = baseMapper.selectVoList(lqw);
+        buildVo(vos);
+        return vos;
+    }
+
+    private LambdaQueryWrapper<DocumentQcTask> buildQueryWrapper(DocumentQcTaskBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<DocumentQcTask> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(DocumentQcTask::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getName()), DocumentQcTask::getName, bo.getName());
+        lqw.eq(bo.getInitiator() != null, DocumentQcTask::getInitiator, bo.getInitiator());
+        lqw.eq(bo.getProjectId() != null, DocumentQcTask::getProjectId, bo.getProjectId());
+        lqw.between(params.get("beginStartDate") != null && params.get("endStartDate") != null,
+            DocumentQcTask::getStartDate ,params.get("beginStartDate"), params.get("endStartDate"));
+        lqw.between(params.get("beginDeadline") != null && params.get("endDeadline") != null,
+            DocumentQcTask::getDeadline ,params.get("beginDeadline"), params.get("endDeadline"));
+        lqw.eq(bo.getStatus() != null, DocumentQcTask::getStatus, bo.getStatus());
+        lqw.eq(bo.getCreateBy() != null, DocumentQcTask::getCreateBy, bo.getCreateBy());
+        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
+            DocumentQcTask::getCreateTime ,params.get("beginCreateTime"), params.get("endCreateTime"));
+        lqw.eq(bo.getUpdateBy() != null, DocumentQcTask::getUpdateBy, bo.getUpdateBy());
+        lqw.between(params.get("beginUpdateTime") != null && params.get("endUpdateTime") != null,
+            DocumentQcTask::getUpdateTime ,params.get("beginUpdateTime"), params.get("endUpdateTime"));
+        return lqw;
+    }
+
+    /**
+     * 新增文档质控任务
+     *
+     * @param bo 文档质控任务
+     * @return 是否新增成功
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean insertByBo(DocumentQcTaskBo bo) {
+        DocumentQcTask add = MapstructUtils.convert(bo, DocumentQcTask.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) == 0;
+        if (flag) {
+            throw new BusinessException("新增指控任务失败");
+        }
+
+        // FIXME 这里存在着问题 : 并发场景下生成了同一个文档的不同指控任务,暂不考虑
+        List<DocumentQcTaskDetailBo> details = bo.getDetails();
+        List<DocumentQcTaskDetail> list = new ArrayList<>();
+        details.forEach(e -> {
+            e.setTaskId(add.getId());
+            list.add(MapstructUtils.convert(e, DocumentQcTaskDetail.class));
+        });
+        boolean detailFlag = detailMapper.insertBatch(list);
+        if (!detailFlag) {
+            throw new BusinessException("指控细节插入失败");
+        }
+
+        return true;
+    }
+
+    /**
+     * 修改文档质控任务
+     *
+     * @param bo 文档质控任务
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(DocumentQcTaskBo bo) {
+        DocumentQcTask update = MapstructUtils.convert(bo, DocumentQcTask.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(DocumentQcTask entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除文档质控任务信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 0 - 145
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentQualityControlTaskServiceImpl.java

@@ -1,145 +0,0 @@
-package com.yingpaipay.business.service.impl;
-
-import org.dromara.common.core.utils.MapstructUtils;
-import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.mybatis.core.page.TableDataInfo;
-import org.dromara.common.mybatis.core.page.PageQuery;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import com.yingpaipay.business.domain.bo.DocumentQualityControlTaskBo;
-import com.yingpaipay.business.domain.vo.DocumentQualityControlTaskVo;
-import com.yingpaipay.business.domain.DocumentQualityControlTask;
-import com.yingpaipay.business.mapper.DocumentQualityControlTaskMapper;
-import com.yingpaipay.business.service.IDocumentQualityControlTaskService;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Collection;
-
-/**
- * 文档质控任务Service业务层处理
- *
- * @author Huanyi
- * @date 2026-01-06
- */
-@Slf4j
-@RequiredArgsConstructor
-@Service
-public class DocumentQualityControlTaskServiceImpl implements IDocumentQualityControlTaskService {
-
-    private final DocumentQualityControlTaskMapper baseMapper;
-
-    /**
-     * 查询文档质控任务
-     *
-     * @param id 主键
-     * @return 文档质控任务
-     */
-    @Override
-    public DocumentQualityControlTaskVo queryById(Long id){
-        return baseMapper.selectVoById(id);
-    }
-
-    /**
-     * 分页查询文档质控任务列表
-     *
-     * @param bo        查询条件
-     * @param pageQuery 分页参数
-     * @return 文档质控任务分页列表
-     */
-    @Override
-    public TableDataInfo<DocumentQualityControlTaskVo> queryPageList(DocumentQualityControlTaskBo bo, PageQuery pageQuery) {
-        LambdaQueryWrapper<DocumentQualityControlTask> lqw = buildQueryWrapper(bo);
-        Page<DocumentQualityControlTaskVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
-        return TableDataInfo.build(result);
-    }
-
-    /**
-     * 查询符合条件的文档质控任务列表
-     *
-     * @param bo 查询条件
-     * @return 文档质控任务列表
-     */
-    @Override
-    public List<DocumentQualityControlTaskVo> queryList(DocumentQualityControlTaskBo bo) {
-        LambdaQueryWrapper<DocumentQualityControlTask> lqw = buildQueryWrapper(bo);
-        return baseMapper.selectVoList(lqw);
-    }
-
-    private LambdaQueryWrapper<DocumentQualityControlTask> buildQueryWrapper(DocumentQualityControlTaskBo bo) {
-        Map<String, Object> params = bo.getParams();
-        LambdaQueryWrapper<DocumentQualityControlTask> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(DocumentQualityControlTask::getId);
-        lqw.like(StringUtils.isNotBlank(bo.getName()), DocumentQualityControlTask::getName, bo.getName());
-        lqw.eq(bo.getInitiator() != null, DocumentQualityControlTask::getInitiator, bo.getInitiator());
-        lqw.eq(bo.getProjectId() != null, DocumentQualityControlTask::getProjectId, bo.getProjectId());
-        lqw.between(params.get("beginStartDate") != null && params.get("endStartDate") != null,
-            DocumentQualityControlTask::getStartDate ,params.get("beginStartDate"), params.get("endStartDate"));
-        lqw.between(params.get("beginDeadline") != null && params.get("endDeadline") != null,
-            DocumentQualityControlTask::getDeadline ,params.get("beginDeadline"), params.get("endDeadline"));
-        lqw.eq(bo.getStatus() != null, DocumentQualityControlTask::getStatus, bo.getStatus());
-        lqw.eq(bo.getCreateBy() != null, DocumentQualityControlTask::getCreateBy, bo.getCreateBy());
-        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
-            DocumentQualityControlTask::getCreateTime ,params.get("beginCreateTime"), params.get("endCreateTime"));
-        lqw.eq(bo.getUpdateBy() != null, DocumentQualityControlTask::getUpdateBy, bo.getUpdateBy());
-        lqw.between(params.get("beginUpdateTime") != null && params.get("endUpdateTime") != null,
-            DocumentQualityControlTask::getUpdateTime ,params.get("beginUpdateTime"), params.get("endUpdateTime"));
-        return lqw;
-    }
-
-    /**
-     * 新增文档质控任务
-     *
-     * @param bo 文档质控任务
-     * @return 是否新增成功
-     */
-    @Override
-    public Boolean insertByBo(DocumentQualityControlTaskBo bo) {
-        DocumentQualityControlTask add = MapstructUtils.convert(bo, DocumentQualityControlTask.class);
-        validEntityBeforeSave(add);
-        boolean flag = baseMapper.insert(add) > 0;
-        if (flag) {
-            bo.setId(add.getId());
-        }
-        return flag;
-    }
-
-    /**
-     * 修改文档质控任务
-     *
-     * @param bo 文档质控任务
-     * @return 是否修改成功
-     */
-    @Override
-    public Boolean updateByBo(DocumentQualityControlTaskBo bo) {
-        DocumentQualityControlTask update = MapstructUtils.convert(bo, DocumentQualityControlTask.class);
-        validEntityBeforeSave(update);
-        return baseMapper.updateById(update) > 0;
-    }
-
-    /**
-     * 保存前的数据校验
-     */
-    private void validEntityBeforeSave(DocumentQualityControlTask entity){
-        //TODO 做一些数据校验,如唯一约束
-    }
-
-    /**
-     * 校验并批量删除文档质控任务信息
-     *
-     * @param ids     待删除的主键集合
-     * @param isValid 是否进行有效性校验
-     * @return 是否删除成功
-     */
-    @Override
-    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
-            //TODO 做一些业务上的校验,判断是否需要校验
-        }
-        return baseMapper.deleteByIds(ids) > 0;
-    }
-}

+ 1 - 4
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentServiceImpl.java

@@ -301,12 +301,9 @@ public class DocumentServiceImpl implements IDocumentService {
 
     @Override
     public boolean filing(DocumentFilingBo bo) {
-        int type = folderService.getSpecificationType(bo.getFolderId(), bo.getProjectId());
         return baseMapper.update(
             Wrappers.lambdaUpdate(Document.class)
                 .eq(Document::getId, bo.getDocumentId())
-                .set(Document::getFolderId, bo.getFolderId())
-                .set(Document::getSpecificationType, type)
                 .set(Document::getStatus, DocumentStatusConst.FILING)
         ) > 0;
     }
@@ -751,7 +748,7 @@ public class DocumentServiceImpl implements IDocumentService {
     private DocumentAuditLog buildLog(Document document, String type, DocumentAuditBo bo) {
         DocumentAuditLog log = new DocumentAuditLog();
         log.setDocumentId(document.getId());
-        log.setOssId(bo.getOssId() != null ? bo.getOssId() : document.getOssId());
+        log.setOssId(document.getOssId());
         log.setAuditorType(type);
         log.setAuditorId(LoginHelper.getUserId());
         log.setResult(bo.getResult());

+ 27 - 13
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/ProjectServiceImpl.java

@@ -361,23 +361,37 @@ public class ProjectServiceImpl implements IProjectService {
             vo.setCode(e.getCode());
             vo.setName(e.getName());
             int total = documentList.size();
+            long onTimeSubmit = 0L, lateSubmit = 0L, notSubmit = 0L;
             List<Document> planDocuments = documentList.stream()
                 .filter(item -> item.getType().equals(DocumentTypeConst.PLAN))
                 .toList();
-            long onTimeSubmit = planDocuments.stream()
-                .filter(item -> item.getOssId() != null && item.getSubmitTime().before(item.getSubmitDeadline()))
-                .count();
-            long lateSubmit = planDocuments.stream()
-                .filter(item -> item.getOssId() != null)
-                .filter(item -> item.getSubmitDeadline().before(item.getSubmitTime()))
-                .count();
-            long notSubmit = planDocuments.stream()
-                .filter(item -> item.getOssId() == null)
-                .filter(item -> item.getSubmitDeadline().before(new Date()))
-                .count();
-            vo.setOnTimeSubmissionRate(Double.valueOf(String.format("%.2f", (double) onTimeSubmit / total * 100)));
+            if (total > 0) {
+                onTimeSubmit = planDocuments.stream()
+                    .filter(item -> item.getOssId() != null && item.getSubmitTime().before(item.getSubmitDeadline()))
+                    .count();
+                lateSubmit = planDocuments.stream()
+                    .filter(item -> item.getOssId() != null)
+                    .filter(item -> item.getSubmitDeadline().before(item.getSubmitTime()))
+                    .count();
+                notSubmit = planDocuments.stream()
+                    .filter(item -> item.getOssId() == null)
+                    .filter(item -> item.getSubmitDeadline().before(new Date()))
+                    .count();
+            }
+//            long onTimeSubmit = planDocuments.stream()
+//                .filter(item -> item.getOssId() != null && item.getSubmitTime().before(item.getSubmitDeadline()))
+//                .count();
+//            long lateSubmit = planDocuments.stream()
+//                .filter(item -> item.getOssId() != null)
+//                .filter(item -> item.getSubmitDeadline().before(item.getSubmitTime()))
+//                .count();
+//            long notSubmit = planDocuments.stream()
+//                .filter(item -> item.getOssId() == null)
+//                .filter(item -> item.getSubmitDeadline().before(new Date()))
+//                .count();
+            vo.setOnTimeSubmissionRate(Double.valueOf(String.format("%.2f", (double) onTimeSubmit / total * 100L)));
             vo.setLateSubmissionCount(lateSubmit);
-            vo.setSubmissionProgress(Double.valueOf(String.format("%.2f", (double) notSubmit / total * 100)));
+            vo.setSubmissionProgress(Double.valueOf(String.format("%.2f", (double) notSubmit / total * 100L)));
             vo.setPdGpd(e.getPdGpd());
             vo.setPmGpm(e.getPmGpm());
             vo.setCtaGcta(e.getCtaGcta());

+ 2 - 0
script/sql/business/create.sql

@@ -124,6 +124,8 @@ CREATE TABLE `document_quality_control_task_detail`
     `id`          bigint unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '序号',
     `task_id`     bigint unsigned             NOT NULL COMMENT '所属任务',
     `document_id` bigint unsigned             NOT NULL COMMENT '质控文件',
+    `project_id`  bigint unsigned             NOT NULL COMMENT '所属项目',
+    `executor`    bigint unsigned             NOT NULL COMMENT '计划执行人',
     `status`      tinyint(1)                  NOT NULL DEFAULT 0 COMMENT '状态',
     `note`        varchar(255) COMMENT '备注',
     `create_dept` bigint(20) COMMENT '创建部门',