拆分兑换凭证纯函数与结果写入
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
export const BAIDU_BEIJING_TIME_URL = 'https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E5%8C%97%E4%BA%AC%E6%97%B6%E9%97%B4&fenlei=256&rsv_pq=0xa7d529b30000a6eb&rsv_t=2d404COdLspTa8jnKQl%2FyCU%2BSCmRyb%2BvSqxxjk%2B7E1scokLYTuDbehaQ0Dvq&rqlang=en&rsv_enter=1&rsv_dl=ih_0&rsv_sug3=1&rsv_sug1=1&rsv_sug7=001&rsv_sug2=1&rsv_btype=i&rsp=0&rsv_sug9=es_2_1&rsv_sug=1'
|
||||||
|
export const BEIJING_TIME_PROOF_VIEWPORT = { width: 1440, height: 860 }
|
||||||
|
export const BEIJING_TIME_PROOF_CLIP = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: BEIJING_TIME_PROOF_VIEWPORT.width,
|
||||||
|
height: 620,
|
||||||
|
}
|
||||||
|
export const DEFAULT_REDEEM_PROOF_MODE = 'full'
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { runtimeConfig } from '../../config/runtime.js'
|
||||||
|
|
||||||
|
import { DEFAULT_REDEEM_PROOF_MODE } from './session-proof-constants.js'
|
||||||
|
|
||||||
|
export function resolveRedeemProofMode(rawMode = runtimeConfig.redeem.proofMode) {
|
||||||
|
const normalized = String(rawMode || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
|
||||||
|
if (normalized === 'basic' || normalized === 'off') {
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_REDEEM_PROOF_MODE
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldCaptureBeijingTimeProof(finalResult) {
|
||||||
|
if (finalResult?.classification?.success === true) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalResult?.classification?.success === false) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const retCode = Number(finalResult?.redeem?.iRet ?? finalResult?.redeem?.ret)
|
||||||
|
return Number.isFinite(retCode) && retCode === 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
export function buildArtifactPaths(outputDir) {
|
||||||
|
return {
|
||||||
|
redeemPageScreenshotPath: path.join(outputDir, 'redeem-page.png'),
|
||||||
|
beijingTimeScreenshotPath: path.join(outputDir, 'beijing-time.png'),
|
||||||
|
screenshotPath: path.join(outputDir, 'redeem-result.png'),
|
||||||
|
htmlPath: path.join(outputDir, 'page.html'),
|
||||||
|
resultPath: path.join(outputDir, 'result.json'),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import fs from 'node:fs/promises'
|
||||||
|
|
||||||
|
import { BAIDU_BEIJING_TIME_URL } from './session-proof-constants.js'
|
||||||
|
|
||||||
|
export async function writeRedeemResultFile(
|
||||||
|
resultPath,
|
||||||
|
{
|
||||||
|
proofMode,
|
||||||
|
sessionId,
|
||||||
|
activityUrl,
|
||||||
|
code,
|
||||||
|
finalResult,
|
||||||
|
attempts,
|
||||||
|
screenshotPath,
|
||||||
|
htmlPath = '',
|
||||||
|
redeemPageScreenshotPath = '',
|
||||||
|
beijingTimeScreenshotPath = '',
|
||||||
|
beijingTimePageUrl = BAIDU_BEIJING_TIME_URL,
|
||||||
|
beijingTimeError = '',
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
await fs.writeFile(
|
||||||
|
resultPath,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
proofMode,
|
||||||
|
sessionId,
|
||||||
|
activityUrl,
|
||||||
|
code,
|
||||||
|
area: finalResult?.role?.area || '',
|
||||||
|
final: finalResult,
|
||||||
|
attempts,
|
||||||
|
redeemPageScreenshotPath,
|
||||||
|
beijingTimeScreenshotPath,
|
||||||
|
beijingTimePageUrl,
|
||||||
|
beijingTimeError,
|
||||||
|
screenshotPath,
|
||||||
|
htmlPath,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
'utf8',
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import fs from 'node:fs/promises'
|
import fs from 'node:fs/promises'
|
||||||
import path from 'node:path'
|
|
||||||
|
|
||||||
import { runtimeConfig } from '../../config/runtime.js'
|
import {
|
||||||
|
BAIDU_BEIJING_TIME_URL,
|
||||||
const BAIDU_BEIJING_TIME_URL = 'https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E5%8C%97%E4%BA%AC%E6%97%B6%E9%97%B4&fenlei=256&rsv_pq=0xa7d529b30000a6eb&rsv_t=2d404COdLspTa8jnKQl%2FyCU%2BSCmRyb%2BvSqxxjk%2B7E1scokLYTuDbehaQ0Dvq&rqlang=en&rsv_enter=1&rsv_dl=ih_0&rsv_sug3=1&rsv_sug1=1&rsv_sug7=001&rsv_sug2=1&rsv_btype=i&rsp=0&rsv_sug9=es_2_1&rsv_sug=1'
|
BEIJING_TIME_PROOF_CLIP,
|
||||||
const BEIJING_TIME_PROOF_VIEWPORT = { width: 1440, height: 860 }
|
BEIJING_TIME_PROOF_VIEWPORT,
|
||||||
const BEIJING_TIME_PROOF_CLIP = {
|
DEFAULT_REDEEM_PROOF_MODE,
|
||||||
x: 0,
|
} from './session-proof-constants.js'
|
||||||
y: 0,
|
import { resolveRedeemProofMode, shouldCaptureBeijingTimeProof } from './session-proof-mode.js'
|
||||||
width: BEIJING_TIME_PROOF_VIEWPORT.width,
|
import { buildArtifactPaths } from './session-proof-paths.js'
|
||||||
height: 620,
|
import { writeRedeemResultFile } from './session-proof-result-writer.js'
|
||||||
}
|
|
||||||
const DEFAULT_REDEEM_PROOF_MODE = 'full'
|
|
||||||
|
|
||||||
export async function saveRedeemArtifacts({
|
export async function saveRedeemArtifacts({
|
||||||
browserContext,
|
browserContext,
|
||||||
@@ -145,82 +142,7 @@ export async function writeRedeemArtifactFiles({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveRedeemProofMode(rawMode = runtimeConfig.redeem.proofMode) {
|
export { resolveRedeemProofMode, shouldCaptureBeijingTimeProof }
|
||||||
const normalized = String(rawMode || '')
|
|
||||||
.trim()
|
|
||||||
.toLowerCase()
|
|
||||||
|
|
||||||
if (normalized === 'basic' || normalized === 'off') {
|
|
||||||
return normalized
|
|
||||||
}
|
|
||||||
|
|
||||||
return DEFAULT_REDEEM_PROOF_MODE
|
|
||||||
}
|
|
||||||
|
|
||||||
export function shouldCaptureBeijingTimeProof(finalResult) {
|
|
||||||
if (finalResult?.classification?.success === true) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finalResult?.classification?.success === false) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const retCode = Number(finalResult?.redeem?.iRet ?? finalResult?.redeem?.ret)
|
|
||||||
return Number.isFinite(retCode) && retCode === 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildArtifactPaths(outputDir) {
|
|
||||||
return {
|
|
||||||
redeemPageScreenshotPath: path.join(outputDir, 'redeem-page.png'),
|
|
||||||
beijingTimeScreenshotPath: path.join(outputDir, 'beijing-time.png'),
|
|
||||||
screenshotPath: path.join(outputDir, 'redeem-result.png'),
|
|
||||||
htmlPath: path.join(outputDir, 'page.html'),
|
|
||||||
resultPath: path.join(outputDir, 'result.json'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeRedeemResultFile(
|
|
||||||
resultPath,
|
|
||||||
{
|
|
||||||
proofMode,
|
|
||||||
sessionId,
|
|
||||||
activityUrl,
|
|
||||||
code,
|
|
||||||
finalResult,
|
|
||||||
attempts,
|
|
||||||
screenshotPath,
|
|
||||||
htmlPath = '',
|
|
||||||
redeemPageScreenshotPath = '',
|
|
||||||
beijingTimeScreenshotPath = '',
|
|
||||||
beijingTimePageUrl = BAIDU_BEIJING_TIME_URL,
|
|
||||||
beijingTimeError = '',
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
await fs.writeFile(
|
|
||||||
resultPath,
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
proofMode,
|
|
||||||
sessionId,
|
|
||||||
activityUrl,
|
|
||||||
code,
|
|
||||||
area: finalResult?.role?.area || '',
|
|
||||||
final: finalResult,
|
|
||||||
attempts,
|
|
||||||
redeemPageScreenshotPath,
|
|
||||||
beijingTimeScreenshotPath,
|
|
||||||
beijingTimePageUrl,
|
|
||||||
beijingTimeError,
|
|
||||||
screenshotPath,
|
|
||||||
htmlPath,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
'utf8',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showResultDialog(page, message) {
|
async function showResultDialog(page, message) {
|
||||||
await page.evaluate((text) => {
|
await page.evaluate((text) => {
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
import test from 'node:test'
|
import test from 'node:test'
|
||||||
import assert from 'node:assert/strict'
|
import assert from 'node:assert/strict'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
import { shouldCaptureBeijingTimeProof } from './session-proof.js'
|
import { resolveRedeemProofMode, shouldCaptureBeijingTimeProof } from './session-proof.js'
|
||||||
|
import { buildArtifactPaths } from './session-proof-paths.js'
|
||||||
|
|
||||||
|
test('resolveRedeemProofMode normalizes configured modes and falls back to full', () => {
|
||||||
|
assert.equal(resolveRedeemProofMode('basic'), 'basic')
|
||||||
|
assert.equal(resolveRedeemProofMode(' OFF '), 'off')
|
||||||
|
assert.equal(resolveRedeemProofMode('full'), 'full')
|
||||||
|
assert.equal(resolveRedeemProofMode('unexpected'), 'full')
|
||||||
|
assert.equal(resolveRedeemProofMode(''), 'full')
|
||||||
|
})
|
||||||
|
|
||||||
test('shouldCaptureBeijingTimeProof only returns true for successful redeem results', () => {
|
test('shouldCaptureBeijingTimeProof only returns true for successful redeem results', () => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
@@ -42,3 +52,13 @@ test('shouldCaptureBeijingTimeProof only returns true for successful redeem resu
|
|||||||
|
|
||||||
assert.equal(shouldCaptureBeijingTimeProof(null), false)
|
assert.equal(shouldCaptureBeijingTimeProof(null), false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('buildArtifactPaths keeps redeem proof artifact filenames stable', () => {
|
||||||
|
const artifactPaths = buildArtifactPaths('/tmp/redeem-proof')
|
||||||
|
|
||||||
|
assert.equal(artifactPaths.redeemPageScreenshotPath, path.join('/tmp/redeem-proof', 'redeem-page.png'))
|
||||||
|
assert.equal(artifactPaths.beijingTimeScreenshotPath, path.join('/tmp/redeem-proof', 'beijing-time.png'))
|
||||||
|
assert.equal(artifactPaths.screenshotPath, path.join('/tmp/redeem-proof', 'redeem-result.png'))
|
||||||
|
assert.equal(artifactPaths.htmlPath, path.join('/tmp/redeem-proof', 'page.html'))
|
||||||
|
assert.equal(artifactPaths.resultPath, path.join('/tmp/redeem-proof', 'result.json'))
|
||||||
|
})
|
||||||
|
|||||||
+339
@@ -0,0 +1,339 @@
|
|||||||
|
# 前后端现状分析
|
||||||
|
|
||||||
|
## 当前结论
|
||||||
|
|
||||||
|
整体架构方向没有跑偏,后端主干仍然是 `routes -> services -> repositories`,前端也已经逐步从“巨石页面”往“薄页面 + composable + 子组件”推进。
|
||||||
|
|
||||||
|
现在最需要继续优化的,不是全项目平均整理,而是两类高风险区域:
|
||||||
|
|
||||||
|
1. 已经明显长成巨石、后续改动容易连锁的后端运行时模块
|
||||||
|
2. 已经完成第一轮拆分、但还需要收口和补测试的前端后台配置模块
|
||||||
|
|
||||||
|
## 已完成
|
||||||
|
|
||||||
|
### 平台配置页前端拆分
|
||||||
|
|
||||||
|
这部分已经不再是最初判断里的“一个页面承载四套完整平台流”。
|
||||||
|
|
||||||
|
- `8e1a4be` 拆分前端后台管理 API 客户端
|
||||||
|
- `apps/frontend/src/services/admin.ts` 已改成聚合入口
|
||||||
|
- 后台 API 已按领域拆到 `services/admin/` 目录
|
||||||
|
|
||||||
|
- `dff7da7` 拆分平台配置页脚本逻辑
|
||||||
|
- `AdminPlatformShopsView.vue` 的平台状态和行为已经拆到多个 composable
|
||||||
|
- 已有:
|
||||||
|
- `useAdminAgisoPlatform.ts`
|
||||||
|
- `useAdminKhhaoPlatform.ts`
|
||||||
|
- `useAdminKuaishouEticketPlatform.ts`
|
||||||
|
- `useAdminCloudtentaclesPlatform.ts`
|
||||||
|
|
||||||
|
- `4650c3e` 抽离平台配置页共享样式
|
||||||
|
- 共享样式已统一收口到 `apps/frontend/src/styles/admin-platform-shops.css`
|
||||||
|
|
||||||
|
- `60c2d91` 拆分平台配置页 Agiso 与 khhao 区块
|
||||||
|
- `254a031` 拆分平台配置页快手核销区块
|
||||||
|
- `8f6a72f` 拆分平台配置页履约调试区块
|
||||||
|
- 四个平台区块已经分别下沉为独立组件:
|
||||||
|
- `AdminPlatformAgisoSection.vue`
|
||||||
|
- `AdminPlatformKhhaoSection.vue`
|
||||||
|
- `AdminPlatformKuaishouEticketSection.vue`
|
||||||
|
- `AdminPlatformCloudtentaclesSection.vue`
|
||||||
|
|
||||||
|
- `58f25fa` 抽离平台配置结果信息卡片
|
||||||
|
- 新增 `AdminResultCard.vue`
|
||||||
|
- `khhao` / `快手核销` / `cloudtentacles` 的重复结果卡片已统一
|
||||||
|
|
||||||
|
### 现阶段对平台配置页的重新判断
|
||||||
|
|
||||||
|
`apps/frontend/src/views/admin/AdminPlatformShopsView.vue` 仍然不算小,但问题性质已经变了:
|
||||||
|
|
||||||
|
- 现在主要问题不再是“大模板混杂”
|
||||||
|
- 现在的主要问题是:
|
||||||
|
- 父页面 `script setup` 仍然偏胖
|
||||||
|
- 几个子组件之间还有少量可复用片段
|
||||||
|
- 前端缺少测试防线
|
||||||
|
|
||||||
|
也就是说,这条线的第一阶段“拆分大页面”已经基本完成,后面不应该继续无限抽象,而应该适时收口。
|
||||||
|
|
||||||
|
## 已过时的判断
|
||||||
|
|
||||||
|
下面这些结论需要更新,不再按最初版本理解:
|
||||||
|
|
||||||
|
### `AdminPlatformShopsView.vue` 是前端第一优先级
|
||||||
|
|
||||||
|
这个判断已经部分过时。
|
||||||
|
|
||||||
|
原因:
|
||||||
|
|
||||||
|
- 该页面的四套平台模板已经完成拆分
|
||||||
|
- 平台行为已经移动到 composable
|
||||||
|
- 剩余问题主要是“收口”和“测试”,不是继续大拆
|
||||||
|
|
||||||
|
新判断:
|
||||||
|
|
||||||
|
- 这条线仍可继续小步优化
|
||||||
|
- 但优先级已经低于后端的 `session.js`、`session-proof.js`、`admin-platform-config-service.js`
|
||||||
|
|
||||||
|
### 继续深挖前端平台配置页
|
||||||
|
|
||||||
|
这个方向不应该再作为主战场。
|
||||||
|
|
||||||
|
剩余可做项有,但应该控制强度:
|
||||||
|
|
||||||
|
- 可以继续抽 `section-title-row` 或 `meta-card` 这一层公共片段
|
||||||
|
- 但不能为了统一而统一
|
||||||
|
- 再往下更应该转向后端高风险链路
|
||||||
|
|
||||||
|
## 仍然优先拆分 / 优先优化
|
||||||
|
|
||||||
|
### 1. `apps/backend/src/services/session/session.js`
|
||||||
|
|
||||||
|
后端第一优先级,判断不变。
|
||||||
|
|
||||||
|
它同时承担:
|
||||||
|
|
||||||
|
- Playwright 浏览器生命周期
|
||||||
|
- 会话内存状态
|
||||||
|
- 二维码抓取
|
||||||
|
- 登录态判断
|
||||||
|
- 角色信息同步
|
||||||
|
- 兑换执行
|
||||||
|
- 截图产物
|
||||||
|
- 自动关闭
|
||||||
|
|
||||||
|
而且仍然是 `@ts-nocheck`。这类文件每继续增长一次,后面改动成本都会上升。
|
||||||
|
|
||||||
|
建议目标:
|
||||||
|
|
||||||
|
- `browser-runtime`
|
||||||
|
- `session-store`
|
||||||
|
- `session-presentation`
|
||||||
|
- `session-redeem-orchestrator`
|
||||||
|
- `session-persistence`
|
||||||
|
|
||||||
|
### 2. `apps/backend/src/services/session/session-proof.js`
|
||||||
|
|
||||||
|
这是下一步最合适的切入点。
|
||||||
|
|
||||||
|
当前职责混在一起:
|
||||||
|
|
||||||
|
- 证明模式判定
|
||||||
|
- 产物路径拼接
|
||||||
|
- 结果页 DOM 注入
|
||||||
|
- 北京时间来源抓取
|
||||||
|
- 截图拼接
|
||||||
|
- 结果 JSON 落盘
|
||||||
|
|
||||||
|
这类文件比 `session.js` 更小,适合作为后端拆分的第一刀,风险更低,能先把拆分节奏跑顺。
|
||||||
|
|
||||||
|
建议拆分方向:
|
||||||
|
|
||||||
|
- `proof-mode`
|
||||||
|
- 处理 `resolveRedeemProofMode`
|
||||||
|
- 处理 `shouldCaptureBeijingTimeProof`
|
||||||
|
|
||||||
|
- `proof-paths`
|
||||||
|
- 处理 `buildArtifactPaths`
|
||||||
|
|
||||||
|
- `proof-result-writer`
|
||||||
|
- 处理 `writeRedeemResultFile`
|
||||||
|
|
||||||
|
- `proof-beijing-time`
|
||||||
|
- 处理北京时间页面抓取与截图
|
||||||
|
|
||||||
|
- `proof-renderer`
|
||||||
|
- 处理结果弹层、合成图、截图表现逻辑
|
||||||
|
|
||||||
|
先保留 `session-proof.js` 作为门面导出层,第一阶段不要改公开接口。
|
||||||
|
|
||||||
|
### 3. `apps/backend/src/services/admin/admin-platform-config-service.js`
|
||||||
|
|
||||||
|
这条线依然偏胖,而且和前端平台配置页是一整条竖线。
|
||||||
|
|
||||||
|
虽然前端页面已经拆开,但后端这条 service 仍然同时承载:
|
||||||
|
|
||||||
|
- 平台配置读写
|
||||||
|
- 登录测试
|
||||||
|
- 订单查询
|
||||||
|
- SKU 调试
|
||||||
|
- 虚拟号调试
|
||||||
|
- 履约绑定
|
||||||
|
|
||||||
|
建议后续按平台拆为:
|
||||||
|
|
||||||
|
- `agiso-admin-service`
|
||||||
|
- `khhao-admin-service`
|
||||||
|
- `kuaishou-eticket-admin-service`
|
||||||
|
- `cloudtentacles-admin-service`
|
||||||
|
|
||||||
|
同时把“配置读写”和“调试接口”分层处理。
|
||||||
|
|
||||||
|
### 4. `apps/backend/src/services/claim/claim-session-service.js`
|
||||||
|
|
||||||
|
仍然是后续重点。
|
||||||
|
|
||||||
|
问题主要在:
|
||||||
|
|
||||||
|
- 管理态 / 用户态创建逻辑平行复制
|
||||||
|
- claim token、浏览器会话、任务同步、库存释放、自动发货收尾混在一起
|
||||||
|
|
||||||
|
建议方向:
|
||||||
|
|
||||||
|
- `claim-context`
|
||||||
|
- `claim-session-lifecycle`
|
||||||
|
- `claim-task-sync`
|
||||||
|
- `claim-fulfillment-finalizer`
|
||||||
|
|
||||||
|
### 5. `apps/backend/src/services/admin/admin-write-service.js`
|
||||||
|
|
||||||
|
依然偏胖,但优先级略低于上面三项。
|
||||||
|
|
||||||
|
它更像“后台万能动作总线”,跨域过多:
|
||||||
|
|
||||||
|
- 库存
|
||||||
|
- claim
|
||||||
|
- webhook
|
||||||
|
- cloudtentacles
|
||||||
|
- 快手核销
|
||||||
|
- 腾讯 session
|
||||||
|
|
||||||
|
建议后续至少拆成:
|
||||||
|
|
||||||
|
- `admin-inventory-write`
|
||||||
|
- `admin-task-actions`
|
||||||
|
- `admin-webhook-actions`
|
||||||
|
- `admin-manual-redeem-actions`
|
||||||
|
|
||||||
|
### 6. `apps/backend/src/services/webhook/webhook-service.js`
|
||||||
|
|
||||||
|
仍然适合改成流水线。
|
||||||
|
|
||||||
|
建议拆成:
|
||||||
|
|
||||||
|
- `webhook-parse`
|
||||||
|
- `webhook-verify`
|
||||||
|
- `webhook-ignore-policy`
|
||||||
|
- `webhook-enrich`
|
||||||
|
- `webhook-process`
|
||||||
|
- `webhook-replay`
|
||||||
|
|
||||||
|
### 7. `apps/backend/src/config/runtime.js`
|
||||||
|
|
||||||
|
仍需要“配置声明化”优化。
|
||||||
|
|
||||||
|
目前问题不是功能错,而是长期可维护性差:
|
||||||
|
|
||||||
|
- env override 越堆越长
|
||||||
|
- 新平台配置接入成本越来越高
|
||||||
|
- 难测、难查、难对照
|
||||||
|
|
||||||
|
建议后期改成:
|
||||||
|
|
||||||
|
- 配置 schema
|
||||||
|
- env 映射表
|
||||||
|
- 默认值与来源声明
|
||||||
|
|
||||||
|
## 质量防线现状
|
||||||
|
|
||||||
|
这个判断仍然成立,而且重要性上升了。
|
||||||
|
|
||||||
|
当前风险:
|
||||||
|
|
||||||
|
- 后端高风险模块仍然存在 `@ts-nocheck`
|
||||||
|
- 前端后台配置页最近已经连续多次重构
|
||||||
|
- 目前主要依靠 `typecheck + build` 防回归
|
||||||
|
|
||||||
|
这能挡住:
|
||||||
|
|
||||||
|
- 类型问题
|
||||||
|
- 组件引用问题
|
||||||
|
- 构建问题
|
||||||
|
|
||||||
|
但挡不住:
|
||||||
|
|
||||||
|
- 交互退化
|
||||||
|
- 条件渲染错误
|
||||||
|
- 任务状态流转异常
|
||||||
|
- 外部平台调试链路断裂
|
||||||
|
|
||||||
|
建议至少补两层最小防线:
|
||||||
|
|
||||||
|
1. 前端
|
||||||
|
- 平台配置页基础渲染 smoke 测试
|
||||||
|
- 关键按钮存在性 / 条件渲染测试
|
||||||
|
|
||||||
|
2. 后端
|
||||||
|
- `session-proof.js` 纯函数单测
|
||||||
|
- 快手 cloud / claim / task 状态流转的集成 smoke 测试
|
||||||
|
|
||||||
|
## 暂时不用急着拆
|
||||||
|
|
||||||
|
### 腾讯浏览器前端链路
|
||||||
|
|
||||||
|
- `TencentBrowserView.vue`
|
||||||
|
- `session-qq.js`
|
||||||
|
- 对应 `wx` 变体
|
||||||
|
|
||||||
|
这些区域当前模式反而比较健康,后续可以作为其它模块拆分时的参照。
|
||||||
|
|
||||||
|
## 下一步计划
|
||||||
|
|
||||||
|
### 已完成阶段
|
||||||
|
|
||||||
|
1. 拆分前端后台管理 API 客户端
|
||||||
|
2. 拆分平台配置页脚本逻辑
|
||||||
|
3. 抽离平台配置页共享样式
|
||||||
|
4. 拆分 Agiso / khhao / 快手核销 / cloudtentacles 四个平台区块
|
||||||
|
5. 抽离结果信息卡片
|
||||||
|
|
||||||
|
### 下一阶段
|
||||||
|
|
||||||
|
#### Phase 1:从 `session-proof.js` 开始
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 先拆一个比 `session.js` 更小、更收敛的后端高风险文件
|
||||||
|
- 建立后端拆分模板
|
||||||
|
- 不改公开接口,不改产物格式,不改调用方
|
||||||
|
|
||||||
|
建议步骤:
|
||||||
|
|
||||||
|
1. 抽纯函数
|
||||||
|
- proof mode
|
||||||
|
- proof path
|
||||||
|
- result writer
|
||||||
|
|
||||||
|
2. 抽副作用模块
|
||||||
|
- 北京时间截图采集
|
||||||
|
- 证明图合成
|
||||||
|
|
||||||
|
3. 保留门面文件
|
||||||
|
- `session-proof.js` 暂时继续导出原接口
|
||||||
|
|
||||||
|
4. 每个小阶段都跑校验并单独提交
|
||||||
|
|
||||||
|
#### Phase 2:再处理 `session.js`
|
||||||
|
|
||||||
|
前提:
|
||||||
|
|
||||||
|
- `session-proof.js` 拆分完成
|
||||||
|
- 对这条运行时链路已经熟悉
|
||||||
|
|
||||||
|
#### Phase 3:回到平台配置后端竖线
|
||||||
|
|
||||||
|
重点转向:
|
||||||
|
|
||||||
|
- `admin-platform-config-service.js`
|
||||||
|
- 相关 route
|
||||||
|
- 相关调试接口
|
||||||
|
|
||||||
|
## 当前建议
|
||||||
|
|
||||||
|
前端平台配置页这条线可以暂时收口,不建议再继续大规模抽模板。
|
||||||
|
|
||||||
|
下一步最合适的是:
|
||||||
|
|
||||||
|
1. 开始拆 `apps/backend/src/services/session/session-proof.js`
|
||||||
|
2. 同时补一层最小测试防线
|
||||||
|
|
||||||
|
如果继续执行,下一刀就从 `session-proof.js` 开始。
|
||||||
Reference in New Issue
Block a user