feat: 迁移渠道与游戏统计及全量汇总
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
"""只读检查渠道/游戏统计接口,区分空结果与业务表未接入。"""
|
||||
import argparse
|
||||
import json
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--base-url", default="http://127.0.0.1:18000")
|
||||
parser.add_argument("--token-file", required=True, type=Path)
|
||||
parser.add_argument("--output", required=True, type=Path)
|
||||
args = parser.parse_args()
|
||||
token = args.token_file.read_text(encoding="utf-8").strip()
|
||||
checks = []
|
||||
for kind in ["channelData", "gameData"]:
|
||||
for query, expected in [("sourceDb=lx,hw,yq&pageNum=1&pageSize=10", 0), ("beginTime=bad&endTime=2026-11-04", 7), ("beginTime=2026-11-05&endTime=2026-11-04", 7)]:
|
||||
path = f"/sw/{kind}/list?{query}"
|
||||
req = urllib.request.Request(args.base_url + path, headers={"x-token": token})
|
||||
with urllib.request.urlopen(req, timeout=30) as response:
|
||||
payload = json.load(response)
|
||||
data = payload.get("data", {})
|
||||
item = {"path": path, "http": response.status, "code": payload.get("code"), "msg": payload.get("msg"), "passed": payload.get("code") == expected}
|
||||
if "available" in data:
|
||||
item.update(available=data["available"], total=data.get("total"), begin=data.get("beginTime"), end=data.get("endTime"))
|
||||
if data["available"] is False:
|
||||
item["passed"] &= data.get("summary") is None
|
||||
checks.append(item)
|
||||
report = {"scope": "真实 sw7 会话,只读请求;完整统计及权限口径另由隔离 TCP HTTP 测试验证。", "checks": checks, "passed": all(item["passed"] for item in checks)}
|
||||
args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
print(json.dumps(report, ensure_ascii=False))
|
||||
if not report["passed"]:
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,62 @@
|
||||
// 真实 Chrome 核对统计筛选、排序、分页和全量汇总;业务接口使用受控数据。
|
||||
const fs = require('node:fs/promises')
|
||||
const path = require('node:path')
|
||||
const assert = require('node:assert/strict')
|
||||
const { chromium } = require(process.env.SW_PLAYWRIGHT_MODULE || 'playwright')
|
||||
|
||||
;(async () => {
|
||||
const token = (await fs.readFile(path.join(process.env.TEMP, 'sw-kra-http-test-token'), 'utf8')).trim()
|
||||
const browser = await chromium.launch({ channel: 'chrome', headless: true })
|
||||
const reports = []
|
||||
try {
|
||||
for (const kind of ['channel', 'game']) {
|
||||
const context = await browser.newContext({ viewport: { width: 1600, height: 1000 } })
|
||||
await context.addInitScript(value => localStorage.setItem('token', value), token)
|
||||
const page = await context.newPage(), queries = [], errors = []
|
||||
let unavailable = false
|
||||
const routeName = kind === 'channel' ? 'channelData' : 'gameData'
|
||||
page.on('pageerror', error => errors.push(error.message))
|
||||
await page.route('http://127.0.0.1:8080/api/**', async route => {
|
||||
const request = route.request(), url = new URL(request.url()), api = url.pathname.slice(4)
|
||||
if (api === `/sw/${routeName}/list`) {
|
||||
queries.push(Object.fromEntries(url.searchParams))
|
||||
const pageNum = Number(url.searchParams.get('pageNum') || 1)
|
||||
const metrics = { newRegisterCount: 2, activeCount: 4, payUserCount: 1, newPayUserCount: 1, totalPay: '10.00', payRate: '25.00', arpu: '2.50', arppu: '10.00' }
|
||||
const row = { ...metrics, sourceDb: 'lx', sourceDbLabel: '龙翔', promoteId: pageNum, promoteName: `测试渠道${pageNum}`, promoteType: 1, busierName: '游戏商务', gameId: pageNum, gameName: `测试游戏${pageNum}` }
|
||||
const summary = { ...metrics, gameName: '全部游戏', promoteName: '全部渠道', newRegisterCount: 60, activeCount: 100, payUserCount: 20, newPayUserCount: 10, totalPay: '1000.00', payRate: '20.00', arpu: '10.00', arppu: '50.00' }
|
||||
return route.fulfill({ json: { code: 0, data: { rows: unavailable ? [] : [row], total: unavailable ? 0 : 21, channelCount: unavailable ? null : 21, totalRegisterCount: unavailable ? null : 60, totalPayAmount: unavailable ? null : '1000.00', avgPayRate: unavailable ? null : '20.00', summary: unavailable ? null : summary, beginTime: url.searchParams.get('beginTime'), endTime: url.searchParams.get('endTime'), available: !unavailable } } })
|
||||
}
|
||||
if (api === '/sw/serverAnalysis/games') return route.fulfill({ json: { code: 0, data: [{ sourceDb: 'lx', gameId: 1, gameName: '筛选游戏' }] } })
|
||||
const response = await route.fetch({ url: 'http://127.0.0.1:18000' + api + url.search, headers: { ...request.headers(), 'x-token': token } })
|
||||
return route.fulfill({ response })
|
||||
})
|
||||
await page.goto(`http://127.0.0.1:8080/#/layout/sw-DataAnalysis/${kind === 'channel' ? 'ChannelDataStatistics' : 'GameAnalytics'}`)
|
||||
await page.getByText(kind === 'channel' ? '测试渠道1' : '测试游戏1', { exact: true }).waitFor()
|
||||
assert.equal((Date.parse(queries[0].endTime) - Date.parse(queries[0].beginTime)) / 86400000, 29)
|
||||
const totals = kind === 'channel' ? page.locator('.report-cards') : page.locator('.el-table__footer-wrapper')
|
||||
await totals.getByText('¥1,000.00', { exact: true }).waitFor()
|
||||
assert.equal(await page.getByRole('button', { name: '导出', exact: true }).count(), 0)
|
||||
await page.locator('.el-pagination .btn-next').click()
|
||||
await page.getByText(kind === 'channel' ? '测试渠道2' : '测试游戏2', { exact: true }).waitFor()
|
||||
assert.equal(queries.at(-1).pageNum, '2')
|
||||
await totals.getByText('¥1,000.00', { exact: true }).waitFor()
|
||||
const form = page.locator('.kra-search-box')
|
||||
await form.locator('.el-form-item').filter({ has: page.locator('label').getByText('游戏', { exact: true }) }).locator('.el-select__wrapper').click()
|
||||
await page.getByRole('option', { name: '筛选游戏', exact: true }).click(); await page.keyboard.press('Escape')
|
||||
if (kind === 'channel') await form.getByPlaceholder('渠道账号(模糊匹配)', { exact: true }).fill('测试')
|
||||
await Promise.all([page.waitForResponse(response => response.url().includes(`/sw/${routeName}/list`) && response.url().includes('gameName=')), form.getByRole('button', { name: kind === 'channel' ? '搜索' : '查询', exact: true }).click()])
|
||||
assert.equal(queries.at(-1).pageNum, '1'); assert.equal(queries.at(-1).gameName, '筛选游戏')
|
||||
if (kind === 'channel') assert.equal(queries.at(-1).promoteName, '测试')
|
||||
await page.locator('.el-loading-mask').first().waitFor({ state: 'hidden' })
|
||||
await page.screenshot({ path: path.join(process.env.TEMP, `sw-${kind}-analytics.png`), animations: 'disabled' })
|
||||
unavailable = true
|
||||
await form.getByRole('button', { name: kind === 'channel' ? '搜索' : '查询', exact: true }).click()
|
||||
await page.getByText('统计所需业务汇总尚未接入。', { exact: true }).waitFor()
|
||||
assert.equal(await totals.getByText('¥1,000.00', { exact: true }).count(), 0)
|
||||
assert.deepEqual(errors, [])
|
||||
const report = { kind, passed: true, fullSummaryAcrossPages: true, filtersResetPage: true, platformAndGameFilters: true, noInventedExport: true, missingDataNotZero: true, pageErrors: errors }
|
||||
reports.push(report); console.log(JSON.stringify(report)); await context.close()
|
||||
}
|
||||
await fs.writeFile('docs/sw6-to-kra/analytics-page-2026-11-04.json', JSON.stringify({ method: '真实 Chrome 渲染,受控业务数据;未写入真实数据', reports }, null, 2) + '\n')
|
||||
} finally { await browser.close() }
|
||||
})().catch(error => { console.error(error); process.exitCode = 1 })
|
||||
Reference in New Issue
Block a user