接入发布校准仓储接口

This commit is contained in:
Codex
2026-06-24 11:08:23 +08:00
parent f3d26e148a
commit 26b09da273
6 changed files with 608 additions and 0 deletions
+209
View File
@@ -1,5 +1,10 @@
import { nanoid } from "nanoid";
import type {
PerformanceSnapshot,
PublicationRecord,
ScoringRun,
} from "../calibration/types";
import type {
ConfirmedFactCard,
ImageInput,
@@ -58,6 +63,41 @@ interface QaReportRow {
report: string;
}
interface ScoringRunRow {
id: string;
job_id: string;
revision: number;
rubric_version_id: string;
dimension_scores: string;
composite_score: number;
rationale: string;
created_at: string;
}
interface PublicationRecordRow {
id: string;
job_id: string;
revision: number;
platform: PublishPlatform;
url: string;
published_at: string;
status: "draft" | "published" | "archived";
notes: string;
created_at: string;
updated_at: string;
}
interface PerformanceSnapshotRow {
id: string;
publication_id: string;
source: "manual" | `adapter:${string}`;
window_label: string;
metrics: string;
feedback_summary: string;
raw_reference: string | null;
snapshot_at: string;
}
function nowIso() {
return new Date().toISOString();
}
@@ -91,6 +131,21 @@ function toArticleJob(row: ArticleJobRow): ArticleJob {
};
}
function toScoringRun(row: ScoringRunRow): ScoringRun {
return {
...row,
dimension_scores: parseJson<Record<string, number>>(row.dimension_scores),
};
}
function toPerformanceSnapshot(row: PerformanceSnapshotRow): PerformanceSnapshot {
return {
...row,
raw_reference: row.raw_reference ?? undefined,
metrics: parseJson<PerformanceSnapshot["metrics"]>(row.metrics),
};
}
export function createD1Repository(db: D1Database): AppRepository {
return {
async createBrandTemplate(input) {
@@ -299,5 +354,159 @@ export function createD1Repository(db: D1Database): AppRepository {
.first<QaReportRow>();
return row ? parseJson<QaReport>(row.report) : null;
},
async saveRubricVersion(rubric) {
await db
.prepare(
`insert into rubric_versions (
id, version, name, dimensions, formula, is_active, created_at
) values (?, ?, ?, ?, ?, ?, ?)
on conflict(id) do update set
version = excluded.version,
name = excluded.name,
dimensions = excluded.dimensions,
formula = excluded.formula,
is_active = excluded.is_active`,
)
.bind(
rubric.id,
rubric.version,
rubric.name,
serialize(rubric.dimensions),
rubric.formula,
rubric.is_active ? 1 : 0,
rubric.created_at,
)
.run();
return rubric;
},
async saveScoringRun(run) {
await db
.prepare(
`insert into scoring_runs (
id, job_id, revision, rubric_version_id, dimension_scores,
composite_score, rationale, created_at
) values (?, ?, ?, ?, ?, ?, ?, ?)`,
)
.bind(
run.id,
run.job_id,
run.revision,
run.rubric_version_id,
serialize(run.dimension_scores),
run.composite_score,
run.rationale,
run.created_at,
)
.run();
return run;
},
async getLatestScoringRun(jobId, revision) {
const row = await db
.prepare(
`select * from scoring_runs
where job_id = ? and revision = ?
order by created_at desc
limit 1`,
)
.bind(jobId, revision)
.first<ScoringRunRow>();
return row ? toScoringRun(row) : null;
},
async createPublicationRecord(input) {
const timestamp = nowIso();
const record: PublicationRecord = {
id: `pub_${nanoid(10)}`,
...input,
created_at: timestamp,
updated_at: timestamp,
};
await db
.prepare(
`insert into publication_records (
id, job_id, revision, platform, url, published_at, status,
notes, created_at, updated_at
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
)
.bind(
record.id,
record.job_id,
record.revision,
record.platform,
record.url,
record.published_at,
record.status,
record.notes,
record.created_at,
record.updated_at,
)
.run();
return record;
},
async listPublicationRecords(jobId) {
const result = await db
.prepare(
"select * from publication_records where job_id = ? order by published_at desc",
)
.bind(jobId)
.all<PublicationRecordRow>();
return result.results;
},
async getPublicationRecord(id) {
return db
.prepare("select * from publication_records where id = ?")
.bind(id)
.first<PublicationRecordRow>();
},
async savePerformanceSnapshot(snapshot) {
await db
.prepare(
`insert into performance_snapshots (
id, publication_id, source, window_label, metrics,
feedback_summary, raw_reference, snapshot_at
) values (?, ?, ?, ?, ?, ?, ?, ?)`,
)
.bind(
snapshot.id,
snapshot.publication_id,
snapshot.source,
snapshot.window_label,
serialize(snapshot.metrics),
snapshot.feedback_summary,
snapshot.raw_reference ?? null,
snapshot.snapshot_at,
)
.run();
return snapshot;
},
async listPerformanceSnapshots(publicationId) {
const result = await db
.prepare(
"select * from performance_snapshots where publication_id = ? order by snapshot_at desc",
)
.bind(publicationId)
.all<PerformanceSnapshotRow>();
return result.results.map(toPerformanceSnapshot);
},
async saveCalibrationEvent(event) {
await db
.prepare(
`insert into calibration_events (
id, publication_id, scoring_run_id, performance_snapshot_id,
direction, observations, recommended_action, created_at
) values (?, ?, ?, ?, ?, ?, ?, ?)`,
)
.bind(
event.id,
event.publication_id,
event.scoring_run_id,
event.performance_snapshot_id,
event.direction,
serialize(event.observations),
event.recommended_action,
event.created_at,
)
.run();
return event;
},
};
}