Files
koc-loop/lib/distribution-release-service.ts

138 lines
3.7 KiB
TypeScript
Raw Permalink Normal View History

type ReleasableDistribution = {
id: string;
task_id: string;
content_id: string;
content_title: string;
partner_id: string;
partner_name: string;
claim_id: string | null;
delegation_bundle_id: string | null;
publish_url: string | null;
};
export class DistributionReleaseError extends Error {
readonly status: number;
constructor(message: string, status: number) {
super(message);
this.name = "DistributionReleaseError";
this.status = status;
}
}
export function distributionReleaseBlockReason(input: {
publishUrl?: string | null;
resultSubmittedAt?: string | null;
}) {
if (input.publishUrl) return "已回填发布链接的笔记不能释放";
if (input.resultSubmittedAt) return "已提交结果截图的任务不能释放";
return null;
}
export async function releaseUnfinishedDistribution(
db: D1Database,
distributionId: string,
) {
if (!distributionId) {
throw new DistributionReleaseError("请选择需要释放的领取记录", 400);
}
const distribution = await db
.prepare(
`SELECT
d.id,
d.task_id,
d.content_id,
c.title AS content_title,
d.partner_id,
p.name AS partner_name,
d.claim_id,
d.delegation_bundle_id,
d.publish_url
FROM distributions d
JOIN contents c ON c.id = d.content_id
JOIN partners p ON p.id = d.partner_id
WHERE d.id = ?`,
)
.bind(distributionId)
.first<ReleasableDistribution>();
if (!distribution) {
throw new DistributionReleaseError("领取记录不存在或已被释放", 404);
}
const blocked = distributionReleaseBlockReason({
publishUrl: distribution.publish_url,
});
if (blocked) throw new DistributionReleaseError(blocked, 409);
const statements: D1PreparedStatement[] = [
db
.prepare("DELETE FROM collection_runs WHERE distribution_id = ?")
.bind(distribution.id),
db.prepare("DELETE FROM distributions WHERE id = ?").bind(distribution.id),
db
.prepare("UPDATE contents SET status = 'available' WHERE id = ?")
.bind(distribution.content_id),
db
.prepare(
`UPDATE tasks
SET claimed_quantity = MAX(claimed_quantity - 1, 0)
WHERE id = ?`,
)
.bind(distribution.task_id),
db
.prepare(
`UPDATE partners
SET claimed_total = MAX(claimed_total - 1, 0)
WHERE id = ?`,
)
.bind(distribution.partner_id),
];
if (distribution.delegation_bundle_id) {
statements.push(
db
.prepare(
`UPDATE delegation_bundles
SET quantity = MAX(quantity - 1, 0),
status = CASE WHEN quantity <= 1 THEN 'revoked' ELSE status END,
revoked_at = CASE WHEN quantity <= 1 THEN CURRENT_TIMESTAMP ELSE revoked_at END,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?`,
)
.bind(distribution.delegation_bundle_id),
);
}
if (distribution.claim_id) {
statements.push(
db
.prepare(
`UPDATE claims
SET quantity = MAX(quantity - 1, 0)
WHERE id = ?`,
)
.bind(distribution.claim_id),
db
.prepare(
`DELETE FROM claims
WHERE id = ?
AND quantity <= 0
AND NOT EXISTS (
SELECT 1 FROM distributions WHERE claim_id = ?
)`,
)
.bind(distribution.claim_id, distribution.claim_id),
);
}
await db.batch(statements);
return {
distributionId: distribution.id,
taskId: distribution.task_id,
contentId: distribution.content_id,
contentTitle: distribution.content_title,
partnerName: distribution.partner_name,
};
}