feat: 积分商城-供应商-对账-发票2
This commit is contained in:
@@ -30,6 +30,13 @@ export const merchantSupplierListController = withErrorBoundary(async (request:
|
||||
return toHttpResponse(await merchantService.listSuppliers())
|
||||
})
|
||||
|
||||
export const merchantOrderRepushSplitController = withErrorBoundary(async (request: Request, _context: Context) => {
|
||||
const methodResponse = guardMethod(request, ['POST'])
|
||||
if (methodResponse) return methodResponse
|
||||
|
||||
return toHttpResponse(await merchantService.repushSplitOrder(await readJson<Pick<MerchantShipInput, 'supplierId' | 'userId' | 'orderId'>>(request)))
|
||||
})
|
||||
|
||||
export const merchantOrderShipController = withErrorBoundary(async (request: Request, _context: Context) => {
|
||||
const methodResponse = guardMethod(request, ['POST'])
|
||||
if (methodResponse) return methodResponse
|
||||
|
||||
@@ -45,7 +45,7 @@ export const supplierSessionController = withErrorBoundary(async (request: Reque
|
||||
const body = await readJson<SupplierSessionRequest>(request)
|
||||
const keyword = String(body.keyword || '')
|
||||
|
||||
return toHttpResponse(supplierService.decryptSupplierSession(keyword))
|
||||
return toHttpResponse(await supplierService.decryptSupplierSession(keyword))
|
||||
})
|
||||
|
||||
export const supplierTokenController = withErrorBoundary(async (request: Request, _context: Context) => {
|
||||
|
||||
@@ -171,32 +171,40 @@ export const orderDao = {
|
||||
})
|
||||
},
|
||||
|
||||
findMerchantSubOrders(supplierId: string, orderState?: number, orderId?: string, userId?: string) {
|
||||
return prisma.supplierOrderSub.findMany({
|
||||
where: {
|
||||
supplierId,
|
||||
deleted: 0,
|
||||
...(Number.isInteger(orderState) ? { orderState } : {}),
|
||||
...(orderId ? { OR: [{ orderId: { contains: orderId } }, { orderNumberId: { contains: orderId } }] } : {}),
|
||||
...(userId ? { userId: { contains: userId } } : {})
|
||||
},
|
||||
include: {
|
||||
products: {
|
||||
where: { deleted: 0 },
|
||||
orderBy: { createAt: 'asc' }
|
||||
}
|
||||
},
|
||||
orderBy: [
|
||||
{ createTime: 'desc' },
|
||||
{ createAt: 'desc' }
|
||||
]
|
||||
})
|
||||
async findMerchantSubOrders(supplierId: string, orderState?: number, orderId?: string, userId?: string, excludedOrderIds: string[] = [], pagination?: { pageNumber: number; pageSize: number }) {
|
||||
const where: Prisma.SupplierOrderSubWhereInput = {
|
||||
...(supplierId ? { supplierId } : {}),
|
||||
deleted: 0,
|
||||
...(Number.isInteger(orderState) ? { orderState } : {}),
|
||||
...(orderId ? { OR: [{ orderId: { contains: orderId } }, { orderNumberId: { contains: orderId } }] } : {}),
|
||||
...(userId ? { userId: { contains: userId } } : {}),
|
||||
...(excludedOrderIds.length ? { orderId: { notIn: excludedOrderIds } } : {})
|
||||
}
|
||||
const [orders, totalCount] = await prisma.$transaction([
|
||||
prisma.supplierOrderSub.findMany({
|
||||
where,
|
||||
include: {
|
||||
products: {
|
||||
where: { deleted: 0 },
|
||||
orderBy: { createAt: 'asc' }
|
||||
}
|
||||
},
|
||||
orderBy: [
|
||||
{ createTime: 'desc' },
|
||||
{ createAt: 'desc' },
|
||||
{ orderId: 'desc' }
|
||||
],
|
||||
...(pagination ? { skip: (pagination.pageNumber - 1) * pagination.pageSize, take: pagination.pageSize } : {})
|
||||
}),
|
||||
prisma.supplierOrderSub.count({ where })
|
||||
])
|
||||
return { orders, totalCount }
|
||||
},
|
||||
|
||||
findMerchantAfterSales(supplierId: string, serviceStates?: number[]) {
|
||||
return prisma.supplierAfterSale.findMany({
|
||||
where: {
|
||||
supplierId,
|
||||
...(supplierId ? { supplierId } : {}),
|
||||
deleted: 0,
|
||||
...(serviceStates?.length ? { serviceState: { in: serviceStates } } : {})
|
||||
},
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { merchantOrderRepushSplitController as default } from '../controllers/merchant.controller.js'
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
merchantAfterSaleShipController,
|
||||
merchantOrderListController,
|
||||
merchantOrderShipController,
|
||||
merchantOrderRepushSplitController,
|
||||
merchantSupplierListController
|
||||
} from './controllers/merchant.controller.js'
|
||||
import {
|
||||
@@ -93,6 +94,7 @@ const handlers: Record<string, FunctionHandler> = {
|
||||
'merchant-after-sale-ship': merchantAfterSaleShipController,
|
||||
'merchant-order-list': merchantOrderListController,
|
||||
'merchant-order-ship': merchantOrderShipController,
|
||||
'merchant-order-repush-split': merchantOrderRepushSplitController,
|
||||
'merchant-supplier-list': merchantSupplierListController,
|
||||
'order-cancel-main': orderCancelMainController,
|
||||
'order-cancel-paid-main': orderCancelPaidMainController,
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
AFTER_SALE_LOGISTICS_STATE,
|
||||
AFTER_SALE_SERVICE_STATE,
|
||||
ORDER_EVENT_TYPE,
|
||||
ORDER_SPLIT_STATE,
|
||||
ORDER_STATE
|
||||
} from '../constants/order.js'
|
||||
import { orderDao } from '../dao/order.dao.js'
|
||||
@@ -9,11 +10,14 @@ import { supplierDao } from '../dao/supplier.dao.js'
|
||||
import { afterSaleService, type AfterSaleAuditInput, type AfterSaleLogisticsInput } from './after-sale.service.js'
|
||||
import { orderActionService } from './order-action.service.js'
|
||||
import { logger } from '../utils/logger.js'
|
||||
import { bridgeFetchWithAuth } from '../utils/bridge-auth.js'
|
||||
import { serviceFail, serviceOk } from '../types/service-result.js'
|
||||
|
||||
export type MerchantOrderListInput = {
|
||||
supplierId?: string
|
||||
orderState?: number
|
||||
pageNumber?: number
|
||||
pageSize?: number
|
||||
orderId?: string
|
||||
userId?: string
|
||||
}
|
||||
@@ -108,19 +112,18 @@ const isOrderSigned = (orderState?: number | null) => signedOrderStates.has(Numb
|
||||
export const merchantService = {
|
||||
async listOrders(input: MerchantOrderListInput) {
|
||||
const supplierId = String(input.supplierId || '').trim()
|
||||
const orderState = input.orderState === undefined ? undefined : Number(input.orderState)
|
||||
const orderState = input.orderState == null || String(input.orderState) === '' ? undefined : Number(input.orderState)
|
||||
const orderId = String(input.orderId || '').trim()
|
||||
const userId = String(input.userId || '').trim()
|
||||
logger.info('merchant order list query', { supplierId, orderState })
|
||||
|
||||
if (!supplierId) {
|
||||
return serviceFail('供应商不能为空')
|
||||
}
|
||||
|
||||
const orders = await orderDao.findMerchantSubOrders(supplierId, orderState, orderId, userId)
|
||||
const pageNumber = Number.isInteger(Number(input.pageNumber)) && Number(input.pageNumber) > 0 ? Number(input.pageNumber) : 1
|
||||
const pageSize = Number.isInteger(Number(input.pageSize)) && Number(input.pageSize) > 0 ? Number(input.pageSize) : 10
|
||||
const pagination = input.pageNumber !== undefined || input.pageSize !== undefined ? { pageNumber, pageSize } : undefined
|
||||
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 => ({
|
||||
const afterSaleSubOrderIds = afterSales.map(item => item.subOrderId).filter((id): id is string => Boolean(id))
|
||||
const { orders, totalCount } = await orderDao.findMerchantSubOrders(supplierId, orderState, orderId, userId, afterSaleSubOrderIds, pagination)
|
||||
const orderItems = orders.map(order => ({
|
||||
orderId: order.orderId,
|
||||
mainOrderId: order.mainOrderId,
|
||||
userId: order.userId || '',
|
||||
@@ -141,7 +144,7 @@ export const merchantService = {
|
||||
}))
|
||||
}))
|
||||
|
||||
return serviceOk({ orderItems })
|
||||
return serviceOk({ orderItems, totalCount, pageNumber, pageSize })
|
||||
},
|
||||
|
||||
async listSuppliers() {
|
||||
@@ -149,6 +152,57 @@ export const merchantService = {
|
||||
return serviceOk({ suppliers })
|
||||
},
|
||||
|
||||
async repushSplitOrder(input: Pick<MerchantShipInput, 'supplierId' | 'userId' | 'orderId'>) {
|
||||
const supplierId = String(input.supplierId || '').trim()
|
||||
const userId = String(input.userId || '').trim()
|
||||
const orderId = String(input.orderId || '').trim()
|
||||
if (!supplierId || !userId || !orderId) {
|
||||
return serviceFail('供应商、用户或子单信息不能为空')
|
||||
}
|
||||
|
||||
const subOrder = await orderDao.findSubOrderWithProducts(supplierId, userId, orderId)
|
||||
if (!subOrder) {
|
||||
return serviceFail('子单不存在', 404, 'SUB_ORDER_NOT_FOUND')
|
||||
}
|
||||
const mainOrder = await orderDao.findMainOrder(supplierId, userId, subOrder.mainOrderId)
|
||||
if (!mainOrder) {
|
||||
return serviceFail('主单不存在', 404, 'MAIN_ORDER_NOT_FOUND')
|
||||
}
|
||||
if (mainOrder.orderState !== ORDER_STATE.FINISH_PAY || subOrder.orderState !== ORDER_STATE.FINISH_PAY) {
|
||||
return serviceFail('仅支持已支付且待发货的订单重新推送拆单')
|
||||
}
|
||||
if (!subOrder.products.length) {
|
||||
return serviceFail('子单缺少商品信息,无法推送拆单')
|
||||
}
|
||||
|
||||
try {
|
||||
const { response, payload } = await bridgeFetchWithAuth<{ resultCode?: number; message?: string }>(
|
||||
supplierId,
|
||||
'/pointOrder/pushOrderInfoMsg',
|
||||
{
|
||||
orderType: ORDER_EVENT_TYPE.SPLIT_SUB_ORDER,
|
||||
orderId: subOrder.orderId,
|
||||
mainOrderId: mainOrder.orderId,
|
||||
userId: mainOrder.userId,
|
||||
time: Date.now()
|
||||
}
|
||||
)
|
||||
if (!response.ok || payload.resultCode !== 0) {
|
||||
return serviceFail(payload.message || '重新推送拆单失败', 502, 'BRIDGE_SPLIT_ORDER_FAILED')
|
||||
}
|
||||
} catch (error) {
|
||||
return serviceFail(error instanceof Error ? error.message : '重新推送拆单失败', 502, 'BRIDGE_SPLIT_ORDER_FAILED')
|
||||
}
|
||||
|
||||
await orderDao.transaction(async tx => {
|
||||
await tx.supplierOrderMain.update({
|
||||
where: { orderId: mainOrder.orderId },
|
||||
data: { isSplit: ORDER_SPLIT_STATE.SPLIT, splitTime: mainOrder.splitTime || new Date(), updateTime: new Date() }
|
||||
})
|
||||
})
|
||||
return serviceOk({ orderId, mainOrderId: mainOrder.orderId })
|
||||
},
|
||||
|
||||
shipOrder(input: MerchantShipInput) {
|
||||
return orderActionService.shipOrder({
|
||||
supplierId: String(input.supplierId || '').trim(),
|
||||
|
||||
@@ -31,7 +31,7 @@ export const supplierFileService = {
|
||||
const now = new Date()
|
||||
const pad = (value: number) => String(value).padStart(2, '0')
|
||||
const datePath = `${now.getFullYear()}/${pad(now.getMonth() + 1)}/${pad(now.getDate())}`
|
||||
const objectName = `invoices/${datePath}/${supplierId}/${fileName}`
|
||||
const objectName = `supplied/invoices/${datePath}/${supplierId}/${fileName}`
|
||||
const stat = statSync(filePath)
|
||||
await client.fPutObject(bucketName, objectName, filePath, {
|
||||
'Content-Type': 'application/pdf'
|
||||
@@ -44,8 +44,8 @@ export const supplierFileService = {
|
||||
return { fileId, objectName }
|
||||
},
|
||||
|
||||
async read(fileId: string) {
|
||||
const file = await prisma.supplierFile.findFirst({ where: { fileId, deleted: 0 } })
|
||||
async read(fileId: string, supplierId: string) {
|
||||
const file = await prisma.supplierFile.findFirst({ where: { fileId, supplierId, deleted: 0 } })
|
||||
if (!file) return null
|
||||
const stream = await client.getObject(file.bucketName, file.objectName)
|
||||
return streamToBuffer(stream)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { existsSync, readFileSync, unlinkSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { supplierDao } from '../dao/supplier.dao.js'
|
||||
import { orderDao } from '../dao/order.dao.js'
|
||||
@@ -72,6 +72,7 @@ export type SupplierFileBase64QueryRequest = {
|
||||
}
|
||||
|
||||
export type SupplierInvoiceQueryRequest = {
|
||||
retry?: boolean
|
||||
supplierId?: string
|
||||
token?: string
|
||||
settlementId?: string
|
||||
@@ -90,8 +91,8 @@ const parseBody = <T extends SupplierOpenEncryptedRequest>(body: T) => {
|
||||
const encryptedData = body.AplParmObjct || body.ApiParamObject
|
||||
if (!encryptedData) return body
|
||||
|
||||
const keyHex = process.env.SM4CBC_KEY_HEX || ''
|
||||
const ivHex = process.env.SM4CBC_IV_HEX || ''
|
||||
const keyHex = process.env.SM4CBC_KEY_HEX || process.env.SUPPLIER_OPEN_SM4_KEY_HEX || ''
|
||||
const ivHex = process.env.SM4CBC_IV_HEX || process.env.SUPPLIER_OPEN_SM4_IV_HEX || ''
|
||||
if (!keyHex || !ivHex) {
|
||||
throw new Error('供应商开放接口SM4配置缺失')
|
||||
}
|
||||
@@ -115,7 +116,7 @@ const numericId = (seed: string, length: number) => BigInt(`0x${createHash('sha2
|
||||
.padStart(length, '0')
|
||||
.slice(0, length)
|
||||
|
||||
const invoiceOutputRoot = () => path.resolve(process.env.INVOICE_PDF_OUTPUT_DIR || path.join(process.cwd(), 'output', 'pdf', 'invoices'))
|
||||
const invoiceOutputRoot = () => path.resolve(process.env.INVOICE_PDF_OUTPUT_DIR || path.join(process.cwd(), 'temp', 'pdf', 'invoices'))
|
||||
|
||||
const storedRequest = (value: unknown) => value && typeof value === 'object' && !Array.isArray(value)
|
||||
? value as Record<string, unknown>
|
||||
@@ -170,7 +171,7 @@ const productImageSource = (product?: Awaited<ReturnType<typeof orderDao.findOpe
|
||||
export const supplierOpenOrderService = {
|
||||
parseBody,
|
||||
|
||||
async applyForInvoices(body: SupplierInvoiceApplyRequest) {
|
||||
async applyForInvoices(body: SupplierInvoiceApplyRequest, notifyPlatform = true) {
|
||||
const supplierId = String(body.supplierId || '').trim()
|
||||
const token = String(body.token || '').trim()
|
||||
const settlementId = String(body.settlementId || '').trim()
|
||||
@@ -265,12 +266,23 @@ export const supplierOpenOrderService = {
|
||||
const requestedInvoiceType = Number(body.invoiceType || 10)
|
||||
const invoiceType = Number.isInteger(requestedInvoiceType) ? requestedInvoiceType : 10
|
||||
const absoluteFilePath = path.join(invoiceOutputRoot(), supplierId, `${invoiceId}.pdf`)
|
||||
const filePath = path.relative(process.cwd(), absoluteFilePath).replaceAll('\\', '/')
|
||||
|
||||
try {
|
||||
generateMockInvoicePdf({
|
||||
...body,
|
||||
taxIdNumber,
|
||||
settlementId,
|
||||
orderIds: orderIdText,
|
||||
orderNum: orders.length,
|
||||
orderTotalPrice: actualTotalPrice,
|
||||
invoiceContent: orders.flatMap(order => order.products.map(product => product.name)).filter(Boolean).join('、'),
|
||||
orderProducts: orders.flatMap(order => order.products.map(product => ({
|
||||
orderId: order.orderId,
|
||||
sku: product.sku,
|
||||
name: product.name,
|
||||
number: product.number,
|
||||
price: Number(product.price || 0)
|
||||
}))),
|
||||
documentInvoiceCode: invoiceCode,
|
||||
invoiceNumber: invoiceId,
|
||||
checkCode,
|
||||
@@ -281,6 +293,11 @@ export const supplierOpenOrderService = {
|
||||
taxRate
|
||||
}, absoluteFilePath)
|
||||
await supplierFileService.uploadPdf(invoiceId, supplierId, `${invoiceId}.pdf`, absoluteFilePath)
|
||||
try {
|
||||
unlinkSync(absoluteFilePath)
|
||||
} catch (error) {
|
||||
logger.error('supplier invoice temp file cleanup failed', { supplierId, settlementId, absoluteFilePath, error })
|
||||
}
|
||||
|
||||
const mainOrderIds = [...new Set(orders.map(order => order.mainOrderId).filter(Boolean))]
|
||||
await orderDao.transaction(async tx => {
|
||||
@@ -299,7 +316,7 @@ export const supplierOpenOrderService = {
|
||||
checkCode,
|
||||
fileType: 'PDF',
|
||||
fileId: invoiceId,
|
||||
filePath,
|
||||
filePath: null,
|
||||
failureMessage: null
|
||||
}
|
||||
})
|
||||
@@ -319,13 +336,29 @@ export const supplierOpenOrderService = {
|
||||
await orderDao.transaction(async tx => {
|
||||
await tx.supplierInvoiceInfo.update({
|
||||
where: { invoiceBizId: settlementId },
|
||||
data: { invoiceStatus: 3, failureMessage }
|
||||
data: {
|
||||
invoiceStatus: 3, failureMessage, invoiceIds: invoiceId, invoiceCode,
|
||||
invoiceDate, invoiceType, invoiceNakeAmount, invoiceTaxRate: taxRate,
|
||||
invoiceTaxAmount, invoiceAmount: orderTotalPrice, checkCode, fileType: 'PDF',
|
||||
fileId: null, filePath: null
|
||||
}
|
||||
})
|
||||
})
|
||||
logger.error('supplier mock invoice generate failed', { supplierId, settlementId, failureMessage })
|
||||
if (notifyPlatform) {
|
||||
await orderPushEventService.pushEvent({
|
||||
supplierId, userId: String(firstOrder?.userId || 'SYSTEM'),
|
||||
orderType: ORDER_EVENT_TYPE.INVOICE_INFO, orderId: settlementId,
|
||||
mainOrderId: firstOrder?.mainOrderId, settlementId, invoiceBizId: settlementId
|
||||
})
|
||||
}
|
||||
return { resultCode: 500, message: failureMessage }
|
||||
}
|
||||
|
||||
if (!notifyPlatform) {
|
||||
return { resultCode: 0, message: '发票重新生成成功' }
|
||||
}
|
||||
|
||||
const pushResult = await orderPushEventService.pushEvent({
|
||||
supplierId,
|
||||
userId: String(firstOrder?.userId || 'SYSTEM'),
|
||||
@@ -337,8 +370,7 @@ export const supplierOpenOrderService = {
|
||||
invoiceCode,
|
||||
invoiceIds: invoiceId,
|
||||
orderIds: orderIdText,
|
||||
fileId: invoiceId,
|
||||
filePath
|
||||
fileId: invoiceId
|
||||
})
|
||||
const pushSuccess = pushResult.success && pushResult.data.pushSuccess
|
||||
|
||||
@@ -365,7 +397,7 @@ export const supplierOpenOrderService = {
|
||||
return { resultCode: 401, message: '签名无效或已过期' }
|
||||
}
|
||||
|
||||
const invoice = await orderDao.findInvoiceBySettlementId(supplierId, settlementId)
|
||||
let invoice = await orderDao.findInvoiceBySettlementId(supplierId, settlementId)
|
||||
if (!invoice) {
|
||||
return { resultCode: 404, message: '未找到对应的开票记录' }
|
||||
}
|
||||
@@ -373,6 +405,38 @@ export const supplierOpenOrderService = {
|
||||
return { resultCode: 409, message: '发票正在开具中' }
|
||||
}
|
||||
|
||||
if (body.retry === true && invoice.invoiceStatus === 3) {
|
||||
const previous = invoice
|
||||
const claimed = await orderDao.transaction(tx => tx.supplierInvoiceInfo.updateMany({
|
||||
where: { invoiceBizId: settlementId, supplierId, invoiceStatus: 3, deleted: 0 },
|
||||
data: { invoiceStatus: 1 }
|
||||
}))
|
||||
if (!claimed.count) {
|
||||
return { resultCode: 409, message: '发票正在重新生成,请勿重复操作' }
|
||||
}
|
||||
try {
|
||||
const result = await this.applyForInvoices({
|
||||
...storedRequest(previous.requestBody),
|
||||
supplierId, settlementId, token, timestamp, RsrvFld1: body.RsrvFld1
|
||||
}, false)
|
||||
if (result.resultCode !== 0) {
|
||||
await orderDao.transaction(tx => tx.supplierInvoiceInfo.updateMany({
|
||||
where: { invoiceBizId: settlementId, supplierId, invoiceStatus: 1 },
|
||||
data: { invoiceStatus: 3, failureMessage: result.message }
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
const failureMessage = error instanceof Error ? error.message : '重新生成发票失败'
|
||||
await orderDao.transaction(tx => tx.supplierInvoiceInfo.updateMany({
|
||||
where: { invoiceBizId: settlementId, supplierId, invoiceStatus: 1 },
|
||||
data: { invoiceStatus: 3, failureMessage }
|
||||
}))
|
||||
return { resultCode: 500, message: failureMessage }
|
||||
}
|
||||
invoice = await orderDao.findInvoiceBySettlementId(supplierId, settlementId)
|
||||
if (!invoice) return { resultCode: 404, message: '未找到对应的开票记录' }
|
||||
}
|
||||
|
||||
const request = storedRequest(invoice.requestBody)
|
||||
const success = invoice.invoiceStatus === 2
|
||||
return {
|
||||
@@ -384,8 +448,10 @@ export const supplierOpenOrderService = {
|
||||
sucOrderIds: success ? invoice.orderIds || '' : '',
|
||||
failOrderIds: success ? '' : invoice.orderIds || '',
|
||||
failMsg: success ? '' : invoice.failureMessage || '开票失败',
|
||||
invoiceInfos: success ? [{
|
||||
invoiceId: invoice.invoiceIds,
|
||||
invoiceInfos: [{
|
||||
status: success ? 1 : 2,
|
||||
remark: String(request.remark || ''),
|
||||
invoiceId: invoice.invoiceIds || numericId(`invoice-id:${supplierId}:${settlementId}`, 20),
|
||||
invoiceCode: invoice.invoiceCode,
|
||||
invoiceDate: invoice.invoiceDate?.toISOString().slice(0, 10),
|
||||
invoiceNakeAmount: Number(invoice.invoiceNakeAmount || 0),
|
||||
@@ -398,7 +464,7 @@ export const supplierOpenOrderService = {
|
||||
fileType: invoice.fileType || 'PDF',
|
||||
checkCode: invoice.checkCode,
|
||||
invoiceAddress: String(request.invoiceAddress || '')
|
||||
}] : []
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -445,17 +511,17 @@ export const supplierOpenOrderService = {
|
||||
if (!invoice || invoice.invoiceStatus !== 2 || (!invoice.fileId && !invoice.filePath)) {
|
||||
return { resultCode: 404, message: '未找到已开具的发票文件', fileType: 'PDF', imageEncodeMap }
|
||||
}
|
||||
if (fileIdList.some(fileId => !invoiceIds.includes(fileId))) {
|
||||
if (!fileIdList.length || invoiceIds.length !== 1 || fileIdList.some(fileId => fileId !== invoiceIds[0]) || (invoice.fileId && invoice.fileId !== invoiceIds[0])) {
|
||||
return { resultCode: 404, message: '发票号码与结算单不匹配', fileType: 'PDF', imageEncodeMap }
|
||||
}
|
||||
const pdf = invoice.fileId
|
||||
? await supplierFileService.read(invoice.fileId)
|
||||
? await supplierFileService.read(invoice.fileId, supplierId)
|
||||
: null
|
||||
if (!pdf && invoice.filePath) {
|
||||
const absoluteFilePath = path.resolve(process.cwd(), invoice.filePath)
|
||||
const outputRoot = invoiceOutputRoot()
|
||||
const relativePath = path.relative(outputRoot, absoluteFilePath)
|
||||
if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath) && existsSync(absoluteFilePath)) {
|
||||
if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath) && path.basename(absoluteFilePath) === `${invoiceIds[0]}.pdf` && existsSync(absoluteFilePath)) {
|
||||
const localPdf = readFileSync(absoluteFilePath)
|
||||
if (localPdf.length <= 3.5 * 1024 * 1024) {
|
||||
fileIdList.forEach(fileId => {
|
||||
|
||||
@@ -12,11 +12,49 @@ type BridgePointsResponse = {
|
||||
}
|
||||
|
||||
export const supplierService = {
|
||||
decryptSupplierSession(keyword: string) {
|
||||
async decryptSupplierSession(keyword: string) {
|
||||
if (!keyword) {
|
||||
return serviceFail('用户参数不能为空')
|
||||
}
|
||||
|
||||
if (/^[a-f0-9]{32}$/i.test(keyword)) {
|
||||
const apiBase = getBridgeApiBase()
|
||||
if (!apiBase) {
|
||||
return serviceFail('平台积分商城接口地址未配置', 502, 'BRIDGE_SESSION_FAILED')
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${apiBase}/connect/supplier-session`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ keyword })
|
||||
})
|
||||
const payload = (await response.json()) as {
|
||||
code?: number
|
||||
msg?: string
|
||||
data?: { userId?: unknown }
|
||||
}
|
||||
|
||||
if (!response.ok || payload.code !== 0) {
|
||||
return serviceFail(payload.msg || '获取用户会话失败')
|
||||
}
|
||||
|
||||
const userId = String(payload.data?.userId || '').trim()
|
||||
if (!userId) {
|
||||
return serviceFail('用户参数无效')
|
||||
}
|
||||
|
||||
return serviceOk({ userId })
|
||||
} catch (error) {
|
||||
logger.warn('supplier session exchange failed', {
|
||||
message: error instanceof Error ? error.message : String(error)
|
||||
})
|
||||
return serviceFail('获取用户会话失败', 502, 'BRIDGE_SESSION_FAILED')
|
||||
}
|
||||
}
|
||||
|
||||
const keyHex = process.env.SM4CBC_KEY_HEX || 'a1b2c3d4e5f60718293a4b5c6d7e8f90'
|
||||
const ivHex = process.env.SM4CBC_IV_HEX || 'f9e8d7c6b5a49382716f0e1d2c3b4a5f'
|
||||
const sessionKeyword = keyword.replace(/ /g, '+')
|
||||
|
||||
Reference in New Issue
Block a user