From 4ee06bfff8bf599fcaee13fb59b52803b55f72fb Mon Sep 17 00:00:00 2001 From: luoweijian <1329394916@qq.com> Date: Mon, 22 Jun 2026 10:41:50 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=84=E6=96=99=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DraftReviewApplicationService.java | 7 ++ .../KnowledgePointApplicationService.java | 5 + .../DraftReviewApplicationServiceImpl.java | 117 ++++++++++++++++++ .../KnowledgePointApplicationServiceImpl.java | 66 ++++++++++ .../controller/InformationController.java | 31 +++++ 5 files changed, 226 insertions(+) diff --git a/src/main/java/com/project/information/application/DraftReviewApplicationService.java b/src/main/java/com/project/information/application/DraftReviewApplicationService.java index 411850b..fca0d87 100644 --- a/src/main/java/com/project/information/application/DraftReviewApplicationService.java +++ b/src/main/java/com/project/information/application/DraftReviewApplicationService.java @@ -1,6 +1,7 @@ package com.project.information.application; import com.project.base.domain.result.Result; +import com.project.information.domain.dto.DraftSaveDTO; import com.project.information.domain.entity.KnowledgePointDraftEntity; import java.util.List; @@ -19,4 +20,10 @@ public interface DraftReviewApplicationService { Result addDraft(Long informationId, String content, Integer knowledgeType); Result confirm(Long informationId); + + /** + * 批量保存草稿并确认(替代逐条编辑+单独确认) + * 前端编辑完所有草稿后一次性提交,提交后自动固化 + */ + Result batchSaveAndConfirm(Long informationId, List draftList); } diff --git a/src/main/java/com/project/information/application/KnowledgePointApplicationService.java b/src/main/java/com/project/information/application/KnowledgePointApplicationService.java index c7268b3..728d060 100644 --- a/src/main/java/com/project/information/application/KnowledgePointApplicationService.java +++ b/src/main/java/com/project/information/application/KnowledgePointApplicationService.java @@ -11,4 +11,9 @@ public interface KnowledgePointApplicationService { Result classicStatistics(Long subLineId) throws Exception; void parse(Map fileMap,Map fileNameMa); + + /** + * 模拟算法服务提取知识点草稿(测试用) + */ + Result mockAiExtract(Long informationId, Integer count); } diff --git a/src/main/java/com/project/information/application/impl/DraftReviewApplicationServiceImpl.java b/src/main/java/com/project/information/application/impl/DraftReviewApplicationServiceImpl.java index 61af3e8..9699a5b 100644 --- a/src/main/java/com/project/information/application/impl/DraftReviewApplicationServiceImpl.java +++ b/src/main/java/com/project/information/application/impl/DraftReviewApplicationServiceImpl.java @@ -2,25 +2,35 @@ package com.project.information.application.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.project.base.domain.exception.BusinessErrorException; +import com.project.base.config.CustomIdGenerator; import com.project.base.domain.result.Result; import com.project.information.application.DraftReviewApplicationService; +import com.project.information.domain.dto.DraftSaveDTO; import com.project.information.domain.entity.InformationEntity; import com.project.information.domain.entity.KnowledgePointDraftEntity; +import com.project.information.domain.entity.KnowledgePointEntity; import com.project.information.domain.enums.AuditStatusEnum; import com.project.information.domain.enums.DraftStatusEnum; import com.project.information.domain.service.ConfirmKnowledgePointDraftDomainService; import com.project.information.domain.service.InformationBaseService; +import com.project.information.domain.service.KnowledgePointBaseService; import com.project.information.domain.service.KnowledgePointDraftBaseService; +import com.project.interaction.application.AlgorithmApplicationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; + +import lombok.extern.slf4j.Slf4j; /** * 知识点草稿审核应用服务实现 * 简单 CRUD 直接调 BaseService,确认固化调 DomainService */ @Service +@Slf4j public class DraftReviewApplicationServiceImpl implements DraftReviewApplicationService { @Autowired @@ -32,6 +42,15 @@ public class DraftReviewApplicationServiceImpl implements DraftReviewApplication @Autowired private ConfirmKnowledgePointDraftDomainService confirmKnowledgePointDraftDomainService; + @Autowired + private KnowledgePointBaseService knowledgePointBaseService; + + @Autowired + private AlgorithmApplicationService algorithmApplicationService; + + @Autowired + private CustomIdGenerator customIdGenerator; + @Override public Result> listDrafts(Long informationId) { if (informationId == null) { @@ -103,4 +122,102 @@ public class DraftReviewApplicationServiceImpl implements DraftReviewApplication public Result confirm(Long informationId) { return confirmKnowledgePointDraftDomainService.confirm(informationId); } + + /** + * 批量保存草稿并确认(替代逐条编辑+单独确认) + * 前端编辑完所有草稿后一次性提交,提交后自动固化 + */ + @Override + public Result batchSaveAndConfirm(Long informationId, List draftList) { + if (informationId == null) { + throw new BusinessErrorException("资料ID不能为空"); + } + if (draftList == null || draftList.isEmpty()) { + throw new BusinessErrorException("草稿数据不能为空"); + } + + // 1. 校验资料状态 + InformationEntity information = informationBaseService.getById(informationId); + if (information == null) { + throw new BusinessErrorException("资料不存在"); + } + if (DraftStatusEnum.APPROVED.getValue().equals(information.getDraftStatus())) { + return Result.success("该资料已审核通过,无需重复操作"); + } + if (!DraftStatusEnum.PENDING.getValue().equals(information.getDraftStatus())) { + throw new BusinessErrorException("该资料不在待审核状态"); + } + + // 2. 获取该资料下所有待审核草稿 + List existingDrafts = knowledgePointDraftBaseService.list( + new LambdaQueryWrapper() + .eq(KnowledgePointDraftEntity::getInformationId, informationId) + .eq(KnowledgePointDraftEntity::getAuditStatus, AuditStatusEnum.PENDING.getValue())); + List existingIds = existingDrafts.stream() + .map(KnowledgePointDraftEntity::getId).toList(); + + // 3. 用户传来的草稿ID集合 + List incomingIds = draftList.stream() + .map(DraftSaveDTO::getId) + .filter(Objects::nonNull) + .toList(); + + // 4. 计算用户删除的草稿ID + List deletedIds = existingIds.stream() + .filter(id -> !incomingIds.contains(id)) + .toList(); + + // 5. 逻辑删除(deleted=1),保留审计记录 + if (!deletedIds.isEmpty()) { + knowledgePointDraftBaseService.lambdaUpdate() + .set(KnowledgePointDraftEntity::getDeleted, true) + .in(KnowledgePointDraftEntity::getId, deletedIds) + .update(); + } + + // 6. 更新/编辑草稿 + List confirmedDrafts = new ArrayList<>(); + for (DraftSaveDTO dto : draftList) { + if (dto.getId() != null) { + // 编辑现有草稿 + KnowledgePointDraftEntity oldDraft = knowledgePointDraftBaseService.getById(dto.getId()); + if (oldDraft != null && AuditStatusEnum.PENDING.getValue().equals(oldDraft.getAuditStatus())) { + oldDraft.setContent(dto.getContent()); + oldDraft.setKnowledgeType(dto.getKnowledgeType() != null ? dto.getKnowledgeType() : oldDraft.getKnowledgeType()); + // aiContent 保留不动(AI原始返回用于审计对比) + oldDraft.setAuditStatus(AuditStatusEnum.APPROVED.getValue()); + knowledgePointDraftBaseService.updateById(oldDraft); + confirmedDrafts.add(oldDraft); + } + } + } + + // 7. 复制草稿content到正式知识点表 + List knowledgePoints = new ArrayList<>(); + for (KnowledgePointDraftEntity draft : confirmedDrafts) { + KnowledgePointEntity kp = new KnowledgePointEntity(); + kp.setId(customIdGenerator.nextId(kp)); + kp.setContent(draft.getContent()); + kp.setKnowledgeType(draft.getKnowledgeType()); + kp.setInformationId(informationId); + kp.setParseName(draft.getParseName()); + knowledgePoints.add(kp); + } + knowledgePointBaseService.saveBatch(knowledgePoints); + + // 8. 更新资料状态为已审核通过(终态) + informationBaseService.lambdaUpdate() + .eq(InformationEntity::getId, informationId) + .set(InformationEntity::getDraftStatus, DraftStatusEnum.APPROVED.getValue()) + .update(); + + // 9. 触发聚类 + List kpList = knowledgePoints.stream() + .map(entity -> entity.toDTO(com.project.information.domain.dto.KnowledgePointDTO::new)) + .toList(); + algorithmApplicationService.postToClusteringByInformationId(informationId, kpList); + + log.info(">>> [草稿审核] 批量保存并确认完成, informationId={}, 草稿数={}", informationId, confirmedDrafts.size()); + return Result.success("保存成功,已审核通过"); + } } diff --git a/src/main/java/com/project/information/application/impl/KnowledgePointApplicationServiceImpl.java b/src/main/java/com/project/information/application/impl/KnowledgePointApplicationServiceImpl.java index af3b16a..38ba308 100644 --- a/src/main/java/com/project/information/application/impl/KnowledgePointApplicationServiceImpl.java +++ b/src/main/java/com/project/information/application/impl/KnowledgePointApplicationServiceImpl.java @@ -4,16 +4,21 @@ import cn.hutool.core.collection.CollUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.project.base.domain.exception.BusinessErrorException; import com.project.base.domain.result.Result; import com.project.information.application.KnowledgePointApplicationService; import com.project.information.domain.dto.KnowledgePointDTO; import com.project.information.domain.dto.KnowledgePointStatisticsDTO; import com.project.information.domain.entity.InformationEntity; +import com.project.information.domain.entity.KnowledgePointDraftEntity; import com.project.information.domain.entity.KnowledgePointEntity; +import com.project.information.domain.enums.DraftStatusEnum; import com.project.information.domain.enums.InformationParseStatusEnum; import com.project.information.domain.service.GetStatisticsKnowledgePointDomainService; import com.project.information.domain.service.InformationBaseService; import com.project.information.domain.service.KnowledgePointBaseService; +import com.project.information.domain.service.KnowledgePointDraftBaseService; +import com.project.base.config.CustomIdGenerator; import com.project.information.utils.MinIoUtils; import com.project.interaction.application.AlgorithmApplicationService; import lombok.extern.slf4j.Slf4j; @@ -45,6 +50,9 @@ public class KnowledgePointApplicationServiceImpl implements KnowledgePointAppli @Autowired private KnowledgePointBaseService knowledgePointBaseService; + + @Autowired + private KnowledgePointDraftBaseService knowledgePointDraftBaseService; @Autowired private InformationBaseService informationBaseService; @@ -66,6 +74,9 @@ public class KnowledgePointApplicationServiceImpl implements KnowledgePointAppli @Autowired private AlgorithmApplicationService algorithmApplicationService; + @Autowired + private CustomIdGenerator customIdGenerator; + @Override public Result getStatistics(Long subLineId) throws Exception { return getStatisticsKnowledgePointDomainService.getStatistics(subLineId); @@ -228,4 +239,59 @@ public class KnowledgePointApplicationServiceImpl implements KnowledgePointAppli informationEntity.setParseStatus(status); informationBaseService.updateById(informationEntity); } + + // =============== 测试用:模拟算法服务提取知识点草稿 =============== + + @Override + public Result mockAiExtract(Long informationId, Integer count) { + if (informationId == null) { + throw new BusinessErrorException("资料ID不能为空"); + } + + // 校验资料是否存在 + InformationEntity information = informationBaseService.getById(informationId); + if (information == null) { + throw new BusinessErrorException("资料不存在"); + } + + // 如果已经审核通过,不允许重复模拟 + if (DraftStatusEnum.APPROVED.getValue().equals(information.getDraftStatus())) { + return Result.success("该资料已审核通过,无需重复模拟"); + } + + // 生成模拟草稿数据(原始资料解析结果:全部精准,{{}}包裹考点) + // AI原始返回也带{{}},content和aiContent保持一致 + List drafts = new ArrayList<>(); + String[] mockData = { + "{{Vue3}}组合式API核心函数包括{{ref()}}、{{reactive()}}和{{computed()}}", + "{{Spring Boot}}的{{@RestController}}注解等同于{{@Controller}}加{{@ResponseBody}}", + "{{MySQL}}的{{InnoDB}}引擎支持{{事务}}和{{外键}},{{MyISAM}}不支持", + "{{Redis}}持久化策略包括{{RDB}}和{{AOF}}两种方式", + "{{Docker}}容器使用{{UnionFS}}联合文件系统,{{镜像}}只读、{{容器}}可写" + }; + int realCount = Math.min(count, mockData.length); + for (int i = 0; i < realCount; i++) { + KnowledgePointDraftEntity entity = new KnowledgePointDraftEntity(); + entity.setId(customIdGenerator.nextId(entity)); + entity.setInformationId(informationId); + entity.setKnowledgeType(0); // 原始资料解析结果:全部精准 + entity.setParseName("mock_parse_" + informationId); + entity.setAiContent(mockData[i]); // AI原始返回,带{{}} + entity.setAuditStatus(0); // 待审核 + drafts.add(entity); + } + + // 批量保存到草稿表 + knowledgePointDraftBaseService.saveBatch(drafts); + + // 更新资料状态为:待审核 + informationBaseService.lambdaUpdate() + .eq(InformationEntity::getId, informationId) + .set(InformationEntity::getDraftStatus, DraftStatusEnum.PENDING.getValue()) + .set(InformationEntity::getParseStatus, InformationParseStatusEnum.Success.getValue()) + .update(); + + log.info(">>> [模拟算法回调] 已生成{}条草稿, informationId={}", realCount, informationId); + return Result.success("模拟生成" + realCount + "条草稿成功"); + } } diff --git a/src/main/java/com/project/information/controller/InformationController.java b/src/main/java/com/project/information/controller/InformationController.java index f437886..e78096c 100644 --- a/src/main/java/com/project/information/controller/InformationController.java +++ b/src/main/java/com/project/information/controller/InformationController.java @@ -1,14 +1,17 @@ package com.project.information.controller; +import com.fasterxml.jackson.databind.ObjectMapper; import com.project.base.domain.result.PageResult; import com.project.base.domain.result.Result; import com.project.information.application.DraftReviewApplicationService; import com.project.information.application.GenerateFromFilesApplicationService; import com.project.information.application.InformationApplicationService; +import com.project.information.application.KnowledgePointApplicationService; import com.project.information.application.ProductLineApplicationService; import com.project.information.application.UploadFilesApplicationService; import com.project.information.domain.dto.UploadedFileDTO; +import com.project.information.domain.dto.DraftSaveDTO; import com.project.information.domain.dto.InformationDTO; import com.project.information.domain.dto.ProductLineDTO; import com.project.information.domain.entity.KnowledgePointDraftEntity; @@ -40,6 +43,9 @@ public class InformationController { @Autowired private GenerateFromFilesApplicationService generateFromFilesApplicationService; + @Autowired + private KnowledgePointApplicationService knowledgePointApplicationService; + @Autowired private UploadFilesApplicationService uploadFilesApplicationService; @@ -125,4 +131,29 @@ public class InformationController { public Result confirmDraft(Long informationId) { return draftReviewApplicationService.confirm(informationId); } + + /** + * 批量保存草稿并确认(替代逐条编辑+单独确认) + * 前端编辑完所有草稿后一次性提交,提交后自动固化 + * 使用 form-data 传参:informationId + draftList(JSON字符串) + */ + @PostMapping("/draft/batchSaveAndConfirm") + public Result batchSaveAndConfirm(Long informationId, @RequestParam("draftList") String draftListJson) throws Exception { + // Postman form-data 会对 JSON 字符串做 URL 编码,先解码 + String decodedJson = java.net.URLDecoder.decode(draftListJson, java.nio.charset.StandardCharsets.UTF_8); + ObjectMapper objectMapper = new ObjectMapper(); + List draftList = objectMapper.readValue(decodedJson, + objectMapper.getTypeFactory().constructCollectionType(List.class, DraftSaveDTO.class)); + return draftReviewApplicationService.batchSaveAndConfirm(informationId, draftList); + } + + // =============== 测试用:模拟算法服务回调生成草稿 =============== + + /** + * 模拟算法服务提取知识点草稿(算法服务未就绪时使用) + */ + @PostMapping("/mockAiExtract") + public Result mockAiExtract(Long informationId, @RequestParam(required = false, defaultValue = "5") Integer count) { + return knowledgePointApplicationService.mockAiExtract(informationId, count); + } }