feat: 积分商城-完成
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user