Files
points-mall/supplied-service/src/services/supplier.service.ts
T

222 lines
7.2 KiB
TypeScript

import { supplierDao } from '../dao/supplier.dao.js'
import { bridgeFetchWithAuth, findSupplierByIdentification, getBridgeApiBase, getBridgeToken, warmBridgeToken } from '../utils/bridge-auth.js'
import { sha256 } from '../utils/crypto.js'
import { logger } from '../utils/logger.js'
import { createSupplierOpenSignature, sm4CbcDecrypt } from '../utils/supplier-open-crypto.js'
import { serviceFail, serviceOk } from '../types/service-result.js'
type BridgePointsResponse = {
resultCode?: number
message?: string
userPoints?: number | string
}
export const supplierService = {
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, '+')
logger.info('supplier session decrypt input', {
keyword,
sessionKeyword,
hasKey: Boolean(keyHex),
hasIv: Boolean(ivHex),
keyLength: keyHex.length,
ivLength: ivHex.length
})
try {
const decryptedText = sm4CbcDecrypt(sessionKeyword, keyHex, ivHex)
logger.info('supplier session decrypt text', { decryptedText })
const payload = JSON.parse(decryptedText) as { userId?: unknown }
const userId = String(payload.userId || '').trim()
if (!userId) {
logger.warn('supplier session userId empty', { payload })
return serviceFail('用户参数无效')
}
return serviceOk({ userId })
} catch (error) {
logger.warn('supplier session decrypt failed', {
message: error instanceof Error ? error.message : String(error)
})
return serviceFail('用户参数无效')
}
},
async getSupplierInfo(supplierIdentification: string) {
logger.info('supplier info query', { supplierIdentification })
if (!supplierIdentification) {
return serviceFail('供应商唯一标识不能为空')
}
const supplier = await supplierDao.findActiveByIdentification(supplierIdentification)
if (!supplier) {
return serviceFail('供应商信息未配置或已停用', 404, 'SUPPLIER_NOT_CONFIGURED')
}
return serviceOk({
id: supplier.id,
supplierIdentification: supplier.supplierIdentification,
supplierId: supplier.supplierId,
supplierName: supplier.supplierName
})
},
async warmSupplierToken(userId: string, supplierIdentification: string) {
logger.info('supplier token warmup', { userId, supplierIdentification })
if (!userId || !supplierIdentification) {
return serviceFail('用户信息和供应商唯一标识不能为空')
}
const supplier = await findSupplierByIdentification(supplierIdentification).catch(error => error)
if (supplier instanceof Error) {
return serviceFail(supplier.message || '供应商信息未配置或已停用', 404, 'SUPPLIER_NOT_CONFIGURED')
}
try {
await warmBridgeToken(supplier)
} catch (error) {
return serviceFail(error instanceof Error ? error.message : '获取平台授权令牌失败', 502, 'BRIDGE_TOKEN_FAILED')
}
return serviceOk({
supplierIdentification,
supplierId: supplier.supplierId,
supplierName: supplier.supplierName,
userId
})
},
async getUserPoints(supplierId: string, userId: string) {
logger.info('supplier points query', { supplierId, userId })
if (!supplierId || !userId) {
return serviceFail('供应商或用户信息不能为空')
}
const { response, payload: pointsPayload } = await bridgeFetchWithAuth<BridgePointsResponse>(
supplierId,
'/pointOrder/queryPoints',
{ userId }
)
if (!response.ok || pointsPayload.resultCode !== 0) {
return serviceFail(pointsPayload.message || '查询用户积分失败', 502, 'BRIDGE_POINTS_FAILED')
}
return serviceOk({
userPoints: Number(pointsPayload.userPoints || 0)
})
},
async getDebugTokens(supplierIdentification: string, forceRefreshZhghToken: boolean) {
logger.info('supplier debug tokens query', { supplierIdentification, forceRefreshZhghToken })
if (!supplierIdentification) {
return serviceFail('供应商唯一标识不能为空')
}
const supplier = await findSupplierByIdentification(supplierIdentification).catch(error => error)
if (supplier instanceof Error) {
return serviceFail(supplier.message || '供应商信息未配置或已停用', 404, 'SUPPLIER_NOT_CONFIGURED')
}
const timestamp = Date.now()
const orderType = 2
const orderId = 'SUB_ORDER_ID_1,SUB_ORDER_ID_2'
const mainOrderId = 'MAIN_ORDER_ID'
const userId = 'USER_ID'
const supplierOpenToken = createSupplierOpenSignature(supplier.supplierId, supplier.clientId, timestamp)
const zhghBridgeToken = await getBridgeToken(supplier.supplierId, forceRefreshZhghToken)
return serviceOk({
supplierOpen: {
name: 'supplierOpenToken',
usage: '调用供应商开放接口 /supplierOpenApi/pointOrder/queryOrderInfo',
supplierId: supplier.supplierId,
clientId: supplier.clientId,
timestamp,
token: supplierOpenToken,
orderType,
orderId,
queryOrderInfoBody: {
supplierId: supplier.supplierId,
token: supplierOpenToken,
orderType,
orderId,
timestamp
}
},
zhghBridge: {
name: 'zhghBridgeToken',
usage: '调用 zhgh /open/pointOrder/* 桥接接口',
apiBase: getBridgeApiBase(),
supplierId: supplier.supplierId,
clientId: supplier.clientId,
timestamp,
sendSha256: sha256(`${supplier.clientId}${supplier.clientSecret}${timestamp}`),
token: zhghBridgeToken,
splitOrderBody: {
supplierId: supplier.supplierId,
token: zhghBridgeToken,
orderType,
orderId,
mainOrderId,
userId,
time: timestamp
}
}
})
}
}