feat: 查积分、支付、拆弹、取消订单

This commit is contained in:
2026-07-28 18:39:34 +08:00
parent 53305757f8
commit c443109869
40 changed files with 1814 additions and 163 deletions
@@ -0,0 +1,132 @@
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 OrderCancelMainRequest = {
supplierId?: string
token?: string
userId?: string
orderId?: string
mainOrderId?: string
}
type BridgeOrderResponse = {
resultCode?: number
message?: string
}
const normalizeBase = (base: string) => base.replace(/\/$/, '')
export default withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['POST'])
if (methodResponse) return methodResponse
const body = await readJson<OrderCancelMainRequest>(request)
const supplierId = String(body.supplierId || '').trim()
const token = String(body.token || '').trim()
const userId = String(body.userId || '').trim()
const mainOrderId = String(body.mainOrderId || body.orderId || '').trim()
const orderId = String(body.orderId || mainOrderId).trim()
if (!supplierId || !token || !userId || !mainOrderId) {
return fail('供应商、用户、授权或订单信息不能为空')
}
const apiBase = normalizeBase(process.env.POINTS_MALL_OPEN_API_BASE || '')
if (!apiBase) {
return fail('平台积分商城接口地址未配置', 500, 'OPEN_API_BASE_MISSING')
}
const order = await prisma.supplierOrderMain.findFirst({
where: {
orderId: mainOrderId,
supplierId,
userId,
deleted: 0
}
})
if (!order) {
return fail('主单不存在或已取消', 404, 'MAIN_ORDER_NOT_FOUND')
}
const bridgeResponse = await fetch(`${apiBase}/pointOrder/pushOrderInfoMsg`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
token
},
body: JSON.stringify({
supplierId,
orderType: 1,
orderId,
mainOrderId,
userId,
time: Date.now(),
token
})
})
const bridgePayload = (await bridgeResponse.json().catch(() => ({}))) as BridgeOrderResponse
if (!bridgeResponse.ok || bridgePayload.resultCode !== 0) {
return fail(bridgePayload.message || '取消主单推送失败', 502, 'BRIDGE_CANCEL_MAIN_FAILED')
}
const subOrders = await prisma.supplierOrderSub.findMany({
where: {
mainOrderId,
supplierId,
userId,
deleted: 0
},
select: {
orderId: true
}
})
const subOrderIds = subOrders.map(item => item.orderId)
await prisma.$transaction(async tx => {
await tx.supplierOrderMain.update({
where: { orderId: mainOrderId },
data: {
deleted: 1,
updateTime: new Date()
}
})
await tx.supplierOrderSub.updateMany({
where: {
mainOrderId,
supplierId,
userId,
deleted: 0
},
data: {
deleted: 1,
updateTime: new Date()
}
})
await tx.supplierOrderProduct.updateMany({
where: {
subOrderId: { in: subOrderIds },
deleted: 0
},
data: {
deleted: 1
}
})
})
return ok({
supplierId,
orderType: 1,
orderId,
mainOrderId,
userId
})
})
@@ -0,0 +1,107 @@
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 OrderCancelSubRequest = {
supplierId?: string
token?: string
userId?: string
orderId?: string
mainOrderId?: string
}
type BridgeOrderResponse = {
resultCode?: number
message?: string
}
const normalizeBase = (base: string) => base.replace(/\/$/, '')
export default withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['POST'])
if (methodResponse) return methodResponse
const body = await readJson<OrderCancelSubRequest>(request)
const supplierId = String(body.supplierId || '').trim()
const token = String(body.token || '').trim()
const userId = String(body.userId || '').trim()
const orderId = String(body.orderId || '').trim()
if (!supplierId || !token || !userId || !orderId) {
return fail('供应商、用户、授权或子单信息不能为空')
}
const apiBase = normalizeBase(process.env.POINTS_MALL_OPEN_API_BASE || '')
if (!apiBase) {
return fail('平台积分商城接口地址未配置', 500, 'OPEN_API_BASE_MISSING')
}
const subOrder = await prisma.supplierOrderSub.findFirst({
where: {
orderId,
supplierId,
userId,
deleted: 0
}
})
if (!subOrder) {
return fail('子单不存在或已取消', 404, 'SUB_ORDER_NOT_FOUND')
}
const mainOrderId = String(body.mainOrderId || subOrder.mainOrderId).trim()
const bridgeResponse = await fetch(`${apiBase}/pointOrder/pushOrderInfoMsg`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
token
},
body: JSON.stringify({
supplierId,
orderType: 3,
orderId,
mainOrderId,
userId,
time: Date.now(),
token
})
})
const bridgePayload = (await bridgeResponse.json().catch(() => ({}))) as BridgeOrderResponse
if (!bridgeResponse.ok || bridgePayload.resultCode !== 0) {
return fail(bridgePayload.message || '取消子单推送失败', 502, 'BRIDGE_CANCEL_SUB_FAILED')
}
await prisma.$transaction(async tx => {
await tx.supplierOrderSub.update({
where: { orderId },
data: {
deleted: 1,
updateTime: new Date()
}
})
await tx.supplierOrderProduct.updateMany({
where: {
subOrderId: orderId,
deleted: 0
},
data: {
deleted: 1
}
})
})
return ok({
supplierId,
orderType: 3,
orderId,
mainOrderId,
userId
})
})
@@ -1,11 +1,259 @@
import type { Context } from '@netlify/functions'
import { guardMethod } from '../utils/method.js'
import { ok } from '../utils/http.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
return ok({ message: '创建订单接口待实现' })
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
})
})
@@ -0,0 +1,106 @@
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 OrderListRequest = {
supplierId?: string
userId?: string
}
type OrderStatusView = {
text: string
statusClass: string
actionText: string
buttonClass: string
}
const statusMap: Record<number, OrderStatusView> = {
0: { text: '待付款', statusClass: 'pending', actionText: '去支付', buttonClass: 'primary-action' },
1: { text: '待发货', statusClass: 'pending', actionText: '查看订单', buttonClass: 'primary-action' },
2: { text: '待发货', statusClass: 'pending', actionText: '查看订单', buttonClass: 'primary-action' },
3: { text: '待收货', statusClass: 'pending', actionText: '确认收货', buttonClass: 'dark-action' },
4: { text: '已完成', statusClass: 'done', actionText: '再次购买', buttonClass: 'dark-action' },
5: { text: '已完成', statusClass: 'done', actionText: '再次购买', buttonClass: 'dark-action' }
}
const toYuanText = (value: { toFixed: (digits: number) => string } | number | string | null | undefined) => {
if (value && typeof value === 'object' && 'toFixed' in value) {
return value.toFixed(2)
}
return Number(value || 0).toFixed(2)
}
const firstImage = (picInfo: unknown) => {
if (Array.isArray(picInfo)) {
return String(picInfo[0] || '')
}
return ''
}
export default withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['POST'])
if (methodResponse) return methodResponse
const body = await readJson<OrderListRequest>(request)
const supplierId = String(body.supplierId || '').trim()
const userId = String(body.userId || '').trim()
if (!supplierId || !userId) {
return fail('供应商或用户信息不能为空')
}
const subOrders = await prisma.supplierOrderSub.findMany({
where: {
supplierId,
userId,
deleted: 0
},
include: {
mainOrder: true,
products: {
where: { deleted: 0 },
orderBy: { createAt: 'asc' }
}
},
orderBy: [
{ createTime: 'desc' },
{ createAt: 'desc' }
]
})
const orders = subOrders.map(order => {
const status = statusMap[order.orderState] || statusMap[1]
const products = order.products.map(product => ({
id: product.id,
sku: product.sku,
name: product.name,
number: product.number,
price: toYuanText(product.price),
image: product.productImgUrl || firstImage(product.picInfo)
}))
return {
id: order.orderId,
orderId: order.orderId,
orderNumberId: order.orderNumberId || '',
mainOrderId: order.mainOrderId,
mainOrderNumberId: order.mainOrder?.orderNumberId || '',
supplierId: order.supplierId,
status: status.text,
statusClass: status.statusClass,
actionText: status.actionText,
buttonClass: status.buttonClass,
orderState: order.orderState,
amount: toYuanText(order.totalPrice),
count: products.reduce((total, item) => total + Number(item.number || 0), 0),
products
}
})
return ok({ orders })
})
+139 -2
View File
@@ -1,11 +1,148 @@
import type { Context } from '@netlify/functions'
import { guardMethod } from '../utils/method.js'
import { ok } from '../utils/http.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 OrderPayRequest = {
supplierId?: string
token?: string
orderId?: string
amount?: number
totalPrice?: number
wPayOrAPay?: number
pointsPrice?: number
}
type BridgePayResponse = {
resultCode?: number
message?: string
}
const normalizeBase = (base: string) => base.replace(/\/$/, '')
const centsToYuan = (value?: number) => Number((Number(value || 0) / 100).toFixed(2))
export default withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['POST'])
if (methodResponse) return methodResponse
return ok({ message: '订单支付接口待实现' })
const body = await readJson<OrderPayRequest>(request)
const supplierId = String(body.supplierId || '').trim()
const token = String(body.token || '').trim()
const orderId = String(body.orderId || '').trim()
const totalPrice = Number(body.totalPrice ?? centsToYuan(body.amount))
const wPayOrAPay = Number(body.wPayOrAPay ?? 0)
const pointsPrice = Number(body.pointsPrice ?? totalPrice)
if (!supplierId || !token || !orderId) {
return fail('供应商、授权或订单信息不能为空')
}
if (totalPrice <= 0) {
return fail('支付金额必须大于0')
}
const apiBase = normalizeBase(process.env.POINTS_MALL_OPEN_API_BASE || '')
if (!apiBase) {
return fail('平台积分商城接口地址未配置', 500, 'OPEN_API_BASE_MISSING')
}
const order = await prisma.supplierOrderMain.findUnique({
where: { orderId }
})
if (!order || order.deleted === 1) {
return fail('待支付订单不存在', 404, 'ORDER_NOT_FOUND')
}
const bridgeResponse = await fetch(`${apiBase}/pointOrder/confirmPurchase`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
token
},
body: JSON.stringify({
supplierId,
orderId,
totalPrice,
wPayOrAPay,
pointsPrice,
token
})
})
const bridgePayload = (await bridgeResponse.json().catch(() => ({}))) as BridgePayResponse
if (!bridgeResponse.ok || bridgePayload.resultCode !== 0) {
return fail(bridgePayload.message || '确认购买失败', 502, 'BRIDGE_CONFIRM_PURCHASE_FAILED')
}
await prisma.$transaction(async tx => {
await tx.supplierOrderMain.update({
where: { orderId },
data: {
orderState: 1,
totalPrice,
wPayOrAPay,
pointsPrice,
updateTime: new Date()
}
})
await tx.supplierOrderSub.updateMany({
where: { mainOrderId: orderId, deleted: 0 },
data: {
orderState: 1,
totalPrice,
wPayOrAPay,
pointsPrice,
updateTime: new Date()
}
})
})
const subOrders = await prisma.supplierOrderSub.findMany({
where: {
mainOrderId: orderId,
deleted: 0
},
select: {
orderId: true
}
})
const subOrderIds = subOrders.map(item => item.orderId).filter(Boolean).join(',')
if (subOrderIds) {
const splitResponse = await fetch(`${apiBase}/pointOrder/pushOrderInfoMsg`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
token
},
body: JSON.stringify({
supplierId,
orderType: 2,
orderId: subOrderIds,
mainOrderId: orderId,
userId: order.userId,
time: Date.now(),
token
})
})
const splitPayload = (await splitResponse.json().catch(() => ({}))) as BridgePayResponse
if (!splitResponse.ok || splitPayload.resultCode !== 0) {
return fail(splitPayload.message || '推送拆单信息失败', 502, 'BRIDGE_SPLIT_ORDER_FAILED')
}
}
return ok({
orderId,
subOrderIds,
totalPrice,
wPayOrAPay,
pointsPrice
})
})
@@ -1,11 +1,63 @@
import type { Context } from '@netlify/functions'
import { guardMethod } from '../utils/method.js'
import { ok } from '../utils/http.js'
import { fail, ok } from '../utils/http.js'
import { prisma } from '../utils/prisma.js'
import { withErrorBoundary } from '../utils/handler.js'
const toProductDetail = (product: NonNullable<Awaited<ReturnType<typeof prisma.supplierProduct.findFirst>>>) => {
const picInfo = Array.isArray(product.picInfo) ? product.picInfo : []
const image = product.imageUrl || String(picInfo[0] || '')
const price = product.price.toFixed(2)
return {
id: product.id,
sku: product.sku,
number: product.number,
price,
name: product.name,
taxRate: Number(product.taxRate),
picInfo,
desc: product.description || '',
detail: product.detail || product.description || '',
originPrice: product.originPrice ? product.originPrice.toFixed(2) : price,
image,
imageUrl: image,
categoryId: product.categoryId || '',
category: product.category || '',
tag: product.tag || '',
sales: product.sales || '',
stock: product.stock
}
}
export default withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['GET'])
if (methodResponse) return methodResponse
return ok({ message: '商品详情接口待实现' })
const url = new URL(request.url)
const id = (url.searchParams.get('id') || '').trim()
const sku = (url.searchParams.get('sku') || '').trim()
const numericId = Number(id)
const querySku = sku || (id && !Number.isInteger(numericId) ? id : '')
if (!id && !sku) {
return fail('商品参数不能为空')
}
const product = await prisma.supplierProduct.findFirst({
where: {
enabled: true,
deleted: 0,
OR: [
...(Number.isInteger(numericId) && numericId > 0 ? [{ id: numericId }] : []),
...(querySku ? [{ sku: querySku }] : [])
]
}
})
if (!product) {
return fail('商品不存在或已下架', 404, 'PRODUCT_NOT_FOUND')
}
return ok(toProductDetail(product))
})
@@ -1,11 +1,69 @@
import type { Context } from '@netlify/functions'
import { guardMethod } from '../utils/method.js'
import { ok } from '../utils/http.js'
import { prisma } from '../utils/prisma.js'
import { withErrorBoundary } from '../utils/handler.js'
const categories = [
{ id: 'recommend', name: '推荐' },
{ id: 'homeLiving', name: '居家生活' },
{ id: 'digitalAppliance', name: '数码家电' },
{ id: 'beautyCare', name: '美妆个护' },
{ id: 'foodDrink', name: '食品饮料' },
{ id: 'motherBaby', name: '母婴玩具' },
{ id: 'fashionBag', name: '服饰鞋包' },
{ id: 'sportsOutdoor', name: '运动户外' }
]
const toProductItem = (product: Awaited<ReturnType<typeof prisma.supplierProduct.findMany>>[number]) => {
const picInfo = Array.isArray(product.picInfo) ? product.picInfo : []
const image = product.imageUrl || String(picInfo[0] || '')
const price = product.price.toFixed(2)
return {
id: product.id,
sku: product.sku,
number: product.number,
price,
name: product.name,
taxRate: Number(product.taxRate),
picInfo,
desc: product.description || '',
detail: product.detail || product.description || '',
originPrice: product.originPrice ? product.originPrice.toFixed(2) : price,
image,
imageUrl: image,
categoryId: product.categoryId || '',
category: product.category || '',
tag: product.tag || '',
sales: product.sales || '',
stock: product.stock
}
}
export default withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['GET'])
if (methodResponse) return methodResponse
return ok({ message: '商品列表接口待实现' })
const url = new URL(request.url)
const keyword = (url.searchParams.get('keyword') || '').trim()
const categoryId = (url.searchParams.get('categoryId') || '').trim()
const products = await prisma.supplierProduct.findMany({
where: {
enabled: true,
deleted: 0,
...(keyword ? { name: { contains: keyword } } : {}),
...(categoryId && categoryId !== 'recommend' ? { categoryId } : {})
},
orderBy: [
{ sortOrder: 'asc' },
{ createdAt: 'desc' }
]
})
return ok({
categories,
products: products.map(toProductItem)
})
})
@@ -32,7 +32,6 @@ export default withErrorBoundary(async (request: Request, _context: Context) =>
id: supplier.id,
supplierIdentification: supplier.supplierIdentification,
supplierId: supplier.supplierId,
supplierName: supplier.supplierName,
clientId: supplier.clientId
supplierName: supplier.supplierName
})
})
@@ -0,0 +1,62 @@
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 { withErrorBoundary } from '../utils/handler.js'
type SupplierPointsRequest = {
supplierId?: string
token?: string
userId?: string
}
type BridgePointsResponse = {
resultCode?: number
message?: string
userPoints?: number | string
}
const normalizeBase = (base: string) => base.replace(/\/$/, '')
export default withErrorBoundary(async (request: Request, _context: Context) => {
const methodResponse = guardMethod(request, ['POST'])
if (methodResponse) return methodResponse
const body = await readJson<SupplierPointsRequest>(request)
const supplierId = String(body.supplierId || '').trim()
const token = String(body.token || '').trim()
const userId = String(body.userId || '').trim()
if (!supplierId || !token || !userId) {
return fail('供应商、用户或授权信息不能为空')
}
const apiBase = normalizeBase(process.env.POINTS_MALL_OPEN_API_BASE || '')
if (!apiBase) {
return fail('平台积分商城接口地址未配置', 500, 'OPEN_API_BASE_MISSING')
}
const response = await fetch(`${apiBase}/pointOrder/queryPoints`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
token
},
body: JSON.stringify({
supplierId,
token,
userId
})
})
const pointsPayload = (await response.json().catch(() => ({}))) as BridgePointsResponse
if (!response.ok || pointsPayload.resultCode !== 0) {
return fail(pointsPayload.message || '查询用户积分失败', 502, 'BRIDGE_POINTS_FAILED')
}
return ok({
userPoints: Number(pointsPayload.userPoints || 0)
})
})
@@ -59,7 +59,8 @@ export default withErrorBoundary(async (request: Request, _context: Context) =>
body: JSON.stringify({
supplierId: supplier.supplierId,
clientId: supplier.clientId,
clientSecret: signature,
clientSecret: supplier.clientSecret,
sendSha256: signature,
code: '',
timestamp
})