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( 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 } } }) } }