diff --git a/app-builder/jane/plugins/aipp-plugin/pom.xml b/app-builder/jane/plugins/aipp-plugin/pom.xml
index 02143fe668..ba2fcde831 100644
--- a/app-builder/jane/plugins/aipp-plugin/pom.xml
+++ b/app-builder/jane/plugins/aipp-plugin/pom.xml
@@ -122,6 +122,10 @@
io.opentelemetry
opentelemetry-api
+
+ com.opencsv
+ opencsv
+
modelengine.jade.service
aipp-prompt-builder-service
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippChatMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippChatMapper.java
index 047ff103b5..07275a9a2b 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippChatMapper.java
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippChatMapper.java
@@ -58,7 +58,8 @@ List selectChatList(@Param("requestParam") QueryChatRequest reques
* @param chatId 会话ID
* @return 会话记录数目
*/
- long getChatListCount(@Param("requestParam") QueryChatRequest request, @Param("chatId") String chatId, @Param("createBy") String createBy);
+ long getChatListCount(@Param("requestParam") QueryChatRequest request, @Param("chatId") String chatId,
+ @Param("createBy") String createBy);
/**
* 查询会话
@@ -115,6 +116,7 @@ List selectChat(@Param("chatId") String chatId, @Param("offset") Intege
/**
* 根据instance id列表批量查询对话消息
+ *
* @param instanceIds instance id列表
* @return 对应会话信息
*/
@@ -178,4 +180,43 @@ List selectChat(@Param("chatId") String chatId, @Param("offset") Intege
*/
List selectChatByCondition(@Param("condition") Map condition,
@Param("requestParam") QueryChatInfoRequest queryChatInfoRequest);
+
+ /**
+ * 获取超期的对话唯一标识。
+ *
+ * @param expiredDays 表示超期时长的 {@code int}。
+ * @param limit 表示查询数量的 {@code int}。
+ * @return 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ List getExpiredChatIds(int expiredDays, int limit);
+
+ /**
+ * 根据对话标识列表强制删除对话。
+ *
+ * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ void forceDeleteChat(List chatIds);
+
+ /**
+ * 根据对话标识列表强制删除对话和任务实例关系。
+ *
+ * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ void deleteWideRelationshipByChatIds(List chatIds);
+
+ /**
+ * 根据对话唯一标识列表批量查询会话记录实体。
+ *
+ * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ * @return 表示会话记录实体列表的 {@link List}{@code <}{@link ChatInfo}{@code >}。
+ */
+ List selectByChatIds(@Param("chatIds") List chatIds);
+
+ /**
+ * 根据对话唯一标识列表批量查询会话记录和任务实例的关系。
+ *
+ * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ * @return 表示会话记录和任务实例的关系的 {@link List}{@code <}{@link ChatAndInstanceMap}{@code >}。
+ */
+ List selectTaskInstanceRelationsByChatIds(@Param("chatIds") List chatIds);
}
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java
index a309ecd39c..83a20d2417 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java
@@ -195,4 +195,28 @@ List selectRecentInstanceIdByAippIds(List aippIds, String aippTy
* @param logIds 表示指定的历史记录 id 的 {@link List}{@code <}{@link Long}{@code >}。
*/
void deleteInstanceLogs(@Param("logIds") List logIds);
+
+ /**
+ * 获取超期的调试对话记录唯一标识列表。
+ *
+ * @param expiredDays 表示超期时间的 {@code int}。
+ * @param limit 表示查询条数的 {@code int}。
+ * @return 表示历史会话记录的id列表的 {@link List}{@code <}{@link Long}{@code >}。
+ */
+ List getExpireInstanceLogIds(String aippType, int expiredDays, int limit);
+
+ /**
+ * 根据实例唯一标识列表强制删除会话记录。
+ *
+ * @param logIds 表示会话实例id列表的 {@link List}{@code <}{@link Long}{@code >}。
+ */
+ void forceDeleteInstanceLogsByIds(List logIds);
+
+ /**
+ * 根据日志唯一标识列表查询会话历史记录。
+ *
+ * @param logIds 标识日志唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。
+ * @return 表示实例历史记录列表的 {@link List}{@code <}{@link AippInstLog}{@code >}。
+ */
+ List selectByLogIds(@Param("logIds") List logIds);
}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderRuntimeInfoMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderRuntimeInfoMapper.java
index 9262a7182a..f1c0ccd569 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderRuntimeInfoMapper.java
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderRuntimeInfoMapper.java
@@ -31,4 +31,20 @@ public interface AppBuilderRuntimeInfoMapper {
* @param appBuilderRuntimeInfoPO {@link AppBuilderRuntimeInfoPo} 对象.
*/
void insertOne(AppBuilderRuntimeInfoPo appBuilderRuntimeInfoPO);
+
+ /**
+ * 获取超期的运行时信息唯一标识列表。
+ *
+ * @param expiredDays 表示超期时间的 {@code int}。
+ * @param limit 表示查询条数的 {@code int}。
+ * @return 表示运行时信息唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。
+ */
+ List getExpiredRuntimeInfos(int expiredDays, int limit);
+
+ /**
+ * 根据运行时信息唯一标识列表强制删除会话记录。
+ *
+ * @param runtimeInfoIds 表示运行时信息唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。
+ */
+ void deleteRuntimeInfos(List runtimeInfoIds);
}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippChatRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippChatRepository.java
new file mode 100644
index 0000000000..5c997cc12d
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippChatRepository.java
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.repository;
+
+import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap;
+import modelengine.fit.jober.aipp.entity.ChatInfo;
+
+import java.util.List;
+
+/**
+ * 应用对话的存储仓库。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-09
+ */
+public interface AippChatRepository {
+ /**
+ * 获取超期的对话唯一标识列表。
+ *
+ * @param expiredDays 表示超期时长的 {@code int}。
+ * @param limit 表示查询数量的 {@code int}。
+ * @return 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ List getExpiredChatIds(int expiredDays, int limit);
+
+ /**
+ * 根据对话标识列表强制删除对话。
+ *
+ * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ void forceDeleteChat(List chatIds);
+
+ /**
+ * 根据对话唯一标识列表批量查询会话记录实体。
+ *
+ * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ * @return 表示会话记录实体列表的 {@link List}{@code <}{@link ChatInfo}{@code >}。
+ */
+ List selectByChatIds(List chatIds);
+
+ /**
+ * 根据对话唯一标识列表批量查询会话记录和任务实例的关系。
+ *
+ * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ * @return 表示会话记录和任务实例的关系的 {@link List}{@code <}{@link ChatAndInstanceMap}{@code >}。
+ */
+ List selectTaskInstanceRelationsByChatIds(List chatIds);
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippInstanceLogRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippInstanceLogRepository.java
new file mode 100644
index 0000000000..2d3d74e602
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippInstanceLogRepository.java
@@ -0,0 +1,43 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.repository;
+
+import modelengine.fit.jober.aipp.entity.AippInstLog;
+
+import java.util.List;
+
+/**
+ * 应用实例历史记录的存储仓库。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-09
+ */
+public interface AippInstanceLogRepository {
+ /**
+ * 获取调试类型的应用过期历史记录。
+ *
+ * @param expiredDays 表示超期天数的 {@code int}。
+ * @param limit 表示查询条数的 {@code int}。
+ * @return 表示超期历史记录id的 {@link List}{@code <}{@link Long}{@code >}。
+ */
+ List getExpireInstanceLogIds(String aippType, int expiredDays, int limit);
+
+ /**
+ * 根据日志唯一标识列表强制删除历史记录。
+ *
+ * @param logIds 表示历史记录的唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。
+ */
+ void forceDeleteInstanceLogs(List logIds);
+
+ /**
+ * 根据日志唯一标识列表查询会话历史记录
+ *
+ * @param logIds 标识日志唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。
+ * @return 表示实例历史记录列表的 {@link List}{@code <}{@link AippInstLog}{@code >}。
+ */
+ List selectByLogIds(List logIds);
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderRuntimeInfoRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderRuntimeInfoRepository.java
index 55b043d806..19d756c6dc 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderRuntimeInfoRepository.java
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderRuntimeInfoRepository.java
@@ -31,4 +31,20 @@ public interface AppBuilderRuntimeInfoRepository {
* @param info {@link AppBuilderRuntimeInfo} 运行时信息.
*/
void insertOne(AppBuilderRuntimeInfo info);
+
+ /**
+ * 获取运行时信息过期历史记录。
+ *
+ * @param expiredDays 表示超期天数的 {@code int}。
+ * @param limit 表示查询条数的 {@code int}。
+ * @return 表示超期运行时信息id的 {@link List}{@code <}{@link Long}{@code >}。
+ */
+ List getExpiredRuntimeInfos(int expiredDays, int limit);
+
+ /**
+ * 根据运行时信息id列表强制删除历史记录。
+ *
+ * @param runtimeInfoIds 表示历史记录的id列表的 {@link List}{@code <}{@link Long}{@code >}。
+ */
+ void deleteRuntimeInfos(List runtimeInfoIds);
}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippChatRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippChatRepositoryImpl.java
new file mode 100644
index 0000000000..71a3a0954f
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippChatRepositoryImpl.java
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.repository.impl;
+
+import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap;
+import modelengine.fit.jober.aipp.entity.ChatInfo;
+import modelengine.fit.jober.aipp.mapper.AippChatMapper;
+import modelengine.fit.jober.aipp.repository.AippChatRepository;
+import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.transaction.Transactional;
+import modelengine.fitframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link AippChatRepository} 对应实现类。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-09
+ */
+@Component
+public class AippChatRepositoryImpl implements AippChatRepository {
+ private final AippChatMapper aippChatMapper;
+
+ /**
+ * 表示用对话持久层构造 {@link AippChatRepositoryImpl} 的实例。
+ *
+ * @param aippChatMapper 表示对话持久层实例的 {@link AippChatMapper}。
+ */
+ public AippChatRepositoryImpl(AippChatMapper aippChatMapper) {this.aippChatMapper = aippChatMapper;}
+
+ @Override
+ public List getExpiredChatIds(int expiredDays, int limit) {
+ return this.aippChatMapper.getExpiredChatIds(expiredDays, limit);
+ }
+
+ @Override
+ @Transactional
+ public void forceDeleteChat(List chatIds) {
+ if (CollectionUtils.isEmpty(chatIds)) {
+ return;
+ }
+ this.aippChatMapper.forceDeleteChat(chatIds);
+ this.aippChatMapper.deleteWideRelationshipByChatIds(chatIds);
+ }
+
+ @Override
+ public List selectByChatIds(List chatIds) {
+ if (CollectionUtils.isEmpty(chatIds)) {
+ return new ArrayList<>();
+ }
+ return this.aippChatMapper.selectByChatIds(chatIds);
+ }
+
+ @Override
+ public List selectTaskInstanceRelationsByChatIds(List chatIds) {
+ if (CollectionUtils.isEmpty(chatIds)) {
+ return new ArrayList<>();
+ }
+ return this.aippChatMapper.selectTaskInstanceRelationsByChatIds(chatIds);
+ }
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImpl.java
new file mode 100644
index 0000000000..394a605d45
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImpl.java
@@ -0,0 +1,51 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.repository.impl;
+
+import modelengine.fit.jober.aipp.entity.AippInstLog;
+import modelengine.fit.jober.aipp.mapper.AippLogMapper;
+import modelengine.fit.jober.aipp.repository.AippInstanceLogRepository;
+import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.util.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * {@link AippInstanceLogRepository} 对应实现类。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-09
+ */
+@Component
+public class AippInstanceLogRepositoryImpl implements AippInstanceLogRepository {
+ private final AippLogMapper aippLogMapper;
+
+ /**
+ * 表示用日志持久层构造 {@link AippInstanceLogRepositoryImpl} 的实例。
+ *
+ * @param aippLogMapper 表示日志持久层实例的 {@link AippLogMapper}。
+ */
+ public AippInstanceLogRepositoryImpl(AippLogMapper aippLogMapper) {this.aippLogMapper = aippLogMapper;}
+
+ @Override
+ public List getExpireInstanceLogIds(String aippType, int expiredDays, int limit) {
+ return this.aippLogMapper.getExpireInstanceLogIds(aippType, expiredDays, limit);
+ }
+
+ @Override
+ public void forceDeleteInstanceLogs(List logIds) {
+ if (CollectionUtils.isEmpty(logIds)) {
+ return;
+ }
+ this.aippLogMapper.forceDeleteInstanceLogsByIds(logIds);
+ }
+
+ @Override
+ public List selectByLogIds(List logIds) {
+ return this.aippLogMapper.selectByLogIds(logIds);
+ }
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImpl.java
index a7b02c19e4..dc45f2978b 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImpl.java
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImpl.java
@@ -11,6 +11,7 @@
import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository;
import modelengine.fit.jober.aipp.serializer.impl.AppBuilderRuntimeInfoSerializer;
import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.util.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
@@ -44,4 +45,17 @@ public List selectByTraceId(String traceId) {
public void insertOne(AppBuilderRuntimeInfo info) {
this.mapper.insertOne(this.serializer.serialize(info));
}
+
+ @Override
+ public List getExpiredRuntimeInfos(int expiredDays, int limit) {
+ return this.mapper.getExpiredRuntimeInfos(expiredDays, limit);
+ }
+
+ @Override
+ public void deleteRuntimeInfos(List runtimeInfoIds) {
+ if (CollectionUtils.isEmpty(runtimeInfoIds)) {
+ return;
+ }
+ this.mapper.deleteRuntimeInfos(runtimeInfoIds);
+ }
}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleaner.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleaner.java
new file mode 100644
index 0000000000..f514edb09e
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleaner.java
@@ -0,0 +1,140 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.service.scheduletask;
+
+import com.opencsv.CSVWriter;
+
+import modelengine.fit.jober.aipp.entity.AippInstLog;
+import modelengine.fit.jober.aipp.enums.AippTypeEnum;
+import modelengine.fit.jober.aipp.repository.AippInstanceLogRepository;
+import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.annotation.Value;
+import modelengine.fitframework.log.Logger;
+import modelengine.fitframework.util.CollectionUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import static modelengine.fit.jober.aipp.service.scheduletask.AppBuilderDbCleanScheduler.FILE_MAX_NUM;
+
+/**
+ * 应用实例日志清理器。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-15
+ */
+@Component
+public class AippInstanceLogCleaner {
+ private static final Logger log = Logger.get(AippInstanceLogCleaner.class);
+ private static final String FILE_NAME = "aipp-instance-log";
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
+ private static final String CONNECTOR = "-";
+
+ private final AippInstanceLogRepository instanceLogRepo;
+ private final CsvWriterHelper csvWriterHelper;
+ private final String aippInstanceLogFilePath;
+
+ /**
+ * 表示用应用日志仓库实例构造 {@link AippInstanceLogCleaner} 的实例。
+ *
+ * @param instanceLogRepo 表示应用日志仓库实例的 {@link AippInstanceLogRepository}。
+ * @param csvWriterHelper 表示文件写入助手实例的 {@link CsvWriterHelper}。
+ * @param aippInstanceLogFilePath 表示日志文件备份路径的 {@link String}。
+ */
+ public AippInstanceLogCleaner(AippInstanceLogRepository instanceLogRepo, CsvWriterHelper csvWriterHelper,
+ @Value("${aipp.instance.log.file.path}") String aippInstanceLogFilePath) {
+ this.instanceLogRepo = instanceLogRepo;
+ this.csvWriterHelper = csvWriterHelper;
+ this.aippInstanceLogFilePath = aippInstanceLogFilePath;
+ }
+
+ /**
+ * 清理已发布的应用对话历史记录表数据,并备份。
+ *
+ * @param expiredDays 表示数据最大保留时长的 {@code int}。
+ * @param limit 表示批量处理数量的 {@code int}。
+ */
+ public void cleanAippInstanceNormalLog(int expiredDays, int limit) {
+ try {
+ while (true) {
+ List instanceLogIds =
+ this.instanceLogRepo.getExpireInstanceLogIds(AippTypeEnum.NORMAL.type(), expiredDays, limit);
+ if (instanceLogIds.isEmpty()) {
+ break;
+ }
+ backupData(instanceLogIds);
+ this.instanceLogRepo.forceDeleteInstanceLogs(instanceLogIds);
+ }
+ cleanupOldBackups(FILE_MAX_NUM);
+ } catch (Exception e) {
+ log.error("Error occurred while business data cleaner, exception:.", e);
+ }
+ }
+
+ private void backupData(List logIds) {
+ List aippInstLogs = this.instanceLogRepo.selectByLogIds(logIds);
+ if (CollectionUtils.isEmpty(aippInstLogs)) {
+ return;
+ }
+ String currentDate = LocalDate.now().format(DATE_FORMATTER);
+ Path backupPath = Paths.get(this.aippInstanceLogFilePath, FILE_NAME + CONNECTOR + currentDate + ".csv");
+ try (CSVWriter csvWriter = this.csvWriterHelper.createCsvWriter(backupPath, true)) {
+ List backupData = aippInstLogs.stream().map(aippInstLog -> new String[] {
+ String.valueOf(aippInstLog.getLogId()), aippInstLog.getAippId(), aippInstLog.getVersion(),
+ aippInstLog.getInstanceId(), aippInstLog.getLogData(), aippInstLog.getLogType(),
+ String.valueOf(aippInstLog.getCreateAt()), aippInstLog.getCreateUserAccount(), aippInstLog.getPath()
+ }).toList();
+ csvWriter.writeAll(backupData);
+ } catch (IOException e) {
+ log.error("Error occurred while writing aipp-instance-log.", e);
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private void cleanupOldBackups(int fileMaxNum) {
+ File backupFolder = this.csvWriterHelper.getFile(this.aippInstanceLogFilePath);
+ File[] backupFiles = backupFolder.listFiles((dir, name) -> name.startsWith(FILE_NAME) && name.endsWith(".csv"));
+ if (backupFiles == null) {
+ return;
+ }
+ List sortedFiles =
+ Arrays.stream(backupFiles).sorted(Comparator.comparing(File::getName).reversed()).toList();
+ for (int i = fileMaxNum; i < sortedFiles.size(); i++) {
+ sortedFiles.get(i).delete();
+ }
+ }
+
+ /**
+ * 清理调试应用对话历史记录表数据。
+ *
+ * @param expiredDays 表示数据最大保留时长的 {@code int}。
+ * @param limit 表示批量处理数量的 {@code int}。
+ */
+ public void cleanAippInstancePreviewLog(int expiredDays, int limit) {
+ log.info("Start cleaning aipp preview instance logs");
+ try {
+ while (true) {
+ List instanceLogIds =
+ this.instanceLogRepo.getExpireInstanceLogIds(AippTypeEnum.PREVIEW.type(), expiredDays, limit);
+ if (instanceLogIds.isEmpty()) {
+ break;
+ }
+ this.instanceLogRepo.forceDeleteInstanceLogs(instanceLogIds);
+ }
+ } catch (Exception e) {
+ log.error("clean instance logs failed, exception:", e);
+ }
+ log.info("Finish cleaning aipp instance logs");
+ }
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderDbCleanScheduler.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderDbCleanScheduler.java
new file mode 100644
index 0000000000..08729ddf36
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderDbCleanScheduler.java
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.service.scheduletask;
+
+import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.annotation.Value;
+import modelengine.fitframework.log.Logger;
+import modelengine.fitframework.schedule.annotation.Scheduled;
+
+/**
+ * 数据库清理定时任务执行器。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-09
+ */
+@Component
+public class AppBuilderDbCleanScheduler {
+ private static final Logger log = Logger.get(AppBuilderDbCleanScheduler.class);
+
+ /**
+ * 表示待清理的数据行数上限。
+ */
+ private static final int LIMIT = 1000;
+
+ /**
+ * 表示备份文件的最大数量。
+ */
+ public static final int FILE_MAX_NUM = 15;
+
+ private final int nonBusinessDataTtl;
+ private final int businessDataTtl;
+ private final AippInstanceLogCleaner aippInstanceLogCleaner;
+ private final ChatSessionCleaner chatSessionCleaner;
+ private final AppBuilderRuntimeInfoCleaner appBuilderRuntimeInfoCleaner;
+
+ /**
+ * 表示用对话清理器和运行时日志清理器构造 {@link AppBuilderDbCleanScheduler} 的实例。
+ *
+ * @param nonBusinessDataTtl 表示非业务数据的过期时间的 {@link String}。
+ * @param businessDataTtl 表示业务数据的过期时间的 {@link String}。
+ * @param aippInstanceLogCleaner 表示日志清理器的 {@link AippInstanceLogCleaner}。
+ * @param chatSessionCleaner 表示对话清理器的 {@link ChatSessionCleaner}。
+ * @param appBuilderRuntimeInfoCleaner 表示运行时信息清理器的 {@link AppBuilderRuntimeInfoCleaner}。
+ */
+ public AppBuilderDbCleanScheduler(@Value("${app-engine.ttl.nonBusinessData}") int nonBusinessDataTtl,
+ @Value("${app-engine.ttl.businessData}") int businessDataTtl, AippInstanceLogCleaner aippInstanceLogCleaner,
+ ChatSessionCleaner chatSessionCleaner, AppBuilderRuntimeInfoCleaner appBuilderRuntimeInfoCleaner) {
+ this.nonBusinessDataTtl = nonBusinessDataTtl;
+ this.businessDataTtl = businessDataTtl;
+ this.aippInstanceLogCleaner = aippInstanceLogCleaner;
+ this.chatSessionCleaner = chatSessionCleaner;
+ this.appBuilderRuntimeInfoCleaner = appBuilderRuntimeInfoCleaner;
+ }
+
+ /**
+ * 每天凌晨 3 点定时清理超期指定天数的应用相关数据。
+ */
+ @Scheduled(strategy = Scheduled.Strategy.CRON, value = "0 0 3 * * ?")
+ public void appBuilderDbCleanSchedule() {
+ try {
+ // 清理非业务数据
+ this.aippInstanceLogCleaner.cleanAippInstancePreviewLog(this.nonBusinessDataTtl, LIMIT);
+ this.appBuilderRuntimeInfoCleaner.clean(this.nonBusinessDataTtl, LIMIT);
+
+ // 清理业务数据
+ this.aippInstanceLogCleaner.cleanAippInstanceNormalLog(this.businessDataTtl, LIMIT);
+ this.chatSessionCleaner.clean(this.businessDataTtl, LIMIT);
+ } catch (Exception e) {
+ log.error("App builder Db Clean Error, exception:", e);
+ }
+ }
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderRuntimeInfoCleaner.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderRuntimeInfoCleaner.java
new file mode 100644
index 0000000000..1e3edbba45
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderRuntimeInfoCleaner.java
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.service.scheduletask;
+
+import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository;
+import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.log.Logger;
+
+import java.util.List;
+
+/**
+ * 应用编排运行时信息清理器。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-15
+ */
+@Component
+public class AppBuilderRuntimeInfoCleaner {
+ private static final Logger log = Logger.get(AppBuilderRuntimeInfoCleaner.class);
+
+ private final AppBuilderRuntimeInfoRepository runtimeInfoRepo;
+
+ public AppBuilderRuntimeInfoCleaner(AppBuilderRuntimeInfoRepository runtimeInfoRepo) {
+ this.runtimeInfoRepo = runtimeInfoRepo;
+ }
+
+ /**
+ * 清理对话运行时表数据,并备份。
+ *
+ * @param expiredDays 表示数据最大保留时长的 {@code int}。
+ * @param limit 表示批量处理数量的 {@code int}。
+ */
+ public void clean(int expiredDays, int limit) {
+ log.info("Start cleaning app builder runtime infos");
+ try {
+ while (true) {
+ List expiredRuntimeInfoIds = this.runtimeInfoRepo.getExpiredRuntimeInfos(expiredDays, limit);
+ if (expiredRuntimeInfoIds.isEmpty()) {
+ break;
+ }
+ this.runtimeInfoRepo.deleteRuntimeInfos(expiredRuntimeInfoIds);
+ }
+ } catch (Exception e) {
+ log.error("cleaning app builder runtime infos failed, exception:", e);
+ }
+ log.info("Finish cleaning app builder runtime infos");
+ }
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleaner.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleaner.java
new file mode 100644
index 0000000000..036d2f8301
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleaner.java
@@ -0,0 +1,144 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.service.scheduletask;
+
+import static modelengine.fit.jober.aipp.service.scheduletask.AppBuilderDbCleanScheduler.FILE_MAX_NUM;
+
+import com.opencsv.CSVWriter;
+
+import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap;
+import modelengine.fit.jober.aipp.entity.ChatInfo;
+import modelengine.fit.jober.aipp.repository.AippChatRepository;
+import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.annotation.Value;
+import modelengine.fitframework.log.Logger;
+import modelengine.fitframework.util.CollectionUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * 聊天会话数据库表清理器。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-15
+ */
+@Component
+public class ChatSessionCleaner {
+ private static final Logger log = Logger.get(AippInstanceLogCleaner.class);
+ private static final String CHAT_SESSION_FILE_NAME = "chat-session";
+ private static final String INSTANCE_RELATIONS_FILE_NAME = "instance-relations";
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
+ private static final String CONNECTOR = "-";
+
+ private final AippChatRepository chatRepo;
+ private final CsvWriterHelper csvWriterHelper;
+ private final String chatSessionFilePath;
+
+ /**
+ * 表示用对话仓库和文件写入助手来构造 {@link ChatSessionCleaner} 的实例。
+ *
+ * @param chatRepo 表示对话仓库实例的 {@link AippChatRepository}。
+ * @param csvWriterHelper 表示文件写入助手实例的 {@link CsvWriterHelper}。
+ * @param chatSessionFilePath 表示对话文件路径的 {@link String}。
+ */
+ public ChatSessionCleaner(AippChatRepository chatRepo, CsvWriterHelper csvWriterHelper,
+ @Value("${chat.session.file.path}") String chatSessionFilePath) {
+ this.chatRepo = chatRepo;
+ this.csvWriterHelper = csvWriterHelper;
+ this.chatSessionFilePath = chatSessionFilePath;
+ }
+
+ /**
+ * 清理对话会话相关数据,并备份。
+ *
+ * @param expiredDays 表示数据最大保留天数的 {@code int}。
+ * @param limit 表示批量处理数量的 {@code int}。
+ */
+ public void clean(int expiredDays, int limit) {
+ try {
+ while (true) {
+ List expiredChatIds = this.chatRepo.getExpiredChatIds(expiredDays, limit);
+ if (expiredChatIds.isEmpty()) {
+ break;
+ }
+ backupChatSessionData(expiredChatIds);
+ backupInstanceRelationData(expiredChatIds);
+ this.chatRepo.forceDeleteChat(expiredChatIds);
+ }
+ cleanupOldBackups(CHAT_SESSION_FILE_NAME, FILE_MAX_NUM);
+ cleanupOldBackups(INSTANCE_RELATIONS_FILE_NAME, FILE_MAX_NUM);
+ } catch (Exception e) {
+ log.error("Error occurred while business data cleaner, exception:.", e);
+ }
+ }
+
+ private void backupChatSessionData(List chatIds) {
+ List chatSessionPos = this.chatRepo.selectByChatIds(chatIds);
+ if (CollectionUtils.isEmpty(chatSessionPos)) {
+ return;
+ }
+ String currentDate = LocalDate.now().format(DATE_FORMATTER);
+ Path backupPath = Paths.get(this.chatSessionFilePath, CHAT_SESSION_FILE_NAME + CONNECTOR + currentDate + ".csv");
+ try (CSVWriter csvWriter = this.csvWriterHelper.createCsvWriter(backupPath, true)) {
+ List backupData = chatSessionPos.stream().map(session -> new String[]{
+ session.getChatId(), session.getAppId(), session.getVersion(), session.getChatName(),
+ session.getAttributes(), String.valueOf(session.getCreateTime()), session.getCreator(),
+ String.valueOf(session.getUpdateTime()), session.getUpdater()
+ }).toList();
+ csvWriter.writeAll(backupData);
+ } catch (IOException e) {
+ log.error("Error occurred while backup chat session data, exception:", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void backupInstanceRelationData(List chatIds) {
+ List relationPos = this.chatRepo.selectTaskInstanceRelationsByChatIds(chatIds);
+ if (CollectionUtils.isEmpty(relationPos)) {
+ return;
+ }
+ String currentDate = LocalDate.now().format(DATE_FORMATTER);
+ Path backupPath = Paths.get(this.chatSessionFilePath, INSTANCE_RELATIONS_FILE_NAME + CONNECTOR + currentDate + ".csv");
+ try (CSVWriter csvWriter = this.csvWriterHelper.createCsvWriter(backupPath, true)) {
+ List backupData = relationPos.stream().map(relationPo -> new String[]{
+ relationPo.getMsgId(), relationPo.getChatId(), relationPo.getInstanceId(),
+ String.valueOf(relationPo.getCreateTime()), relationPo.getCreator(),
+ String.valueOf(relationPo.getUpdateTime()), relationPo.getUpdater()
+ }).toList();
+ csvWriter.writeAll(backupData);
+ } catch (IOException e) {
+ log.error("Error occurred while backup instance relation data, exception:", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void cleanupOldBackups(String fileName, int fileMaxNum) {
+ try {
+ File backupFolder = this.csvWriterHelper.getFile(this.chatSessionFilePath);
+ File[] backupFiles =
+ backupFolder.listFiles((dir, name) -> name.startsWith(fileName) && name.endsWith(".csv"));
+ if (backupFiles == null) {
+ return;
+ }
+ List sortedFiles =
+ Arrays.stream(backupFiles).sorted(Comparator.comparing(File::getName).reversed()).toList();
+ for (int i = fileMaxNum; i < sortedFiles.size(); i++) {
+ sortedFiles.get(i).delete();
+ }
+ } catch (Exception e) {
+ log.error("Cleanup old backups failed, filename:{}, exception:", fileName, e);
+ }
+ }
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/CsvWriterHelper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/CsvWriterHelper.java
new file mode 100644
index 0000000000..6d33098801
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/CsvWriterHelper.java
@@ -0,0 +1,47 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.service.scheduletask;
+
+import com.opencsv.CSVWriter;
+
+import modelengine.fitframework.annotation.Component;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+
+/**
+ * csv 文件写入助手。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-18
+ */
+@Component
+public class CsvWriterHelper {
+ /**
+ * 生成 CsvWriter 对象。
+ *
+ * @param path 表示文件路径的 {@link Path}。
+ * @param isAppend 表示是否追加的 {@link Boolean}。
+ * @return 表示生成 CSVWriter类的 {@link CSVWriter}。
+ * @throws IOException 表示可能抛出异常的 {@link IOException}。
+ */
+ public CSVWriter createCsvWriter(Path path, boolean isAppend) throws IOException {
+ return new CSVWriter(new FileWriter(path.toFile(), isAppend));
+ }
+
+ /**
+ * 生成 File 对象。
+ *
+ * @param path 表示文件路径的 {@link Path}。
+ * @return 表示生成 File 对象的 {@link File}。
+ */
+ public File getFile(String path) {
+ return new File(path);
+ }
+}
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/application-prod.yml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/application-prod.yml
index 54c5d33be4..6bba3a8641 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/application-prod.yml
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/application-prod.yml
@@ -51,6 +51,9 @@ app-engine:
file:
upload:
maxStorageRatio: 0.9
+ ttl:
+ businessData: 15
+ nonBusinessData: 1
elsa:
endpoint:
elsaKey:
@@ -79,4 +82,13 @@ export-meta:
sensitive:
replace:
- pattern: "\"timestamp\":\"[^\"]+\""
- to: "\"timestamp\":\"***\""
\ No newline at end of file
+ to: "\"timestamp\":\"***\""
+aipp:
+ instance:
+ log:
+ file:
+ path: /var/share/backup/aipp-instance-log/
+chat:
+ session:
+ file:
+ path: /var/share/backup/chat-session/
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml
index b9b61ead42..d5ae54c057 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml
@@ -15,6 +15,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
chat_id, app_id, app_version, name, attributes, create_at, create_by, update_at, update_by, status
@@ -220,4 +242,54 @@
order by update_at desc
offset #{requestParam.offset} limit #{requestParam.limit}
+
+
+
+
+ DELETE FROM
+ t_chat_session
+ where chat_id in
+
+ #{item}
+
+
+
+
+ DELETE FROM
+ t_chat_session_task_instance_wide_relationship
+ where chat_id in
+
+ #{item}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippLogMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippLogMapper.xml
index e4ede1eca0..75315fc9cd 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippLogMapper.xml
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippLogMapper.xml
@@ -263,4 +263,37 @@
#{item}
+
+
+
+
+ DELETE FROM
+ aipp_instance_log
+ where log_id in
+
+ #{item}
+
+
+
+
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderRuntimeInfoMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderRuntimeInfoMapper.xml
index b85dbd4178..5019761064 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderRuntimeInfoMapper.xml
+++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderRuntimeInfoMapper.xml
@@ -88,4 +88,24 @@
where trace_id = #{traceId}
order by start_time asc
+
+
+
+
+ DELETE
+ FROM app_builder_runtime_info
+ where id in
+
+ #{item}
+
+
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImplTest.java
new file mode 100644
index 0000000000..8b71beafec
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImplTest.java
@@ -0,0 +1,90 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.repository.impl;
+
+import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto;
+import modelengine.fit.jober.aipp.entity.AippInstLog;
+import modelengine.fit.jober.aipp.mapper.AippLogMapper;
+import modelengine.fit.jober.aipp.repository.AippInstanceLogRepository;
+import modelengine.fit.jober.aipp.service.DatabaseBaseTest;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@link AippInstanceLogRepositoryImpl} 对应测试类。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-10
+ */
+public class AippInstanceLogRepositoryImplTest extends DatabaseBaseTest {
+ private final AippLogMapper mapper = sqlSessionManager.openSession(true).getMapper(AippLogMapper.class);
+ private AippInstanceLogRepository repo;
+
+ @BeforeEach
+ void setUp() {
+ this.repo = new AippInstanceLogRepositoryImpl(this.mapper);
+ }
+
+ @Test
+ @DisplayName("测试成功刪除调试信息")
+ void testForceDeleteInstanceLogsSuccess() {
+ AippLogCreateDto dto = AippLogCreateDto.builder()
+ .logId("1")
+ .aippId("1")
+ .aippType("PREVIEW")
+ .instanceId("1")
+ .logData("{}")
+ .logType("QUESTION")
+ .createUserAccount("yyy")
+ .build();
+ this.mapper.insertOne(dto);
+ this.repo.forceDeleteInstanceLogs(Collections.singletonList(1L));
+ List expirePreviewInstanceLogs = this.repo.selectByLogIds(Collections.singletonList(1L));
+ Assertions.assertEquals(0, expirePreviewInstanceLogs.size());
+ }
+
+ @Test
+ @DisplayName("测试成功获取过期调试信息")
+ void testGetExpiredPreviewInstanceLogsSuccess() {
+ AippLogCreateDto dto = AippLogCreateDto.builder()
+ .logId("2")
+ .aippId("2")
+ .aippType("PREVIEW")
+ .instanceId("2")
+ .logData("{}")
+ .logType("QUESTION")
+ .createUserAccount("yyy")
+ .build();
+ this.mapper.insertOne(dto);
+ List expirePreviewInstanceLogs = this.repo.selectByLogIds(Collections.singletonList(2L));
+ Assertions.assertEquals(1, expirePreviewInstanceLogs.size());
+ }
+
+ @Test
+ @DisplayName("测试根据对话 id 成功查询对话详细信息")
+ void testSelectByLogIdSuccess() {
+ AippLogCreateDto dto = AippLogCreateDto.builder()
+ .logId("3")
+ .aippId("3")
+ .aippType("PREVIEW")
+ .instanceId("3")
+ .logData("{}")
+ .logType("QUESTION")
+ .createUserAccount("yyy")
+ .build();
+ this.mapper.insertOne(dto);
+ List aippInstLogs = this.repo.selectByLogIds(Collections.singletonList(3L));
+ Assertions.assertEquals(1, aippInstLogs.size());
+ Assertions.assertEquals("3", aippInstLogs.get(0).getInstanceId());
+ }
+}
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImplTest.java
new file mode 100644
index 0000000000..2c8bea58bc
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImplTest.java
@@ -0,0 +1,74 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.repository.impl;
+
+import modelengine.fit.jober.aipp.domain.AppBuilderRuntimeInfo;
+import modelengine.fit.jober.aipp.mapper.AppBuilderRuntimeInfoMapper;
+import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository;
+import modelengine.fit.jober.aipp.service.DatabaseBaseTest;
+import modelengine.fit.runtime.entity.Parameter;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * {@link AppBuilderRuntimeInfoRepositoryImpl} 对应测试类。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-10
+ */
+class AppBuilderRuntimeInfoRepositoryImplTest extends DatabaseBaseTest {
+ private final AppBuilderRuntimeInfoMapper mapper =
+ sqlSessionManager.openSession(true).getMapper(AppBuilderRuntimeInfoMapper.class);
+ private AppBuilderRuntimeInfoRepository runtimeInfoRepository;
+
+ @BeforeEach
+ void setUp() {
+ this.runtimeInfoRepository = new AppBuilderRuntimeInfoRepositoryImpl(mapper);
+ }
+
+ @Test
+ @DisplayName("测试成功获取超期的运行时信息")
+ void testGetExpiredRuntimeInfosSuccess() {
+ AppBuilderRuntimeInfo info = genRuntimeInfo();
+ this.runtimeInfoRepository.insertOne(info);
+ List expiredRuntimeInfos = this.runtimeInfoRepository.selectByTraceId("0");
+ Assertions.assertEquals(0, expiredRuntimeInfos.size());
+ }
+
+ private AppBuilderRuntimeInfo genRuntimeInfo() {
+ Parameter parameter = new Parameter();
+ return AppBuilderRuntimeInfo.builder()
+ .traceId("1")
+ .flowDefinitionId("1")
+ .instanceId("1")
+ .nodeId("1")
+ .nodeType("PREVIEW")
+ .startTime(1)
+ .endTime(2)
+ .status("ERROR")
+ .parameters(List.of(parameter))
+ .createAt(LocalDateTime.now())
+ .updateAt(LocalDateTime.now())
+ .build();
+ }
+
+ @Test
+ @DisplayName("测试成功根据 id 列表删除运行信息")
+ void testDeleteRuntimeInfosSuccess() {
+ this.runtimeInfoRepository.insertOne(genRuntimeInfo());
+ this.mapper.deleteRuntimeInfos(Arrays.asList(1L, 2L));
+ List expiredRuntimeInfos = this.runtimeInfoRepository.selectByTraceId("1");
+ Assertions.assertEquals(0, expiredRuntimeInfos.size());
+ }
+}
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleanerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleanerTest.java
new file mode 100644
index 0000000000..5e026fa5ce
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleanerTest.java
@@ -0,0 +1,107 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.service.scheduletask;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.opencsv.CSVWriter;
+
+import modelengine.fit.jober.aipp.entity.AippInstLog;
+import modelengine.fit.jober.aipp.enums.AippTypeEnum;
+import modelengine.fit.jober.aipp.repository.AippInstanceLogRepository;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@link AippInstanceLogCleaner} 对应测试类。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-18
+ */
+@ExtendWith(MockitoExtension.class)
+public class AippInstanceLogCleanerTest {
+ @Mock
+ private AippInstanceLogRepository instanceLogRepo;
+
+ @Mock
+ private CsvWriterHelper csvWriterHelper;
+
+ private AippInstanceLogCleaner logCleaner;
+
+ @BeforeEach
+ void setup() {
+ String aippInstanceLogFilePath = "/var/share/backup/aipp-instance-log/";
+ this.logCleaner = new AippInstanceLogCleaner(this.instanceLogRepo, this.csvWriterHelper, aippInstanceLogFilePath);
+ }
+
+ @Test
+ @DisplayName("测试清理 Normal 信息并备份")
+ void cleanAippInstanceNormalLogShouldBackupAndDelete() throws Exception {
+ List mockLogIds = List.of(1L, 2L);
+ when(this.instanceLogRepo.getExpireInstanceLogIds(AippTypeEnum.NORMAL.type(), 30, 100)).thenReturn(mockLogIds)
+ .thenReturn(Collections.emptyList());
+ AippInstLog mockLog = new AippInstLog();
+ when(this.instanceLogRepo.selectByLogIds(anyList())).thenReturn(List.of(mockLog));
+ CSVWriter csvWriter = mock(CSVWriter.class);
+ when(this.csvWriterHelper.createCsvWriter(any(), anyBoolean())).thenReturn(csvWriter);
+ File file = mock(File.class);
+ File csvFile = mock(File.class);
+ when(this.csvWriterHelper.getFile(anyString())).thenReturn(file);
+ when(file.listFiles(any(FilenameFilter.class))).thenReturn(new File[] {csvFile});
+ this.logCleaner.cleanAippInstanceNormalLog(30, 100);
+ verify(this.instanceLogRepo, times(2)).getExpireInstanceLogIds(anyString(), anyInt(), anyInt());
+ verify(this.instanceLogRepo, times(1)).forceDeleteInstanceLogs(anyList());
+ verify(csvWriter, times(1)).writeAll(anyList());
+ verify(csvFile, times(0)).delete();
+ }
+
+ @Test
+ @DisplayName("测试 Normal 信息备份失败时不清理数据")
+ void cleanAippInstanceNormalLogShouldNotDeleteWhenBackupFailed() throws Exception {
+ List mockLogIds = List.of(1L, 2L);
+ when(this.instanceLogRepo.getExpireInstanceLogIds(AippTypeEnum.NORMAL.type(), 30, 100)).thenReturn(mockLogIds)
+ .thenReturn(Collections.emptyList());
+ when(this.instanceLogRepo.selectByLogIds(anyList())).thenReturn(Collections.singletonList(new AippInstLog()));
+ CSVWriter csvWriter = mock(CSVWriter.class);
+ when(this.csvWriterHelper.createCsvWriter(any(), anyBoolean())).thenThrow(new IOException("error"));
+ this.logCleaner.cleanAippInstanceNormalLog(30, 100);
+ verify(this.instanceLogRepo, times(1)).getExpireInstanceLogIds(anyString(), anyInt(), anyInt());
+ verify(this.instanceLogRepo, times(0)).forceDeleteInstanceLogs(anyList());
+ verify(csvWriter, times(0)).writeAll(anyList());
+ }
+
+ @Test
+ public void cleanAippInstancePreviewLogMultipleBatches_DeletesAll() {
+ Mockito.when(this.instanceLogRepo.getExpireInstanceLogIds(Mockito.eq(AippTypeEnum.PREVIEW.type()),
+ Mockito.anyInt(),
+ Mockito.anyInt())).thenReturn(Arrays.asList(1L, 2L)).thenReturn(Collections.emptyList());
+ this.logCleaner.cleanAippInstancePreviewLog(30, 100);
+ Mockito.verify(this.instanceLogRepo, Mockito.times(2)).getExpireInstanceLogIds(AippTypeEnum.PREVIEW.type(), 30, 100);
+ Mockito.verify(this.instanceLogRepo).forceDeleteInstanceLogs(Arrays.asList(1L, 2L));
+ }
+}
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleanerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleanerTest.java
new file mode 100644
index 0000000000..fe3e473105
--- /dev/null
+++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleanerTest.java
@@ -0,0 +1,108 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
+ * This file is a part of the ModelEngine Project.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+package modelengine.fit.jober.aipp.service.scheduletask;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.opencsv.CSVWriter;
+
+import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap;
+import modelengine.fit.jober.aipp.entity.ChatInfo;
+import modelengine.fit.jober.aipp.repository.AippChatRepository;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@link ChatSessionCleaner} 对应测试类。
+ *
+ * @author 杨祥宇
+ * @since 2025-04-18
+ */
+@ExtendWith(MockitoExtension.class)
+public class ChatSessionCleanerTest {
+ @Mock
+ private AippChatRepository chatRepo;
+
+ @Mock
+ private CsvWriterHelper csvWriterHelper;
+
+ private ChatSessionCleaner chatSessionCleaner;
+
+ @BeforeEach
+ void setup() {
+ String chatSessionFilePath = "/var/share/backup/chat-session/";
+ this.chatSessionCleaner = new ChatSessionCleaner(this.chatRepo, this.csvWriterHelper, chatSessionFilePath);
+ }
+
+ @Test
+ @DisplayName("测试没有过期数据时直接返回,不会做备份和删除操作")
+ void cleanNoExpiredDataShouldDoNothing() throws IOException {
+ when(this.chatRepo.getExpiredChatIds(anyInt(), anyInt())).thenReturn(Collections.emptyList());
+ this.chatSessionCleaner.clean(30, 100);
+ verify(this.chatRepo, never()).forceDeleteChat(anyList());
+ verify(this.csvWriterHelper, never()).createCsvWriter(any(), anyBoolean());
+ }
+
+ @Test
+ @DisplayName("测试有过期数据时备份并成功删除")
+ void cleanWithExpiredDataShouldBackupAndDelete() throws Exception {
+ List expiredChatIds = List.of("chat1", "chat2");
+ when(this.chatRepo.getExpiredChatIds(anyInt(), anyInt())).thenReturn(expiredChatIds)
+ .thenReturn(Collections.emptyList());
+ when(this.chatRepo.selectByChatIds(expiredChatIds)).thenReturn(List.of(new ChatInfo(), new ChatInfo()));
+ when(this.chatRepo.selectTaskInstanceRelationsByChatIds(expiredChatIds)).thenReturn(List.of(new ChatAndInstanceMap(),
+ new ChatAndInstanceMap()));
+ CSVWriter csvWriter = mock(CSVWriter.class);
+ when(this.csvWriterHelper.createCsvWriter(any(), anyBoolean())).thenReturn(csvWriter);
+ File file = mock(File.class);
+ File csvFile = mock(File.class);
+ when(this.csvWriterHelper.getFile(anyString())).thenReturn(file);
+ when(file.listFiles(any(FilenameFilter.class))).thenReturn(new File[] {csvFile});
+ this.chatSessionCleaner.clean(30, 100);
+ verify(this.chatRepo, times(1)).forceDeleteChat(expiredChatIds);
+ verify(this.csvWriterHelper, atLeast(2)).createCsvWriter(any(), eq(true));
+ verify(csvWriter, times(2)).writeAll(anyList());
+ verify(csvFile, times(0)).delete();
+ }
+
+ @Test
+ @DisplayName("测试数据备份失败时不会删除过期数据")
+ void cleanShouldNotDeleteWhenBackupFailed() throws Exception {
+ List expiredChatIds = List.of("chat1", "chat2");
+ when(this.chatRepo.getExpiredChatIds(anyInt(), anyInt())).thenReturn(expiredChatIds)
+ .thenReturn(Collections.emptyList());
+ when(this.chatRepo.selectByChatIds(anyList())).thenReturn(Collections.singletonList(new ChatInfo()));
+ CSVWriter csvWriter = mock(CSVWriter.class);
+ when(this.csvWriterHelper.createCsvWriter(any(), anyBoolean())).thenThrow(new IOException("error"));
+ this.chatSessionCleaner.clean(30, 100);
+ verify(this.chatRepo, times(1)).getExpiredChatIds(anyInt(), anyInt());
+ verify(this.chatRepo, times(0)).forceDeleteChat(anyList());
+ verify(csvWriter, times(0)).writeAll(anyList());
+ }
+}
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/clear_table.sql b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/clear_table.sql
index 4dbd79dd8c..b3bf632b7b 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/clear_table.sql
+++ b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/clear_table.sql
@@ -16,4 +16,8 @@ truncate table app_builder_form;
truncate table app_builder_form_property;
-truncate table app_template;
\ No newline at end of file
+truncate table app_template;
+
+truncate table app_builder_runtime_info;
+
+truncate table t_chat_session;
\ No newline at end of file
diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/init.sql b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/init.sql
index 6858e0a7cf..7bebe480be 100644
--- a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/init.sql
+++ b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/init.sql
@@ -145,4 +145,38 @@ create table if not exists app_template (
update_by varchar(64) not null,
update_at timestamp(6) not null default CURRENT_TIMESTAMP,
is_deleted int2 not null default 0
+);
+
+create table if not exists app_builder_runtime_info
+(
+ id BIGSERIAL primary key,
+ trace_id varchar(64) not null,
+ flow_definition_id varchar(64) not null,
+ instance_id varchar(64) not null,
+ node_id varchar(64) not null,
+ node_type varchar(32) not null,
+ start_time bigint not null,
+ end_time bigint not null,
+ status varchar(32),
+ published smallint not null,
+ error_msg text,
+ next_position_id varchar(64),
+ parameters json not null DEFAULT '[]',
+ create_by varchar(64),
+ create_at timestamp not null default current_timestamp,
+ update_by varchar(64),
+ update_at timestamp not null default current_timestamp
+);
+
+CREATE TABLE IF NOT EXISTS t_chat_session (
+ chat_id VARCHAR(32) NOT NULL DEFAULT NULL,
+ app_id VARCHAR(32) NULL DEFAULT NULL,
+ app_version VARCHAR(32) NULL DEFAULT NULL,
+ name VARCHAR(2000) NULL DEFAULT NULL,
+ attributes JSON NULL DEFAULT NULL,
+ create_at TIMESTAMP(6) NULL DEFAULT NULL,
+ create_by VARCHAR(32) NULL DEFAULT NULL,
+ update_at TIMESTAMP(6) NULL DEFAULT NULL,
+ update_by VARCHAR(32) NULL DEFAULT NULL,
+ status INT4 NULL DEFAULT NULL
);
\ No newline at end of file
diff --git a/app-builder/jane/task-new/pom.xml b/app-builder/jane/task-new/pom.xml
index d0c7beb5c9..bdfe476a91 100644
--- a/app-builder/jane/task-new/pom.xml
+++ b/app-builder/jane/task-new/pom.xml
@@ -63,6 +63,10 @@
modelengine.fit.jane
aipp-service
+
+ org.fitframework.extension
+ fit-schedule
+
diff --git a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/MetaInstanceServiceImpl.java b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/MetaInstanceServiceImpl.java
index 873db4fe9d..7726d0d0f3 100644
--- a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/MetaInstanceServiceImpl.java
+++ b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/MetaInstanceServiceImpl.java
@@ -20,6 +20,10 @@
import modelengine.fit.task_new.repository.MetaInstanceRepository;
import modelengine.fit.task_new.util.UUIDUtil;
import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.annotation.Value;
+import modelengine.fitframework.log.Logger;
+import modelengine.fitframework.schedule.annotation.Scheduled;
+import modelengine.fitframework.util.CollectionUtils;
import java.util.Collections;
import java.util.List;
@@ -33,10 +37,26 @@
*/
@Component
public class MetaInstanceServiceImpl implements MetaInstanceService {
+ private static final Logger log = Logger.get(MetaInstanceServiceImpl.class);
+
+ /**
+ * 表示待清理数据的数量的上限。
+ */
+ private static final int LIMIT = 1000;
+
private final MetaInstanceRepository metaInstanceRepository;
+ private final int expiredDays;
- public MetaInstanceServiceImpl(MetaInstanceRepository metaInstanceRepository) {
+ /**
+ * 表示用元数据仓库和过期时间构造 {@link MetaInstanceServiceImpl} 的实例。
+ *
+ * @param metaInstanceRepository 表示元数据仓库实例的 {@link MetaInstanceRepository}。
+ * @param expiredDays 表示过期时间的 {@link String}。
+ */
+ public MetaInstanceServiceImpl(MetaInstanceRepository metaInstanceRepository,
+ @Value("${task.expiredDays}") int expiredDays) {
this.metaInstanceRepository = metaInstanceRepository;
+ this.expiredDays = expiredDays;
}
@Override
@@ -104,4 +124,24 @@ public Instance retrieveById(String instanceId, OperationContext context) {
RangedResultSet resultSet = this.list(Collections.singletonList(instanceId), 0, 1, null);
return resultSet.getResults().get(0);
}
+
+ /**
+ * 每天凌晨 3 点定时清理超期指定天数的任务实例数据。
+ */
+ @Scheduled(strategy = Scheduled.Strategy.CRON, value = "0 0 3 * * ?")
+ public void taskInstanceDbCleanSchedule() {
+ log.info("Start clean task instance db");
+ try {
+ while (true) {
+ List expiredInstanceIds = this.metaInstanceRepository.getExpiredInstanceIds(expiredDays, LIMIT);
+ if (CollectionUtils.isEmpty(expiredInstanceIds)) {
+ break;
+ }
+ this.metaInstanceRepository.forceDelete(expiredInstanceIds);
+ }
+ } catch (Exception e) {
+ log.error("Error clean task instance db, exception:", e);
+ }
+ log.info("Finish clean task instance db");
+ }
}
diff --git a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/mapper/MetaInstanceMapper.java b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/mapper/MetaInstanceMapper.java
index e6cf8d3c51..262fe5786b 100644
--- a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/mapper/MetaInstanceMapper.java
+++ b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/mapper/MetaInstanceMapper.java
@@ -12,7 +12,7 @@
import java.util.List;
/**
- * Meta 实例数据库 Mapper 类
+ * Meta 实例数据库 Mapper 类。
*
* @author 邬涨财
* @since 2025-03-31
@@ -54,4 +54,20 @@ public interface MetaInstanceMapper {
* @return 表示查询后的结果的 {@code long}
*/
long count(MetaInstanceCondition cond);
+
+ /**
+ * 根据元数据实例唯一标识列表强制删除会话记录。
+ *
+ * @param ids 表示元数据实例唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ void forceDelete(List ids);
+
+ /**
+ * 获取超期的元数据实例唯一标识列表。
+ *
+ * @param expiredDays 表示超期时间的 {@code int}。
+ * @param limit 表示查询条数的 {@code int}。
+ * @return 表示元数据实例的唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ List getExpiredInstanceIds(int expiredDays, int limit);
}
diff --git a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/MetaInstanceRepository.java b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/MetaInstanceRepository.java
index c452aa86cc..6b96e39452 100644
--- a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/MetaInstanceRepository.java
+++ b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/MetaInstanceRepository.java
@@ -9,10 +9,11 @@
import modelengine.fit.task_new.condition.MetaInstanceCondition;
import modelengine.fit.task_new.entity.MetaInstance;
+import java.time.LocalDateTime;
import java.util.List;
/**
- * Meta 实例数据库 Repo 层接口
+ * Meta 实例数据库 Repo 层接口。
*
* @author 邬涨财
* @since 2025-03-31
@@ -54,4 +55,20 @@ public interface MetaInstanceRepository {
* @return 表示查询后的结果的 {@code long}
*/
long count(MetaInstanceCondition condition);
+
+ /**
+ * 获取超期的元数据实例唯一标识列表。
+ *
+ * @param expiredDays 表示超期时间的 {@link LocalDateTime}。
+ * @param limit 表示查询条数的 {@code int}。
+ * @return 表示元数据实例的唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ List getExpiredInstanceIds(int expiredDays, int limit);
+
+ /**
+ * 根据元数据实例唯一标识列表强制删除会话记录。
+ *
+ * @param ids 表示元数据实例唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。
+ */
+ void forceDelete(List ids);
}
diff --git a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/impl/MetaInstanceRepositoryImpl.java b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/impl/MetaInstanceRepositoryImpl.java
index 087cf20a03..cc1ed45b91 100644
--- a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/impl/MetaInstanceRepositoryImpl.java
+++ b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/impl/MetaInstanceRepositoryImpl.java
@@ -12,12 +12,13 @@
import modelengine.fit.task_new.repository.MetaInstanceRepository;
import modelengine.fit.task_new.serializer.impl.MetaInstanceSerializer;
import modelengine.fitframework.annotation.Component;
+import modelengine.fitframework.util.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
/**
- * Meta 实例数据库 Repo 层实现
+ * Meta 实例数据库 Repo 层实现。
*
* @author 邬涨财
* @since 2025-03-31
@@ -59,4 +60,17 @@ public List select(MetaInstanceCondition cond) {
public long count(MetaInstanceCondition cond) {
return this.metaInstanceMapper.count(cond);
}
+
+ @Override
+ public List getExpiredInstanceIds(int expiredDays, int limit) {
+ return this.metaInstanceMapper.getExpiredInstanceIds(expiredDays, limit);
+ }
+
+ @Override
+ public void forceDelete(List ids) {
+ if (CollectionUtils.isEmpty(ids)) {
+ return;
+ }
+ this.metaInstanceMapper.forceDelete(ids);
+ }
}
diff --git a/app-builder/jane/task-new/src/main/resources/application-prod.yml b/app-builder/jane/task-new/src/main/resources/application-prod.yml
index 41312e05d6..280f27b086 100644
--- a/app-builder/jane/task-new/src/main/resources/application-prod.yml
+++ b/app-builder/jane/task-new/src/main/resources/application-prod.yml
@@ -25,4 +25,6 @@ fit:
testOnBorrow: false
testOnReturn: false
mybatis:
- mapper-locations: mapper/*Mapper.xml
\ No newline at end of file
+ mapper-locations: mapper/*Mapper.xml
+task:
+ expiredDays: 1
\ No newline at end of file
diff --git a/app-builder/jane/task-new/src/main/resources/mapper/MetaInstanceMapper.xml b/app-builder/jane/task-new/src/main/resources/mapper/MetaInstanceMapper.xml
index d411d60a8d..3911ecb134 100644
--- a/app-builder/jane/task-new/src/main/resources/mapper/MetaInstanceMapper.xml
+++ b/app-builder/jane/task-new/src/main/resources/mapper/MetaInstanceMapper.xml
@@ -139,4 +139,25 @@
+
+
+
+
+ DELETE FROM
+ task_instance_new
+ where id in
+
+ #{item}
+
+
\ No newline at end of file
diff --git a/app-builder/waterflow/java/waterflow-service/src/main/resources/application.yml b/app-builder/waterflow/java/waterflow-service/src/main/resources/application.yml
index e2a62fd7f9..6a861ad0e9 100644
--- a/app-builder/waterflow/java/waterflow-service/src/main/resources/application.yml
+++ b/app-builder/waterflow/java/waterflow-service/src/main/resources/application.yml
@@ -16,6 +16,6 @@ jane:
scheduleRate: 10000
maxCount: 0
isNeedFlowCallbackAdapt: false
- contextExpiredDays: 7
+ contextExpiredDays: 1
distributed-lock-provider: databaseDistributedLockProvider
\ No newline at end of file
diff --git a/app-knowledge/plugins/edm-knowledge/src/test/java/modelengine/jade/knowledge/EdmKnowledgeBaseManagerTest.java b/app-knowledge/plugins/edm-knowledge/src/test/java/modelengine/jade/knowledge/EdmKnowledgeBaseManagerTest.java
index dc2b49756a..bd72e62822 100644
--- a/app-knowledge/plugins/edm-knowledge/src/test/java/modelengine/jade/knowledge/EdmKnowledgeBaseManagerTest.java
+++ b/app-knowledge/plugins/edm-knowledge/src/test/java/modelengine/jade/knowledge/EdmKnowledgeBaseManagerTest.java
@@ -76,8 +76,8 @@ public void shouldOkWhenListRepo() {
"lxh-2",
"description",
"VECTOR",
- Timestamp.valueOf("2024-09-26 16:16:21.054")),
- tuple(1L, "lxh-k", "", "VECTOR", Timestamp.valueOf("2024-09-26 12:12:14.320")));
+ Timestamp.valueOf("2024-09-26 08:16:21.054")),
+ tuple(1L, "lxh-k", "", "VECTOR", Timestamp.valueOf("2024-09-26 04:12:14.320")));
}
@Test
diff --git a/common/dependency/pom.xml b/common/dependency/pom.xml
index 06b01bfd6e..55e1e650f7 100644
--- a/common/dependency/pom.xml
+++ b/common/dependency/pom.xml
@@ -56,6 +56,7 @@
1.14.5
2.24
1.12.468
+ 5.7.1
3.22.0
@@ -552,6 +553,11 @@
aws-java-sdk-s3
${aws.version}
+
+ com.opencsv
+ opencsv
+ ${opencsv.version}
+