panatgithub_AnkiHeadingSync/src/application/services/QaGroupModelService.ts
Dusk bbe1eece12 QA Group 模型同步修复 / Fix QA Group model sync
- 修复 QA Group model 管理的 AnkiConnect payload 契约,模板与样式更新改为嵌套 model payload
- 修复 QA Group model 自检与更新链路,补充带 action/model 上下文的错误信息与回归测试
- 解耦 QA Group 与旧 semantic-qa mapping 路径,设置页移除保留 model 的旧映射入口并增加只读状态说明
- Fix the AnkiConnect payload contract for QA Group model management by using nested model payloads for template and styling updates
- Repair the QA Group model ensure/update flow with action/model-aware errors and regression tests
- Decouple QA Group from the legacy semantic-qa mapping path by removing the reserved model from old mapping controls and adding read-only status guidance
2026-04-20 15:20:20 +08:00

107 lines
No EOL
3.2 KiB
TypeScript

import type { AnkiGroupGateway } from "@/application/ports/AnkiGateway";
import { buildQaGroupModelDefinition } from "./QaGroupModelDefinition";
export class QaGroupModelService {
constructor(private readonly ankiGateway: AnkiGroupGateway) {}
async ensureModel(): Promise<void> {
const definition = buildQaGroupModelDefinition();
const modelNames = await this.runModelAction(
"listNoteModels",
definition.modelName,
undefined,
() => this.ankiGateway.listNoteModels(),
);
if (!modelNames.includes(definition.modelName)) {
await this.runModelAction(
"createModel",
definition.modelName,
`fields=${definition.fieldNames.length}, templates=${definition.templates.length}`,
() => this.ankiGateway.createModel(definition),
);
return;
}
const existingFieldNames = await this.runModelAction(
"getModelFieldNames",
definition.modelName,
undefined,
() => this.ankiGateway.getModelFieldNames(definition.modelName),
);
for (const fieldName of definition.fieldNames) {
if (existingFieldNames.includes(fieldName)) {
continue;
}
await this.runModelAction(
"addModelField",
definition.modelName,
`field=${fieldName}`,
() => this.ankiGateway.addModelField(definition.modelName, fieldName),
);
}
const existingTemplates = await this.runModelAction(
"getModelTemplates",
definition.modelName,
undefined,
() => this.ankiGateway.getModelTemplates(definition.modelName),
);
for (const template of definition.templates) {
const existingTemplate = existingTemplates[template.name];
if (!existingTemplate) {
await this.runModelAction(
"addModelTemplate",
definition.modelName,
`template=${template.name}`,
() => this.ankiGateway.addModelTemplate(definition.modelName, template),
);
continue;
}
if (existingTemplate.front !== template.front || existingTemplate.back !== template.back) {
await this.runModelAction(
"updateModelTemplate",
definition.modelName,
`template=${template.name}`,
() => this.ankiGateway.updateModelTemplate(definition.modelName, template),
);
}
}
const existingCss = await this.runModelAction(
"getModelStyling",
definition.modelName,
undefined,
() => this.ankiGateway.getModelStyling(definition.modelName),
);
if (existingCss !== definition.css) {
await this.runModelAction(
"updateModelStyling",
definition.modelName,
`cssLength=${definition.css.length}`,
() => this.ankiGateway.updateModelStyling(definition.modelName, definition.css),
);
}
}
private async runModelAction<T>(
action: string,
modelName: string,
summary: string | undefined,
operation: () => Promise<T>,
): Promise<T> {
try {
return await operation();
} catch (error) {
const details = summary ? ` (${summary})` : "";
const message = error instanceof Error ? error.message : String(error);
throw new Error(`QA Group model sync failed during ${action} for "${modelName}"${details}: ${message}`);
}
}
}