fix(scheduling): enforce one active intervention per current vehicle
Business rule: a running vehicle can hold AT MOST ONE active (sent|executed) intervention. Switching to a different candidate requires cancelling the prior one first. - Server: insertNotification dedup key changes from (suggestion_id, candidate_plate) to just suggestion_id; 409 response includes the blocking candidate plate - Detail modal: shows a banner naming the locked candidate; non-active candidates render a disabled "该车已有其他干预,请先解除" hint instead of the action button - Batch: pickBestCandidate returns null for any suggestion already holding an active intervention — the whole suggestion is excluded Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -61,16 +61,20 @@ export async function fetchActiveNotificationMap(): Promise<
|
||||
async function insertNotification(
|
||||
req: NotifyRequest,
|
||||
operator: { id: string | null; name: string | null },
|
||||
): Promise<NotificationRecord | { skipped: true }> {
|
||||
// Check if a non-cancelled notification already exists for this pair
|
||||
): Promise<NotificationRecord | { skipped: true; existingPlate: string }> {
|
||||
// Business rule: each current vehicle (suggestion) can have AT MOST ONE
|
||||
// active intervention at a time. Any non-cancelled record for the same
|
||||
// suggestion_id blocks further interventions until it is cancelled.
|
||||
const [existing] = (await pool.execute(
|
||||
`SELECT id FROM tab_scheduling_notifications
|
||||
WHERE suggestion_id = ? AND candidate_plate = ? AND status != 'cancelled'
|
||||
`SELECT id, candidate_plate FROM tab_scheduling_notifications
|
||||
WHERE suggestion_id = ? AND status != 'cancelled'
|
||||
LIMIT 1`,
|
||||
[req.suggestionId, req.candidatePlate],
|
||||
[req.suggestionId],
|
||||
)) as [any[], unknown];
|
||||
|
||||
if (existing.length > 0) return { skipped: true };
|
||||
if (existing.length > 0) {
|
||||
return { skipped: true, existingPlate: existing[0].candidate_plate as string };
|
||||
}
|
||||
|
||||
const [result] = (await pool.execute(
|
||||
`INSERT INTO tab_scheduling_notifications
|
||||
@@ -110,7 +114,10 @@ app.post('/', async (c) => {
|
||||
|
||||
const result = await insertNotification(body, operator);
|
||||
if ('skipped' in result) {
|
||||
return c.json({ success: false, message: '该建议已处理' }, 409);
|
||||
return c.json(
|
||||
{ success: false, message: `此车已有干预(候选车 ${result.existingPlate}),请先解除` },
|
||||
409,
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
|
||||
Reference in New Issue
Block a user