260 lines
7.7 KiB
TypeScript
260 lines
7.7 KiB
TypeScript
import type { Context } from '@netlify/functions'
|
|
import { guardMethod } from '../utils/method.js'
|
|
import { fail, ok } from '../utils/http.js'
|
|
import { readJson } from '../utils/request.js'
|
|
import { prisma } from '../utils/prisma.js'
|
|
import { withErrorBoundary } from '../utils/handler.js'
|
|
|
|
type CheckoutGoods = {
|
|
id?: string | number
|
|
sku?: string
|
|
count?: number
|
|
number?: number
|
|
price?: string | number
|
|
name?: string
|
|
image?: string
|
|
imageUrl?: string
|
|
picInfo?: string[]
|
|
taxRate?: number
|
|
}
|
|
|
|
type OrderCreateRequest = {
|
|
supplierId?: string
|
|
token?: string
|
|
userId?: string
|
|
orderNo?: string
|
|
orderNumberId?: string
|
|
amount?: number
|
|
goods?: CheckoutGoods[]
|
|
}
|
|
|
|
type BridgeOrderResponse = {
|
|
resultCode?: number
|
|
message?: string
|
|
}
|
|
|
|
const normalizeBase = (base: string) => base.replace(/\/$/, '')
|
|
|
|
const nowText = () => {
|
|
const now = new Date()
|
|
const pad = (value: number) => String(value).padStart(2, '0')
|
|
|
|
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
|
|
}
|
|
|
|
const createOrderNumberId = () => {
|
|
const now = new Date()
|
|
const pad = (value: number, size = 2) => String(value).padStart(size, '0')
|
|
|
|
return `TR${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}${pad(now.getMilliseconds(), 3)}`
|
|
}
|
|
|
|
const toYuan = (centAmount?: number) => Number(((Number(centAmount || 0)) / 100).toFixed(2))
|
|
|
|
const toOrderProducts = (goods: CheckoutGoods[]) => goods.map((item, index) => {
|
|
const image = item.image || item.imageUrl || item.picInfo?.[0] || ''
|
|
|
|
return {
|
|
sku: String(item.sku || item.id || `SKU_${index + 1}`),
|
|
number: Number(item.count || item.number || 1),
|
|
price: Number(item.price || 0),
|
|
name: String(item.name || ''),
|
|
taxRate: Number(item.taxRate || 0.13),
|
|
picInfo: item.picInfo?.length ? item.picInfo : image ? [image] : [],
|
|
fileId: image || null,
|
|
filePath: image || null
|
|
}
|
|
})
|
|
|
|
export default withErrorBoundary(async (request: Request, _context: Context) => {
|
|
const methodResponse = guardMethod(request, ['POST'])
|
|
if (methodResponse) return methodResponse
|
|
|
|
const body = await readJson<OrderCreateRequest>(request)
|
|
const supplierId = String(body.supplierId || '').trim()
|
|
const token = String(body.token || '').trim()
|
|
const userId = String(body.userId || '').trim()
|
|
const orderId = String(body.orderNo || '').trim()
|
|
const amount = toYuan(body.amount)
|
|
const goods = Array.isArray(body.goods) ? body.goods : []
|
|
|
|
if (!supplierId || !token || !userId || !orderId) {
|
|
return fail('供应商、用户、授权或订单信息不能为空')
|
|
}
|
|
|
|
if (amount <= 0) {
|
|
return fail('订单金额必须大于0')
|
|
}
|
|
|
|
const apiBase = normalizeBase(process.env.POINTS_MALL_OPEN_API_BASE || '')
|
|
if (!apiBase) {
|
|
return fail('平台积分商城接口地址未配置', 500, 'OPEN_API_BASE_MISSING')
|
|
}
|
|
|
|
const orderNumberId = String(body.orderNumberId || createOrderNumberId())
|
|
const subOrderId = `${orderId}_001`
|
|
const subOrderNumberId = `${orderNumberId}_001`
|
|
const orderTime = nowText()
|
|
const orderProInfos = toOrderProducts(goods)
|
|
const orderInfo = {
|
|
orderId,
|
|
orderNumberId,
|
|
supplierId,
|
|
aplName: '积分商城',
|
|
createTime: orderTime,
|
|
updateTime: orderTime,
|
|
deliveryTime: null,
|
|
orderFinishTime: null,
|
|
completeTime: null,
|
|
deliveryStatus: '0',
|
|
freightCost: 0,
|
|
wPayOrAPayFreightCost: 0,
|
|
refund: 0,
|
|
wPayOrAPayRefund: 0,
|
|
logisticsOrderId: null,
|
|
crrgBsnNm: null,
|
|
orderState: 1,
|
|
totalPrice: amount,
|
|
wPayOrAPay: 0,
|
|
pointsPrice: amount,
|
|
invoiceCode: null,
|
|
orderProInfos
|
|
}
|
|
|
|
await prisma.$transaction(async tx => {
|
|
await tx.supplierOrderMain.upsert({
|
|
where: { orderId },
|
|
update: {
|
|
orderNumberId,
|
|
supplierId,
|
|
userId,
|
|
aplName: orderInfo.aplName,
|
|
updateTime: new Date(),
|
|
deliveryStatus: orderInfo.deliveryStatus,
|
|
freightCost: orderInfo.freightCost,
|
|
wPayOrAPayFreightCost: orderInfo.wPayOrAPayFreightCost,
|
|
refund: orderInfo.refund,
|
|
wPayOrAPayRefund: orderInfo.wPayOrAPayRefund,
|
|
orderState: orderInfo.orderState,
|
|
totalPrice: orderInfo.totalPrice,
|
|
wPayOrAPay: orderInfo.wPayOrAPay,
|
|
pointsPrice: orderInfo.pointsPrice,
|
|
invoiceCode: orderInfo.invoiceCode,
|
|
deleted: 0
|
|
},
|
|
create: {
|
|
orderId,
|
|
orderNumberId,
|
|
supplierId,
|
|
userId,
|
|
aplName: orderInfo.aplName,
|
|
createTime: new Date(),
|
|
updateTime: new Date(),
|
|
deliveryStatus: orderInfo.deliveryStatus,
|
|
freightCost: orderInfo.freightCost,
|
|
wPayOrAPayFreightCost: orderInfo.wPayOrAPayFreightCost,
|
|
refund: orderInfo.refund,
|
|
wPayOrAPayRefund: orderInfo.wPayOrAPayRefund,
|
|
orderState: orderInfo.orderState,
|
|
totalPrice: orderInfo.totalPrice,
|
|
wPayOrAPay: orderInfo.wPayOrAPay,
|
|
pointsPrice: orderInfo.pointsPrice,
|
|
invoiceCode: orderInfo.invoiceCode
|
|
}
|
|
})
|
|
|
|
await tx.supplierOrderSub.upsert({
|
|
where: { orderId: subOrderId },
|
|
update: {
|
|
orderNumberId: subOrderNumberId,
|
|
mainOrderId: orderId,
|
|
supplierId,
|
|
token,
|
|
userId,
|
|
aplName: orderInfo.aplName,
|
|
updateTime: new Date(),
|
|
deliveryStatus: orderInfo.deliveryStatus,
|
|
freightCost: orderInfo.freightCost,
|
|
wPayOrAPayFreightCost: orderInfo.wPayOrAPayFreightCost,
|
|
refund: orderInfo.refund,
|
|
wPayOrAPayRefund: orderInfo.wPayOrAPayRefund,
|
|
orderState: orderInfo.orderState,
|
|
totalPrice: orderInfo.totalPrice,
|
|
wPayOrAPay: orderInfo.wPayOrAPay,
|
|
pointsPrice: orderInfo.pointsPrice,
|
|
invoiceCode: orderInfo.invoiceCode,
|
|
deleted: 0
|
|
},
|
|
create: {
|
|
orderId: subOrderId,
|
|
orderNumberId: subOrderNumberId,
|
|
mainOrderId: orderId,
|
|
supplierId,
|
|
token,
|
|
userId,
|
|
aplName: orderInfo.aplName,
|
|
createTime: new Date(),
|
|
updateTime: new Date(),
|
|
deliveryStatus: orderInfo.deliveryStatus,
|
|
freightCost: orderInfo.freightCost,
|
|
wPayOrAPayFreightCost: orderInfo.wPayOrAPayFreightCost,
|
|
refund: orderInfo.refund,
|
|
wPayOrAPayRefund: orderInfo.wPayOrAPayRefund,
|
|
orderState: orderInfo.orderState,
|
|
totalPrice: orderInfo.totalPrice,
|
|
wPayOrAPay: orderInfo.wPayOrAPay,
|
|
pointsPrice: orderInfo.pointsPrice,
|
|
invoiceCode: orderInfo.invoiceCode
|
|
}
|
|
})
|
|
|
|
await tx.supplierOrderProduct.deleteMany({
|
|
where: { subOrderId }
|
|
})
|
|
|
|
if (orderProInfos.length) {
|
|
await tx.supplierOrderProduct.createMany({
|
|
data: orderProInfos.map(item => ({
|
|
subOrderId,
|
|
sku: item.sku,
|
|
number: item.number,
|
|
price: item.price,
|
|
name: item.name,
|
|
taxRate: item.taxRate,
|
|
productImgUrl: item.picInfo[0] || null,
|
|
fileId: item.fileId,
|
|
filePath: item.filePath,
|
|
picInfo: item.picInfo
|
|
}))
|
|
})
|
|
}
|
|
})
|
|
|
|
const bridgeResponse = await fetch(`${apiBase}/pointOrder/pendingOrderInfo`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
token
|
|
},
|
|
body: JSON.stringify({
|
|
supplierId,
|
|
userId,
|
|
orderInfo,
|
|
token
|
|
})
|
|
})
|
|
|
|
const bridgePayload = (await bridgeResponse.json().catch(() => ({}))) as BridgeOrderResponse
|
|
|
|
if (!bridgeResponse.ok || bridgePayload.resultCode !== 0) {
|
|
return fail(bridgePayload.message || '提交待支付订单失败', 502, 'BRIDGE_PENDING_ORDER_FAILED')
|
|
}
|
|
|
|
return ok({
|
|
orderId,
|
|
orderNumberId,
|
|
subOrderId,
|
|
orderInfo
|
|
})
|
|
})
|