diff --git a/Tiku.Api/Contracts/LearningDtos.cs b/Tiku.Api/Contracts/LearningDtos.cs index b6054de..2262137 100644 --- a/Tiku.Api/Contracts/LearningDtos.cs +++ b/Tiku.Api/Contracts/LearningDtos.cs @@ -172,7 +172,7 @@ public sealed class SubmitPracticeSessionDto [Required] public Guid PracticeSessionId { get; set; } - [Range(1, long.MaxValue)] + [Required, Range(1, long.MaxValue)] public long ExpectedSessionVersion { get; set; } [Required, StringLength(200, MinimumLength = 1)] @@ -196,13 +196,13 @@ public sealed class SubmitAnswerDto /// /// 客户端读取会话时获得的版本。 /// - [Range(1, long.MaxValue)] + [Required, Range(1, long.MaxValue)] public long ExpectedSessionVersion { get; set; } /// /// 客户端在本会话内单调递增的序列。 /// - [Range(1, long.MaxValue)] + [Required, Range(1, long.MaxValue)] public long ClientSequence { get; set; } /// diff --git a/Tiku.Domain/Learning/LearningReportEntities.cs b/Tiku.Domain/Learning/LearningReportEntities.cs index 720b998..224cc1b 100644 --- a/Tiku.Domain/Learning/LearningReportEntities.cs +++ b/Tiku.Domain/Learning/LearningReportEntities.cs @@ -122,8 +122,8 @@ public sealed class PracticeSessionReport : AuditableTenantEntity public enum PracticeReportStatus { - PendingReview, Final, + PendingReview, LegacyUnverified } diff --git a/Tiku.PlatformAdmin.Web/scripts/generate-platform-operations.mjs b/Tiku.PlatformAdmin.Web/scripts/generate-platform-operations.mjs index 4134a2d..23f4f62 100644 --- a/Tiku.PlatformAdmin.Web/scripts/generate-platform-operations.mjs +++ b/Tiku.PlatformAdmin.Web/scripts/generate-platform-operations.mjs @@ -46,6 +46,6 @@ for (const [route, pathItem] of Object.entries(document.paths || {})) { operations.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method)); const schemas = document.components?.schemas || {}; -const source = `/* eslint-disable */\n// 由 scripts/generate-platform-operations.mjs 根据后端 OpenAPI 自动生成,请勿手改。\nimport type { PlatformOperation } from './types';\n\nexport const generatedAt = ${JSON.stringify(new Date().toISOString())};\nexport const platformOperations = ${JSON.stringify(operations, null, 2)} as const satisfies readonly PlatformOperation[];\nexport const openApiSchemas = ${JSON.stringify(schemas, null, 2)} as const;\n`; +const source = `/* eslint-disable */\n// 由 scripts/generate-platform-operations.mjs 根据后端 OpenAPI 自动生成,请勿手改。\nimport type { PlatformOperation } from './types';\n\nexport const platformOperations = ${JSON.stringify(operations, null, 2)} as const satisfies readonly PlatformOperation[];\nexport const openApiSchemas = ${JSON.stringify(schemas, null, 2)} as const;\n`; writeFileSync(path.join(apiDir, 'platform-operations.generated.ts'), source); console.log(`已生成 ${operations.length} 个平台接口契约,来源:${openApiUrl}`); diff --git a/Tiku.PlatformAdmin.Web/src/api/platform-operations.generated.ts b/Tiku.PlatformAdmin.Web/src/api/platform-operations.generated.ts index 5ff0537..29c9754 100644 --- a/Tiku.PlatformAdmin.Web/src/api/platform-operations.generated.ts +++ b/Tiku.PlatformAdmin.Web/src/api/platform-operations.generated.ts @@ -2,7 +2,6 @@ // 由 scripts/generate-platform-operations.mjs 根据后端 OpenAPI 自动生成,请勿手改。 import type { PlatformOperation } from './types'; -export const generatedAt = "2026-08-01T08:13:39.099Z"; export const platformOperations = [ { "id": "GET /api/backoffice/platform/bootstrap", @@ -3137,10 +3136,13 @@ export const openApiSchemas = { "id", "sessionQuestionId", "practiceSessionId", - "selectedOptions", + "selectedOptionIndices", "answerText", - "isCorrect", - "answeredAt" + "status", + "revision", + "clientSequence", + "sessionVersion", + "acceptedAt" ], "type": "object", "properties": { @@ -3156,7 +3158,7 @@ export const openApiSchemas = { "type": "string", "format": "uuid" }, - "selectedOptions": { + "selectedOptionIndices": { "$ref": "#/components/schemas/JsonElement" }, "answerText": { @@ -3165,13 +3167,34 @@ export const openApiSchemas = { "string" ] }, - "isCorrect": { - "type": [ - "null", - "boolean" - ] + "status": { + "type": "string" }, - "answeredAt": { + "revision": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "clientSequence": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "sessionVersion": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "acceptedAt": { "type": "string", "format": "date-time" } @@ -19690,6 +19713,9 @@ export const openApiSchemas = { "PracticeMode": { "type": "integer" }, + "PracticeReportStatus": { + "type": "integer" + }, "PracticeSessionDetailItem": { "required": [ "session", @@ -19736,7 +19762,9 @@ export const openApiSchemas = { "finishedAt", "expiresAt", "metadata", - "status" + "status", + "version", + "lastClientSequence" ], "type": "object", "properties": { @@ -19857,7 +19885,23 @@ export const openApiSchemas = { "$ref": "#/components/schemas/JsonElement" }, "status": { - "type": "string" + "$ref": "#/components/schemas/PracticeSessionStatus" + }, + "version": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "lastClientSequence": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" } } }, @@ -19873,8 +19917,7 @@ export const openApiSchemas = { "tags", "versionId", "content", - "options", - "explanation" + "options" ], "type": "object", "properties": { @@ -19929,12 +19972,6 @@ export const openApiSchemas = { }, "options": { "$ref": "#/components/schemas/JsonElement" - }, - "explanation": { - "type": [ - "null", - "string" - ] } } }, @@ -19956,6 +19993,11 @@ export const openApiSchemas = { "durationSeconds", "startedAt", "submittedAt", + "status", + "version", + "isFinal", + "pendingReviewCount", + "scoringVersion", "sectionStats", "questionResults", "wrongQuestionIds", @@ -20071,6 +20113,36 @@ export const openApiSchemas = { "type": "string", "format": "date-time" }, + "status": { + "$ref": "#/components/schemas/PracticeReportStatus" + }, + "version": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "isFinal": { + "type": "boolean" + }, + "pendingReviewCount": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "scoringVersion": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, "sectionStats": { "$ref": "#/components/schemas/JsonElement" }, @@ -20085,6 +20157,9 @@ export const openApiSchemas = { } } }, + "PracticeSessionStatus": { + "type": "integer" + }, "PreviewReconciliationImportDto": { "required": [ "provider", @@ -24332,7 +24407,10 @@ export const openApiSchemas = { }, "SubmitAnswerDto": { "required": [ - "sessionQuestionId" + "sessionQuestionId", + "expectedSessionVersion", + "clientSequence", + "idempotencyKey" ], "type": "object", "properties": { @@ -24341,15 +24419,48 @@ export const openApiSchemas = { "description": "会话题目 ID。", "format": "uuid" }, - "selectedOptions": { + "expectedSessionVersion": { + "maximum": 9223372036854776000, + "minimum": 1, + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "description": "客户端读取会话时获得的版本。", + "format": "int64" + }, + "clientSequence": { + "maximum": 9223372036854776000, + "minimum": 1, + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "description": "客户端在本会话内单调递增的序列。", + "format": "int64" + }, + "idempotencyKey": { + "maxLength": 200, + "minLength": 1, + "type": "string", + "description": "网络重试幂等键。" + }, + "selectedOptionIndices": { "type": [ "null", "array" ], "items": { - "type": "string" + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" }, - "description": "已选选项。" + "description": "已选选项的零基索引。" }, "answerText": { "maxLength": 10000, @@ -24359,13 +24470,6 @@ export const openApiSchemas = { "string" ], "description": "文字答案。" - }, - "selfJudgedCorrect": { - "type": [ - "null", - "boolean" - ], - "description": "主观题自评是否正确。" } }, "description": "提交答案请求 DTO。" @@ -24448,7 +24552,9 @@ export const openApiSchemas = { }, "SubmitPracticeSessionDto": { "required": [ - "practiceSessionId" + "practiceSessionId", + "expectedSessionVersion", + "idempotencyKey" ], "type": "object", "properties": { @@ -24456,6 +24562,21 @@ export const openApiSchemas = { "type": "string", "description": "练习会话 ID。", "format": "uuid" + }, + "expectedSessionVersion": { + "maximum": 9223372036854776000, + "minimum": 1, + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "idempotencyKey": { + "maxLength": 200, + "minLength": 1, + "type": "string" } }, "description": "提交练习会话请求 DTO。" diff --git a/Tiku.PlatformAdmin.Web/src/api/schema.generated.d.ts b/Tiku.PlatformAdmin.Web/src/api/schema.generated.d.ts index 50fe5e8..fca8d8d 100644 --- a/Tiku.PlatformAdmin.Web/src/api/schema.generated.d.ts +++ b/Tiku.PlatformAdmin.Web/src/api/schema.generated.d.ts @@ -19623,11 +19623,17 @@ export interface components { sessionQuestionId: string; /** Format: uuid */ practiceSessionId: string; - selectedOptions: components["schemas"]["JsonElement"]; + selectedOptionIndices: components["schemas"]["JsonElement"]; answerText: null | string; - isCorrect: null | boolean; + status: string; + /** Format: int32 */ + revision: number | string; + /** Format: int64 */ + clientSequence: number | string; + /** Format: int64 */ + sessionVersion: number | string; /** Format: date-time */ - answeredAt: string; + acceptedAt: string; }; AppAssetCatalogItem: { /** Format: uuid */ @@ -24291,6 +24297,7 @@ export interface components { accuracy: null | number | string; }; PracticeMode: number; + PracticeReportStatus: number; PracticeSessionDetailItem: { session: components["schemas"]["PracticeSessionItem"]; questions: components["schemas"]["PracticeSessionQuestionItem"][]; @@ -24332,7 +24339,11 @@ export interface components { /** Format: date-time */ expiresAt: null | string; metadata: components["schemas"]["JsonElement"]; - status: string; + status: components["schemas"]["PracticeSessionStatus"]; + /** Format: int64 */ + version: number | string; + /** Format: int64 */ + lastClientSequence: number | string; }; PracticeSessionQuestionItem: { /** Format: uuid */ @@ -24351,7 +24362,6 @@ export interface components { versionId: null | string; content: null | string; options: components["schemas"]["JsonElement"]; - explanation: null | string; }; PracticeSessionReportItem: { /** Format: uuid */ @@ -24385,11 +24395,20 @@ export interface components { startedAt: null | string; /** Format: date-time */ submittedAt: string; + status: components["schemas"]["PracticeReportStatus"]; + /** Format: int32 */ + version: number | string; + isFinal: boolean; + /** Format: int32 */ + pendingReviewCount: number | string; + /** Format: int32 */ + scoringVersion: number | string; sectionStats: components["schemas"]["JsonElement"]; questionResults: components["schemas"]["JsonElement"]; wrongQuestionIds: components["schemas"]["JsonElement"]; metadata: components["schemas"]["JsonElement"]; }; + PracticeSessionStatus: number; /** @description 预览对账导入请求 DTO。 */ PreviewReconciliationImportDto: { /** @description 服务提供方。 */ @@ -25534,12 +25553,22 @@ export interface components { * @description 会话题目 ID。 */ sessionQuestionId: string; - /** @description 已选选项。 */ - selectedOptions?: null | string[]; + /** + * Format: int64 + * @description 客户端读取会话时获得的版本。 + */ + expectedSessionVersion: number | string; + /** + * Format: int64 + * @description 客户端在本会话内单调递增的序列。 + */ + clientSequence: number | string; + /** @description 网络重试幂等键。 */ + idempotencyKey: string; + /** @description 已选选项的零基索引。 */ + selectedOptionIndices?: null | (number | string)[]; /** @description 文字答案。 */ answerText?: null | string; - /** @description 主观题自评是否正确。 */ - selfJudgedCorrect?: null | boolean; }; /** @description 提交反馈请求 DTO。 */ SubmitFeedbackDto: { @@ -25572,6 +25601,9 @@ export interface components { * @description 练习会话 ID。 */ practiceSessionId: string; + /** Format: int64 */ + expectedSessionVersion: number | string; + idempotencyKey: string; }; SvipPlanCatalogItem: { /** Format: uuid */