feat(subscriptions): overview groups, coverage matrix, multi-version auto-fill, form field normalization (W9-E)
- 0035: subscription.last_progress_at, acquisition_slot.position (primary version)
- GET /subscriptions: server-side groups + counts, card fields, sort/filter, offset cursor
- GET /subscriptions/{id}/coverage: season x episode (movie: edition) x slot matrix, 30s cache
- auto-fill for non-primary slots: inherit/on/off, per-subscription daily budget, upgrade priority
- params normalization with MP/legacy aliases; duplicate 409; pause/resume/resubscribe/batch/fill/slot-order
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
e77fdc6549
commit
410d7fd636
12 files changed
+2158
-252
No files matched your search
@@ -629,6 +629,89 @@ export const Api = {
|
||||
},
|
||||
R,
|
||||
),
|
||||
/** 总览:在 crud 列表上加分组/排序/筛选,条目带卡片派生字段,另给各分组计数(docs/design/subscriptions.md §1) */
|
||||
list: ep({
|
||||
method: "get",
|
||||
path: "/subscriptions",
|
||||
scope: "read",
|
||||
summary:
|
||||
"订阅总览(分组、进度、各版本摘要、上次更新、下次搜索、站点、规则组;cancelled 只在显式 status=cancelled 时返回)",
|
||||
query: listQuery({
|
||||
status: policy.SubscriptionStatus.optional(),
|
||||
mediaEntityId: Id.optional(),
|
||||
mediaKind: policy.SubscriptionMediaKind.optional(),
|
||||
/** 作品标题 / 原名 / 别名 / 未建档检索词,模糊匹配 */
|
||||
q: z.string().max(200).optional(),
|
||||
group: policy.SubscriptionGroup.optional(),
|
||||
sort: policy.SubscriptionSort.optional(),
|
||||
order: z.enum(["asc", "desc"]).optional(),
|
||||
siteId: Id.optional(),
|
||||
ruleGroupId: Id.optional(),
|
||||
origin: policy.SubscriptionOrigin.optional(),
|
||||
upgrading: z.enum(["true", "false"]).optional(),
|
||||
}),
|
||||
response: policy.SubscriptionOverviewPage,
|
||||
}),
|
||||
coverage: ep({
|
||||
method: "get",
|
||||
path: "/subscriptions/{id}/coverage",
|
||||
scope: "read",
|
||||
summary:
|
||||
"覆盖矩阵:季 → 集 × 版本目标(电影:内容版本 × 版本目标),每格 have/inflight/missing/skipped;缓存 30s,refresh=true 强制重算",
|
||||
params: IdParam,
|
||||
query: z.object({ refresh: z.enum(["true", "false"]).optional() }),
|
||||
response: policy.SubscriptionCoverage,
|
||||
}),
|
||||
pause: ep({
|
||||
method: "post",
|
||||
path: "/subscriptions/{id}/pause",
|
||||
scope: "admin",
|
||||
summary: "暂停订阅(不评估、不搜索)",
|
||||
params: IdParam,
|
||||
response: policy.Subscription,
|
||||
}),
|
||||
resume: ep({
|
||||
method: "post",
|
||||
path: "/subscriptions/{id}/resume",
|
||||
scope: "admin",
|
||||
summary: "恢复已暂停的订阅(TMDB 未就绪回到 pending),立即重评并排上搜索",
|
||||
params: IdParam,
|
||||
response: policy.Subscription,
|
||||
}),
|
||||
resubscribe: ep({
|
||||
method: "post",
|
||||
path: "/subscriptions/{id}/resubscribe",
|
||||
scope: "admin",
|
||||
summary: "重新订阅已完成/已取消的订阅(历史保留),立即重评并搜索",
|
||||
params: IdParam,
|
||||
response: policy.Subscription,
|
||||
}),
|
||||
batch: ep({
|
||||
method: "post",
|
||||
path: "/subscriptions/batch",
|
||||
scope: "admin",
|
||||
summary: "批量:立即搜索 / 暂停 / 恢复 / 删除(删除 = 取消,写审计)",
|
||||
body: policy.SubscriptionBatch,
|
||||
response: policy.SubscriptionBatchResult,
|
||||
}),
|
||||
fillSlot: ep({
|
||||
method: "post",
|
||||
path: "/subscriptions/{id}/slots/{slotId}/fill",
|
||||
scope: "admin",
|
||||
summary: "手动补全某版本目标(不受自动补全开关与补全预算限制)",
|
||||
params: z.object({ id: Id, slotId: Id }),
|
||||
body: policy.SlotFillRequest,
|
||||
response: z.object({ runId: z.string(), deduplicated: z.boolean() }),
|
||||
}),
|
||||
slotOrder: ep({
|
||||
method: "put",
|
||||
path: "/subscriptions/{id}/slot-order",
|
||||
scope: "admin",
|
||||
summary: "版本目标排序(第一个为主版本)",
|
||||
params: IdParam,
|
||||
body: policy.SlotOrderSet,
|
||||
response: z.array(policy.AcquisitionSlot),
|
||||
}),
|
||||
search: ep({
|
||||
method: "post",
|
||||
path: "/subscriptions/{id}/search",
|
||||
|
||||
Reference in new issue
Block a user