From bccfef69d60c590232b95fe04a44665efac6c451 Mon Sep 17 00:00:00 2001 From: wuwenbo Date: Mon, 29 Jun 2026 16:24:02 +0800 Subject: [PATCH] Add questionnaire upload flow test --- .../TH1_Logic/Editor/SteamEditorWindow.cs | 186 +++++++++++++++++- 1 file changed, 182 insertions(+), 4 deletions(-) diff --git a/Unity/Assets/Scripts/TH1_Logic/Editor/SteamEditorWindow.cs b/Unity/Assets/Scripts/TH1_Logic/Editor/SteamEditorWindow.cs index c021e7bee..f6c1baae9 100644 --- a/Unity/Assets/Scripts/TH1_Logic/Editor/SteamEditorWindow.cs +++ b/Unity/Assets/Scripts/TH1_Logic/Editor/SteamEditorWindow.cs @@ -8,6 +8,7 @@ using System; using System.Text; +using Logic.CrashSight; using Logic.Multilingual; using Steamworks; using TH1_Logic.Core; @@ -208,6 +209,9 @@ namespace Logic.Editor private string _multilingualSelectedText = "测试多语言选中文本"; private string _multilingualDescription = "测试多语言问题自述"; private MultilingualType _multilingualLanguage = MultilingualType.EN; + private string _questionnaireId = "upload-flow-test"; + private string _questionnaireSingleChoiceId = "test-choice-a"; + private string _questionnaireOpenText = "测试问卷开放题回答"; private string _status = "就绪"; private string _lastObjectKey = ""; @@ -304,6 +308,10 @@ namespace Logic.Editor _multilingualSelectedText = EditorGUILayout.TextArea(_multilingualSelectedText, GUILayout.MinHeight(55)); EditorGUILayout.LabelField("多语言玩家自述"); _multilingualDescription = EditorGUILayout.TextArea(_multilingualDescription, GUILayout.MinHeight(55)); + _questionnaireId = EditorGUILayout.TextField("问卷 ID", _questionnaireId); + _questionnaireSingleChoiceId = EditorGUILayout.TextField("问卷单选选项 ID", _questionnaireSingleChoiceId); + EditorGUILayout.LabelField("问卷开放题回答"); + _questionnaireOpenText = EditorGUILayout.TextArea(_questionnaireOpenText, GUILayout.MinHeight(45)); EditorGUILayout.EndVertical(); } @@ -323,7 +331,9 @@ namespace Logic.Editor TestBugReportUpload(); if (GUILayout.Button("4. 多语言汇报上传 type=multilingualreport", GUILayout.Height(28))) TestMultilingualReportUpload(); - if (GUILayout.Button("顺序测试 1-4", GUILayout.Height(32))) + if (GUILayout.Button("5. 问卷上传 type=questionnaire", GUILayout.Height(28))) + TestQuestionnaireUpload(); + if (GUILayout.Button("顺序测试 1-5", GUILayout.Height(32))) TestAll(); } @@ -412,9 +422,29 @@ namespace Logic.Editor }); } + private async void TestQuestionnaireUpload() + { + await RunTest("问卷上传", async () => + { + var steamId = GetSteamId(); + var authTicket = GetAuthTicket(); + var questionnaireBytes = BuildQuestionnairePayloadBytes(steamId); + var credentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "questionnaire", + _version); + if (!IsQuestionnaireObjectKey(credentials?.objectKey)) + throw new Exception($"服务端返回了非问卷 objectKey: {credentials?.objectKey}"); + var success = await _uploadService.UploadFileAsync(credentials, questionnaireBytes, + "application/json", MaxQuestionnaireUploadBytes); + _lastObjectKey = credentials.objectKey ?? ""; + return success + ? $"问卷上传成功: {credentials.objectKey}" + : $"问卷上传失败: {credentials.objectKey}"; + }); + } + private async void TestAll() { - await RunTest("顺序测试 1-4", async () => + await RunTest("顺序测试 1-5", async () => { var steamId = GetSteamId(); var authTicket = GetAuthTicket(); @@ -438,8 +468,16 @@ namespace Logic.Editor var multilingualOk = await OssManager.Instance.UploadPlayerMultilingualReportAsync(steamId, multilingualPackage.Data, multilingualPackage.Manifest.version); - _lastObjectKey = multilingualOk.objectKey ?? ""; - return $"顺序测试完成: steamauth cached={warmup.cached}; ossdata={(normalOk ? "成功" : "失败")} {credentials.objectKey}; bugreport={(bugOk.success ? "成功" : "失败")} {bugOk.objectKey}; multilingualreport={(multilingualOk.success ? "成功" : "失败")} {multilingualOk.objectKey}"; + var questionnaireBytes = BuildQuestionnairePayloadBytes(steamId); + var questionnaireCredentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, + "questionnaire", _version); + if (!IsQuestionnaireObjectKey(questionnaireCredentials?.objectKey)) + throw new Exception($"服务端返回了非问卷 objectKey: {questionnaireCredentials?.objectKey}"); + var questionnaireOk = await _uploadService.UploadFileAsync(questionnaireCredentials, + questionnaireBytes, "application/json", MaxQuestionnaireUploadBytes); + + _lastObjectKey = questionnaireCredentials.objectKey ?? ""; + return $"顺序测试完成: steamauth cached={warmup.cached}; ossdata={(normalOk ? "成功" : "失败")} {credentials.objectKey}; bugreport={(bugOk.success ? "成功" : "失败")} {bugOk.objectKey}; multilingualreport={(multilingualOk.success ? "成功" : "失败")} {multilingualOk.objectKey}; questionnaire={(questionnaireOk ? "成功" : "失败")} {questionnaireCredentials.objectKey}"; }); } @@ -511,5 +549,145 @@ namespace Logic.Editor return "(Steam 未初始化)"; } } + + private const int MaxQuestionnaireUploadBytes = 512 * 1024; + + private static bool IsQuestionnaireObjectKey(string objectKey) + { + return !string.IsNullOrEmpty(objectKey) + && objectKey.StartsWith("questionnaire/", StringComparison.Ordinal) + && objectKey.EndsWith(".json", StringComparison.OrdinalIgnoreCase); + } + + [Serializable] + private class QuestionnaireTestPayload + { + public string schema = "th1.questionnaire-answer.v1"; + public string responseId; + public string questionnaireId; + public long submittedAtUnix; + public string submittedAtUtc; + public string createdAtUtc; + public string createdAtLocal; + public string timezone; + public string steamId; + public string version; + public string unityVersion; + public string platform; + public string crashSightDeviceId; + public string deviceModel; + public string deviceName; + public string operatingSystem; + public string processorType; + public int processorCount; + public int systemMemorySizeMb; + public string graphicsDeviceName; + public int graphicsMemorySizeMb; + public QuestionnaireTestAnswerSheet answerSheet; + } + + [Serializable] + private class QuestionnaireTestAnswerSheet + { + public string QuestionnaireId; + public long SubmittedAtUnix; + public string SubmittedAtUtc; + public string LastUploadObjectKey = ""; + public string LastUploadedAtUtc = ""; + public string LastUploadError = ""; + public QuestionnaireTestAnswer[] Answers; + } + + [Serializable] + private class QuestionnaireTestAnswer + { + public string QuestionId; + public int QuestionType; + public string[] SelectedOptionIds; + public string OpenText; + } + + private byte[] BuildQuestionnairePayloadBytes(string steamId) + { + var now = DateTimeOffset.UtcNow; + var questionnaireId = string.IsNullOrWhiteSpace(_questionnaireId) + ? "upload-flow-test" + : _questionnaireId.Trim(); + var selectedOptionId = string.IsNullOrWhiteSpace(_questionnaireSingleChoiceId) + ? "test-choice-a" + : _questionnaireSingleChoiceId.Trim(); + var openText = string.IsNullOrWhiteSpace(_questionnaireOpenText) + ? "测试问卷开放题回答" + : _questionnaireOpenText.Trim(); + var submittedAtUtc = now.UtcDateTime.ToString("O"); + var answerSheet = new QuestionnaireTestAnswerSheet + { + QuestionnaireId = questionnaireId, + SubmittedAtUnix = now.ToUnixTimeSeconds(), + SubmittedAtUtc = submittedAtUtc, + Answers = new[] + { + new QuestionnaireTestAnswer + { + QuestionId = "upload-flow-single-choice", + QuestionType = 1, + SelectedOptionIds = new[] { selectedOptionId }, + OpenText = "" + }, + new QuestionnaireTestAnswer + { + QuestionId = "upload-flow-open", + QuestionType = 0, + SelectedOptionIds = Array.Empty(), + OpenText = openText + } + } + }; + + var payload = new QuestionnaireTestPayload + { + responseId = Guid.NewGuid().ToString("N"), + questionnaireId = questionnaireId, + submittedAtUnix = answerSheet.SubmittedAtUnix, + submittedAtUtc = submittedAtUtc, + createdAtUtc = DateTime.UtcNow.ToString("O"), + createdAtLocal = DateTime.Now.ToString("O"), + timezone = GetLocalTimezone(), + steamId = steamId ?? "", + version = string.IsNullOrWhiteSpace(_version) ? PlayerBugReportService.GetCurrentVersion() : _version.Trim(), + unityVersion = Application.unityVersion, + platform = Application.platform.ToString(), + crashSightDeviceId = CrashSightManager.GetCrashSightDeviceId(), + deviceModel = SystemInfo.deviceModel, + deviceName = SystemInfo.deviceName, + operatingSystem = SystemInfo.operatingSystem, + processorType = SystemInfo.processorType, + processorCount = SystemInfo.processorCount, + systemMemorySizeMb = SystemInfo.systemMemorySize, + graphicsDeviceName = SystemInfo.graphicsDeviceName, + graphicsMemorySizeMb = SystemInfo.graphicsMemorySize, + answerSheet = answerSheet + }; + + var bytes = new UTF8Encoding(false).GetBytes(JsonUtility.ToJson(payload, true)); + if (bytes.Length > MaxQuestionnaireUploadBytes) + throw new Exception($"问卷测试 payload 大小 {bytes.Length} 超过 {MaxQuestionnaireUploadBytes} 字节限制"); + + return bytes; + } + + private static string GetLocalTimezone() + { + try + { + var offset = DateTimeOffset.Now.Offset; + var sign = offset < TimeSpan.Zero ? "-" : "+"; + return $"{TimeZoneInfo.Local.Id} (UTC{sign}{offset.Duration():hh\\:mm})"; + } + catch + { + return DateTimeOffset.Now.Offset.ToString(); + } + } } }