Files
file-online-preview/server/src/main/java/cn/keking/utils/FileConvertStatusManager.java

257 lines
8.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cn.keking.utils;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 文件转换状态管理器(增强版)
* 支持实时状态跟踪和状态锁定机制
*/
public class FileConvertStatusManager {
// 存储转换状态key=文件名value=转换状态对象
private static final ConcurrentMap<String, ConvertState> STATUS_MAP = new ConcurrentHashMap<>();
// 记录最终状态(超时或异常),防止重复转换
private static final ConcurrentMap<String, Status> FINAL_STATUS_MAP = new ConcurrentHashMap<>();
/**
* 开始转换,创建初始状态
* @param fileName 文件名
*/
public static void startConvert(String fileName) {
STATUS_MAP.putIfAbsent(fileName, new ConvertState(Status.CONVERTING, "等待转换", 0));
// 清除可能存在的最终状态,因为要开始新的转换
FINAL_STATUS_MAP.remove(fileName);
}
/**
* 更新转换进度
* @param fileName 文件名
* @param message 状态消息
* @param progress 进度百分比(0-100)
*/
public static void updateProgress(String fileName, String message, int progress) {
STATUS_MAP.computeIfPresent(fileName, (key, state) -> {
state.update(message, progress);
logger.debug("更新转换进度: {} -> {} ({}%)", fileName, message, progress);
return state;
});
}
/**
* 标记转换超时 - 记录为最终状态
* @param fileName 文件名
*/
public static void markTimeout(String fileName) {
STATUS_MAP.put(fileName, new ConvertState(Status.TIMEOUT, "转换超时,请重试", 0));
// 记录为最终状态
FINAL_STATUS_MAP.put(fileName, Status.TIMEOUT);
logger.warn("标记文件转换超时: {}", fileName);
}
/**
* 标记转换失败 - 记录为最终状态
* @param fileName 文件名
* @param errorMessage 错误信息
*/
public static void markError(String fileName, String errorMessage) {
STATUS_MAP.put(fileName, new ConvertState(Status.FAILED, errorMessage, 0));
// 记录为最终状态
FINAL_STATUS_MAP.put(fileName, Status.FAILED);
logger.warn("标记文件转换失败: {}, 错误: {}", fileName, errorMessage);
}
/**
* 查询文件转换状态
* @param fileName 文件名
* @return 转换状态对象如果不存在返回null
*/
public static ConvertStatus getConvertStatus(String fileName) {
// 先检查是否有最终状态
Status finalStatus = FINAL_STATUS_MAP.get(fileName);
if ((finalStatus == Status.TIMEOUT || finalStatus == Status.FAILED)) {
ConvertState state = STATUS_MAP.get(fileName);
if (state == null) {
// 如果STATUS_MAP中没有创建一个最终状态
if (finalStatus == Status.TIMEOUT) {
return new ConvertStatus(Status.TIMEOUT, "转换超时,请重试", 0, 0);
} else {
return new ConvertStatus(Status.FAILED, "转换失败", 0, 0);
}
}
// 返回最终状态
return new ConvertStatus(state.status, state.message, state.progress, 0);
}
ConvertState state = STATUS_MAP.get(fileName);
if (state == null) {
return null;
}
// 如果是转换中状态,计算已等待时间
long waitingSeconds = 0;
if (state.status == Status.CONVERTING) {
waitingSeconds = (System.currentTimeMillis() - state.startTime) / 1000;
}
return new ConvertStatus(
state.status,
state.message,
state.progress,
waitingSeconds
);
}
/**
* 转换成功
* @param fileName 文件名
*/
public static void convertSuccess(String fileName) {
STATUS_MAP.remove(fileName);
// 清除最终状态,允许重新转换
FINAL_STATUS_MAP.remove(fileName);
}
/**
* 清理状态(强制重置,允许重新转换)
* @param fileName 文件名
* @return true: 清理成功; false: 清理失败
*/
public static boolean clearStatus(String fileName) {
boolean removed1 = STATUS_MAP.remove(fileName) != null;
boolean removed2 = FINAL_STATUS_MAP.remove(fileName) != null;
logger.info("清理文件状态: {}, STATUS_MAP: {}, FINAL_STATUS_MAP: {}",
fileName, removed1, removed2);
return removed1 || removed2;
}
/**
* 清理过期状态(长时间未清理的状态)
* @param expireHours 过期时间(小时)
* @return 清理的数量
*/
public static int cleanupExpiredStatus(int expireHours) {
long expireMillis = expireHours * 3600 * 1000L;
long currentTime = System.currentTimeMillis();
// 清理STATUS_MAP中的过期状态
int count1 = (int) STATUS_MAP.entrySet().stream()
.filter(entry -> {
ConvertState state = entry.getValue();
if (state.status == Status.CONVERTING) {
return false; // 转换中的不清理
}
long elapsed = currentTime - state.startTime;
return elapsed > expireMillis;
})
.count();
// 清理FINAL_STATUS_MAP中的过期状态
// 注意FINAL_STATUS_MAP没有时间戳无法基于时间清理
// 如果需要清理,可以设置一个独立的过期机制
logger.info("清理了 {} 个过期的转换状态", count1);
return count1;
}
/**
* 转换状态枚举
*/
public enum Status {
CONVERTING("转换中"),
FAILED("转换失败"),
TIMEOUT("转换超时"),
QUEUED("排队中");
private final String description;
Status(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 内部状态存储类
*/
private static class ConvertState {
private final Status status;
private String message;
private int progress; // 0-100
private final long startTime;
public ConvertState(Status status, String message, int progress) {
this.status = status;
this.message = message;
this.progress = Math.max(0, Math.min(100, progress));
this.startTime = System.currentTimeMillis();
}
public void update(String message, int progress) {
this.message = message;
this.progress = Math.max(0, Math.min(100, progress));
}
}
/**
* 对外暴露的转换状态封装类
*/
public static class ConvertStatus {
private final Status status;
private final String message;
private final int progress;
private final long waitingSeconds;
private final long timestamp;
public ConvertStatus(Status status, String message, int progress, long waitingSeconds) {
this.status = status;
this.message = message;
this.progress = progress;
this.waitingSeconds = waitingSeconds;
this.timestamp = System.currentTimeMillis();
}
// 获取实时状态信息
public String getRealTimeMessage() {
if (status == Status.CONVERTING) {
if (progress > 0) {
return String.format("%s: %s (进度: %d%%,已等待 %d 秒)",
status.getDescription(), message, progress, waitingSeconds);
} else {
return String.format("%s: %s已等待 %d 秒",
status.getDescription(), message, waitingSeconds);
}
}
return message;
}
// Getters
public Status getStatus() { return status; }
public String getMessage() { return message; }
public int getProgress() { return progress; }
public long getTimestamp() { return timestamp; }
@Override
public String toString() {
return "ConvertStatus{" +
"status=" + status +
", message='" + message + '\'' +
", progress=" + progress +
", waitingSeconds=" + waitingSeconds +
", timestamp=" + timestamp +
'}';
}
}
// 日志记录器
private static final org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger(FileConvertStatusManager.class);
}