1241 lines
51 KiB
C#
1241 lines
51 KiB
C#
using Dto;
|
||
using MotionFramework;
|
||
using MotionFramework.Scripts.Runtime.Engine.Engine.Network.WebRequest;
|
||
using Newtonsoft.Json;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using Cysharp.Threading.Tasks;
|
||
using DefaultNamespace.ProcessMode;
|
||
using Framework.Utils;
|
||
using UnityEngine;
|
||
using Zion.ERP.Inventory;
|
||
using System.IO.Compression;
|
||
using UnityEditor;
|
||
using System.Linq;
|
||
using Framework.ProcessMode;
|
||
using UnityEngine.UI;
|
||
|
||
namespace DefaultNamespace
|
||
{
|
||
public static class FileComponent
|
||
{
|
||
private static string _localFilePath = string.Empty;
|
||
private static string _answerFilePath = string.Empty; //答题卡
|
||
private static List<float> _scores = new List<float>();
|
||
|
||
// 文件下载配置管理器
|
||
private static ExamFileConfigManager _configManager;
|
||
|
||
// 已下载文件路径跟踪(题目名称 -> 文件路径列表)
|
||
private static Dictionary<string, List<string>> _downloadedFiles = new Dictionary<string, List<string>>();
|
||
|
||
// 配置文件路径
|
||
private static readonly string CONFIG_FILE_PATH = "DataConfig/exam_file_config.json";
|
||
|
||
// 答题卡按钮引用(需要在运行时设置)
|
||
private static Button _answerSheetButton;
|
||
|
||
// 答题卡文件路径(题目名称 -> 答题卡文件路径)
|
||
private static Dictionary<string, string> _answerSheetFiles = new Dictionary<string, string>();
|
||
|
||
// 文件下载完成事件(文件名 -> 文件路径)
|
||
public static event System.Action<string, string> OnFileDownloadCompleted;
|
||
|
||
|
||
/// <summary>
|
||
/// 设置答题卡按钮引用
|
||
/// </summary>
|
||
/// <param name="button">答题卡按钮</param>
|
||
public static void SetAnswerSheetButton(Button button)
|
||
{
|
||
_answerSheetButton = button;
|
||
Debug.Log("FileComponent: 答题卡按钮引用已设置");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置过程步骤的分数
|
||
/// </summary>
|
||
/// <param name="processSteps"></param>
|
||
public static void SetProcessSteps(List<ProcessStep> processSteps)
|
||
{
|
||
_scores.Clear();
|
||
foreach (var step in processSteps)
|
||
{
|
||
foreach (var action in step.Actions)
|
||
{
|
||
_scores.Add(action.Score);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化文件下载配置管理器
|
||
/// </summary>
|
||
private static async UniTask InitializeConfigManager()
|
||
{
|
||
if (_configManager != null) return;
|
||
|
||
try
|
||
{
|
||
Debug.Log("开始初始化文件下载配置管理器");
|
||
|
||
// 读取配置文件
|
||
string configPath = Path.Combine(Application.streamingAssetsPath, CONFIG_FILE_PATH);
|
||
if (!File.Exists(configPath))
|
||
{
|
||
Debug.LogError($"配置文件不存在:{configPath}");
|
||
_configManager = new ExamFileConfigManager();
|
||
return;
|
||
}
|
||
|
||
string jsonContent = await File.ReadAllTextAsync(configPath);
|
||
_configManager = JsonConvert.DeserializeObject<ExamFileConfigManager>(jsonContent);
|
||
|
||
if (_configManager == null)
|
||
{
|
||
Debug.LogError("配置文件解析失败");
|
||
_configManager = new ExamFileConfigManager();
|
||
return;
|
||
}
|
||
|
||
// 验证配置
|
||
if (!_configManager.Validate())
|
||
{
|
||
Debug.LogError("配置文件验证失败");
|
||
}
|
||
|
||
Debug.Log($"文件下载配置管理器初始化完成,共加载 {_configManager.examFileConfigs.Count} 个考试类型配置");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"初始化文件下载配置管理器失败:{ex.Message}");
|
||
_configManager = new ExamFileConfigManager();
|
||
}
|
||
}
|
||
|
||
// 移除了ExamQuestionTypes版本的GetExamFileInfos方法,统一使用字符串版本
|
||
|
||
/// <summary>
|
||
/// 获取指定题目的所有文件下载信息(使用题目名字)
|
||
/// </summary>
|
||
/// <param name="examName">题目名字</param>
|
||
/// <returns>文件下载信息列表</returns>
|
||
public static async UniTask<List<FileDownloadInfo>> GetExamFileInfos(string examName)
|
||
{
|
||
await InitializeConfigManager();
|
||
|
||
var config = _configManager.GetConfigByExamType(examName);
|
||
if (config == null)
|
||
{
|
||
Debug.LogWarning($"未找到题目 '{examName}' 的文件配置");
|
||
return new List<FileDownloadInfo>();
|
||
}
|
||
|
||
return config.files;
|
||
}
|
||
|
||
// 移除了所有ExamQuestionTypes版本的方法重载,统一使用字符串题目名称
|
||
|
||
/// <summary>
|
||
/// 获取当前题目的文件下载信息(包含名称和URL,使用全局题目名字)
|
||
/// </summary>
|
||
/// <returns>文件下载信息列表(包含名称和URL)</returns>
|
||
public static async UniTask<List<(string name, string url)>> GetExamFileInfoWithUrls()
|
||
{
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
var fileInfos = await GetExamFileInfos(examName);
|
||
return fileInfos.Select(f => (f.name, f.url)).ToList();
|
||
}
|
||
|
||
// 移除了ExamQuestionTypes版本的DownloadSingleFile方法,统一使用字符串版本
|
||
|
||
// /// <summary>
|
||
// /// 下载单个文件(通过索引)
|
||
// /// </summary>
|
||
// /// <param name="examType">考试类型</param>
|
||
// /// <param name="fileIndex">文件索引(从0开始)</param>
|
||
// public static async UniTaskVoid DownloadSingleFileByIndex(ExamQuestionTypes examType, int fileIndex)
|
||
// {
|
||
// Debug.Log($"FileComponent.DownloadSingleFileByIndex 被调用,考试类型:{examType},文件索引:{fileIndex}");
|
||
//
|
||
// try
|
||
// {
|
||
// // 从配置文件获取该考试类型的所有文件信息
|
||
// var fileInfos = await GetExamFileInfos(examType);
|
||
// if (fileInfos.Count == 0)
|
||
// {
|
||
// Debug.LogWarning($"考试类型 '{examType}' 在配置文件中没有配置任何文件");
|
||
// return;
|
||
// }
|
||
//
|
||
// if (fileIndex < 0 || fileIndex >= fileInfos.Count)
|
||
// {
|
||
// Debug.LogError($"文件索引 {fileIndex} 超出范围,配置文件中共有 {fileInfos.Count} 个文件");
|
||
// return;
|
||
// }
|
||
//
|
||
// var targetFile = fileInfos[fileIndex];
|
||
// Debug.Log($"开始下载索引为 {fileIndex} 的文件:{targetFile.name}");
|
||
//
|
||
// // 下载单个文件
|
||
// await DownloadSingleFileAsync(examType, targetFile);
|
||
//
|
||
// // 更新已下载文件列表
|
||
// if (!_downloadedFiles.ContainsKey(examType))
|
||
// {
|
||
// _downloadedFiles[examType] = new List<string>();
|
||
// }
|
||
//
|
||
// // 重新获取已下载的文件路径
|
||
// var config = _configManager.GetConfigByExamType(examType);
|
||
// if (config != null)
|
||
// {
|
||
// var downloadedPaths = config.GetDownloadedFilePaths();
|
||
// _downloadedFiles[examType] = downloadedPaths;
|
||
// }
|
||
//
|
||
// Debug.Log($"索引为 {fileIndex} 的文件 '{targetFile.name}' 下载完成");
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Debug.LogError($"下载索引为 {fileIndex} 的文件时发生错误:{ex.Message}");
|
||
// }
|
||
// }
|
||
|
||
/// <summary>
|
||
/// 从本地StreamingAssets/biaodan目录查找并打开文件(使用全局题目名字)
|
||
/// </summary>
|
||
/// <param name="fileName">要查找的文件名称</param>
|
||
public static async UniTaskVoid DownloadSingleFile(string fileName)
|
||
{
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
Debug.Log($"FileComponent.DownloadSingleFile 被调用,全局题目名字:{examName},文件名称:{fileName}");
|
||
|
||
try
|
||
{
|
||
// 从配置文件获取该题目的所有文件信息
|
||
var fileInfos = await GetExamFileInfos(examName);
|
||
if (fileInfos.Count == 0)
|
||
{
|
||
Debug.LogWarning($"题目 '{examName}' 在配置文件中没有配置任何文件");
|
||
return;
|
||
}
|
||
|
||
// 查找指定名称的文件配置
|
||
var targetFile = fileInfos.FirstOrDefault(f => f.name == fileName);
|
||
if (targetFile == null)
|
||
{
|
||
Debug.LogError($"在题目 '{examName}' 的配置文件中未找到文件:{fileName}");
|
||
return;
|
||
}
|
||
|
||
// 在StreamingAssets/biaodan目录下查找文件
|
||
string biaodanDir = Path.Combine(Application.streamingAssetsPath, "biaodan");
|
||
if (!Directory.Exists(biaodanDir))
|
||
{
|
||
Debug.LogError($"目录不存在:{biaodanDir}");
|
||
return;
|
||
}
|
||
|
||
// 根据配置中的fileName查找匹配的文件
|
||
string targetFileName = targetFile.fileName;
|
||
string[] files = Directory.GetFiles(biaodanDir, "*", SearchOption.AllDirectories);
|
||
|
||
// 查找完全匹配或包含目标文件名的文件
|
||
string foundFilePath = files.FirstOrDefault(f =>
|
||
{
|
||
string file = Path.GetFileName(f);
|
||
return file == targetFileName ||
|
||
file.Contains(targetFileName) ||
|
||
targetFileName.Contains(file);
|
||
});
|
||
|
||
if (string.IsNullOrEmpty(foundFilePath))
|
||
{
|
||
// 如果精确匹配失败,尝试使用文件名(不含扩展名)匹配
|
||
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(targetFileName);
|
||
foundFilePath = files.FirstOrDefault(f =>
|
||
{
|
||
string file = Path.GetFileNameWithoutExtension(f);
|
||
return file == fileNameWithoutExt || file.Contains(fileNameWithoutExt);
|
||
});
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(foundFilePath) || !File.Exists(foundFilePath))
|
||
{
|
||
Debug.LogError($"在目录 '{biaodanDir}' 中未找到文件:{targetFileName}");
|
||
return;
|
||
}
|
||
|
||
Debug.Log($"在本地找到文件:{foundFilePath},准备打开");
|
||
|
||
// 检测文件是否已打开
|
||
if (IsFileOpen(foundFilePath))
|
||
{
|
||
Debug.Log($"文件 '{fileName}' 已打开,无需重复打开");
|
||
return;
|
||
}
|
||
|
||
// 打开文件
|
||
bool openResult = FileUtils.OpenFile(foundFilePath);
|
||
if (!openResult)
|
||
{
|
||
Debug.LogError($"打开文件失败:{foundFilePath}");
|
||
return;
|
||
}
|
||
|
||
Debug.Log($"成功打开文件:{foundFilePath}");
|
||
|
||
// 更新已下载文件列表(用于后续上传等功能)
|
||
if (!_downloadedFiles.ContainsKey(examName))
|
||
{
|
||
_downloadedFiles[examName] = new List<string>();
|
||
}
|
||
|
||
// 如果文件不在列表中,则添加
|
||
if (!_downloadedFiles[examName].Contains(foundFilePath))
|
||
{
|
||
_downloadedFiles[examName].Add(foundFilePath);
|
||
}
|
||
|
||
// 检测是否为答题卡文件
|
||
if (IsAnswerSheetFile(fileName))
|
||
{
|
||
Debug.Log($"检测到答题卡文件:{fileName}");
|
||
_answerSheetFiles[examName] = foundFilePath;
|
||
ShowAnswerSheetButton();
|
||
}
|
||
|
||
// 触发文件下载完成事件(保持兼容性)
|
||
OnFileDownloadCompleted?.Invoke(fileName, foundFilePath);
|
||
|
||
Debug.Log($"文件 '{fileName}' 处理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"处理文件 '{fileName}' 时发生错误:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 上传当前考试的文件(完全基于配置文件,使用全局题目名字)
|
||
/// </summary>
|
||
public static async UniTask<(bool, string)> UploadExamFiles()
|
||
{
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
Debug.Log($"FileComponent.UploadExamFiles 被调用,全局题目名字:{examName}");
|
||
|
||
|
||
// 先上传文件,获取文件URL
|
||
var (fileUrl, success) = await UploadExamFilesAsync(examName);
|
||
if (!success)
|
||
{
|
||
Debug.LogError($"文件上传失败:{fileUrl}");
|
||
return (false, fileUrl); // 上传失败,直接返回
|
||
}
|
||
|
||
// 然后提交题目数据,将文件URL作为备注
|
||
await UploadData(fileUrl);
|
||
|
||
Debug.Log($"题目 '{examName}' 的所有数据和文件上传完成(基于配置文件),文件URL:{fileUrl}");
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Debug.LogError($"上传题目 '{examName}' 的文件时发生错误:{ex.Message}");
|
||
// }
|
||
return (true, "提交成功");
|
||
}
|
||
|
||
|
||
private static async UniTask UploadData(string fileUrl = null)
|
||
{
|
||
// try
|
||
// {
|
||
ExamInfo examInfo = MotionEngine.GetModule<GlobalDataStorage>().ExamInfo;
|
||
|
||
// 构建上传数据
|
||
UploadExamDto uploadExamDto = new UploadExamDto();
|
||
float score = MotionEngine.GetModule<ProcessManager>().CalculateTotalScore();
|
||
// 设置DTO中的所有字段
|
||
uploadExamDto.id = "0";
|
||
uploadExamDto.appId = examInfo.AppID;
|
||
uploadExamDto.courseId = examInfo.CourseId;
|
||
uploadExamDto.examId = examInfo.PaperId;
|
||
uploadExamDto.examinationId = examInfo.ExamRoomId;
|
||
uploadExamDto.stuId = examInfo.StudentId;
|
||
uploadExamDto.groupId = examInfo.GroupId;
|
||
uploadExamDto.machineId = string.Empty;
|
||
uploadExamDto.batchId = examInfo.BatchId;
|
||
uploadExamDto.operationType = "3";
|
||
uploadExamDto.isComp = uploadExamDto.examId.ToString();
|
||
uploadExamDto.currentProcess = "0";
|
||
uploadExamDto.sumProcess = "0";
|
||
uploadExamDto.score = score.ToString();
|
||
uploadExamDto.preScore = "0";
|
||
uploadExamDto.inTimes = (MotionEngine.GetModule<ProcessManager>().GetUsedTimeInSeconds()).ToString();
|
||
uploadExamDto.remark = fileUrl; // 将文件URL作为备注
|
||
uploadExamDto.stepName = string.Empty;
|
||
uploadExamDto.testPoint = string.Empty;
|
||
uploadExamDto.defaultScore = "0";
|
||
|
||
// 构建考试步骤列表
|
||
UploadExamStepList uploadExamStepList = new UploadExamStepList();
|
||
uploadExamStepList.time = (MotionEngine.GetModule<ProcessManager>().GetUsedTimeInSeconds()).ToString();
|
||
|
||
|
||
uploadExamStepList.score = score.ToString();
|
||
|
||
uploadExamStepList.stepList = new List<StepListItem>();
|
||
|
||
|
||
// 获取流程管理器
|
||
var processManager = MotionEngine.GetModule<ProcessManager>();
|
||
|
||
// 获取当前流程集合中的所有步骤
|
||
var steps = processManager.CurrentProcessCollection.Steps;
|
||
|
||
// 遍历所有步骤和动作,创建步骤列表项
|
||
for (int stepIndex = 0; stepIndex < steps.Count; stepIndex++)
|
||
{
|
||
var step = steps[stepIndex];
|
||
float stepScore = (float)step.Score; // 确保转换为float类型
|
||
int actionCount = step.Actions.Count;
|
||
|
||
// 精确计算每个动作的分数,完全保留小数点精度
|
||
float actionScorePerAction = 0f;
|
||
if (actionCount > 0)
|
||
{
|
||
actionScorePerAction = stepScore / (float)actionCount; // 移除Math.Round,保留原始精度
|
||
}
|
||
|
||
for (int actionIndex = 0; actionIndex < step.Actions.Count; actionIndex++)
|
||
{
|
||
var action = step.Actions[actionIndex];
|
||
|
||
// 精确计算每个答案的分数,完全保留小数点精度
|
||
int answerCount = GetTotalQuestionsCount(action);
|
||
float answerScorePerAnswer = 0f;
|
||
if (answerCount > 0)
|
||
{
|
||
answerScorePerAnswer = actionScorePerAction / (float)answerCount; // 移除Math.Round,保留原始精度
|
||
}
|
||
|
||
// 计算动作实际得分(考虑错误扣分)
|
||
float actionActualScore = 0;
|
||
if (action.IsCompleted)
|
||
{
|
||
var currentActionErrors = processManager._incorrectClicksPerStep
|
||
.Where(kvp => kvp.Key.stepIndex == stepIndex && kvp.Key.actionIndex == actionIndex)
|
||
.SelectMany(kvp => kvp.Value)
|
||
.ToList();
|
||
|
||
if (currentActionErrors.Count > 0)
|
||
{
|
||
// 每个错误答案扣一个答案的分数,完全保留小数点精度
|
||
float totalDeduction = answerScorePerAnswer * (float)currentActionErrors.Count; // 移除Math.Round,保留原始精度
|
||
totalDeduction = Math.Min(totalDeduction, actionScorePerAction); // 扣分不超过动作总分
|
||
actionActualScore = Math.Max(0, actionScorePerAction - totalDeduction);
|
||
}
|
||
else
|
||
{
|
||
actionActualScore = actionScorePerAction;
|
||
}
|
||
}
|
||
|
||
// 创建步骤项
|
||
StepListItem stepItem = new StepListItem
|
||
{
|
||
stepName = step.StepDescription,
|
||
testPoint = action.Title,
|
||
setScore = actionActualScore, // 动作实际得分(考虑错误扣分),完全保留小数点
|
||
defaultScore = actionScorePerAction, // 动作应该得到的分数(步骤分数 ÷ 动作数量),完全保留小数点
|
||
isKey = "1",
|
||
batch = (stepIndex + 1).ToString(),
|
||
isTrue = action.IsCompleted ? "1" : "0",
|
||
cause = "",
|
||
answers = GenerateAnswersList(stepIndex, actionIndex, action)
|
||
};
|
||
|
||
// 添加步骤项到列表
|
||
uploadExamStepList.stepList.Add(stepItem);
|
||
}
|
||
}
|
||
|
||
// // 创建步骤项
|
||
// StepListItem stepItem = new StepListItem
|
||
// {
|
||
// stepName = "测试步骤",
|
||
// testPoint = "测试点",
|
||
// setScore = 100,
|
||
// defaultScore = 100,
|
||
// isKey = "1",
|
||
// batch = "1",
|
||
// isTrue = "1",
|
||
// cause = ""
|
||
// };
|
||
|
||
// 序列化考试步骤数据
|
||
uploadExamDto.recordContent = JsonConvert.SerializeObject(uploadExamStepList);
|
||
|
||
// 输出上传数据用于调试
|
||
string jsonString = JsonConvert.SerializeObject(uploadExamDto);
|
||
Debug.Log("准备上传的数据: " + jsonString);
|
||
|
||
// 发送网络请求
|
||
string jsonText = await MotionEngine.GetModule<WebRequestManager>().PostTextAsync(
|
||
url: ApiUrls.TransitInventory.AddSubmitDetail,
|
||
jsonData: jsonString, MotionEngine.GetModule<GlobalDataStorage>().ExamInfo.Token
|
||
);
|
||
|
||
// 输出原始JSON数据用于调试
|
||
Debug.Log("服务器返回数据: " + jsonText);
|
||
|
||
// 解析返回结果
|
||
var response = JsonConvert.DeserializeObject<MessageDto>(jsonText);
|
||
Debug.Log(response);
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// // 处理异常
|
||
// Debug.LogError("上传考试数据时发生错误: " + ex.Message);
|
||
// }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传指定题目的文件(完全基于配置文件,使用题目名字,统一使用 _downloadedFiles 字典)
|
||
/// </summary>
|
||
/// <param name="examName">题目名字</param>
|
||
private static async UniTask<(string, bool)> UploadExamFilesAsync(string examName)
|
||
{
|
||
string token = MotionEngine.GetModule<GlobalDataStorage>().ExamInfo.Token;
|
||
|
||
try
|
||
{
|
||
Debug.Log($"开始上传题目 '{examName}' 的文件(基于配置文件,统一使用 _downloadedFiles 字典)");
|
||
|
||
// 从配置文件读取该题目的所有文件信息
|
||
var fileInfos = await GetExamFileInfos(examName);
|
||
if (fileInfos.Count == 0)
|
||
{
|
||
Debug.LogWarning($"题目 '{examName}' 在配置文件中没有配置任何文件,跳过上传");
|
||
return (null, true); // 返回 (null, true) 表示没有文件可上传,但操作成功
|
||
}
|
||
|
||
// 获取已下载的文件路径(统一使用 _downloadedFiles 字典)
|
||
var downloadedPaths = GetDownloadedFilePaths(examName);
|
||
|
||
if (downloadedPaths.Count == 0)
|
||
{
|
||
Debug.LogWarning($"题目 '{examName}' 没有已下载的文件,跳过上传(统一使用 _downloadedFiles 字典)");
|
||
return (null, true); // 返回 (null, true) 表示没有文件可上传,但操作成功
|
||
}
|
||
|
||
// 检测文档是否处于打开状态
|
||
var openDocuments = CheckOpenDocuments(downloadedPaths);
|
||
if (openDocuments.Count > 0)
|
||
{
|
||
string openFileNames = string.Join("、", openDocuments.Select(Path.GetFileName));
|
||
string errorMessage = $"以下文档仍处于打开状态,请先关闭后再上传:{openFileNames}";
|
||
Debug.LogError(errorMessage);
|
||
// 返回错误信息和失败状态
|
||
return ("当前有未关闭的文档,请先关闭在提交!", false);
|
||
}
|
||
|
||
Debug.Log($"从配置文件读取到 {fileInfos.Count} 个文件配置,找到 {downloadedPaths.Count} 个已下载的文件,准备上传(统一使用 _downloadedFiles 字典)");
|
||
|
||
// 统一使用压缩包上传,无论文件数量多少
|
||
string zipPath = await CreateZipFileFromPathsAsync(downloadedPaths, examName);
|
||
if (!string.IsNullOrEmpty(zipPath))
|
||
{
|
||
string result = await MotionEngine.GetModule<WebRequestManager>().UploadFileWithFormData(
|
||
ApiUrls.TransitInventory.UploadFileAndParam, zipPath, token);
|
||
Debug.Log($"压缩包上传结果:{result}(包含 {downloadedPaths.Count} 个文件,统一使用 _downloadedFiles 字典)");
|
||
|
||
// 解析上传结果,提取文件URL
|
||
string fileUrl = ParseUploadResult(result);
|
||
Debug.Log($"解析出的文件URL:{fileUrl}");
|
||
|
||
// 上传完成后删除临时ZIP文件
|
||
if (File.Exists(zipPath))
|
||
{
|
||
File.Delete(zipPath);
|
||
Debug.Log($"已删除临时ZIP文件:{zipPath}");
|
||
}
|
||
|
||
return (fileUrl, true); // 返回解析出的文件URL和成功状态
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError("创建ZIP文件失败");
|
||
return ("创建ZIP文件失败", false);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"上传题目 '{examName}' 的文件时发生错误:{ex.Message}");
|
||
return (ex.Message, false);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析上传结果,提取文件URL
|
||
/// </summary>
|
||
/// <param name="uploadResult">上传返回的JSON结果</param>
|
||
/// <returns>文件URL,如果解析失败则返回null</returns>
|
||
private static string ParseUploadResult(string uploadResult)
|
||
{
|
||
try
|
||
{
|
||
if (string.IsNullOrEmpty(uploadResult))
|
||
{
|
||
Debug.LogWarning("上传结果为空,无法解析文件URL");
|
||
return null;
|
||
}
|
||
|
||
// 解析JSON结果
|
||
var response = JsonConvert.DeserializeObject<UploadResponse>(uploadResult);
|
||
|
||
if (response?.code == 200 && response?.data?.url != null)
|
||
{
|
||
Debug.Log($"成功解析文件URL:{response.data.url}");
|
||
return response.data.url;
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"上传失败或返回结果异常:code={response?.code}, msg={response?.msg}");
|
||
return null;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"解析上传结果时发生错误:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传响应数据结构
|
||
/// </summary>
|
||
[System.Serializable]
|
||
private class UploadResponse
|
||
{
|
||
public int code { get; set; }
|
||
public string msg { get; set; }
|
||
public UploadFileData data { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传数据信息
|
||
/// </summary>
|
||
[System.Serializable]
|
||
private class UploadFileData
|
||
{
|
||
public string name { get; set; }
|
||
public string url { get; set; }
|
||
public string id { get; set; }
|
||
public string data { get; set; }
|
||
}
|
||
|
||
// /// <summary>
|
||
// /// 将两个文件压缩成ZIP包
|
||
// /// </summary>
|
||
// /// <param name="filePath1">第一个文件路径</param>
|
||
// /// <param name="filePath2">第二个文件路径</param>
|
||
// /// <returns>ZIP文件路径</returns>
|
||
// private static async UniTask<string> CreateZipFileAsync(string filePath1, string filePath2)
|
||
// {
|
||
// try
|
||
// {
|
||
// // 创建临时ZIP文件路径
|
||
// string zipPath = Path.Combine(Application.temporaryCachePath, $"ExamFiles_{DateTime.Now:yyyyMMdd_HHmmss}.zip");
|
||
//
|
||
// // 确保目录存在
|
||
// Directory.CreateDirectory(Path.GetDirectoryName(zipPath));
|
||
//
|
||
// // 创建ZIP文件
|
||
// if (File.Exists(zipPath))
|
||
// {
|
||
// File.Delete(zipPath);
|
||
// }
|
||
//
|
||
// using (ZipArchive archive = ZipFile.Open(zipPath, ZipArchiveMode.Create))
|
||
// {
|
||
// // 添加第一个文件
|
||
// if (File.Exists(filePath1))
|
||
// {
|
||
// string fileName1 = Path.GetFileName(filePath1);
|
||
// archive.CreateEntryFromFile(filePath1, fileName1);
|
||
// }
|
||
//
|
||
// // 添加第二个文件
|
||
// if (File.Exists(filePath2))
|
||
// {
|
||
// string fileName2 = Path.GetFileName(filePath2);
|
||
// archive.CreateEntryFromFile(filePath2, fileName2);
|
||
// }
|
||
// }
|
||
//
|
||
// Debug.Log($"ZIP文件已创建: {zipPath}");
|
||
// return zipPath;
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Debug.LogError($"创建ZIP文件时发生错误: {ex.Message}");
|
||
// return string.Empty;
|
||
// }
|
||
// }
|
||
|
||
// 移除了ExamQuestionTypes版本的DownloadSingleFileAsync方法
|
||
|
||
/// <summary>
|
||
/// 下载单个文件(使用题目名字)
|
||
/// </summary>
|
||
/// <param name="examName">题目名字</param>
|
||
/// <param name="fileInfo">文件信息</param>
|
||
private static async UniTask DownloadSingleFileAsync(string examName, FileDownloadInfo fileInfo)
|
||
{
|
||
try
|
||
{
|
||
Debug.Log($"开始下载文件:{fileInfo.name},URL:{fileInfo.url}(来自配置文件)");
|
||
|
||
fileInfo.downloadStatus = DownloadStatus.Downloading;
|
||
|
||
// 下载文件
|
||
var fileData = await MotionEngine.GetModule<WebRequestManager>().GetFileAsync(fileInfo.url);
|
||
|
||
if (fileData == null || fileData.Length == 0)
|
||
{
|
||
fileInfo.downloadStatus = DownloadStatus.Failed;
|
||
fileInfo.errorMessage = "下载的文件数据为空";
|
||
Debug.LogError($"下载文件 '{fileInfo.name}' 失败:文件数据为空");
|
||
return;
|
||
}
|
||
|
||
// 创建保存目录
|
||
string saveDir = Path.Combine(Application.streamingAssetsPath, "Excel", examName);
|
||
Directory.CreateDirectory(saveDir);
|
||
|
||
// 生成文件路径 - 修复时间戳位置,确保在文件名和扩展名之间
|
||
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileInfo.fileName);
|
||
string fileExtension = Path.GetExtension(fileInfo.fileName);
|
||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||
string fileName = $"{fileNameWithoutExt}_{timestamp}_{MotionEngine.GetModule<GlobalDataStorage>().ExamInfo.StudentId}{fileExtension}";
|
||
fileInfo.localFilePath = Path.Combine(saveDir, fileName);
|
||
|
||
Debug.Log($"生成文件名 - 原始文件名:{fileInfo.fileName},处理后:{fileName}");
|
||
|
||
// 保存文件
|
||
await File.WriteAllBytesAsync(fileInfo.localFilePath, fileData);
|
||
|
||
fileInfo.isDownloaded = true;
|
||
fileInfo.downloadStatus = DownloadStatus.Completed;
|
||
fileInfo.errorMessage = null;
|
||
|
||
Debug.Log($"文件 '{fileInfo.name}' 下载完成,保存路径:{fileInfo.localFilePath}(基于配置文件)");
|
||
|
||
// 触发文件下载完成事件
|
||
OnFileDownloadCompleted?.Invoke(fileInfo.name, fileInfo.localFilePath);
|
||
|
||
// 检测是否为答题卡文件
|
||
if (IsAnswerSheetFile(fileInfo.name))
|
||
{
|
||
Debug.Log($"检测到答题卡文件:{fileInfo.name}");
|
||
_answerSheetFiles[examName] = fileInfo.localFilePath;
|
||
ShowAnswerSheetButton();
|
||
}
|
||
|
||
// 打开文件
|
||
FileUtils.OpenFile(fileInfo.localFilePath);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
fileInfo.downloadStatus = DownloadStatus.Failed;
|
||
fileInfo.errorMessage = ex.Message;
|
||
Debug.LogError($"下载文件 '{fileInfo.name}' 时发生错误:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
// 移除了ExamQuestionTypes版本的CreateZipFileFromPathsAsync方法
|
||
|
||
/// <summary>
|
||
/// 从文件路径列表创建ZIP文件(使用题目名字)
|
||
/// </summary>
|
||
/// <param name="filePaths">文件路径列表</param>
|
||
/// <param name="examName">题目名字</param>
|
||
/// <returns>ZIP文件路径</returns>
|
||
private static async UniTask<string> CreateZipFileFromPathsAsync(List<string> filePaths, string examName)
|
||
{
|
||
try
|
||
{
|
||
Debug.Log($"开始创建ZIP包,包含 {filePaths.Count} 个文件(基于配置文件)");
|
||
|
||
// 创建临时ZIP文件路径
|
||
string zipPath = Path.Combine(Application.temporaryCachePath, $"{examName}_Files_{DateTime.Now:yyyyMMdd_HHmmss}.zip");
|
||
|
||
// 确保目录存在
|
||
Directory.CreateDirectory(Path.GetDirectoryName(zipPath));
|
||
|
||
// 删除已存在的ZIP文件
|
||
if (File.Exists(zipPath))
|
||
{
|
||
File.Delete(zipPath);
|
||
}
|
||
|
||
using (ZipArchive archive = ZipFile.Open(zipPath, ZipArchiveMode.Create))
|
||
{
|
||
int addedCount = 0;
|
||
|
||
// 遍历配置文件中的所有文件路径
|
||
foreach (string filePath in filePaths)
|
||
{
|
||
if (File.Exists(filePath))
|
||
{
|
||
string fileName = Path.GetFileName(filePath);
|
||
archive.CreateEntryFromFile(filePath, fileName);
|
||
addedCount++;
|
||
Debug.Log($"已添加文件到ZIP:{fileName}(来自配置文件)");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"文件不存在,跳过:{filePath}(配置文件中的文件)");
|
||
}
|
||
}
|
||
|
||
Debug.Log($"ZIP包创建完成,共添加 {addedCount} 个文件(基于配置文件中的 {filePaths.Count} 个文件)");
|
||
}
|
||
|
||
Debug.Log($"ZIP文件已创建: {zipPath}(基于配置文件)");
|
||
return zipPath;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"创建ZIP文件时发生错误: {ex.Message}");
|
||
return string.Empty;
|
||
}
|
||
}
|
||
|
||
// 移除了ExamQuestionTypes版本的GetDownloadedFilePaths方法
|
||
|
||
/// <summary>
|
||
/// 获取指定题目的已下载文件路径列表(使用题目名字,统一使用 _downloadedFiles 字典)
|
||
/// </summary>
|
||
/// <param name="examName">题目名字</param>
|
||
/// <returns>已下载文件的本地路径列表</returns>
|
||
public static List<string> GetDownloadedFilePaths(string examName)
|
||
{
|
||
Debug.Log($"GetDownloadedFilePaths 被调用,题目名字:{examName}");
|
||
|
||
if (_downloadedFiles.ContainsKey(examName))
|
||
{
|
||
// 过滤掉不存在的文件
|
||
var existingFiles = _downloadedFiles[examName].Where(path => File.Exists(path)).ToList();
|
||
_downloadedFiles[examName] = existingFiles;
|
||
Debug.Log($"获取题目 '{examName}' 的已下载文件路径,共 {existingFiles.Count} 个文件");
|
||
return existingFiles;
|
||
}
|
||
|
||
Debug.Log($"题目 '{examName}' 没有已下载的文件记录");
|
||
return new List<string>();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测指定文件路径列表中哪些文件处于打开状态
|
||
/// </summary>
|
||
/// <param name="filePaths">要检测的文件路径列表</param>
|
||
/// <returns>处于打开状态的文件路径列表</returns>
|
||
private static List<string> CheckOpenDocuments(List<string> filePaths)
|
||
{
|
||
List<string> openDocuments = new List<string>();
|
||
|
||
foreach (string filePath in filePaths)
|
||
{
|
||
if (IsFileOpen(filePath))
|
||
{
|
||
openDocuments.Add(filePath);
|
||
}
|
||
}
|
||
|
||
return openDocuments;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测当前题目的所有已下载文件是否处于打开状态(外部可调用,无需传值)
|
||
/// </summary>
|
||
/// <returns>处于打开状态的文件路径列表</returns>
|
||
public static List<string> CheckOpenDocuments()
|
||
{
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
var downloadedPaths = GetDownloadedFilePaths(examName);
|
||
return CheckOpenDocuments(downloadedPaths);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测单个文件是否处于打开状态
|
||
/// </summary>
|
||
/// <param name="filePath">文件路径</param>
|
||
/// <returns>如果文件处于打开状态返回true,否则返回false</returns>
|
||
private static bool IsFileOpen(string filePath)
|
||
{
|
||
try
|
||
{
|
||
// 尝试以独占模式打开文件
|
||
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
||
{
|
||
// 如果能成功打开,说明文件没有被其他进程占用
|
||
return false;
|
||
}
|
||
}
|
||
catch (IOException)
|
||
{
|
||
// 如果抛出IOException,说明文件被其他进程占用(可能是打开状态)
|
||
return true;
|
||
}
|
||
catch (UnauthorizedAccessException)
|
||
{
|
||
// 如果抛出UnauthorizedAccessException,也可能是文件被占用
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 其他异常,记录日志但不认为文件是打开的
|
||
Debug.LogWarning($"检测文件 '{filePath}' 状态时发生异常:{ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取动作的总题目数量
|
||
/// </summary>
|
||
/// <param name="action">动作对象</param>
|
||
/// <returns>题目总数</returns>
|
||
private static int GetTotalQuestionsCount(ProcessStepDescription action)
|
||
{
|
||
switch (action.ActionType)
|
||
{
|
||
case ProcessActionType.默认:
|
||
return action.TargetObjects.Count;
|
||
case ProcessActionType.判断题:
|
||
return action.JudgmentQuestions?.Count ?? 0;
|
||
case ProcessActionType.多选题:
|
||
return action.MultipleChoiceQuestions?.Count ?? 0;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成答案列表
|
||
/// </summary>
|
||
/// <param name="stepIndex">步骤索引</param>
|
||
/// <param name="actionIndex">动作索引</param>
|
||
/// <param name="action">动作对象</param>
|
||
/// <returns>答案列表</returns>
|
||
private static List<StepListDto> GenerateAnswersList(int stepIndex, int actionIndex, ProcessStepDescription action)
|
||
{
|
||
var answers = new List<StepListDto>();
|
||
var processManager = MotionEngine.GetModule<ProcessManager>();
|
||
|
||
switch (action.ActionType)
|
||
{
|
||
case ProcessActionType.判断题:
|
||
if (action.JudgmentQuestions != null)
|
||
{
|
||
for (int i = 0; i < action.JudgmentQuestions.Count; i++)
|
||
{
|
||
var question = action.JudgmentQuestions[i];
|
||
var answerKey = (stepIndex, actionIndex, i);
|
||
|
||
if (processManager._questionAnswers.ContainsKey(answerKey))
|
||
{
|
||
var answerData = processManager._questionAnswers[answerKey];
|
||
Debug.Log($"<color=green>【框架消息】</color>【GenerateAnswersList】从_questionAnswers读取答案:answerKey={answerKey}, 学生答案={answerData.studentAnswer}, 正确答案={answerData.correctAnswer}");
|
||
answers.Add(new StepListDto
|
||
{
|
||
question = answerData.question,
|
||
studentAnswer = answerData.studentAnswer,
|
||
YAnwser = answerData.correctAnswer
|
||
});
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"<color=green>【框架消息】</color>【GenerateAnswersList】answerKey={answerKey} 不存在于_questionAnswers中,使用默认值");
|
||
// 如果没有记录,使用默认值
|
||
answers.Add(new StepListDto
|
||
{
|
||
question = question.Question,
|
||
studentAnswer = "",
|
||
YAnwser = question.CorrectAnswer
|
||
});
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case ProcessActionType.多选题:
|
||
if (action.MultipleChoiceQuestions != null)
|
||
{
|
||
for (int i = 0; i < action.MultipleChoiceQuestions.Count; i++)
|
||
{
|
||
var question = action.MultipleChoiceQuestions[i];
|
||
var answerKey = (stepIndex, actionIndex, i);
|
||
|
||
if (processManager._questionAnswers.ContainsKey(answerKey))
|
||
{
|
||
var answerData = processManager._questionAnswers[answerKey];
|
||
answers.Add(new StepListDto
|
||
{
|
||
question = answerData.question,
|
||
studentAnswer = answerData.studentAnswer,
|
||
YAnwser = answerData.correctAnswer
|
||
});
|
||
}
|
||
else
|
||
{
|
||
// 如果没有记录,使用默认值
|
||
answers.Add(new StepListDto
|
||
{
|
||
question = question.Question,
|
||
studentAnswer = "",
|
||
YAnwser = string.Join(",", question.CorrectAnswers)
|
||
});
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case ProcessActionType.默认:
|
||
if (action.TargetObjects != null)
|
||
{
|
||
for (int i = 0; i < action.TargetObjects.Count; i++)
|
||
{
|
||
var obj = action.TargetObjects[i];
|
||
var answerKey = (stepIndex, actionIndex, i);
|
||
|
||
if (processManager._questionAnswers.ContainsKey(answerKey))
|
||
{
|
||
var answerData = processManager._questionAnswers[answerKey];
|
||
answers.Add(new StepListDto
|
||
{
|
||
question = answerData.question,
|
||
studentAnswer = answerData.studentAnswer,
|
||
YAnwser = answerData.correctAnswer
|
||
});
|
||
}
|
||
else
|
||
{
|
||
// 如果没有记录,使用默认值
|
||
bool isClicked = action.ClickedObjects.Contains(obj.ObjectName);
|
||
answers.Add(new StepListDto
|
||
{
|
||
question = obj.ObjectName,
|
||
studentAnswer = isClicked ? "true" : "false",
|
||
YAnwser = "true"
|
||
});
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
return answers;
|
||
}
|
||
|
||
// 移除了GetExamTypeFromName方法,不再需要枚举映射
|
||
|
||
// 移除了ExamQuestionTypes版本的GetDownloadStatus方法
|
||
|
||
/// <summary>
|
||
/// 获取当前题目的下载状态信息(使用全局题目名字)
|
||
/// </summary>
|
||
/// <returns>下载状态信息</returns>
|
||
public static async UniTask<DownloadStatusInfo> GetDownloadStatus()
|
||
{
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
await InitializeConfigManager();
|
||
|
||
var config = _configManager.GetConfigByExamType(examName);
|
||
if (config == null)
|
||
{
|
||
return new DownloadStatusInfo
|
||
{
|
||
TotalFiles = 0,
|
||
DownloadedFiles = 0,
|
||
Progress = 1f,
|
||
Status = DownloadStatus.Completed
|
||
};
|
||
}
|
||
|
||
var downloadedCount = config.GetDownloadedFiles().Count;
|
||
var totalCount = config.files.Count;
|
||
var progress = totalCount > 0 ? (float)downloadedCount / totalCount : 1f;
|
||
|
||
var status = progress == 1f ? DownloadStatus.Completed :
|
||
progress > 0f ? DownloadStatus.Downloading :
|
||
DownloadStatus.NotStarted;
|
||
|
||
return new DownloadStatusInfo
|
||
{
|
||
TotalFiles = totalCount,
|
||
DownloadedFiles = downloadedCount,
|
||
Progress = progress,
|
||
Status = status
|
||
};
|
||
}
|
||
|
||
// 移除了所有ExamQuestionTypes版本的状态查询和管理方法
|
||
|
||
/// <summary>
|
||
/// 检测是否为答题卡文件
|
||
/// </summary>
|
||
/// <param name="fileName">文件名</param>
|
||
/// <returns>是否为答题卡文件</returns>
|
||
private static bool IsAnswerSheetFile(string fileName)
|
||
{
|
||
if (string.IsNullOrEmpty(fileName))
|
||
return false;
|
||
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
|
||
// 特殊处理:物料凭证分析基础知识题目的文件都包含答题卡功能
|
||
if (examName == "物料凭证分析基础知识")
|
||
{
|
||
Debug.Log($"FileComponent: 检测到物料凭证分析基础知识题目,文件 '{fileName}' 被视为答题卡文件");
|
||
return true;
|
||
}
|
||
|
||
// 检测文件名中是否包含"答题卡"关键词
|
||
return fileName.Contains("答题卡");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示答题卡按钮
|
||
/// </summary>
|
||
private static void ShowAnswerSheetButton()
|
||
{
|
||
if (_answerSheetButton != null)
|
||
{
|
||
_answerSheetButton.gameObject.SetActive(true);
|
||
Debug.Log("FileComponent: 答题卡按钮已显示");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("FileComponent: 答题卡按钮引用未设置,无法显示按钮");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 隐藏答题卡按钮
|
||
/// </summary>
|
||
public static void HideAnswerSheetButton()
|
||
{
|
||
if (_answerSheetButton != null)
|
||
{
|
||
_answerSheetButton.gameObject.SetActive(false);
|
||
Debug.Log("FileComponent: 答题卡按钮已隐藏");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 答题卡按钮点击处理方法
|
||
/// </summary>
|
||
public static void OnAnswerSheetButtonClick()
|
||
{
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
|
||
if (!_answerSheetFiles.ContainsKey(examName))
|
||
{
|
||
Debug.LogWarning($"FileComponent: 题目 '{examName}' 没有答题卡文件");
|
||
return;
|
||
}
|
||
|
||
string answerSheetPath = _answerSheetFiles[examName];
|
||
|
||
if (string.IsNullOrEmpty(answerSheetPath) || !File.Exists(answerSheetPath))
|
||
{
|
||
Debug.LogError($"FileComponent: 答题卡文件不存在:{answerSheetPath}");
|
||
return;
|
||
}
|
||
|
||
// 检测文件是否已打开
|
||
if (IsFileOpen(answerSheetPath))
|
||
{
|
||
Debug.Log("FileComponent: 答题卡文件已打开,无需重复打开");
|
||
return;
|
||
}
|
||
|
||
// 打开答题卡文件
|
||
bool openResult = FileUtils.OpenFile(answerSheetPath);
|
||
if (openResult)
|
||
{
|
||
Debug.Log($"FileComponent: 成功打开答题卡文件:{answerSheetPath}");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"FileComponent: 打开答题卡文件失败:{answerSheetPath}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前题目的答题卡文件路径
|
||
/// </summary>
|
||
/// <returns>答题卡文件路径,如果没有则返回null</returns>
|
||
public static string GetCurrentAnswerSheetPath()
|
||
{
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
return _answerSheetFiles.ContainsKey(examName) ? _answerSheetFiles[examName] : null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打开指定文件名的已下载文件
|
||
/// </summary>
|
||
/// <param name="fileName">文件名</param>
|
||
public static void OpenDownloadedFile(string fileName)
|
||
{
|
||
string examName = MotionEngine.GetModule<GlobalDataStorage>().ExamName;
|
||
var downloadedPaths = GetDownloadedFilePaths(examName);
|
||
|
||
// 查找包含指定文件名的文件路径
|
||
var targetPath = downloadedPaths.FirstOrDefault(path =>
|
||
Path.GetFileNameWithoutExtension(path).Contains(fileName) ||
|
||
Path.GetFileName(path).Contains(fileName));
|
||
|
||
if (!string.IsNullOrEmpty(targetPath) && File.Exists(targetPath))
|
||
{
|
||
// 检测文件是否已打开
|
||
if (IsFileOpen(targetPath))
|
||
{
|
||
Debug.Log($"文件 '{fileName}' 已打开,无需重复打开");
|
||
return;
|
||
}
|
||
|
||
// 打开文件
|
||
bool openResult = FileUtils.OpenFile(targetPath);
|
||
if (openResult)
|
||
{
|
||
Debug.Log($"成功打开文件:{targetPath}");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"打开文件失败:{targetPath}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"未找到已下载的文件:{fileName}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 下载状态信息
|
||
/// </summary>
|
||
public class DownloadStatusInfo
|
||
{
|
||
public int TotalFiles { get; set; }
|
||
public int DownloadedFiles { get; set; }
|
||
public float Progress { get; set; }
|
||
public DownloadStatus Status { get; set; }
|
||
}
|
||
}
|
||
} |