feat: 积分商城-完成

This commit is contained in:
2026-07-31 19:08:15 +08:00
parent 898e239463
commit 20d059443c
21 changed files with 2122 additions and 181 deletions
@@ -37,6 +37,13 @@ export const merchantAfterSaleListController = withErrorBoundary(async (request:
return toHttpResponse(await merchantService.listAfterSales(await readJson<MerchantAfterSaleListInput>(request)))
})
export const merchantAfterSaleDetailController = withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['POST'])
if (methodResponse) return methodResponse
return toHttpResponse(await merchantService.detailAfterSale(await readJson<Pick<AfterSaleAuditInput, 'supplierId' | 'serviceOrderId'>>(request)))
})
export const merchantAfterSaleAuditController = withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['POST'])
if (methodResponse) return methodResponse
+14
View File
@@ -236,6 +236,20 @@ export const orderDao = {
return prisma.supplierAfterSale.count({ where })
},
findProcessingAfterSaleOrderIds(supplierId: string, userId: string) {
return prisma.supplierAfterSale.findMany({
where: {
supplierId,
userId,
deleted: 0,
serviceState: { in: [0, 1, 3, 4] } // PENDING_AUDIT, APPROVED, USER_SHIPPED, RECEIVED
},
select: {
subOrderId: true
}
})
},
updatePushLog(id: number, data: Parameters<typeof prisma.supplierOrderPushLog.update>[0]['data']) {
return prisma.supplierOrderPushLog.update({
where: { id },
+29
View File
@@ -37,6 +37,7 @@ import {
} from './controllers/order.controller.js'
import {
merchantAfterSaleAuditController,
merchantAfterSaleDetailController,
merchantAfterSaleListController,
merchantAfterSaleReceiveController,
merchantAfterSaleShipController,
@@ -68,6 +69,7 @@ const handlers: Record<string, FunctionHandler> = {
'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,
@@ -124,6 +126,16 @@ const writeResponse = async (ctx: Koa.Context, response: Response) => {
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
@@ -144,6 +156,23 @@ app.use(async (ctx, next) => {
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.post('/supplierOpenApi/pointOrder/queryOrderInfo', dispatch('supplier-order-info'))
router.post('/supplierOpenApi/debug/tokens', dispatch('supplier-debug-tokens'))
@@ -6,6 +6,7 @@ import {
} from '../constants/order.js'
import { orderDao } from '../dao/order.dao.js'
import { productDao } from '../dao/product.dao.js'
import { bridgeFetchWithAuth } from '../utils/bridge-auth.js'
import { logger } from '../utils/logger.js'
import { serviceFail, serviceOk } from '../types/service-result.js'
@@ -28,6 +29,7 @@ export type AfterSaleApplyInput = {
exchangeProductSku?: string
exchangeProductName?: string
exchangeProductPrice?: number | string
isSigned?: boolean
}
export type AfterSaleListInput = {
@@ -72,6 +74,12 @@ const supportedServiceTypes = new Set<number>([
const afterSaleOrderStates = new Set<number>([
ORDER_STATE.FINISH_PAY,
ORDER_STATE.IN_PROGRESS,
ORDER_STATE.RECEIVED,
ORDER_STATE.COMPLETED
])
const signedOrderStates = new Set<number>([
ORDER_STATE.RECEIVED,
ORDER_STATE.COMPLETED
])
@@ -128,6 +136,71 @@ const createServiceOrderId = (subOrderId: string, serviceType: number) => {
return `AS${serviceType}_${subOrderId}_${Date.now()}`
}
const isOrderSigned = (orderState?: number | null) => signedOrderStates.has(Number(orderState))
const pushReturnOrder = async (record: NonNullable<Awaited<ReturnType<typeof orderDao.findAfterSale>>>) => {
if (!record.supplierId || !record.userId || !record.subOrderId || !record.mainOrderId) {
return serviceFail('售后订单信息不完整,无法推送退货单')
}
const { response, payload } = await bridgeFetchWithAuth<{ resultCode?: number; message?: string }>(record.supplierId, '/pointOrder/pushOrderInfoMsg', {
supplierId: record.supplierId,
orderType: ORDER_EVENT_TYPE.RETURN_ORDER,
orderId: record.serviceOrderId,
mainOrderId: record.subOrderId,
userId: record.userId,
time: Date.now()
})
if (!response.ok || payload.resultCode !== 0) {
return serviceFail(payload.message || '退货单推送失败', 502, 'BRIDGE_RETURN_ORDER_FAILED')
}
return serviceOk({ serviceOrderId: record.serviceOrderId })
}
const pushExchangeOrder = async (record: NonNullable<Awaited<ReturnType<typeof orderDao.findAfterSale>>>) => {
if (!record.supplierId || !record.userId || !record.subOrderId || !record.mainOrderId) {
return serviceFail('售后订单信息不完整,无法推送换货单')
}
const { response, payload } = await bridgeFetchWithAuth<{ resultCode?: number; message?: string }>(record.supplierId, '/pointOrder/pushOrderInfoMsg', {
supplierId: record.supplierId,
orderType: ORDER_EVENT_TYPE.EXCHANGE_ORDER,
orderId: record.serviceOrderId,
mainOrderId: record.subOrderId,
userId: record.userId,
time: Date.now()
})
if (!response.ok || payload.resultCode !== 0) {
return serviceFail(payload.message || '换货单推送失败', 502, 'BRIDGE_EXCHANGE_ORDER_FAILED')
}
return serviceOk({ serviceOrderId: record.serviceOrderId })
}
const pushCancelSubOrder = async (record: NonNullable<Awaited<ReturnType<typeof orderDao.findAfterSale>>>) => {
if (!record.supplierId || !record.userId || !record.subOrderId || !record.mainOrderId) {
return serviceFail('售后订单信息不完整,无法取消子单')
}
const { response, payload } = await bridgeFetchWithAuth<{ resultCode?: number; message?: string }>(record.supplierId, '/pointOrder/pushOrderInfoMsg', {
supplierId: record.supplierId,
orderType: ORDER_EVENT_TYPE.CANCEL_SUB,
orderId: record.subOrderId,
mainOrderId: record.mainOrderId,
userId: record.userId,
time: Date.now()
})
if (!response.ok || payload.resultCode !== 0) {
return serviceFail(payload.message || '取消子单推送失败', 502, 'BRIDGE_CANCEL_SUB_FAILED')
}
return serviceOk({ serviceOrderId: record.serviceOrderId })
}
const afterSaleDetailPayload = (record: NonNullable<Awaited<ReturnType<typeof orderDao.findAfterSale>>>, subOrder?: Awaited<ReturnType<typeof orderDao.findOpenApiSubOrders>>[number] | null) => {
const requestBody = record.requestBody && typeof record.requestBody === 'object' ? record.requestBody as AfterSaleApplyInput : {}
const products = subOrder?.products.map(product => ({
@@ -168,6 +241,7 @@ const afterSaleDetailPayload = (record: NonNullable<Awaited<ReturnType<typeof or
price: requestBody.exchangeProductPrice || ''
} : null,
images: Array.isArray(record.images) ? record.images : [],
isSigned: Boolean(record.pickupMethod || record.pickupName || record.pickupMobile || record.pickupAddress) || isOrderSigned(subOrder?.orderState),
auditRemark: record.auditRemark || '',
userLogisticsNo: record.userLogisticsNo || '',
userLogisticsCompany: record.userLogisticsCompany || '',
@@ -247,6 +321,7 @@ export const afterSaleService = {
const now = new Date()
const amount = Number(input.amount || subOrder.totalPrice || 0)
const serviceOrderId = createServiceOrderId(subOrderId, serviceType)
const { isSigned: _isSigned, ...requestBody } = input
await orderDao.transaction(async tx => {
await tx.supplierAfterSale.create({
@@ -270,7 +345,7 @@ export const afterSaleService = {
pickupAddress: pickupAddress || null,
images,
applyTime: now,
requestBody: input
requestBody
}
})
await tx.supplierOrderOperateLog.create({
@@ -370,6 +445,30 @@ export const afterSaleService = {
return serviceOk({ afterSale: afterSaleDetailPayload(record, subOrders[0]) })
},
async merchantDetail(input: Pick<AfterSaleDetailInput, 'supplierId' | 'serviceOrderId'>) {
const supplierId = String(input.supplierId || '').trim()
const serviceOrderId = String(input.serviceOrderId || '').trim()
logger.info('merchant after sale detail query', { supplierId, serviceOrderId })
if (!supplierId || !serviceOrderId) {
return serviceFail('供应商或售后单不能为空')
}
const record = await orderDao.findAfterSale({
supplierId,
serviceOrderId,
deleted: 0
})
if (!record) {
return serviceFail('售后申请不存在', 404, 'AFTER_SALE_NOT_FOUND')
}
const subOrders = record.subOrderId
? await orderDao.findOpenApiSubOrders(supplierId, [record.subOrderId])
: []
return serviceOk({ afterSale: afterSaleDetailPayload(record, subOrders[0]) })
},
async audit(input: AfterSaleAuditInput) {
const supplierId = String(input.supplierId || '').trim()
const serviceOrderId = String(input.serviceOrderId || '').trim()
@@ -385,7 +484,27 @@ export const afterSaleService = {
return serviceFail('退款/售后申请不存在', 404, 'AFTER_SALE_NOT_FOUND')
}
const subOrders = record.subOrderId
? await orderDao.findOpenApiSubOrders(supplierId, [record.subOrderId])
: []
const isSigned = isOrderSigned(subOrders[0]?.orderState)
const approved = auditStatus === 1
const isExchange = record.serviceType === ORDER_EVENT_TYPE.EXCHANGE_ORDER
const finishUnsignedReturn = approved && !isSigned && record.serviceType === ORDER_EVENT_TYPE.RETURN_ORDER
const nextServiceState = approved
? (isSigned || isExchange ? AFTER_SALE_SERVICE_STATE.APPROVED : AFTER_SALE_SERVICE_STATE.FINISHED)
: AFTER_SALE_SERVICE_STATE.REJECTED
const nextLogisticsState = approved
? (isSigned ? AFTER_SALE_LOGISTICS_STATE.WAIT_USER_SHIP : AFTER_SALE_LOGISTICS_STATE.NONE)
: AFTER_SALE_LOGISTICS_STATE.NONE
if (finishUnsignedReturn) {
const cancelSubOrder = await pushCancelSubOrder(record)
if (!cancelSubOrder.success) {
return cancelSubOrder
}
}
const now = new Date()
await orderDao.transaction(async tx => {
await tx.supplierAfterSale.update({
@@ -394,13 +513,27 @@ export const afterSaleService = {
auditStatus,
auditRemark: auditRemark || null,
auditTime: now,
serviceState: approved ? AFTER_SALE_SERVICE_STATE.APPROVED : AFTER_SALE_SERVICE_STATE.REJECTED,
logisticsState: approved ? AFTER_SALE_LOGISTICS_STATE.WAIT_USER_SHIP : AFTER_SALE_LOGISTICS_STATE.NONE
serviceState: nextServiceState,
logisticsState: nextLogisticsState
}
})
if (finishUnsignedReturn && record.subOrderId) {
await tx.supplierOrderSub.updateMany({
where: {
supplierId,
orderId: record.subOrderId,
deleted: 0
},
data: {
orderState: ORDER_STATE.REFUNDED,
orderFinishTime: now,
updateTime: now
}
})
}
})
return serviceOk({ serviceOrderId, auditStatus, serviceState: approved ? AFTER_SALE_SERVICE_STATE.APPROVED : AFTER_SALE_SERVICE_STATE.REJECTED })
return serviceOk({ serviceOrderId, auditStatus, serviceState: nextServiceState })
},
async updateLogistics(input: AfterSaleLogisticsInput) {
@@ -417,9 +550,24 @@ export const afterSaleService = {
return serviceFail('退款/售后申请不存在', 404, 'AFTER_SALE_NOT_FOUND')
}
if (record.serviceType === ORDER_EVENT_TYPE.EXCHANGE_ORDER && logisticsState === AFTER_SALE_LOGISTICS_STATE.MERCHANT_SHIPPED) {
const exchangeOrder = await pushExchangeOrder(record)
if (!exchangeOrder.success) {
return exchangeOrder
}
}
if (record.serviceType === ORDER_EVENT_TYPE.RETURN_ORDER && logisticsState === AFTER_SALE_LOGISTICS_STATE.MERCHANT_RECEIVED) {
const returnOrder = await pushReturnOrder(record)
if (!returnOrder.success) {
return returnOrder
}
}
const now = new Date()
const finish = logisticsState === AFTER_SALE_LOGISTICS_STATE.USER_RECEIVED ||
(record.serviceType === ORDER_EVENT_TYPE.RETURN_ORDER && logisticsState === AFTER_SALE_LOGISTICS_STATE.MERCHANT_RECEIVED)
const finishReturn = record.serviceType === ORDER_EVENT_TYPE.RETURN_ORDER &&
logisticsState === AFTER_SALE_LOGISTICS_STATE.MERCHANT_RECEIVED
await orderDao.transaction(async tx => {
await tx.supplierAfterSale.update({
@@ -441,6 +589,20 @@ export const afterSaleService = {
finishTime: finish ? now : undefined
}
})
if (finishReturn && record.subOrderId) {
await tx.supplierOrderSub.updateMany({
where: {
supplierId,
orderId: record.subOrderId,
deleted: 0
},
data: {
orderState: ORDER_STATE.REFUNDED,
orderFinishTime: now,
updateTime: now
}
})
}
})
return serviceOk({ serviceOrderId, logisticsState, logisticsStateText: logisticsStateText(logisticsState) })
@@ -76,10 +76,32 @@ const afterSaleStatesByScene = (scene: string) => {
AFTER_SALE_SERVICE_STATE.RECEIVED
]
}
if (scene === 'history') {
return [
AFTER_SALE_SERVICE_STATE.REJECTED,
AFTER_SALE_SERVICE_STATE.FINISHED,
AFTER_SALE_SERVICE_STATE.CANCELLED
]
}
return undefined
}
const pendingShipExcludeAfterSaleStates = [
AFTER_SALE_SERVICE_STATE.PENDING_AUDIT,
AFTER_SALE_SERVICE_STATE.APPROVED,
AFTER_SALE_SERVICE_STATE.USER_SHIPPED,
AFTER_SALE_SERVICE_STATE.RECEIVED,
AFTER_SALE_SERVICE_STATE.FINISHED
]
const signedOrderStates = new Set<number>([
ORDER_STATE.RECEIVED,
ORDER_STATE.COMPLETED
])
const isOrderSigned = (orderState?: number | null) => signedOrderStates.has(Number(orderState))
export const merchantService = {
async listOrders(input: MerchantOrderListInput) {
const supplierId = String(input.supplierId || '').trim()
@@ -91,7 +113,9 @@ export const merchantService = {
}
const orders = await orderDao.findMerchantSubOrders(supplierId, orderState)
const orderItems = orders.map(order => ({
const afterSales = await orderDao.findMerchantAfterSales(supplierId, pendingShipExcludeAfterSaleStates)
const afterSaleSubOrderIds = new Set(afterSales.map(item => item.subOrderId).filter(Boolean))
const orderItems = orders.filter(order => !afterSaleSubOrderIds.has(order.orderId)).map(order => ({
orderId: order.orderId,
mainOrderId: order.mainOrderId,
userId: order.userId || '',
@@ -153,6 +177,7 @@ export const merchantService = {
serviceState: record.serviceState,
serviceStateText: serviceStateText(record.serviceState),
logisticsState: record.logisticsState,
isSigned: isOrderSigned(subOrder?.orderState),
reason: record.reason || '',
problemDescription: record.problemDescription || '',
pickupMethod: record.pickupMethod || '',
@@ -176,6 +201,10 @@ export const merchantService = {
return serviceOk({ afterSaleItems })
},
detailAfterSale(input: Pick<AfterSaleAuditInput, 'supplierId' | 'serviceOrderId'>) {
return afterSaleService.merchantDetail(input)
},
auditAfterSale(input: AfterSaleAuditInput) {
return afterSaleService.audit(input)
},
@@ -19,6 +19,7 @@ const statusMap: Record<number, OrderStatusView> = {
[ORDER_STATE.EXCHANGE]: { text: '换货', statusClass: 'muted', actionText: '查看订单', buttonClass: 'dark-action' },
[ORDER_STATE.REFUNDED]: { text: '退货', statusClass: 'muted', actionText: '查看订单', buttonClass: 'dark-action' }
}
const hiddenOrderStates = new Set<number>([ORDER_STATE.EXCHANGE, ORDER_STATE.REFUNDED])
const toYuanText = (value: { toFixed: (digits: number) => string } | number | string | null | undefined) => {
if (value && typeof value === 'object' && 'toFixed' in value) {
@@ -204,6 +205,10 @@ export const orderListService = {
return serviceFail('供应商或用户信息不能为空')
}
// 查询正在售后中的订单ID列表
const processingAfterSales = await orderDao.findProcessingAfterSaleOrderIds(supplierId, userId)
const excludeOrderIds = processingAfterSales.map(item => item.subOrderId)
const mainOrders = await orderDao.findMainOrdersWithSubs(supplierId, userId)
const orderItems = mainOrders.flatMap(mainOrder => {
const mainStatus = statusMap[mainOrder.orderState] || statusMap[ORDER_STATE.FINISH_PAY]
@@ -259,7 +264,9 @@ export const orderListService = {
products,
subOrders
}]
: subOrders.map(subOrder => ({
: subOrders
.filter(subOrder => !excludeOrderIds.includes(subOrder.orderId) && !hiddenOrderStates.has(Number(subOrder.orderState)))
.map(subOrder => ({
...subOrder,
viewId: `sub-${subOrder.orderId}`,
displayType: 'sub',
@@ -267,7 +274,10 @@ export const orderListService = {
}))
})
logger.info('order list result', { supplierId, userId, count: orderItems.length })
return serviceOk({ orderItems })
// 过滤掉正在售后中的订单
const filteredOrderItems = orderItems.filter(item => !excludeOrderIds.includes(item.orderId))
logger.info('order list result', { supplierId, userId, count: filteredOrderItems.length })
return serviceOk({ orderItems: filteredOrderItems })
}
}
@@ -1,5 +1,6 @@
import { supplierDao } from '../dao/supplier.dao.js'
import { orderDao } from '../dao/order.dao.js'
import { ORDER_EVENT_TYPE, ORDER_STATE } from '../constants/order.js'
import { logger } from '../utils/logger.js'
import { sm4CbcDecrypt, verifySupplierOpenSignature } from '../utils/supplier-open-crypto.js'
@@ -40,6 +41,17 @@ const productSku = (product: { sku: string; subOrderId: string; name: string })
return `${product.subOrderId}-${product.name || 'SKU'}`
}
const toOrderProductInfo = (order: Awaited<ReturnType<typeof orderDao.findOpenApiSubOrders>>[number]) => order.products.map(product => ({
sku: productSku(product),
number: product.number,
price: toMoney(product.price),
name: product.name,
taxRate: Number(product.taxRate || 0),
picInfo: Array.isArray(product.picInfo) ? product.picInfo : product.productImgUrl ? [product.productImgUrl] : [],
fileId: product.fileId,
filePath: product.filePath
}))
export const supplierOpenOrderService = {
parseBody,
@@ -67,6 +79,48 @@ export const supplierOpenOrderService = {
return { resultCode: 401, message: '签名无效或已过期', orderInfo: [] }
}
if ([ORDER_EVENT_TYPE.RETURN_ORDER, ORDER_EVENT_TYPE.EXCHANGE_ORDER].includes(Number(body.orderType))) {
const records = await orderDao.findAfterSales({
supplierId,
serviceOrderId: { in: orderIds },
deleted: 0
})
const subOrderIds = records.map(item => item.subOrderId).filter(Boolean) as string[]
const subOrders = subOrderIds.length ? await orderDao.findOpenApiSubOrders(supplierId, subOrderIds) : []
const subOrderMap = new Map(subOrders.map(item => [item.orderId, item]))
const orderInfo = records.map(record => {
const order = record.subOrderId ? subOrderMap.get(record.subOrderId) : null
return {
orderId: record.serviceOrderId,
orderNumberId: order?.orderNumberId || record.serviceOrderId,
supplierId: record.supplierId,
token: order?.token || '',
aplName: order?.aplName || '',
createTime: toText(record.applyTime || record.createTime),
updateTime: toText(record.updateTime),
deliveryTime: toText(order?.deliveryTime),
orderFinishTime: toText(record.finishTime),
completeTime: toText(record.finishTime),
deliveryStatus: order?.deliveryStatus || '',
freightCost: toMoney(order?.freightCost),
wPayOrAPayFreightCost: toMoney(order?.wPayOrAPayFreightCost),
refund: record.serviceType === ORDER_EVENT_TYPE.RETURN_ORDER ? toMoney(record.amount) : '0.00',
wPayOrAPayRefund: '0.00',
logisticsOrderId: order?.logisticsOrderId || '',
crrgBsnNm: order?.crrgBsnNm || '',
orderState: record.serviceType === ORDER_EVENT_TYPE.RETURN_ORDER ? ORDER_STATE.REFUNDED : ORDER_STATE.EXCHANGE,
totalPrice: toMoney(record.amount),
wPayOrAPay: '0.00',
pointsPrice: toMoney(record.amount),
invoiceCode: order?.invoiceCode || '',
orderProInfos: order ? toOrderProductInfo(order) : []
}
})
return { resultCode: 0, message: '成功', orderInfo }
}
const orders = await orderDao.findOpenApiSubOrders(supplierId, orderIds)
const orderInfo = orders.map(order => ({
orderId: order.orderId,
@@ -91,16 +145,7 @@ export const supplierOpenOrderService = {
wPayOrAPay: toMoney(order.wPayOrAPay),
pointsPrice: toMoney(order.pointsPrice),
invoiceCode: order.invoiceCode,
orderProInfos: order.products.map(product => ({
sku: productSku(product),
number: product.number,
price: toMoney(product.price),
name: product.name,
taxRate: Number(product.taxRate || 0),
picInfo: Array.isArray(product.picInfo) ? product.picInfo : product.productImgUrl ? [product.productImgUrl] : [],
fileId: product.fileId,
filePath: product.filePath
}))
orderProInfos: toOrderProductInfo(order)
}))
return { resultCode: 0, message: '成功', orderInfo }
+14 -5
View File
@@ -1,6 +1,7 @@
import type { SupplierInfo } from '@prisma/client'
import { supplierDao } from '../dao/supplier.dao.js'
import { createBridgeSignature } from './crypto.js'
import { logger } from './logger.js'
import { getRedis } from './redis.js'
type BridgeTokenResponse = {
@@ -168,17 +169,25 @@ export const bridgeFetchWithAuth = async <T>(
const call = async (forceRefresh = false) => {
const token = await getBridgeToken(supplierId, forceRefresh)
const requestBody = {
...body,
supplierId,
token
}
logger.supplierDebug('zhgh-send', '向ZHGH发送请求', {
method: 'POST',
path,
supplierId,
url: `${apiBase}${path}`,
body: requestBody
})
const response = await fetch(`${apiBase}${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
token
},
body: JSON.stringify({
...body,
supplierId,
token
})
body: JSON.stringify(requestBody)
})
const payload = (await response.json().catch(() => ({}))) as T
return { response, payload, token }
+28 -1
View File
@@ -1,4 +1,17 @@
type LogLevel = 'info' | 'warn' | 'error'
type SupplierLogType = 'backend-receive' | 'zhgh-send' | 'zhgh-receive'
export type SupplierDebugLog = {
id: number
type: SupplierLogType
title: string
time: string
content: unknown
}
const supplierDebugLogs: SupplierDebugLog[] = []
let supplierDebugLogId = 0
const MAX_SUPPLIER_DEBUG_LOGS = 200
const write = (level: LogLevel, message: string, meta?: unknown) => {
const line = `[supplied-service] ${new Date().toISOString()} ${level.toUpperCase()} ${message}`
@@ -19,5 +32,19 @@ const write = (level: LogLevel, message: string, meta?: unknown) => {
export const logger = {
info: (message: string, meta?: unknown) => write('info', message, meta),
warn: (message: string, meta?: unknown) => write('warn', message, meta),
error: (message: string, meta?: unknown) => write('error', message, meta)
error: (message: string, meta?: unknown) => write('error', message, meta),
supplierDebug: (type: SupplierLogType, title: string, content: unknown) => {
supplierDebugLogs.unshift({
id: ++supplierDebugLogId,
type,
title,
time: new Date().toISOString(),
content
})
supplierDebugLogs.splice(MAX_SUPPLIER_DEBUG_LOGS)
},
getSupplierDebugLogs: () => supplierDebugLogs,
clearSupplierDebugLogs: () => {
supplierDebugLogs.splice(0)
}
}