后端: - 新增 leai 模块:认证、Webhook、数据同步、定时对账 - 新增 LeaiConfig/RestTemplateConfig/SchedulingConfig 配置 - 新增 FlywayRepairConfig 处理迁移修复 - 新增 V5__leai_integration.sql 迁移脚本 - 扩展所有实体类添加 tenantId 等字段 - 更新 SecurityConfig 放行 leai 公开接口 - 添加 application-test.yml 测试环境配置 前端: - 添加乐读派认证 API (public.ts) - 优化 Generating.vue 生成页 - 添加 Playwright E2E 测试配置及依赖 - 添加测试 fixtures、utils、mock-h5.html - 添加 leai 模块完整 E2E 测试套件 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
import { test as authTest, expect, type APIRequestContext } from './auth.fixture'
|
||
import {
|
||
buildStatusPayload,
|
||
buildProgressPayload,
|
||
buildCompletedPayload,
|
||
buildDubbedPayload,
|
||
buildFailedPayload,
|
||
randomWorkId,
|
||
randomEventId,
|
||
API_BASE,
|
||
} from '../utils/webhook-helper'
|
||
|
||
/**
|
||
* 乐读派集成测试专用 Fixture
|
||
* 封装 Webhook 发送、乐读派 Token API 调用等
|
||
*/
|
||
|
||
type LeaiFixtures = {
|
||
/** 发送 Webhook 请求到后端 */
|
||
sendWebhook: (
|
||
payload: Record<string, unknown>,
|
||
options?: {
|
||
eventType?: string
|
||
validSignature?: boolean
|
||
timestamp?: string
|
||
},
|
||
) => Promise<{ status: number; body: Record<string, unknown> }>
|
||
/** 乐读派 Token API(需登录) */
|
||
leaiTokenApi: APIRequestContext
|
||
/** Webhook API(无需登录) */
|
||
webhookApi: APIRequestContext
|
||
}
|
||
|
||
export const test = authTest.extend<LeaiFixtures>({
|
||
|
||
sendWebhook: async ({ request }, use) => {
|
||
const sendWebhook = async (
|
||
payload: Record<string, unknown>,
|
||
options: {
|
||
eventType?: string
|
||
validSignature?: boolean
|
||
timestamp?: string
|
||
} = {},
|
||
) => {
|
||
const eventId = options.eventType === 'duplicate'
|
||
? 'evt_duplicate_test'
|
||
: randomEventId()
|
||
|
||
// 这里简化签名验证:直接构造请求
|
||
// 实际签名需要与 LeaiApiClient.verifyWebhookSignature 对齐
|
||
const crypto = await import('crypto')
|
||
const body = JSON.stringify(payload)
|
||
const timestamp = options.timestamp || Date.now().toString()
|
||
const appSecret = 'leai_mnoi9q1a_mtcawrn8y'
|
||
|
||
const signData = `${eventId}.${timestamp}.${body}`
|
||
const signature = 'HMAC-SHA256=' + crypto.createHmac('sha256', appSecret).update(signData).digest('hex')
|
||
|
||
const resp = await request.post(`${API_BASE}/webhook/leai`, {
|
||
headers: {
|
||
'X-Webhook-Id': eventId,
|
||
'X-Webhook-Event': options.eventType || 'work.status_changed',
|
||
'X-Webhook-Timestamp': timestamp,
|
||
'X-Webhook-Signature': options.validSignature === false
|
||
? 'HMAC-SHA256=invalid_signature'
|
||
: signature,
|
||
'Content-Type': 'application/json',
|
||
},
|
||
data: payload,
|
||
})
|
||
|
||
return {
|
||
status: resp.status(),
|
||
body: await resp.json().catch(() => ({})),
|
||
}
|
||
}
|
||
await use(sendWebhook)
|
||
},
|
||
|
||
leaiTokenApi: async ({ authedApi }, use) => {
|
||
await use(authedApi)
|
||
},
|
||
|
||
webhookApi: async ({ request }, use) => {
|
||
await use(request)
|
||
},
|
||
})
|
||
|
||
export { expect }
|
||
export {
|
||
buildStatusPayload,
|
||
buildProgressPayload,
|
||
buildCompletedPayload,
|
||
buildDubbedPayload,
|
||
buildFailedPayload,
|
||
randomWorkId,
|
||
randomEventId,
|
||
}
|