zksu
/
API
forked from yoone/API
1
0
Fork 0
API/src/controller/site-api.controller.ts

1592 lines
61 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Body, Controller, Del, Get, ILogger, Inject, Param, Post, Put, Query } from '@midwayjs/core';
import { ApiBody, ApiOkResponse } from '@midwayjs/swagger';
import { UnifiedPaginationDTO, UnifiedSearchParamsDTO } from '../dto/api.dto';
import { BatchOperationDTO, BatchOperationResultDTO } from '../dto/batch.dto';
import {
BatchFulfillmentsDTO,
CancelFulfillmentDTO,
CreateReviewDTO,
CreateWebhookDTO,
UnifiedCustomerDTO,
UnifiedCustomerPaginationDTO,
UnifiedMediaPaginationDTO,
UnifiedOrderDTO,
UnifiedOrderPaginationDTO,
UnifiedOrderTrackingDTO,
UnifiedProductDTO,
UnifiedProductPaginationDTO,
UnifiedReviewDTO,
UnifiedReviewPaginationDTO,
UnifiedSubscriptionPaginationDTO,
UnifiedWebhookDTO,
UpdateReviewDTO,
UpdateWebhookDTO,
UploadMediaDTO,
} from '../dto/site-api.dto';
import { SiteApiService } from '../service/site-api.service';
import { errorResponse, successResponse } from '../utils/response.util';
@Controller('/site-api')
export class SiteApiController {
@Inject()
siteApiService: SiteApiService;
@Inject()
logger: ILogger;
@Post('/:siteId/media/create')
@ApiOkResponse({ type: UnifiedMediaPaginationDTO })
@ApiBody({ type: UploadMediaDTO })
async createMedia(
@Param('siteId') siteId: number,
@Body() body: UploadMediaDTO,
) {
this.logger.debug(`[Site API] 上传媒体文件开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.createMedia(body.file);
this.logger.debug(`[Site API] 上传媒体文件成功, siteId: ${siteId}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 上传媒体文件失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/reviews')
@ApiOkResponse({ type: UnifiedReviewPaginationDTO })
async getReviews(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
this.logger.debug(`[Site API] 获取评论列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getReviews(query);
this.logger.debug(`[Site API] 获取评论列表成功, siteId: ${siteId}, 共获取到 ${data.total} 条评论`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取评论列表失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/reviews')
@ApiOkResponse({ type: UnifiedReviewDTO })
async createReview(
@Param('siteId') siteId: number,
@Body() body: CreateReviewDTO
) {
this.logger.debug(`[Site API] 创建评论开始, siteId: ${siteId}, body: ${JSON.stringify(body)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.createReview(body);
this.logger.debug(`[Site API] 创建评论成功, siteId: ${siteId}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 创建评论失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Put('/:siteId/reviews/:id')
@ApiOkResponse({ type: UnifiedReviewDTO })
async updateReview(
@Param('siteId') siteId: number,
@Param('id') id: number,
@Body() body: UpdateReviewDTO
) {
this.logger.debug(`[Site API] 更新评论开始, siteId: ${siteId}, id: ${id}, body: ${JSON.stringify(body)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.updateReview(id, body);
this.logger.debug(`[Site API] 更新评论成功, siteId: ${siteId}, id: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 更新评论失败, siteId: ${siteId}, id: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Del('/:siteId/reviews/:id')
@ApiOkResponse({ type: Boolean })
async deleteReview(
@Param('siteId') siteId: number,
@Param('id') id: number
) {
this.logger.debug(`[Site API] 删除评论开始, siteId: ${siteId}, id: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.deleteReview(id);
this.logger.debug(`[Site API] 删除评论成功, siteId: ${siteId}, id: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 删除评论失败, siteId: ${siteId}, id: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/webhooks')
@ApiOkResponse({ type: UnifiedPaginationDTO<UnifiedWebhookDTO> })
async getWebhooks(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
this.logger.debug(`[Site API] 获取webhooks列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getWebhooks(query);
this.logger.debug(`[Site API] 获取webhooks列表成功, siteId: ${siteId}, 共获取到 ${data.total} 个webhooks`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取webhooks列表失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/webhooks/:id')
@ApiOkResponse({ type: UnifiedWebhookDTO })
async getWebhook(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.debug(`[Site API] 获取单个webhook开始, siteId: ${siteId}, id: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getWebhook(id);
this.logger.debug(`[Site API] 获取单个webhook成功, siteId: ${siteId}, id: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取单个webhook失败, siteId: ${siteId}, id: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/webhooks')
@ApiOkResponse({ type: UnifiedWebhookDTO })
@ApiBody({ type: CreateWebhookDTO })
async createWebhook(
@Param('siteId') siteId: number,
@Body() body: CreateWebhookDTO
) {
this.logger.debug(`[Site API] 创建webhook开始, siteId: ${siteId}, body: ${JSON.stringify(body)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.createWebhook(body);
this.logger.debug(`[Site API] 创建webhook成功, siteId: ${siteId}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 创建webhook失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Put('/:siteId/webhooks/:id')
@ApiOkResponse({ type: UnifiedWebhookDTO })
@ApiBody({ type: UpdateWebhookDTO })
async updateWebhook(
@Param('siteId') siteId: number,
@Param('id') id: string,
@Body() body: UpdateWebhookDTO
) {
this.logger.debug(`[Site API] 更新webhook开始, siteId: ${siteId}, id: ${id}, body: ${JSON.stringify(body)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.updateWebhook(id, body);
this.logger.debug(`[Site API] 更新webhook成功, siteId: ${siteId}, id: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 更新webhook失败, siteId: ${siteId}, id: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Del('/:siteId/webhooks/:id')
@ApiOkResponse({ type: Boolean })
async deleteWebhook(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.debug(`[Site API] 删除webhook开始, siteId: ${siteId}, id: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.deleteWebhook(id);
this.logger.debug(`[Site API] 删除webhook成功, siteId: ${siteId}, id: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 删除webhook失败, siteId: ${siteId}, id: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/products')
@ApiOkResponse({ type: UnifiedProductPaginationDTO })
async getProducts(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
this.logger.debug(`[Site API] 获取产品列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getProducts(query);
// 如果包含ERP产品信息则增强商品数据
if (data && data.items && data.items.length > 0) {
const enrichedItems = await this.siteApiService.enrichSiteProductsWithErpInfo(siteId, data.items);
data.items = enrichedItems;
}
this.logger.debug(`[Site API] 获取产品列表成功, siteId: ${siteId}, 共获取到 ${data.total} 个产品`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取产品列表失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/products/export')
async exportProducts(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const perPage = (query.per_page) || 100;
let page = 1;
const all: any[] = [];
while (true) {
const data = await adapter.getProducts({ ...query, page, per_page: perPage });
const items = data.items || [];
all.push(...items);
const totalPages = data.totalPages || Math.ceil((data.total || 0) / (data.per_page || perPage));
if (!items.length || page >= totalPages) break;
page += 1;
}
let items = all;
if (query.where?.ids) {
const ids = new Set(String(query.where.ids).split(',').map(v => v.trim()).filter(Boolean));
items = items.filter(i => ids.has(String(i.id)));
}
const header = ['id', 'name', 'type', 'status', 'sku', 'regular_price', 'sale_price', 'price', 'stock_status', 'stock_quantity', 'image_src'];
const rows = items.map((p: any) => [
p.id,
p.name,
p.type,
p.status,
p.sku,
p.regular_price,
p.sale_price,
p.price,
p.stock_status,
p.stock_quantity,
((p.images && p.images[0]?.src) || ''),
]);
const toCsvValue = (val: any) => {
const s = String(val ?? '');
const escaped = s.replace(/"/g, '""');
return `"${escaped}"`;
};
const csv = [header.map(toCsvValue).join(','), ...rows.map(r => r.map(toCsvValue).join(','))].join('\n');
return successResponse({ csv });
} catch (error) {
return errorResponse(error.message);
}
}
// 平台特性产品导出特殊CSV走平台服务
@Get('/:siteId/links')
async getLinks(
@Param('siteId') siteId: number
) {
this.logger.debug(`[Site API] 获取站点链接列表开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getLinks();
this.logger.debug(`[Site API] 获取站点链接列表成功, siteId: ${siteId}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取站点链接列表失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/products/export-special')
async exportProductsSpecial(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
try {
const site = await this.siteApiService.siteService.get(siteId, true);
if (site.type === 'woocommerce') {
const page = query.page || 1;
const perPage = (query.per_page) || 100;
const res = await this.siteApiService.wpService.getProducts(site, page, perPage);
const header = ['id', 'name', 'type', 'status', 'sku', 'regular_price', 'sale_price', 'stock_status', 'stock_quantity'];
const rows = (res.items || []).map((p: any) => [p.id, p.name, p.type, p.status, p.sku, p.regular_price, p.sale_price, p.stock_status, p.stock_quantity]);
const toCsvValue = (val: any) => {
const s = String(val ?? '');
const escaped = s.replace(/"/g, '""');
return `"${escaped}"`;
};
const csv = [header.map(toCsvValue).join(','), ...rows.map(r => r.map(toCsvValue).join(','))].join('\n');
return successResponse({ csv });
}
if (site.type === 'shopyy') {
const res = await this.siteApiService.shopyyService.getProducts(site, query.page || 1, query.per_page || 100);
const header = ['id', 'name', 'type', 'status', 'sku', 'price', 'stock_status', 'stock_quantity'];
const rows = (res.items || []).map((p: any) => [p.id, p.name, p.type, p.status, p.sku, p.price, p.stock_status, p.stock_quantity]);
const csv = [header.join(','), ...rows.map(r => r.map(v => String(v ?? '')).join(','))].join('\n');
return successResponse({ csv });
}
throw new Error('Unsupported site type for special export');
} catch (error) {
return errorResponse(error.message);
}
}
@Get('/:siteId/products/:id')
@ApiOkResponse({ type: UnifiedProductDTO })
async getProduct(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.info(`[Site API] 获取单个产品开始, siteId: ${siteId}, productId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getProduct(id);
// 如果获取到商品数据则增强ERP产品信息
if (data) {
const enrichedData = await this.siteApiService.enrichSiteProductWithErpInfo(siteId, data);
this.logger.info(`[Site API] 获取单个产品成功, siteId: ${siteId}, productId: ${id}`);
return successResponse(enrichedData);
}
this.logger.info(`[Site API] 获取单个产品成功, siteId: ${siteId}, productId: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取单个产品失败, siteId: ${siteId}, productId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/products')
@ApiOkResponse({ type: UnifiedProductDTO })
async createProduct(
@Param('siteId') siteId: number,
@Body() body: UnifiedProductDTO
) {
this.logger.info(`[Site API] 创建产品开始, siteId: ${siteId}, 产品名称: ${body.name}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.createProduct(body);
this.logger.info(`[Site API] 创建产品成功, siteId: ${siteId}, 产品ID: ${data.id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 创建产品失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/products/import')
@ApiOkResponse({ type: Object })
async importProducts(
@Param('siteId') siteId: number,
@Body() body: { items?: any[]; csv?: string }
) {
try {
const adapter = await this.siteApiService.getAdapter(siteId);
let items = body.items || [];
if (!items.length && body.csv) {
const lines = body.csv.split(/\r?\n/).filter(Boolean);
const header = lines.shift()?.split(',') || [];
items = lines.map((line) => {
const cols = line.split(',');
const obj: any = {};
header.forEach((h, i) => (obj[h] = cols[i]));
return obj;
});
}
const created: any[] = [];
const failed: any[] = [];
for (const item of items) {
try {
const data = await adapter.createProduct(item);
created.push(data);
} catch (e) {
failed.push({ item, error: (e as any).message });
}
}
return successResponse({ created, failed });
} catch (error) {
return errorResponse(error.message);
}
}
// 平台特性产品导入特殊CSV走平台服务
@Post('/:siteId/products/import-special')
@ApiOkResponse({ type: Object })
async importProductsSpecial(
@Param('siteId') siteId: number,
@Body() body: { csv?: string; items?: any[] }
) {
try {
const site = await this.siteApiService.siteService.get(siteId, true);
const csvText = body.csv || '';
const items = body.items || [];
const created: any[] = [];
const failed: any[] = [];
if (site.type === 'woocommerce') {
// 解析 CSV 为对象数组(若传入 items 则优先 items
let payloads = items;
if (!payloads.length && csvText) {
const lines = csvText.split(/\r?\n/).filter(Boolean);
const header = lines.shift()?.split(',') || [];
payloads = lines.map((line) => {
const cols = line.split(',');
const obj: any = {};
header.forEach((h, i) => (obj[h] = cols[i]));
return obj;
});
}
for (const item of payloads) {
try {
const res = await this.siteApiService.wpService.createProduct(site, item);
created.push(res);
} catch (e) {
failed.push({ item, error: (e as any).message });
}
}
return successResponse({ created, failed });
}
if (site.type === 'shopyy') {
throw new Error('ShopYY 暂不支持特殊CSV导入');
}
throw new Error('Unsupported site type for special import');
} catch (error) {
return errorResponse(error.message);
}
}
@Put('/:siteId/products/:id')
@ApiOkResponse({ type: UnifiedProductDTO })
async updateProduct(
@Param('siteId') siteId: number,
@Param('id') id: string,
@Body() body: UnifiedProductDTO
) {
this.logger.info(`[Site API] 更新产品开始, siteId: ${siteId}, productId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.updateProduct(id, body);
this.logger.info(`[Site API] 更新产品成功, siteId: ${siteId}, productId: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 更新产品失败, siteId: ${siteId}, productId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/products/upsert')
@ApiOkResponse({ type: UnifiedProductDTO })
async upsertProduct(
@Param('siteId') siteId: number,
@Body() body: UnifiedProductDTO
) {
this.logger.info(`[Site API] 更新或创建产品开始, siteId: ${siteId}, 产品名称: ${body.name}`);
try {
const data = await this.siteApiService.upsertProduct(siteId, body);
this.logger.info(`[Site API] 更新或创建产品成功, siteId: ${siteId}, 产品ID: ${data.id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 更新或创建产品失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Put('/:siteId/products/:productId/variations/:variationId')
@ApiOkResponse({ type: Object })
async updateVariation(
@Param('siteId') siteId: number,
@Param('productId') productId: string,
@Param('variationId') variationId: string,
@Body() body: any
) {
this.logger.info(`[Site API] 更新产品变体开始, siteId: ${siteId}, productId: ${productId}, variationId: ${variationId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.updateVariation(productId, variationId, body);
this.logger.info(`[Site API] 更新产品变体成功, siteId: ${siteId}, productId: ${productId}, variationId: ${variationId}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 更新产品变体失败, siteId: ${siteId}, productId: ${productId}, variationId: ${variationId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Del('/:siteId/products/:id')
@ApiOkResponse({ type: Boolean })
async deleteProduct(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.info(`[Site API] 删除产品开始, siteId: ${siteId}, productId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const success = await adapter.deleteProduct(id);
this.logger.info(`[Site API] 删除产品成功, siteId: ${siteId}, productId: ${id}`);
return successResponse(success);
} catch (error) {
this.logger.error(`[Site API] 删除产品失败, siteId: ${siteId}, productId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/products/batch')
@ApiOkResponse({ type: BatchOperationResultDTO })
async batchProducts(
@Param('siteId') siteId: number,
@Body() body: BatchOperationDTO
) {
this.logger.info(`[Site API] 批量处理产品开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
if (adapter.batchProcessProducts) {
const res = await adapter.batchProcessProducts(body);
this.logger.info(`[Site API] 批量处理产品成功, siteId: ${siteId}`);
return successResponse(res);
}
const created: any[] = [];
const updated: any[] = [];
const deleted: Array<string | number> = [];
const errors: Array<{identifier: string, error: string}> = [];
if (body.create?.length) {
for (const item of body.create) {
try {
const data = await adapter.createProduct(item);
created.push(data);
} catch (e) {
errors.push({
identifier: String(item.id || item.sku || 'unknown'),
error: (e as any).message
});
}
}
}
if (body.update?.length) {
for (const item of body.update) {
try {
const id = item.id;
const data = await adapter.updateProduct(id, item);
updated.push(data);
} catch (e) {
errors.push({
identifier: String(item.id || 'unknown'),
error: (e as any).message
});
}
}
}
if (body.delete?.length) {
for (const id of body.delete) {
try {
const ok = await adapter.deleteProduct(id);
if (ok) deleted.push(id);
else errors.push({
identifier: String(id),
error: 'delete failed'
});
} catch (e) {
errors.push({
identifier: String(id),
error: (e as any).message
});
}
}
}
this.logger.info(`[Site API] 批量处理产品完成, siteId: ${siteId}`);
return successResponse({
total: (body.create?.length || 0) + (body.update?.length || 0) + (body.delete?.length || 0),
processed: created.length + updated.length + deleted.length,
created: created.length,
updated: updated.length,
deleted: deleted.length,
errors: errors
});
} catch (error) {
this.logger.error(`[Site API] 批量处理产品失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/products/batch-upsert')
@ApiOkResponse({ type: BatchOperationResultDTO })
async batchUpsertProduct(
@Param('siteId') siteId: number,
@Body() body: { items: UnifiedProductDTO[] }
) {
this.logger.info(`[Site API] 批量更新或创建产品开始, siteId: ${siteId}, 产品数量: ${body.items?.length || 0}`);
try {
const result = await this.siteApiService.batchUpsertProduct(siteId, body.items || []);
this.logger.info(`[Site API] 批量更新或创建产品完成, siteId: ${siteId}, 创建: ${result.created.length}, 更新: ${result.updated.length}, 错误: ${result.errors.length}`);
return successResponse({
total: (body.items || []).length,
processed: result.created.length + result.updated.length,
created: result.created.length,
updated: result.updated.length,
errors: result.errors.map(err => ({
identifier: String(err.product.id || err.product.sku || 'unknown'),
error: err.error
}))
});
} catch (error) {
this.logger.error(`[Site API] 批量更新或创建产品失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/orders')
@ApiOkResponse({ type: UnifiedOrderPaginationDTO })
async getOrders(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
this.logger.info(`[Site API] 获取订单列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getOrders(query);
this.logger.info(`[Site API] 获取订单列表成功, siteId: ${siteId}, 共获取到 ${data.total} 个订单`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取订单列表失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/orders/count')
@ApiOkResponse({ type: Object })
async countOrders(
@Param('siteId') siteId: number,
@Query() query: any
) {
this.logger.info(`[Site API] 获取订单总数开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const total = await adapter.countOrders(query);
this.logger.info(`[Site API] 获取订单总数成功, siteId: ${siteId}, total: ${total}`);
return successResponse({ total });
} catch (error) {
this.logger.error(`[Site API] 获取订单总数失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/customers/:customerId/orders')
@ApiOkResponse({ type: UnifiedOrderPaginationDTO })
async getCustomerOrders(
@Param('siteId') siteId: number,
@Param('customerId') customerId: number,
@Query() query: UnifiedSearchParamsDTO
) {
this.logger.info(`[Site API] 获取客户订单列表开始, siteId: ${siteId}, customerId: ${customerId}, query: ${JSON.stringify(query)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const where = { ...(query.where || {}), customer: customerId };
const data = await adapter.getOrders({ ...query, where });
this.logger.info(`[Site API] 获取客户订单列表成功, siteId: ${siteId}, customerId: ${customerId}, 共获取到 ${data.total} 个订单`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取客户订单列表失败, siteId: ${siteId}, customerId: ${customerId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/orders/export')
async exportOrders(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const perPage = (query.per_page) || 100;
let page = 1;
const all: any[] = [];
while (true) {
const data = await adapter.getOrders({ ...query, page, per_page: perPage });
const items = data.items || [];
all.push(...items);
const totalPages = data.totalPages || Math.ceil((data.total || 0) / (data.per_page || perPage));
if (!items.length || page >= totalPages) break;
page += 1;
}
let items = all;
if (query.where?.ids) {
const ids = new Set(String(query.where.ids).split(',').map(v => v.trim()).filter(Boolean));
items = items.filter(i => ids.has(String(i.id)));
}
const header = ['id', 'number', 'status', 'currency', 'total', 'customer_id', 'customer_name', 'email', 'payment_method', 'phone', 'billing_full_address', 'shipping_full_address', 'date_created'];
const rows = items.map((o: any) => [
o.id,
o.number,
o.status,
o.currency,
o.total,
o.customer_id,
o.customer_name,
o.email,
o.payment_method,
(o.shipping?.phone || o.billing?.phone || ''),
(o.billing_full_address || ''),
(o.shipping_full_address || ''),
o.date_created,
]);
const toCsvValue = (val: any) => {
const s = String(val ?? '');
const escaped = s.replace(/"/g, '""');
return `"${escaped}"`;
};
const csv = [header.map(toCsvValue).join(','), ...rows.map(r => r.map(toCsvValue).join(','))].join('\n');
return successResponse({ csv });
} catch (error) {
return errorResponse(error.message);
}
}
@Get('/:siteId/orders/:id')
@ApiOkResponse({ type: UnifiedOrderDTO })
async getOrder(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.info(`[Site API] 获取单个订单开始, siteId: ${siteId}, orderId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getOrder(id);
this.logger.info(`[Site API] 获取单个订单成功, siteId: ${siteId}, orderId: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取单个订单失败, siteId: ${siteId}, orderId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/orders')
@ApiOkResponse({ type: UnifiedOrderDTO })
async createOrder(
@Param('siteId') siteId: number,
@Body() body: any
) {
this.logger.info(`[Site API] 创建订单开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.createOrder(body);
this.logger.info(`[Site API] 创建订单成功, siteId: ${siteId}, orderId: ${data.id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 创建订单失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/orders/import')
@ApiOkResponse({ type: Object })
async importOrders(
@Param('siteId') siteId: number,
@Body() body: { items?: any[]; csv?: string }
) {
try {
const adapter = await this.siteApiService.getAdapter(siteId);
let items = body.items || [];
if (!items.length && body.csv) {
const lines = body.csv.split(/\r?\n/).filter(Boolean);
const header = lines.shift()?.split(',') || [];
items = lines.map((line) => {
const cols = line.split(',');
const obj: any = {};
header.forEach((h, i) => (obj[h] = cols[i]));
return obj;
});
}
const created: any[] = [];
const failed: any[] = [];
for (const item of items) {
try {
const data = await adapter.createOrder(item);
created.push(data);
} catch (e) {
failed.push({ item, error: (e as any).message });
}
}
return successResponse({ created, failed });
} catch (error) {
return errorResponse(error.message);
}
}
@Put('/:siteId/orders/:id')
@ApiOkResponse({ type: Boolean })
async updateOrder(
@Param('siteId') siteId: number,
@Param('id') id: string,
@Body() body: any
) {
this.logger.info(`[Site API] 更新订单开始, siteId: ${siteId}, orderId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const ok = await adapter.updateOrder(id, body);
this.logger.info(`[Site API] 更新订单成功, siteId: ${siteId}, orderId: ${id}`);
return successResponse(ok);
} catch (error) {
this.logger.error(`[Site API] 更新订单失败, siteId: ${siteId}, orderId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Del('/:siteId/orders/:id')
@ApiOkResponse({ type: Boolean })
async deleteOrder(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.info(`[Site API] 删除订单开始, siteId: ${siteId}, orderId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const ok = await adapter.deleteOrder(id);
this.logger.info(`[Site API] 删除订单成功, siteId: ${siteId}, orderId: ${id}`);
return successResponse(ok);
} catch (error) {
this.logger.error(`[Site API] 删除订单失败, siteId: ${siteId}, orderId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/orders/batch')
@ApiOkResponse({ type: BatchOperationResultDTO })
async batchOrders(
@Param('siteId') siteId: number,
@Body() body: BatchOperationDTO
) {
this.logger.info(`[Site API] 批量处理订单开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const created: any[] = [];
const updated: any[] = [];
const deleted: Array<string | number> = [];
const errors: Array<{identifier: string, error: string}> = [];
if (body.create?.length) {
for (const item of body.create) {
try {
const data = await adapter.createOrder(item);
created.push(data);
} catch (e) {
errors.push({
identifier: String(item.id || item.order_number || 'unknown'),
error: (e as any).message
});
}
}
}
if (body.update?.length) {
for (const item of body.update) {
try {
const id = item.id;
const ok = await adapter.updateOrder(id, item);
if (ok) updated.push(item);
else errors.push({
identifier: String(item.id || 'unknown'),
error: 'update failed'
});
} catch (e) {
errors.push({
identifier: String(item.id || 'unknown'),
error: (e as any).message
});
}
}
}
if (body.delete?.length) {
for (const id of body.delete) {
try {
const ok = await adapter.deleteOrder(id);
if (ok) deleted.push(id);
else errors.push({
identifier: String(id),
error: 'delete failed'
});
} catch (e) {
errors.push({
identifier: String(id),
error: (e as any).message
});
}
}
}
this.logger.info(`[Site API] 批量处理订单完成, siteId: ${siteId}`);
return successResponse({
total: (body.create?.length || 0) + (body.update?.length || 0) + (body.delete?.length || 0),
processed: created.length + updated.length + deleted.length,
created: created.length,
updated: updated.length,
deleted: deleted.length,
errors: errors
});
} catch (error) {
this.logger.error(`[Site API] 批量处理订单失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/orders/:id/notes')
@ApiOkResponse({ type: Object })
async getOrderNotes(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.info(`[Site API] 获取订单备注开始, siteId: ${siteId}, orderId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getOrderNotes(id);
this.logger.info(`[Site API] 获取订单备注成功, siteId: ${siteId}, orderId: ${id}, 共获取到 ${data.length} 条备注`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取订单备注失败, siteId: ${siteId}, orderId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/orders/:id/notes')
@ApiOkResponse({ type: Object })
async createOrderNote(
@Param('siteId') siteId: number,
@Param('id') id: string,
@Body() body: any
) {
this.logger.info(`[Site API] 创建订单备注开始, siteId: ${siteId}, orderId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.createOrderNote(id, body);
this.logger.info(`[Site API] 创建订单备注成功, siteId: ${siteId}, orderId: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 创建订单备注失败, siteId: ${siteId}, orderId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/orders/:id/cancel-fulfill')
@ApiOkResponse({ type: Object })
async cancelFulfillment(
@Param('siteId') siteId: number,
@Param('id') id: string,
@Body() body: CancelFulfillmentDTO
) {
this.logger.info(`[Site API] 取消订单履约开始, siteId: ${siteId}, orderId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.cancelFulfillment(id, body);
this.logger.info(`[Site API] 取消订单履约成功, siteId: ${siteId}, orderId: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 取消订单履约失败, siteId: ${siteId}, orderId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/orders/batch-fulfill')
@ApiOkResponse({ type: Object })
async batchFulfillOrders(
@Param('siteId') siteId: number,
@Body() body: BatchFulfillmentsDTO
) {
this.logger.info(`[Site API] 批量订单履约开始, siteId: ${siteId}, 订单数量: ${body.orders.length}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const results = await Promise.allSettled(
body.orders.map(order =>
adapter.createOrderFulfillment(order.order_id, {
tracking_number: order.tracking_number,
shipping_provider: order.shipping_provider,
items: order.items,
}).catch(error => ({
order_id: order.order_id,
success: false,
error: error.message
}))
)
);
const successful = results
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<any>).value);
const failed = results
.filter(result => result.status === 'rejected')
.map(result => (result as PromiseRejectedResult).reason);
this.logger.info(`[Site API] 批量订单履约完成, siteId: ${siteId}, 成功: ${successful.length}, 失败: ${failed.length}`);
return successResponse({
successful: successful.length,
failed: failed.length,
results: {
successful,
failed
}
});
} catch (error) {
this.logger.error(`[Site API] 批量订单履约失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/orders/:orderId/fulfillments')
@ApiOkResponse({ type: Object })
async getOrderFulfillments(
@Param('siteId') siteId: number,
@Param('orderId') orderId: string
) {
this.logger.info(`[Site API] 获取订单履约信息开始, siteId: ${siteId}, orderId: ${orderId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getOrderFulfillments(orderId);
this.logger.info(`[Site API] 获取订单物流跟踪信息成功, siteId: ${siteId}, orderId: ${orderId}, 共获取到 ${data.length} 条跟踪信息`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取订单物流跟踪信息失败, siteId: ${siteId}, orderId: ${orderId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/orders/:orderId/fulfillments')
@ApiOkResponse({ type: UnifiedOrderTrackingDTO })
@ApiBody({ type: UnifiedOrderTrackingDTO })
async createOrderFulfillment(
@Param('siteId') siteId: number,
@Param('orderId') orderId: string,
@Body() body: UnifiedOrderTrackingDTO
) {
this.logger.info(`[Site API] 创建订单履约跟踪信息开始, siteId: ${siteId}, orderId: ${orderId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.createOrderFulfillment(orderId, {
tracking_number: body.tracking_number,
shipping_provider: body.shipping_provider,
shipping_method: body.shipping_method,
status: body.status,
date_created: body.date_created,
});
this.logger.info(`[Site API] 创建订单履约跟踪信息成功, siteId: ${siteId}, orderId: ${orderId}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 创建订单履约跟踪信息失败, siteId: ${siteId}, orderId: ${orderId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Put('/:siteId/orders/:orderId/fulfillments/:fulfillmentId')
@ApiOkResponse({ type: UnifiedOrderTrackingDTO })
@ApiBody({ type: UnifiedOrderTrackingDTO })
async updateOrderFulfillment(
@Param('siteId') siteId: number,
@Param('orderId') orderId: string,
@Param('fulfillmentId') fulfillmentId: string,
@Body() body: UnifiedOrderTrackingDTO
) {
this.logger.info(`[Site API] 更新订单履约跟踪信息开始, siteId: ${siteId}, orderId: ${orderId}, fulfillmentId: ${fulfillmentId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.updateOrderFulfillment(orderId, fulfillmentId, {
tracking_number: body.tracking_number,
shipping_provider: body.shipping_provider,
shipping_method: body.shipping_method,
status: body.status,
date_created: body.date_created,
});
this.logger.info(`[Site API] 更新订单履约跟踪信息成功, siteId: ${siteId}, orderId: ${orderId}, fulfillmentId: ${fulfillmentId}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 更新订单履约跟踪信息失败, siteId: ${siteId}, orderId: ${orderId}, fulfillmentId: ${fulfillmentId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Del('/:siteId/orders/:orderId/fulfillments/:fulfillmentId')
@ApiOkResponse({ type: Boolean })
async deleteOrderFulfillment(
@Param('siteId') siteId: number,
@Param('orderId') orderId: string,
@Param('fulfillmentId') fulfillmentId: string
) {
this.logger.info(`[Site API] 删除订单履约跟踪信息开始, siteId: ${siteId}, orderId: ${orderId}, fulfillmentId: ${fulfillmentId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const ok = await adapter.deleteOrderFulfillment(orderId, fulfillmentId);
this.logger.info(`[Site API] 删除订单履约跟踪信息成功, siteId: ${siteId}, orderId: ${orderId}, fulfillmentId: ${fulfillmentId}`);
return successResponse(ok);
} catch (error) {
this.logger.error(`[Site API] 删除订单履约跟踪信息失败, siteId: ${siteId}, orderId: ${orderId}, fulfillmentId: ${fulfillmentId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/subscriptions')
@ApiOkResponse({ type: UnifiedSubscriptionPaginationDTO })
async getSubscriptions(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
this.logger.info(`[Site API] 获取订阅列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getSubscriptions(query);
this.logger.info(`[Site API] 获取订阅列表成功, siteId: ${siteId}, 共获取到 ${data.total} 个订阅`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取订阅列表失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/subscriptions/export')
async exportSubscriptions(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const perPage = (query.per_page) || 100;
let page = 1;
const all: any[] = [];
while (true) {
const data = await adapter.getSubscriptions({ ...query, page, per_page: perPage });
const items = data.items || [];
all.push(...items);
const totalPages = data.totalPages || Math.ceil((data.total || 0) / (data.per_page || perPage));
if (!items.length || page >= totalPages) break;
page += 1;
}
let items = all;
if (query.where?.ids) {
const ids = new Set(String(query.where.ids).split(',').map(v => v.trim()).filter(Boolean));
items = items.filter(i => ids.has(String(i.id)));
}
const header = ['id', 'status', 'customer_id', 'billing_period', 'billing_interval', 'start_date', 'next_payment_date'];
const rows = items.map((s: any) => [s.id, s.status, s.customer_id, s.billing_period, s.billing_interval, s.start_date, s.next_payment_date]);
const csv = [header.join(','), ...rows.map(r => r.map(v => String(v ?? '')).join(','))].join('\n');
return successResponse({ csv });
} catch (error) {
return errorResponse(error.message);
}
}
@Get('/:siteId/media')
@ApiOkResponse({ type: UnifiedMediaPaginationDTO })
async getMedia(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
this.logger.info(`[Site API] 获取媒体列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getMedia(query);
this.logger.info(`[Site API] 获取媒体列表成功, siteId: ${siteId}, 共获取到 ${data.total} 个媒体`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取媒体列表失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/media/export')
async exportMedia(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const perPage = (query.per_page) || 100;
let page = 1;
const all: any[] = [];
while (true) {
const data = await adapter.getMedia({ ...query, page, per_page: perPage });
const items = data.items || [];
all.push(...items);
const totalPages = data.totalPages || Math.ceil((data.total || 0) / (data.per_page || perPage));
if (!items.length || page >= totalPages) break;
page += 1;
}
let items = all;
if (query.where?.ids) {
const ids = new Set(String(query.where.ids).split(',').map(v => v.trim()).filter(Boolean));
items = items.filter(i => ids.has(String(i.id)));
}
const header = ['id', 'title', 'media_type', 'mime_type', 'source_url', 'date_created'];
const rows = items.map((m: any) => [m.id, m.title, m.media_type, m.mime_type, m.source_url, m.date_created]);
const csv = [header.join(','), ...rows.map(r => r.map(v => String(v ?? '')).join(','))].join('\n');
return successResponse({ csv });
} catch (error) {
return errorResponse(error.message);
}
}
@Del('/:siteId/media/:id')
@ApiOkResponse({ type: Boolean })
async deleteMedia(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.info(`[Site API] 删除媒体开始, siteId: ${siteId}, mediaId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const api: any = adapter as any;
if (api.deleteMedia) {
const success = await api.deleteMedia(id);
this.logger.info(`[Site API] 删除媒体成功, siteId: ${siteId}, mediaId: ${id}`);
return successResponse(success);
}
throw new Error('Media delete not supported');
} catch (error) {
this.logger.error(`[Site API] 删除媒体失败, siteId: ${siteId}, mediaId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Put('/:siteId/media/:id')
@ApiOkResponse({ type: Object })
async updateMedia(
@Param('siteId') siteId: number,
@Param('id') id: string,
@Body() body: any
) {
this.logger.info(`[Site API] 更新媒体开始, siteId: ${siteId}, mediaId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const api: any = adapter as any;
if (api.updateMedia) {
const res = await api.updateMedia(id, body);
this.logger.info(`[Site API] 更新媒体成功, siteId: ${siteId}, mediaId: ${id}`);
return successResponse(res);
}
throw new Error('Media update not supported');
} catch (error) {
this.logger.error(`[Site API] 更新媒体失败, siteId: ${siteId}, mediaId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/media/batch')
@ApiOkResponse({ type: Object })
async batchMedia(
@Param('siteId') siteId: number,
@Body() body: { update?: any[]; delete?: Array<string | number> }
) {
this.logger.info(`[Site API] 批量处理媒体开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const updated: any[] = [];
const deleted: Array<string | number> = [];
const failed: any[] = [];
const api: any = adapter as any;
if (body.update?.length) {
for (const item of body.update) {
try {
if (!api.updateMedia) throw new Error('Media update not supported');
const res = await api.updateMedia(item.id, item);
updated.push(res);
} catch (e) {
failed.push({ action: 'update', item, error: (e as any).message });
}
}
}
if (body.delete?.length) {
for (const id of body.delete) {
try {
if (!api.deleteMedia) throw new Error('Media delete not supported');
const ok = await api.deleteMedia(id);
if (ok) deleted.push(id);
else failed.push({ action: 'delete', id, error: 'delete failed' });
} catch (e) {
failed.push({ action: 'delete', id, error: (e as any).message });
}
}
}
this.logger.info(`[Site API] 批量处理媒体完成, siteId: ${siteId}`);
return successResponse({ updated, deleted, failed });
} catch (error) {
this.logger.error(`[Site API] 批量处理媒体失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/media/convert-webp')
@ApiOkResponse({ type: Object })
async convertMediaToWebp(
@Param('siteId') siteId: number,
@Body() body: { ids: Array<string | number> }
) {
this.logger.info(`[Site API] 批量转换媒体为 webp 开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const api: any = adapter as any;
// 条件判断 如果未提供 ids 列表则抛出错误
if (!body?.ids || body.ids.length === 0) {
throw new Error('未提供需要转换的媒体ID列表');
}
if (!api.convertMediaToWebp) {
throw new Error('当前站点不支持媒体转换');
}
const res = await api.convertMediaToWebp(body.ids);
this.logger.info(`[Site API] 批量转换媒体为 webp 成功, siteId: ${siteId}`);
return successResponse(res);
} catch (error) {
this.logger.error(`[Site API] 批量转换媒体为 webp 失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/customers')
@ApiOkResponse({ type: UnifiedCustomerPaginationDTO })
async getCustomers(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
this.logger.info(`[Site API] 获取客户列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getCustomers(query);
this.logger.info(`[Site API] 获取客户列表成功, siteId: ${siteId}, 共获取到 ${data.total} 个客户`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取客户列表失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Get('/:siteId/customers/export')
async exportCustomers(
@Param('siteId') siteId: number,
@Query() query: UnifiedSearchParamsDTO
) {
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const perPage = (query.per_page) || 100;
let page = 1;
const all: any[] = [];
while (true) {
const data = await adapter.getCustomers({ ...query, page, per_page: perPage});
const items = data.items || [];
all.push(...items);
const totalPages = data.totalPages || Math.ceil((data.total || 0) / (data.per_page || perPage));
if (!items.length || page >= totalPages) break;
page += 1;
}
let items = all;
const header = ['id', 'email', 'first_name', 'last_name', 'fullname', 'username', 'phone', 'orders', 'total_spend', 'role', 'billing_full_address', 'shipping_full_address', 'date_created'];
const formatAddress = (addr: any) => [
addr?.fullname,
addr?.company,
addr?.address_1,
addr?.address_2,
addr?.city,
addr?.state,
addr?.postcode,
addr?.country,
addr?.phone,
].filter(Boolean).join(', ');
const rows = items.map((c: any) => [
c.id,
c.email,
c.first_name,
c.last_name,
c.fullname,
(c.username || c.raw?.username || ''),
(c.phone || c.billing?.phone || c.shipping?.phone || ''),
c.orders,
c.total_spend,
(c.role || c.raw?.role || ''),
formatAddress(c.billing || {}),
formatAddress(c.shipping || {}),
c.date_created,
]);
const csv = [header.join(','), ...rows.map(r => r.map(v => String(v ?? '')).join(','))].join('\n');
return successResponse({ csv });
} catch (error) {
return errorResponse(error.message);
}
}
@Get('/:siteId/customers/:id')
@ApiOkResponse({ type: UnifiedCustomerDTO })
async getCustomer(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.info(`[Site API] 获取单个客户开始, siteId: ${siteId}, customerId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.getCustomer(id);
this.logger.info(`[Site API] 获取单个客户成功, siteId: ${siteId}, customerId: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 获取单个客户失败, siteId: ${siteId}, customerId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/customers')
@ApiOkResponse({ type: UnifiedCustomerDTO })
async createCustomer(
@Param('siteId') siteId: number,
@Body() body: UnifiedCustomerDTO
) {
this.logger.info(`[Site API] 创建客户开始, siteId: ${siteId}, 客户邮箱: ${body.email}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.createCustomer(body);
this.logger.info(`[Site API] 创建客户成功, siteId: ${siteId}, customerId: ${data.id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 创建客户失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/customers/import')
@ApiOkResponse({ type: Object })
async importCustomers(
@Param('siteId') siteId: number,
@Body() body: { items?: any[]; csv?: string }
) {
try {
const adapter = await this.siteApiService.getAdapter(siteId);
let items = body.items || [];
if (!items.length && body.csv) {
const lines = body.csv.split(/\r?\n/).filter(Boolean);
const header = lines.shift()?.split(',') || [];
items = lines.map((line) => {
const cols = line.split(',');
const obj: any = {};
header.forEach((h, i) => (obj[h] = cols[i]));
return obj;
});
}
const created: any[] = [];
const failed: any[] = [];
for (const item of items) {
try {
const data = await adapter.createCustomer(item);
created.push(data);
} catch (e) {
failed.push({ item, error: (e as any).message });
}
}
return successResponse({ created, failed });
} catch (error) {
return errorResponse(error.message);
}
}
@Put('/:siteId/customers/:id')
@ApiOkResponse({ type: UnifiedCustomerDTO })
async updateCustomer(
@Param('siteId') siteId: number,
@Param('id') id: string,
@Body() body: UnifiedCustomerDTO
) {
this.logger.info(`[Site API] 更新客户开始, siteId: ${siteId}, customerId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const data = await adapter.updateCustomer(id, body);
this.logger.info(`[Site API] 更新客户成功, siteId: ${siteId}, customerId: ${id}`);
return successResponse(data);
} catch (error) {
this.logger.error(`[Site API] 更新客户失败, siteId: ${siteId}, customerId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Del('/:siteId/customers/:id')
@ApiOkResponse({ type: Boolean })
async deleteCustomer(
@Param('siteId') siteId: number,
@Param('id') id: string
) {
this.logger.info(`[Site API] 删除客户开始, siteId: ${siteId}, customerId: ${id}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const success = await adapter.deleteCustomer(id);
this.logger.info(`[Site API] 删除客户成功, siteId: ${siteId}, customerId: ${id}`);
return successResponse(success);
} catch (error) {
this.logger.error(`[Site API] 删除客户失败, siteId: ${siteId}, customerId: ${id}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
@Post('/:siteId/customers/batch')
@ApiOkResponse({ type: Object })
async batchCustomers(
@Param('siteId') siteId: number,
@Body() body: { create?: any[]; update?: any[]; delete?: Array<string | number> }
) {
this.logger.info(`[Site API] 批量处理客户开始, siteId: ${siteId}`);
try {
const adapter = await this.siteApiService.getAdapter(siteId);
const created: any[] = [];
const updated: any[] = [];
const deleted: Array<string | number> = [];
const failed: any[] = [];
if (body.create?.length) {
for (const item of body.create) {
try {
const data = await adapter.createCustomer(item);
created.push(data);
} catch (e) {
failed.push({ action: 'create', item, error: (e as any).message });
}
}
}
if (body.update?.length) {
for (const item of body.update) {
try {
const id = item.id;
const data = await adapter.updateCustomer(id, item);
updated.push(data);
} catch (e) {
failed.push({ action: 'update', item, error: (e as any).message });
}
}
}
if (body.delete?.length) {
for (const id of body.delete) {
try {
const ok = await adapter.deleteCustomer(id);
if (ok) deleted.push(id);
else failed.push({ action: 'delete', id, error: 'delete failed' });
} catch (e) {
failed.push({ action: 'delete', id, error: (e as any).message });
}
}
}
this.logger.info(`[Site API] 批量处理客户完成, siteId: ${siteId}`);
return successResponse({ created, updated, deleted, failed });
} catch (error) {
this.logger.error(`[Site API] 批量处理客户失败, siteId: ${siteId}, 错误信息: ${error.message}`);
return errorResponse(error.message);
}
}
}