import 'dotenv/config' import Koa from 'koa' import Router from '@koa/router' import bodyParser from 'koa-bodyparser' import type { FunctionHandler } from './utils/handler.js' import { logger } from './utils/logger.js' import { addressDefaultController, addressDeleteController, addressListController, addressSaveController } from './controllers/address.controller.js' import { cartAddController, loginController, uploadController, uploadPreviewController, userInfoController } from './controllers/basic.controller.js' import { afterSaleApplyController, afterSaleAuditController, afterSaleDetailController, afterSaleListController, afterSaleLogisticsController, orderCancelMainController, orderCancelPaidMainController, orderCancelSubController, orderCountController, orderCreateController, orderDetailController, orderListController, orderPayController, orderPushEventController, orderShipController, supplierFileBase64InfoController, supplierInvoiceApplyController, supplierInvoiceInfoController, supplierOrderInfoController, supplierReconciliationInfoController } from './controllers/order.controller.js' import { merchantReconciliationListController, merchantReconciliationUpdateController, supplierReconciliationResultController, supplierUnReconciledMsgController } from './controllers/reconciliation.controller.js' import { merchantAfterSaleAuditController, merchantAfterSaleDetailController, merchantAfterSaleListController, merchantAfterSaleReceiveController, merchantAfterSaleShipController, merchantOrderListController, merchantOrderShipController, merchantSupplierListController } from './controllers/merchant.controller.js' import { merchantProductDeleteController, merchantProductListController, merchantProductSaveController, productDetailController, productListController } from './controllers/product.controller.js' import { supplierDebugTokensController, supplierInfoController, supplierPointsController, supplierSessionController, supplierTokenController } from './controllers/supplier.controller.js' const app = new Koa() const router = new Router() const port = Number(process.env.PORT || 8888) const handlers: Record = { 'address-default': addressDefaultController, 'address-delete': addressDeleteController, 'address-list': addressListController, 'address-save': addressSaveController, 'after-sale-apply': afterSaleApplyController, 'after-sale-audit': afterSaleAuditController, 'after-sale-detail': afterSaleDetailController, 'after-sale-list': afterSaleListController, 'after-sale-logistics': afterSaleLogisticsController, 'cart-add': cartAddController, login: loginController, 'merchant-after-sale-audit': merchantAfterSaleAuditController, 'merchant-after-sale-detail': merchantAfterSaleDetailController, 'merchant-after-sale-list': merchantAfterSaleListController, 'merchant-after-sale-receive': merchantAfterSaleReceiveController, 'merchant-after-sale-ship': merchantAfterSaleShipController, 'merchant-order-list': merchantOrderListController, 'merchant-order-ship': merchantOrderShipController, 'merchant-supplier-list': merchantSupplierListController, 'order-cancel-main': orderCancelMainController, 'order-cancel-paid-main': orderCancelPaidMainController, 'order-cancel-sub': orderCancelSubController, 'order-count': orderCountController, 'order-create': orderCreateController, 'order-detail': orderDetailController, 'order-list': orderListController, 'order-pay': orderPayController, 'order-push-event': orderPushEventController, 'order-ship': orderShipController, 'product-detail': productDetailController, 'product-list': productListController, 'merchant-product-list': merchantProductListController, 'merchant-product-save': merchantProductSaveController, 'merchant-product-delete': merchantProductDeleteController, 'supplier-debug-tokens': supplierDebugTokensController, 'supplier-file-base64-info': supplierFileBase64InfoController, 'supplier-invoice-apply': supplierInvoiceApplyController, 'supplier-invoice-info': supplierInvoiceInfoController, 'supplier-info': supplierInfoController, 'supplier-order-info': supplierOrderInfoController, 'supplier-reconciliation-info': supplierReconciliationInfoController, 'supplier-reconciliation-result': supplierReconciliationResultController, 'supplier-unreconciled-msg': supplierUnReconciledMsgController, 'merchant-reconciliation-list': merchantReconciliationListController, 'merchant-reconciliation-update': merchantReconciliationUpdateController, 'supplier-points': supplierPointsController, 'supplier-session': supplierSessionController, 'supplier-token': supplierTokenController, upload: uploadController, 'upload-preview': uploadPreviewController, 'user-info': userInfoController } const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH, DELETE, OPTIONS' } const requestBody = (ctx: Koa.Context) => { if (ctx.method === 'GET' || ctx.method === 'HEAD') { return undefined } return JSON.stringify(ctx.request.body || {}) } const toRequest = (ctx: Koa.Context) => new Request(`${ctx.protocol}://${ctx.host}${ctx.originalUrl}`, { method: ctx.method, headers: ctx.headers as HeadersInit, body: requestBody(ctx) }) const ba7ResponseBody = (content: string) => ({ status: 200, msg: 'success', data: { Body: { RspSvcHeader: { ReturnCode: '000000000000', ReturnMsg: '交易成功' }, RspSvcBody: { ReturnStCd: '200', ReturnCntnt: content } } } }) const writeResponse = async (ctx: Koa.Context, response: Response, wrapBa7 = false) => { ctx.status = response.status Object.entries(corsHeaders).forEach(([key, value]) => ctx.set(key, value)) response.headers.forEach((value, key) => ctx.set(key, value)) const responseText = response.body ? await response.text() : '' ctx.body = wrapBa7 ? ba7ResponseBody(responseText) : responseText || undefined } const dispatch = (name: string) => async (ctx: Koa.Context) => { logger.info('dispatch api request', { name, method: ctx.method, path: ctx.path }) logger.supplierDebug(ctx.path.startsWith('/supplierOpenApi/') ? 'zhgh-receive' : 'backend-receive', ctx.path.startsWith('/supplierOpenApi/') ? '接收ZHGH请求' : '当前后端接收请求', { name, method: ctx.method, path: ctx.path, url: ctx.originalUrl, query: ctx.query, body: ctx.request.body || {} }) const handler = handlers[name] if (!handler) { ctx.status = 404 ctx.body = { success: false, error: { code: 'NOT_FOUND', message: '接口不存在' } } return } const response = await handler(toRequest(ctx), {}) await writeResponse(ctx, response, ctx.path.startsWith('/supplierOpenApi/')) } app.use(bodyParser({ jsonLimit: '10mb' })) app.use(async (ctx, next) => { Object.entries(corsHeaders).forEach(([key, value]) => ctx.set(key, value)) if (ctx.method === 'OPTIONS') { ctx.status = 204 return } await next() }) router.get('/api/supplier-logs', ctx => { Object.entries(corsHeaders).forEach(([key, value]) => ctx.set(key, value)) ctx.body = { success: true, data: { logs: logger.getSupplierDebugLogs() } } }) router.delete('/api/supplier-logs', ctx => { Object.entries(corsHeaders).forEach(([key, value]) => ctx.set(key, value)) logger.clearSupplierDebugLogs() ctx.body = { success: true, data: {} } }) router.all('/api/:name', async ctx => dispatch(ctx.params.name)(ctx)) router.all('/api/admin/:name', async ctx => dispatch(ctx.params.name)(ctx)) router.post('/supplierOpenApi/pointOrder/queryOrderInfo', dispatch('supplier-order-info')) router.post('/supplierOpenApi/pointOrder/queryReconciliationInfo', dispatch('supplier-reconciliation-info')) router.post('/supplierOpenApi/pointOrder/applyForInvoices', dispatch('supplier-invoice-apply')) router.post('/supplierOpenApi/pointOrder/queryInvoicesInfo', dispatch('supplier-invoice-info')) router.post('/supplierOpenApi/pointOrder/queryFileBase64Info', dispatch('supplier-file-base64-info')) router.post('/supplierOpenApi/pointOrder/reconciliationResult', dispatch('supplier-reconciliation-result')) router.post('/supplierOpenApi/pointOrder/unReconciledMsg', dispatch('supplier-unreconciled-msg')) router.post('/supplierOpenApi/debug/tokens', dispatch('supplier-debug-tokens')) app.use(router.routes()) app.use(router.allowedMethods()) app.listen(port, () => { logger.info(`Koa server running at http://localhost:${port}`) })