feat(product-edit):商品编辑页初版
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import type { DatabaseTransaction } from '@atlas/database';
|
||||
import { ApiError } from './api-error.js';
|
||||
|
||||
/**
|
||||
* Shared optimistic-lock conflict response. Versioned writes still keep their
|
||||
* domain-specific SET clauses, but they must use a conditional `revision =
|
||||
* expected` update and this helper when no row was affected.
|
||||
*/
|
||||
const resources = {
|
||||
collection_item: {
|
||||
table: 'atlas.collection_items',
|
||||
label: '采集记录',
|
||||
active: ' AND deleted_at IS NULL',
|
||||
},
|
||||
product: { table: 'atlas.products', label: '商品', active: '' },
|
||||
product_platform_profile: {
|
||||
table: 'atlas.product_platform_profiles',
|
||||
label: '商品发布配置',
|
||||
active: '',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export async function throwRevisionConflict(
|
||||
transaction: DatabaseTransaction,
|
||||
resource: keyof typeof resources,
|
||||
id: string,
|
||||
expected: number,
|
||||
): Promise<never> {
|
||||
const target = resources[resource];
|
||||
const current = await transaction.query<{ revision: number }>(
|
||||
`SELECT revision FROM ${target.table} WHERE id = $1${target.active}`,
|
||||
[id],
|
||||
);
|
||||
if (!current.rows[0])
|
||||
throw new ApiError(404, 'RESOURCE_NOT_FOUND', `${target.label}不存在`);
|
||||
if (current.rows[0].revision !== expected) {
|
||||
throw new ApiError(409, 'VERSION_CONFLICT', `${target.label}已被更新`, [
|
||||
{
|
||||
field: 'If-Match',
|
||||
code: 'VERSION_CONFLICT',
|
||||
message: `当前版本为 ${current.rows[0].revision}`,
|
||||
current_revision: current.rows[0].revision,
|
||||
},
|
||||
]);
|
||||
}
|
||||
// A conditional write with a matching version affecting no rows is a
|
||||
// resource-state error (for example, a concurrent soft delete), not a
|
||||
// successful retry.
|
||||
throw new ApiError(409, 'VERSION_CONFLICT', `${target.label}状态已变化`);
|
||||
}
|
||||
Reference in New Issue
Block a user