diff --git a/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/institution/EvalProcessControlExecutor.java b/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/institution/EvalProcessControlExecutor.java index 3ee6c0b..90baeb4 100644 --- a/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/institution/EvalProcessControlExecutor.java +++ b/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/institution/EvalProcessControlExecutor.java @@ -1,22 +1,19 @@ package org.qinan.safetyeval.app.executor.institution; +import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.qinan.safetyeval.app.executor.institution.support.EvalCmdCopy; import org.qinan.safetyeval.app.executor.institution.support.EvalNodeCoConverter; import org.qinan.safetyeval.app.executor.institution.support.EvalProjectNodeSupport; import org.qinan.safetyeval.app.item.convertor.EvalReviewItemCmdConvertor; import org.qinan.safetyeval.client.api.institution.EvalProcessControlApi; -import org.qinan.safetyeval.client.co.institution.EvalProcessControlCO; -import org.qinan.safetyeval.client.co.institution.EvalProcessControlDetailCO; -import org.qinan.safetyeval.client.co.institution.EvalProjectFileCO; -import org.qinan.safetyeval.client.co.institution.ProjectNodeCO; +import org.qinan.safetyeval.client.co.institution.*; import org.qinan.safetyeval.client.dto.SingleResponse; -import org.qinan.safetyeval.client.dto.institution.EvalProcessArchiveCmd; -import org.qinan.safetyeval.client.dto.institution.EvalProcessControlAssignCmd; -import org.qinan.safetyeval.client.dto.institution.EvalProcessControlConfirmCmd; -import org.qinan.safetyeval.client.dto.institution.EvalProcessControlSaveCmd; -import org.qinan.safetyeval.client.dto.institution.EvalProcessHandoverFormCmd; +import org.qinan.safetyeval.client.dto.institution.*; import org.qinan.safetyeval.client.item.request.EvalReviewItemAddCmd; import org.qinan.safetyeval.client.item.response.EvalReviewItemCo; import org.qinan.safetyeval.domain.constant.*; @@ -39,12 +36,14 @@ import org.qinan.safetyeval.domain.waring.gateway.EvalProjectWaringGateway; import org.qinan.safetyeval.domain.waring.model.EvalProjectWaringE; import org.qinan.safetyeval.infrastructure.convertor.EvalProcessControlConvertor; import org.qinan.safetyeval.infrastructure.convertor.EvalProjectMemberDoConvertor; -import org.qinan.safetyeval.infrastructure.dataobject.EvalInternalReviewDO; import org.qinan.safetyeval.infrastructure.dataobject.EvalProcessControlDO; +import org.qinan.safetyeval.infrastructure.dataobject.EvalProjectDocDO; import org.qinan.safetyeval.infrastructure.dataobject.EvalProjectMemberDO; import org.qinan.safetyeval.infrastructure.mapper.EvalProcessControlMapper; +import org.qinan.safetyeval.infrastructure.mapper.EvalProjectDocMapper; import org.qinan.safetyeval.infrastructure.mapper.EvalProjectMemberMapper; import org.qinan.safetyeval.infrastructure.persistence.queryservice.*; +import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; @@ -52,6 +51,7 @@ import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -102,6 +102,8 @@ public class EvalProcessControlExecutor implements EvalProcessControlApi { private EvalReportQueryService evalReportQueryService; @Resource private EvalProcessControlMapper evalProcessControlMapper; + @Resource + private EvalProjectDocMapper evalProjectDocMapper; @Override public SingleResponse getOrSave(EvalProcessControlSaveCmd cmd) { @@ -303,6 +305,8 @@ public class EvalProcessControlExecutor implements EvalProcessControlApi { projectStageSyncService.onArchived(projectId); // 写入项目预警记录(存在则更新,不存在则新增) upsertProjectWaring(project); + // 归档项目资料:将所有项目文件以 ARCHIVED 资料类型存入 eval_project_doc + upsertArchivedProjectDocs(projectId, project); return SingleResponse.success(EvalNodeCoConverter.toProcessCO(entity)); } @@ -328,9 +332,68 @@ public class EvalProcessControlExecutor implements EvalProcessControlApi { // 过程控制-归档报告 files.addAll(evalReportQueryService.listProjectFiles(projectId, orgId)); + // 统一规范化 projectDocUrl 格式为 [{"url":"xxx","name":"xxx"}] + files.forEach(this::normalizeProjectDocUrl); + return SingleResponse.success(files); } + /** + * 将 projectDocUrl 统一规范化为 [{"url":"xxx","name":"xxx"}] 格式。 + * 支持以下输入: + * 1. 纯字符串URL:https://xxx.com/file.pdf + * 2. JSON字符串数组:["url1","url2"] + * 3. 已规范化的JSON对象数组:[{"url":"xxx","name":"xxx"}] + */ + private void normalizeProjectDocUrl(EvalProjectFileCO co) { + String raw = co.getProjectDocUrl(); + if (!StringUtils.hasText(raw)) { + return; + } + String trimmed = raw.trim(); + JSONArray result = new JSONArray(); + if (trimmed.startsWith("[")) { + try { + JSONArray arr = JSON.parseArray(trimmed); + for (int i = 0; i < arr.size(); i++) { + Object item = arr.get(i); + if (item instanceof JSONObject) { + result.add(item); + } else { + result.add(buildUrlNameObj(String.valueOf(item))); + } + } + co.setProjectDocUrl(result.toJSONString()); + return; + } catch (Exception ignored) { + // 非合法JSON数组,按纯字符串处理 + } + } + result.add(buildUrlNameObj(trimmed)); + co.setProjectDocUrl(result.toJSONString()); + } + + private JSONObject buildUrlNameObj(String url) { + JSONObject obj = new JSONObject(); + obj.put("url", url); + obj.put("name", extractFileName(url)); + return obj; + } + + private String extractFileName(String url) { + if (!StringUtils.hasText(url)) { + return ""; + } + String noQuery = url; + int queryIdx = noQuery.indexOf('?'); + if (queryIdx > 0) { + noQuery = noQuery.substring(0, queryIdx); + } + int slashIdx = noQuery.lastIndexOf('/'); + String name = slashIdx >= 0 ? noQuery.substring(slashIdx + 1) : noQuery; + return StringUtils.hasText(name) ? name : url; + } + private EvalReportEntity buildReport(EvalProjectEntity project) { EvalReportEntity report = new EvalReportEntity(); report.setOrgId(project.getOrgId()); @@ -399,6 +462,53 @@ public class EvalProcessControlExecutor implements EvalProcessControlApi { } } + /** + * 归档时将 listProjectFiles 查询出的所有项目文件以 ARCHIVED 资料类型合并写入 eval_project_doc.project_doc_url。 + * 写入前先移除原有 materialType=ARCHIVED 的记录,再追加本次归档的文件后更新。 + */ + private void upsertArchivedProjectDocs(Long projectId, EvalProjectEntity project) { + List files = listProjectFiles(projectId).getData(); + List archivedItems = CollectionUtils.isEmpty(files) + ? new ArrayList<>() + : files.stream().map(f -> { + EvalProjectDocMaterialItemCO item = new EvalProjectDocMaterialItemCO(); + item.setMaterialName(f.getMaterialName()); + item.setProjectDocUrl(f.getProjectDocUrl()); + item.setMaterialType(MaterialCategory.ARCHIVED.getVal()); + return item; + }).collect(Collectors.toList()); + + EvalProjectDocDO docDO = evalProjectDocMapper.selectOne(new LambdaQueryWrapper() + .select(EvalProjectDocDO::getId, EvalProjectDocDO::getProjectDocUrl) + .eq(EvalProjectDocDO::getProjectId, projectId)); + if (docDO == null) { + docDO = new EvalProjectDocDO(); + docDO.setOrgId(project.getOrgId()); + docDO.setProjectId(projectId); + docDO.setProjectNo(project.getProjectNo()); + docDO.setProjectName(project.getProjectName()); + docDO.setEvalTypeCode(project.getEvalTypeCode()); + docDO.setProjectDocUrl(JSON.toJSONString(archivedItems)); + InsertFieldDefaults.apply(docDO); + evalProjectDocMapper.insert(docDO); + } else { + List existing = new ArrayList<>(); + if (StringUtils.hasText(docDO.getProjectDocUrl())) { + List parsed = JSONUtil.toList(docDO.getProjectDocUrl(), EvalProjectDocMaterialItemCO.class); + if (parsed != null) { + existing.addAll(parsed); + } + } + // 先移除原有 ARCHIVED 类型的资料,再追加本次归档的文件 + String archivedVal = MaterialCategory.ARCHIVED.getVal(); + existing.removeIf(item -> archivedVal.equals(item.getMaterialType())); + existing.addAll(archivedItems); + evalProjectDocMapper.update(new LambdaUpdateWrapper() + .eq(EvalProjectDocDO::getId, docDO.getId()) + .set(EvalProjectDocDO::getProjectDocUrl, JSON.toJSONString(existing))); + } + } + /** * 验证项目所有签字任务是否已完成(taskStatusCode=2 已签)。 * 无签字任务或存在未完成/未作废的任务均视为未完成。 diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalInternalReviewAssignCmd.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalInternalReviewAssignCmd.java index 1051388..3c01322 100644 --- a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalInternalReviewAssignCmd.java +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalInternalReviewAssignCmd.java @@ -3,12 +3,23 @@ package org.qinan.safetyeval.client.dto.institution; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + @Data public class EvalInternalReviewAssignCmd { @ApiModelProperty("项目id") + @NotNull private Long projectId; + @ApiModelProperty("签字任务人员") + @NotNull + @Valid + private SignTaskPersonCmd signTaskPersonCmd; + + @ApiModelProperty(value = "审核签字任务状态 dispatched 下发,non_dispatched 未下发", example = "non_dispatched", hidden = true) private String reviewerSignatureTaskStatus; } diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalProcessControlAssignCmd.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalProcessControlAssignCmd.java index a2d472f..d88694b 100644 --- a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalProcessControlAssignCmd.java +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalProcessControlAssignCmd.java @@ -3,12 +3,21 @@ package org.qinan.safetyeval.client.dto.institution; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + @Data public class EvalProcessControlAssignCmd { @ApiModelProperty("项目id") private Long projectId; + @ApiModelProperty("签字任务人员") + @NotNull + @Valid + private SignTaskPersonCmd signTaskPersonCmd; + @ApiModelProperty(value = "审核签字任务状态 dispatched 下发,non_dispatched 未下发", example = "non_dispatched", hidden = true) private String reviewerSignatureTaskStatus; } diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalTechReviewAssignCmd.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalTechReviewAssignCmd.java index 58795a3..b525786 100644 --- a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalTechReviewAssignCmd.java +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/EvalTechReviewAssignCmd.java @@ -3,12 +3,22 @@ package org.qinan.safetyeval.client.dto.institution; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + @Data public class EvalTechReviewAssignCmd { @ApiModelProperty("项目id") + @NotNull private Long projectId; + @ApiModelProperty("签字任务人员") + @NotNull + @Valid + private SignTaskPersonCmd signTaskPersonCmd; + @ApiModelProperty(value = "审核签字任务状态 dispatched 下发,non_dispatched 未下发", example = "non_dispatched", hidden = true) private String reviewerSignatureTaskStatus; } diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/SignTaskPersonCmd.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/SignTaskPersonCmd.java new file mode 100644 index 0000000..e96dea1 --- /dev/null +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/institution/SignTaskPersonCmd.java @@ -0,0 +1,21 @@ +package org.qinan.safetyeval.client.dto.institution; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class SignTaskPersonCmd { + + @ApiModelProperty("待签人员id") + @NotNull + private Long signerPersonnelId; + + @ApiModelProperty("待签人员姓名") + @NotBlank + @Size(max = 50) + private String signerName; +}