Files
order_site/apps/backend/src/repositories/fulfillment-profile-repo.ts
T

308 lines
7.7 KiB
TypeScript

import { query, withTransaction } from '../db/client.js'
type FulfillmentProfileRow = {
id: number
profile_key: string
name: string
executor_key: string
requires_claim: boolean
auto_dispatch: boolean
inventory_strategy: string
config_json: string | Record<string, unknown>
created_at: string
updated_at: string
}
type FulfillmentProfileRequirementRow = {
id: number
profile_id: number
role_key: string
roleKey?: string
credential_type: string
credentialType?: string
quantity_per_unit: number
is_required: boolean
config_json: string | Record<string, unknown>
created_at: string
updated_at: string
}
type SkuFulfillmentBindingRow = {
id: number
sku_code: string
provider: string
platform: string
shop_id: string
profile_id: number
enabled: boolean
priority: number
config_json: string | Record<string, unknown>
created_at: string
updated_at: string
profile_key?: string
name?: string
profile_name?: string
executor_key?: string
requires_claim?: boolean
auto_dispatch?: boolean
inventory_strategy?: string
profile_config_json?: string | Record<string, unknown>
}
type FulfillmentProfileUpsertInput = {
profileKey: string
name: string
executorKey: string
requiresClaim?: boolean
autoDispatch?: boolean
inventoryStrategy?: string
configJson?: string | Record<string, unknown>
createdAt: string
updatedAt: string
}
export type FulfillmentProfileRequirementInput = {
roleKey: string
credentialType: string
quantityPerUnit?: number | string
isRequired?: boolean
configJson?: string | Record<string, unknown>
}
type SkuFulfillmentBindingUpsertInput = {
skuCode: string
provider?: string
platform?: string
shopId?: string
profileId: number | string
enabled?: boolean
priority?: number | string
configJson?: string | Record<string, unknown>
createdAt: string
updatedAt: string
}
type FulfillmentBindingResolveInput = {
skuCode: string
provider?: string
platform?: string
shopId?: string
}
export async function getFulfillmentProfileByKey(profileKey: string): Promise<FulfillmentProfileRow | null> {
const result = await query<FulfillmentProfileRow>(
'SELECT * FROM fulfillment_profiles WHERE profile_key = $1 LIMIT 1',
[String(profileKey || '').trim()],
)
return result.rows[0] || null
}
export async function upsertFulfillmentProfile(
input: FulfillmentProfileUpsertInput,
): Promise<FulfillmentProfileRow | null> {
const result = await query<FulfillmentProfileRow>(
`
INSERT INTO fulfillment_profiles (
profile_key,
name,
executor_key,
requires_claim,
auto_dispatch,
inventory_strategy,
config_json,
created_at,
updated_at
) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9)
ON CONFLICT (profile_key) DO UPDATE
SET
name = EXCLUDED.name,
executor_key = EXCLUDED.executor_key,
requires_claim = EXCLUDED.requires_claim,
auto_dispatch = EXCLUDED.auto_dispatch,
inventory_strategy = EXCLUDED.inventory_strategy,
config_json = EXCLUDED.config_json,
updated_at = EXCLUDED.updated_at
RETURNING *
`,
[
input.profileKey,
input.name,
input.executorKey,
Boolean(input.requiresClaim),
Boolean(input.autoDispatch),
input.inventoryStrategy || 'static_pool',
input.configJson || '{}',
input.createdAt,
input.updatedAt,
],
)
return result.rows[0] || null
}
export async function replaceFulfillmentProfileRequirements(
profileId: number | string,
requirements: FulfillmentProfileRequirementInput[] = [],
timestamp: string,
): Promise<void> {
await withTransaction(async (client) => {
await client.query(
'DELETE FROM fulfillment_profile_requirements WHERE profile_id = $1',
[Number(profileId)],
)
for (const requirement of requirements) {
await client.query(
`
INSERT INTO fulfillment_profile_requirements (
profile_id,
role_key,
credential_type,
quantity_per_unit,
is_required,
config_json,
created_at,
updated_at
) VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7, $8)
`,
[
Number(profileId),
requirement.roleKey,
requirement.credentialType,
Number(requirement.quantityPerUnit || 1),
requirement.isRequired !== false,
requirement.configJson || '{}',
timestamp,
timestamp,
],
)
}
})
}
export async function listFulfillmentProfileRequirements(
profileId: number | string,
): Promise<FulfillmentProfileRequirementRow[]> {
const result = await query<FulfillmentProfileRequirementRow>(
`
SELECT *
FROM fulfillment_profile_requirements
WHERE profile_id = $1
ORDER BY id ASC
`,
[Number(profileId)],
)
return result.rows
}
export async function upsertSkuFulfillmentBinding(
input: SkuFulfillmentBindingUpsertInput,
): Promise<SkuFulfillmentBindingRow | null> {
const existing = await query<SkuFulfillmentBindingRow>(
`
SELECT *
FROM sku_fulfillment_bindings
WHERE sku_code = $1 AND provider = $2 AND platform = $3 AND shop_id = $4
LIMIT 1
`,
[input.skuCode, input.provider || '', input.platform || '', input.shopId || ''],
)
if (!existing.rows[0]) {
const inserted = await query<SkuFulfillmentBindingRow>(
`
INSERT INTO sku_fulfillment_bindings (
sku_code,
provider,
platform,
shop_id,
profile_id,
enabled,
priority,
config_json,
created_at,
updated_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9, $10)
RETURNING *
`,
[
input.skuCode,
input.provider || '',
input.platform || '',
input.shopId || '',
Number(input.profileId),
input.enabled !== false,
Number(input.priority || 100),
input.configJson || '{}',
input.createdAt,
input.updatedAt,
],
)
return inserted.rows[0] || null
}
const updated = await query<SkuFulfillmentBindingRow>(
`
UPDATE sku_fulfillment_bindings
SET
profile_id = $1,
enabled = $2,
priority = $3,
config_json = $4::jsonb,
updated_at = $5
WHERE id = $6
RETURNING *
`,
[
Number(input.profileId),
input.enabled !== false,
Number(input.priority || 100),
input.configJson || '{}',
input.updatedAt,
Number(existing.rows[0].id),
],
)
return updated.rows[0] || null
}
export async function resolveFulfillmentBinding({
skuCode,
provider = '',
platform = '',
shopId = '',
}: FulfillmentBindingResolveInput): Promise<SkuFulfillmentBindingRow | null> {
const result = await query<SkuFulfillmentBindingRow>(
`
SELECT
sfb.*,
fp.profile_key,
fp.name AS profile_name,
fp.executor_key,
fp.requires_claim,
fp.auto_dispatch,
fp.inventory_strategy,
fp.config_json AS profile_config_json
FROM sku_fulfillment_bindings sfb
JOIN fulfillment_profiles fp ON fp.id = sfb.profile_id
WHERE sfb.enabled = TRUE
AND sfb.sku_code = $1
AND (sfb.provider = '' OR sfb.provider = $2)
AND (sfb.platform = '' OR sfb.platform = $3)
AND (sfb.shop_id = '' OR sfb.shop_id = $4)
ORDER BY
CASE WHEN sfb.shop_id = '' THEN 1 ELSE 0 END,
CASE WHEN sfb.platform = '' THEN 1 ELSE 0 END,
CASE WHEN sfb.provider = '' THEN 1 ELSE 0 END,
sfb.priority ASC,
sfb.id ASC
LIMIT 1
`,
[skuCode, provider, platform, shopId],
)
return result.rows[0] || null
}