fix: 催办按任务类型分流 + 领取事务加锁防超发
- 临期/超期/自动释放查询按 task_type 区分完成口径:内容任务看 publish_url,截图任务看 result_submitted_at,截图任务不再被永久误判为未发布 - portal 领取改为事务内 SELECT ... FOR UPDATE 锁定可用内容,消除并发领取超发窗口
This commit is contained in:
@@ -563,18 +563,6 @@ async function handlePost(request: Request) {
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
const available = await db
|
||||
.prepare(
|
||||
`SELECT id FROM contents
|
||||
WHERE task_id = ? AND status = 'available'
|
||||
ORDER BY COALESCE(source_row, 999999), created_at, id
|
||||
LIMIT ${quantity}`,
|
||||
)
|
||||
.bind(task.id)
|
||||
.all<{ id: string }>();
|
||||
if (available.results.length === 0) {
|
||||
return Response.json({ error: "当前任务已领完" }, { status: 409 });
|
||||
}
|
||||
const partnerId = wecomIdentity
|
||||
? (await resolveWecomClaimant(db, wecomIdentity.externalUserId)).partnerId
|
||||
: `partner-ext-${hashText(
|
||||
@@ -585,86 +573,102 @@ async function handlePost(request: Request) {
|
||||
: (claimantIdentifier?.display ?? "");
|
||||
const claimId = uid("claim");
|
||||
const claimToken = crypto.randomUUID().replaceAll("-", "");
|
||||
const statements: DatabaseStatement[] = [
|
||||
wecomIdentity
|
||||
? db
|
||||
.prepare(
|
||||
`INSERT INTO partners
|
||||
(id, name, wecom_name, owner, claimed_total, completed_total,
|
||||
wecom_external_user_id)
|
||||
VALUES (?, ?, ?, '外部KOC', 0, 0, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
wecom_name = excluded.wecom_name,
|
||||
wecom_external_user_id = excluded.wecom_external_user_id`,
|
||||
)
|
||||
.bind(
|
||||
partnerId,
|
||||
claimantName,
|
||||
claimantName,
|
||||
wecomIdentity.externalUserId,
|
||||
)
|
||||
: db
|
||||
.prepare(
|
||||
`INSERT INTO partners
|
||||
(id, name, wecom_name, owner, claimed_total, completed_total)
|
||||
VALUES (?, ?, ?, '外部KOC', 0, 0)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
wecom_name = excluded.wecom_name`,
|
||||
)
|
||||
.bind(partnerId, claimantName, claimantName),
|
||||
db
|
||||
const claimResult = await db.transaction(async (tx) => {
|
||||
const available = await tx
|
||||
.prepare(
|
||||
`INSERT INTO claims
|
||||
(id, task_id, partner_id, claimant_name, claim_token, quantity)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
`SELECT id FROM contents
|
||||
WHERE task_id = ? AND status = 'available'
|
||||
ORDER BY COALESCE(source_row, 999999), created_at, id
|
||||
LIMIT ${quantity}
|
||||
FOR UPDATE`,
|
||||
)
|
||||
.bind(
|
||||
claimId,
|
||||
task.id,
|
||||
partnerId,
|
||||
claimantName,
|
||||
claimToken,
|
||||
available.results.length,
|
||||
),
|
||||
];
|
||||
for (const content of available.results) {
|
||||
.bind(task.id)
|
||||
.all<{ id: string }>();
|
||||
if (available.results.length === 0) return null;
|
||||
const statements: DatabaseStatement[] = [
|
||||
wecomIdentity
|
||||
? db
|
||||
.prepare(
|
||||
`INSERT INTO partners
|
||||
(id, name, wecom_name, owner, claimed_total, completed_total,
|
||||
wecom_external_user_id)
|
||||
VALUES (?, ?, ?, '外部KOC', 0, 0, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
wecom_name = excluded.wecom_name,
|
||||
wecom_external_user_id = excluded.wecom_external_user_id`,
|
||||
)
|
||||
.bind(
|
||||
partnerId,
|
||||
claimantName,
|
||||
claimantName,
|
||||
wecomIdentity.externalUserId,
|
||||
)
|
||||
: db
|
||||
.prepare(
|
||||
`INSERT INTO partners
|
||||
(id, name, wecom_name, owner, claimed_total, completed_total)
|
||||
VALUES (?, ?, ?, '外部KOC', 0, 0)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
wecom_name = excluded.wecom_name`,
|
||||
)
|
||||
.bind(partnerId, claimantName, claimantName),
|
||||
db
|
||||
.prepare(
|
||||
`INSERT INTO claims
|
||||
(id, task_id, partner_id, claimant_name, claim_token, quantity)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
)
|
||||
.bind(
|
||||
claimId,
|
||||
task.id,
|
||||
partnerId,
|
||||
claimantName,
|
||||
claimToken,
|
||||
available.results.length,
|
||||
),
|
||||
];
|
||||
for (const content of available.results) {
|
||||
statements.push(
|
||||
db
|
||||
.prepare(
|
||||
`INSERT INTO distributions
|
||||
(id, task_id, content_id, partner_id, claim_id, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'claimed')`,
|
||||
)
|
||||
.bind(uid("dist"), task.id, content.id, partnerId, claimId),
|
||||
db
|
||||
.prepare(
|
||||
`UPDATE contents SET status = 'allocated'
|
||||
WHERE id = ? AND status = 'available'`,
|
||||
)
|
||||
.bind(content.id),
|
||||
);
|
||||
}
|
||||
statements.push(
|
||||
db
|
||||
.prepare(
|
||||
`INSERT INTO distributions
|
||||
(id, task_id, content_id, partner_id, claim_id, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'claimed')`,
|
||||
`UPDATE tasks SET claimed_quantity = claimed_quantity + ?
|
||||
WHERE id = ?`,
|
||||
)
|
||||
.bind(uid("dist"), task.id, content.id, partnerId, claimId),
|
||||
.bind(available.results.length, task.id),
|
||||
db
|
||||
.prepare(
|
||||
`UPDATE contents SET status = 'allocated'
|
||||
WHERE id = ? AND status = 'available'`,
|
||||
`UPDATE partners SET claimed_total = claimed_total + ?
|
||||
WHERE id = ?`,
|
||||
)
|
||||
.bind(content.id),
|
||||
.bind(available.results.length, partnerId),
|
||||
);
|
||||
}
|
||||
statements.push(
|
||||
db
|
||||
.prepare(
|
||||
`UPDATE tasks SET claimed_quantity = claimed_quantity + ?
|
||||
WHERE id = ?`,
|
||||
)
|
||||
.bind(available.results.length, task.id),
|
||||
db
|
||||
.prepare(
|
||||
`UPDATE partners SET claimed_total = claimed_total + ?
|
||||
WHERE id = ?`,
|
||||
)
|
||||
.bind(available.results.length, partnerId),
|
||||
);
|
||||
await db.batch(statements);
|
||||
return Response.json({
|
||||
claimToken,
|
||||
claimedCount: available.results.length,
|
||||
for (const statement of statements) {
|
||||
await tx.execute(statement.sql, statement.values());
|
||||
}
|
||||
return { claimToken, claimedCount: available.results.length };
|
||||
});
|
||||
if (!claimResult) {
|
||||
return Response.json({ error: "当前任务已领完" }, { status: 409 });
|
||||
}
|
||||
return Response.json(claimResult);
|
||||
}
|
||||
|
||||
if (body.action === "create_delegation") {
|
||||
|
||||
Reference in New Issue
Block a user