import { $fetch } from 'ofetch' import { createError, defineEventHandler, readBody } from 'h3' import { useRuntimeConfig } from '#imports' import { getTenantFromContext } from '../../../utils/tenant' import { getRealClientIp } from '../../../utils/ip' /** * 证件 OCR 识别(身份证人像面 / 营业执照) * POST /api/tender/ocr/recognize { materialType: 'idcard_front' | 'license', fileUrl } * 代理到 mp-api /api/hjc/ocr/recognize(匿名放行,注册页在登录前调用)。 * * OCR 未配置 AK 时后端返回失败,前端提示「识别失败,请手动填写」,不阻断提交。 */ export default defineEventHandler(async (event) => { const config = useRuntimeConfig() const ctx = getTenantFromContext(event, config) const body = await readBody(event) // 透传真实客户端 IP:后端按 IP 对匿名付费的 OCR 接口限流(注册页在登录前调用)。 // 不透传时后端 IpUtil 只能拿到 Nuxt 服务器的出口 IP,限流会退化成「整个官网共用一份配额」, // 正常用户反而被误限,而 H5 浏览器直连后端却能拿到真实 IP —— 同一策略两条链路行为相反。 const clientIp = getRealClientIp(event) try { const res = await $fetch('/hjc/ocr/recognize', { baseURL: config.public.modulesApiBase, method: 'POST', headers: { TenantId: ctx.tenantId, 'X-Forwarded-For': clientIp }, query: { TenantId: ctx.tenantId }, body }) as any return res && res.data !== undefined ? res.data : res } catch (error: any) { throw createError({ statusCode: error?.statusCode || error?.response?.status || 502, statusMessage: error?.data?.message || error?.statusMessage || '识别失败' }) } })