需求与确认(用户逐项确认): - 老数据「按次 / 通行证 / 深渊」统一迁移成按日,代码里彻底删除三种模式 - 打手归属一并移除(只剩一个人,颜色与指派没有区分意义) - 盲水印做成完全不可见:只存在于 DOM / 源码,肉眼与截图都看不到 - 拼写采用更正后的 Powered by Baka春秋 - 版本号与品牌全站统一带 -L - screen_public / portal_public 保留解析但不起作用,保证旧 ini 能直接启动 - README 同步更新 品牌与版本(全站统一): - const version = "1.11.5-L":<title>、启动横幅、日志前缀 [代打台-L]、 设置页版本号、页脚版本号全部带 -L - 左上角导航品牌 代打台 → 代打台-L(登录页、初始化页同步) - 页脚新增可见署名 Powered by Baka春秋 盲水印: - _layout.html 在 <body> 起始处注入 .wm-blind 容器(含注释与 data-wm 双标记), 覆盖包括登录/初始化在内的所有页面 - app.css 用零尺寸 + 透明 + opacity:0 + clip-path:inset(50%) 四重隐藏, 同时留同名注释;不占位、不上色、不进无障碍树、不影响打印 功能移除: - 大屏:删除 handlers_screen.go、pages/screen.html、_screen_layout.html、 static/screen.css、static/screen.js、store.Stats/ScreenStats; 导航「大屏 ↗」、页脚「大屏」、设置页「大屏刷新/大屏」行、启动横幅大屏行一并删除 - 客户自查:删除 handlers_portal.go、pages/portal.html 与 .portal-*/.pcal-* 样式, 设置页「客户自助」行删除 - 多管理员:删除 handleUserCreate/Toggle/Color/UserEditPage/UserEditSave、 pages/user_form.html、store 侧 ActiveUsers/SetUserColor/SetUserActive/ UpdateUserByAdmin/NormalizeColor/AssigneePalette;设置页「管理员」整块移除, 只保留首次启动创建的那一个账户与「修改我的密码」 - 打手归属:删除 customers.assignee_id 相关读写与 UI(客户详情指派下拉、 客户表单「归属打手」、日历格左侧打手色竖条、任务卡打手标签、套餐详情客户列) - 登录页与初始化页的说明文案同步改为单账户口径(「只有一个管理员账户」 「创建后不能再添加其他管理员」),字数与 main 对齐,卡片不换行、高度不变 - 触摸小键盘:删除 3×4 键盘、⌨/触控开关、localStorage 记忆、.keypad* 样式 - 套餐计费方式:只保留按日。删除按次/通行证/深渊的 Package 与 Subscription 字段、 domain 里的 UnitCount/StateUsedUp/CycleIndex、package_form 计费方式单选与 通行证/深渊/有效期字段、日历整段色块(has-block/cell-block/season-band)、 追加次数分支、SeasonComplete/AddCount、CountActiveSubs 的次数条件 数据库迁移(幂等,Migrate 内自动执行): - normalizeLegacyData:套餐一律 unit='day' 并清 season/abyss/cycle_days/ default_validity_days/season_*;订阅清 season/abyss/cycle_days/season_done - 通行证、深渊当年就是 unit='day' 且起止日完整,仅清标记,服务区间原样保留 - 按次按「1 次 = 1 天」折算:结束日 = 开始日 + 总次数 − 1;仍缺结束日退回开始日 - 只动数据,不删表不删列:老库的 season/abyss/cycle_days/total_count/used_count 保留但不再使用,升级可逆、不会因为 DDL 失败卡住 - 新库 schema 干净:不再创建上述列,users.color / customers.assignee_id 也不创建 - 老库 ensureColumn 保留 calendar_separate / tag / archived 三项(真增量列) 兼容: - config 仍解析 screen_public / portal_public(结构体字段注明已废弃), 默认 ini 模板里改为「已废弃,写了不报错、不生效」 测试: - go vet ./... 通过;go test ./... 全部通过 (新增 TestNormalizeLegacyTypeData 验证三模式折算成按日; TestSingleUserAccount / TestSessionLifecycle 覆盖单用户与会话; 删除按次、深渊、打手颜色、多用户等已不存在的能力的用例) - 新增 scripts/smoke-lite.ps1:起隔离实例,19 个页面级请求全部 200, 已移除的 /screen、/customer、/api/screen/day 均 404, 水印/品牌/v1.11.5-L/无残留词 12 项断言通过, 真实数据下日历出现待打任务、「剩 30 天」、今天到期徽标 4 项断言通过, 本次 0 失败 README:改为 lite 版说明,删除大屏/客户自查/通行证/深渊/按次/小键盘/多用户章节, 新增「盲水印」与「与 main 分支的差异」两节(含老数据折算规则与列保留说明)。
127 lines
6.1 KiB
PowerShell
127 lines
6.1 KiB
PowerShell
# lite 版冒烟:起一个隔离实例,把所有页面都渲染一遍(用 curl.exe,绕开受约束语言模式)
|
||
$ErrorActionPreference = 'Stop'
|
||
$root = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path }
|
||
if (-not (Test-Path (Join-Path $root 'go.mod'))) { $root = Join-Path $root 'subtrack' }
|
||
|
||
$port = 17779
|
||
$exe = Join-Path $root 'daili-lite-smoke.exe'
|
||
$db = Join-Path $root '.gotmp\smoke-lite.db'
|
||
$jar = Join-Path $root '.gotmp\cookies.txt'
|
||
$base = "http://127.0.0.1:$port"
|
||
$resp = @{}
|
||
$i = 0
|
||
$fail = 0
|
||
|
||
New-Item -ItemType Directory -Force -Path (Join-Path $root '.gotmp') | Out-Null
|
||
foreach ($f in @($db, $jar)) { if (Test-Path $f) { Remove-Item $f -Force } }
|
||
|
||
$env:GOFLAGS='-mod=mod'; $env:GOPROXY='off'; $env:GOSUMDB='off'
|
||
$env:GOCACHE=(Join-Path $root '.gocache'); $env:GOTMPDIR=(Join-Path $root '.gotmp')
|
||
Push-Location $root
|
||
go build -o $exe .
|
||
$buildOk = ($LASTEXITCODE -eq 0)
|
||
Pop-Location
|
||
if (-not $buildOk) { Write-Output 'BUILD FAILED'; exit 1 }
|
||
Write-Output 'build ok'
|
||
|
||
$logFile = Join-Path $root '.gotmp\smoke-server.log'
|
||
$proc = Start-Process -FilePath $exe -ArgumentList @('-addr', "127.0.0.1:$port", '-db', $db, '-no-open', '-no-backup') -PassThru -RedirectStandardOutput $logFile -RedirectStandardError ($logFile + '.err') -WindowStyle Hidden
|
||
Start-Sleep -Seconds 2
|
||
|
||
function Req($name, $path, $method = 'GET', $data = $null) {
|
||
$body = Join-Path $root ('.gotmp\resp-{0}.html' -f $global:i)
|
||
$global:i = $global:i + 1
|
||
$a = @('-s', '-o', $body, '-w', '%{http_code}', '-b', $global:jar, '-c', $global:jar, '-L', '--max-time', '20')
|
||
if ($method -eq 'POST') {
|
||
# 不要用 -X POST:curl 会在 303 重定向后继续 POST,导致下一跳 405
|
||
foreach ($k in $data.Keys) { $a = $a + @('--data-urlencode', ($k + '=' + $data[$k])) }
|
||
}
|
||
$a = $a + @($global:base + $path)
|
||
$code = (& curl.exe @a) -join ''
|
||
$text = ''
|
||
if (Test-Path $body) { $text = (Get-Content -Raw -Encoding utf8 $body) }
|
||
if ($null -eq $text) { $text = '' }
|
||
$bad = $text -match '页面渲染失败|模板不存在'
|
||
$status = if ($bad) { 'TEMPLATE-ERROR' } else { 'ok' }
|
||
if ($bad -or $code -ne '200') { $global:fail = $global:fail + 1 }
|
||
Write-Output ('{0,-26} {1} {2} len={3}' -f $name, $code, $status, $text.Length)
|
||
$global:resp[$name] = $text
|
||
}
|
||
|
||
function Post($name, $path, $data) { Req $name $path 'POST' $data }
|
||
|
||
Write-Output '--- 页面渲染 ---'
|
||
Req 'GET /login' '/login'
|
||
Req 'GET /setup' '/setup'
|
||
Post 'POST /setup' '/setup' @{ username = 'admin'; display_name = 'boss'; password = 'daili123'; password2 = 'daili123' }
|
||
|
||
Req 'GET /calendar' '/calendar'
|
||
Req 'GET /settings' '/settings'
|
||
Req 'GET /logs' '/logs'
|
||
Req 'GET /packages' '/packages'
|
||
Req 'GET /packages/new' '/packages/new'
|
||
Post 'POST /packages' '/packages' @{ name = 'monthly'; default_amount = '30'; price = '300'; color = '#3B6EA5'; sort_order = '0'; note = 'smoke' }
|
||
Req 'GET /packages/1' '/packages/1'
|
||
Req 'GET /packages/1/edit' '/packages/1/edit'
|
||
Req 'GET /customers' '/customers'
|
||
Req 'GET /customers/new' '/customers/new'
|
||
Post 'POST /customers' '/customers' @{ tail = '1234'; name = 'laozhang'; game_name = 'wzry'; game_server = 'wx'; contact = 'wx'; source = 'douyin'; tags = 'vip'; note = 'smoke' }
|
||
Req 'GET /customers/1' '/customers/1'
|
||
Req 'GET /customers/1/edit' '/customers/1/edit'
|
||
Post 'POST /sub-create' '/customers/1/subscriptions' @{ package_id = '1'; start_date = '2026-01-01'; days = '30'; end_date = ''; note = 'smoke' }
|
||
Req 'GET /subscriptions' '/subscriptions'
|
||
Req 'GET /calendar?extend' '/calendar?extend=1'
|
||
Req 'GET /calendar?pick' '/calendar?pick=1234'
|
||
Post 'POST /checkin' '/subscriptions/1/checkin' @{ date = '2026-01-01'; status = 'done'; note = '' }
|
||
Post 'POST /settings' '/settings' @{ warn_days = '5' }
|
||
Post 'POST /password' '/settings/password' @{ old_password = 'daili123'; password = 'daili456'; password2 = 'daili456' }
|
||
|
||
Write-Output '--- 真实数据(今天开通 → 日历应有待打任务)---'
|
||
$today = (Get-Date).ToString('yyyy-MM-dd')
|
||
Post 'POST /packages2' '/packages' @{ name = 'weekly'; default_amount = '7'; price = '80'; color = '#884422'; sort_order = '0' }
|
||
Post 'POST /sub2' '/customers/1/subscriptions' @{ package_id = '2'; start_date = $today; days = '30'; end_date = '' }
|
||
Req 'GET /calendar-live' '/calendar'
|
||
$live = $resp['GET /calendar-live']
|
||
|
||
Write-Output '--- 已移除的路由 ---'
|
||
foreach ($gone in @('/screen', '/customer', '/api/screen/day')) {
|
||
$code = (& curl.exe -s -o NUL -w '%{http_code}' -b $jar --max-time 10 ($base + $gone)) -join ''
|
||
$tag = if ($code -eq '404') { 'ok (404)' } else { 'UNEXPECTED'; }
|
||
if ($code -ne '404') { $fail = $fail + 1 }
|
||
Write-Output ('{0,-26} {1} {2}' -f ('GONE ' + $gone), $code, $tag)
|
||
}
|
||
|
||
Write-Output '--- 内容断言 ---'
|
||
$cal = $resp['GET /calendar']
|
||
function Assert($label, $haystack, $pattern, $expectFound) {
|
||
$found = $haystack -match $pattern
|
||
$pass = ($found -eq $expectFound)
|
||
if (-not $pass) { $global:fail = $global:fail + 1 }
|
||
$verdict = if ($pass) { 'ok' } else { 'FAIL' }
|
||
$what = if ($expectFound) { 'present' } else { 'absent' }
|
||
Write-Output ('{0,-34} {1} ({2} expected)' -f $label, $verdict, $what)
|
||
}
|
||
Assert '盲水印 Powered by Baka春秋' $cal 'Powered by Baka春秋' $true
|
||
Assert 'wm-blind 容器' $cal 'class="wm-blind"' $true
|
||
Assert '品牌 代打台-L' $cal '代打台-L' $true
|
||
Assert '页脚版本 v1.11.5-L' $cal 'v1\.11\.5-L' $true
|
||
Assert '大屏入口' $cal '大屏' $false
|
||
Assert '/screen 链接' $cal '/screen' $false
|
||
Assert '通行证' $cal '通行证' $false
|
||
Assert '深渊' $cal '深渊' $false
|
||
Assert '按次' $cal '按次' $false
|
||
Assert '小键盘' $cal '小键盘' $false
|
||
Assert '打手' $cal '打手' $false
|
||
Assert 'keypad 标记' $cal 'data-keypad' $false
|
||
|
||
Write-Output '--- 实时数据断言 ---'
|
||
Assert '日历有待打任务' $live '今日打卡|当日待完成' $true
|
||
Assert '剩余 30 天' $live '剩 30 天' $true
|
||
Assert '客户尾号 1234' $live '1234' $true
|
||
Assert '今天到期徽标' $live '今日到期' $true
|
||
|
||
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
|
||
Write-Output '--- server log ---'
|
||
if (Test-Path $logFile) { Get-Content $logFile -Encoding utf8 | Select-Object -First 30 }
|
||
Write-Output ('SMOKE-FAILURES=' + $fail)
|