126 lines
4.6 KiB
TypeScript
126 lines
4.6 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import type { Database, DatabaseTransaction } from '@atlas/database';
|
|
import { ApiError } from '../common/api-error.js';
|
|
import { DATABASE } from '../database/database.module.js';
|
|
|
|
type PlatformRow = {
|
|
id: string;
|
|
code: string;
|
|
name: string;
|
|
capabilities: Record<string, unknown>;
|
|
supported_currencies: string[];
|
|
};
|
|
|
|
@Injectable()
|
|
export class PlatformsService {
|
|
public constructor(@Inject(DATABASE) private readonly database: Database) {}
|
|
|
|
public async list() {
|
|
const rows = await this.database.query<PlatformRow>(
|
|
`SELECT p.id, p.code, p.name, p.capabilities,
|
|
coalesce(array_agg(pcs.currency_code ORDER BY pcs.currency_code)
|
|
FILTER (WHERE pcs.is_enabled AND c.is_enabled), ARRAY[]::char(3)[]) AS supported_currencies
|
|
FROM atlas.platforms p
|
|
LEFT JOIN atlas.platform_currency_support pcs ON pcs.platform_id = p.id
|
|
LEFT JOIN atlas.currencies c ON c.code = pcs.currency_code
|
|
WHERE p.status = 'active'
|
|
GROUP BY p.id
|
|
ORDER BY CASE WHEN p.code = 'OZON' THEN 0 ELSE 1 END, p.name`,
|
|
);
|
|
return rows.rows.map((platform) => ({
|
|
id: platform.id,
|
|
code: platform.code,
|
|
name: platform.name,
|
|
supported_currencies: platform.supported_currencies,
|
|
// 能力未配置时使用 null,而不是把 Ozon 的市场或语言假设扩散到通用模型。
|
|
default_market_code: stringCapability(
|
|
platform.capabilities,
|
|
'default_market_code',
|
|
),
|
|
default_locale: stringCapability(
|
|
platform.capabilities,
|
|
'default_locale',
|
|
),
|
|
}));
|
|
}
|
|
|
|
public async context(workspaceId: string, platformId: string) {
|
|
return this.database.workspaceTransaction(
|
|
workspaceId,
|
|
async (transaction) => {
|
|
const platform = await this.requireActivePlatform(
|
|
transaction,
|
|
platformId,
|
|
);
|
|
const stores = await transaction.query<{
|
|
id: string;
|
|
name: string;
|
|
status: string;
|
|
credential_status: string;
|
|
count: string;
|
|
}>(
|
|
`SELECT s.id, s.name, s.status, s.credential_status,
|
|
count(*) OVER ()::text AS count
|
|
FROM atlas.stores s
|
|
WHERE s.platform_id = $1 AND s.deleted_at IS NULL AND s.is_default
|
|
LIMIT 1`,
|
|
[platformId],
|
|
);
|
|
// 默认门店不存在时仍要正确返回总数,不能用默认行的窗口函数猜测。
|
|
const count = await transaction.query<{ count: string }>(
|
|
`SELECT count(*)::text AS count FROM atlas.stores
|
|
WHERE platform_id = $1 AND deleted_at IS NULL`,
|
|
[platformId],
|
|
);
|
|
const defaultStore = stores.rows[0];
|
|
return {
|
|
platform: {
|
|
id: platform.id,
|
|
code: platform.code,
|
|
name: platform.name,
|
|
},
|
|
default_store: defaultStore
|
|
? {
|
|
id: defaultStore.id,
|
|
name: defaultStore.name,
|
|
status: defaultStore.status,
|
|
credential_status: defaultStore.credential_status,
|
|
}
|
|
: null,
|
|
store_count: Number(count.rows[0]?.count ?? 0),
|
|
};
|
|
},
|
|
);
|
|
}
|
|
|
|
private async requireActivePlatform(
|
|
transaction: DatabaseTransaction,
|
|
platformId: string,
|
|
) {
|
|
const rows = await transaction.query<{
|
|
id: string;
|
|
code: string;
|
|
name: string;
|
|
}>(
|
|
`SELECT id, code, name FROM atlas.platforms WHERE id = $1 AND status = 'active'`,
|
|
[platformId],
|
|
);
|
|
if (!rows.rows[0]) {
|
|
throw new ApiError(
|
|
422,
|
|
'PLATFORM_UNAVAILABLE',
|
|
'平台不存在或已停用',
|
|
);
|
|
}
|
|
return rows.rows[0];
|
|
}
|
|
}
|
|
|
|
function stringCapability(
|
|
capabilities: Record<string, unknown>,
|
|
key: string,
|
|
): string | null {
|
|
const value = capabilities[key];
|
|
return typeof value === 'string' ? value : null;
|
|
}
|