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 */