Files
subtrack/README.md
T
chun_qiu cde1c84c9a feat(v1.11.5-L): lite 精简版——移除大屏/客户自查/多用户/小键盘,套餐仅保留按日
需求与确认(用户逐项确认):
- 老数据「按次 / 通行证 / 深渊」统一迁移成按日,代码里彻底删除三种模式
- 打手归属一并移除(只剩一个人,颜色与指派没有区分意义)
- 盲水印做成完全不可见:只存在于 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 分支的差异」两节(含老数据折算规则与列保留说明)。
2026-10-21 03:33:27 +08:00

323 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代打台-L · 代打管理平台(lite 版)
一个给代打工作室用的单文件管理程序:**套餐 → 订阅 → 追加时长 → 日历打卡**。
整个程序就是一个 `daili.exe` + 一个 `daili.ini`,数据存在同目录的 `daili.db` 里,局域网内手机、电视都能打开。界面支持亮色 / 暗色切换,默认跟随系统。
> **这是 lite 分支**:只保留「按日(包时)」一种套餐,去掉了大屏、客户自助查询、多管理员、触摸小键盘。
> 版本号统一带 `-L` 后缀(当前 `1.11.5-L`),界面品牌为「代打台-L」,页脚署名 `Powered by Baka春秋`。
> 与 `main` 分支的差异见文末「[10. 与 main 分支的差异](#10-与-main-分支的差异)」。
---
## 1. 快速开始
1. 把 `daili.exe` 放进任意文件夹(建议路径不含空格,例如 `D:\代打台\`);
2. 双击 `daili.exe`:
- 首次运行会自动生成配置文件 `daili.ini`;
- 弹出一个黑色窗口,打印访问地址(默认 **17777** 端口);
- 默认会自动打开浏览器;
3. 浏览器打开 `http://127.0.0.1:17777` → 第一次会让你**创建管理员账号**(这是唯一的账号,之后不能再添加);
4. 之后按「套餐 → 客户 → 订阅」的顺序录入即可。
```
┌───────────────────────────────────────────────┐
│ 代 打 台 - L · 代打管理平台 │
└───────────────────────────────────────────────┘
版本 1.11.5-L 配置文件 daili.ini
数据库 D:\代打台\daili.db
访问地址:
本机 http://127.0.0.1:17777
局域网 http://192.168.1.23:17777
按 Ctrl+C 停止服务
```
> 关掉黑窗口 = 停止服务。数据已经落盘,下次打开继续用。
---
## 2. 配置文件 daili.ini
配置文件固定放在 **exe 同级目录**,首次运行自动生成(带中文注释),改完**重启生效**。
写法:`key = value`,`#` 或 `;` 开头的整行是注释,不支持分节;**相对路径相对于 exe 所在目录**。
| 配置项 | 默认值 | 说明 |
| --- | --- | --- |
| `listen_ip` | `0.0.0.0` | 监听 IP。`0.0.0.0` = 所有网卡(局域网可访问);`127.0.0.1` = 只允许本机 |
| `listen_port` | `17777` | 监听端口(1–65535) |
| `database_path` | `daili.db` | SQLite 数据库文件(单文件,含全部数据) |
| `backup_enabled` | `true` | 是否每日自动备份 |
| `backup_dir` | `backup` | 备份目录(留空则用 exe 同级 `backup/`) |
| `backup_keep` | `14` | 备份保留份数(1–365),超出自动清理 |
| `timezone` | 空 | 留空跟随系统;可固定 `Asia/Shanghai`。**影响所有“自然日”计算** |
| `open_browser` | `true` | 启动后自动打开浏览器 |
| `log_enabled` | `true` | 运行日志写文件(控制台始终输出) |
| `log_dir` | `logs` | 日志目录,按天切分 `daili-YYYY-MM-DD.log` |
> `screen_public`(大屏)与 `portal_public`(客户自助)在 lite 版**已废弃**:仍然接受解析,但不起任何作用。
> 保留它们只是为了让你从 `main` 升级时,旧 `daili.ini` 能直接启动,不会被「未知配置项」拦住。
**行为约定**
- 文件不存在 → 自动生成默认配置,照常启动;
- 文件写错 → **报错退出并指出行号**,原文件不会被修改。例如:
```
[代打台-L] 配置文件有问题,已停止启动:
第 3 行: listen_port 必须在 1-65535 之间,当前是 99999
修好后重新启动即可(原文件未被修改)
```
- 未知配置项、重复配置项、布尔值写成 `maybe`、时区写错,都会带行号报错;
- 端口被占用不会自动换端口,而是明确报错并提示改 `listen_port`。
### 命令行参数(临时覆盖配置,优先级更高)
| 参数 | 说明 |
| --- | --- |
| `-config <路径>` | 指定别的配置文件(默认 exe 同级 `daili.ini`) |
| `-addr <ip:端口>` | 临时覆盖监听地址,如 `-addr 127.0.0.1:19000` |
| `-db <路径>` | 临时覆盖数据库路径 |
| `-backup-keep <n>` | 临时覆盖备份保留份数 |
| `-no-backup` | 本次运行关闭自动备份 |
| `-no-open` | 本次运行不自动打开浏览器 |
示例:`daili.exe -addr 127.0.0.1:19000 -no-open`
> 业务参数(到期预警天数)不在配置文件里,仍在网页「设置」中修改。
---
## 3. 第一次使用(5 分钟走完)
1. **建套餐**:`套餐 → 新增套餐`
- 「包月日常」默认 30 天,色签绿;
- 只有「按日」一种计费方式:有开始日与结束日,每天一条待打任务。
> 以后要加套餐 D/E/F,直接再加一行就行,**不会影响任何已有客户数据**。
2. **建客户**:`客户 → 新建客户`,填手机尾号 + 客户名(这两项组合唯一)。
3. **开通订阅**:进客户详情 → 选套餐、开始日、天数 → 开通订阅。
4. **日常使用**:打开「日历」,今天那格里就是这个客户今天要打的单,点「完成」打卡。
5. **续费**:客户转账后,点订阅上的「追加时长」→ 在日历上点新的终止日期 → 确认。
面板会实时显示「计算起点 / 原结束日 / 新结束日 / 本次追加几天 / 追加后剩余几天」,并在客户页留下追加记录。
> 想先看效果再录自己的数据?程序启动后运行 `powershell -ExecutionPolicy Bypass -File scripts\seed.ps1`,会写入演示客户与套餐(默认账号 `admin` / `daili123`)。
---
## 4. 业务规则(和 PRD 一致)
**剩余时长**
- `剩余天数 = 结束日 − 今天 + 1`,结束日当天仍算服务日;
- 没有次数、没有周期:一个订阅就是从开始日到结束日的一段连续服务期。
**追加时长(核心)**
- 打开追加面板,**在月历上点选新的终止日期**,系统自动反算;
- 未到期的订阅:从原结束日的**次日**起算,追加天数 = 新结束日 − 原结束日;
- 已到期的订阅:**从今天起算**(不会把中间空档的天数白送给客户);
- 追加记录保留:计算起点、旧结束日、新结束日、天数、备注、操作人、时间。
**暂停 / 恢复**
- 暂停期间冻结剩余时长,日历上不出现待打任务;
- 恢复时结束日**自动顺延**暂停的自然日天数,时间轴上是灰色斜纹段。
**打卡**
- 每天一条「完成 / 跳过」,只做记录不扣天数(天数按自然日流逝);
- 撤销打卡会删掉这条记录,不影响结束日。
**预警**
- 剩余 ≤3 天橙色、今天到期红色、已过期灰色;顶栏常驻「今日到期 / 3 天内 / 7 天内」三个数字。
**查询**
- 顶栏搜索框输入手机尾号即可定位客户(也支持姓名、游戏名、联系方式),按 `/` 快速聚焦。
---
## 4.1 工作台速用
- **按尾号快速完成**:日历右侧「当日待完成」顶部输入手机尾号 → 回车。
- 只匹配到 1 项:直接完成并提示;
- 同一尾号有多个客户,或一个客户今天有多个服务:弹出勾选面板,默认全选,可多选后一次完成。
- **加急**:每条任务右侧点「加急」,会加红色 `加急` 标签并排到最前;日历格子出现红点。再点一次取消。
- **按套餐分日历**:套餐编辑页勾选「在日历中独立显示」后,工作台顶部会出现套餐标签(全部 / 每日任务 / …),可以只看某类委托的排单。
- **日历格子**:只保留大字「已完成 / 总数」(如 1/2 = 2 单里做完 1 单)。
- **按钮收纳**:任务行只露出「完成」,跳过 / 加急 / 追加收进「⋯」菜单,点空白处自动收起。
- **停用客户**:客户详情页可「停用」(默认从列表与日历隐藏,订阅保留)或「彻底删除」(连订阅/打卡/追加记录一起删,需二次确认);列表右上角可切换到「显示已停用」再恢复;
- **7 天内到期**:顶栏徽标,固定统计未来 7 天内到期的订阅;
- **抽屉时钟**:右侧当日面板顶部有时钟,24 小时制精确到秒,下面一行是当天日期;
- **已完成折叠**:当日清单里已完成/已跳过的单子自动折叠成一行(尾号 + 姓名 + 套餐 + 状态),点这一行展开看剩余,以及撤销/加急/追加;未完成的行不受影响;
- **主题**:顶栏右上角 ☾ / ☀ 切换亮 / 暗,偏好记在浏览器里;没选过时由页面在首屏按系统偏好自动决定。整套界面只认 `<html data-bs-theme>` 一个开关,不会出现「半亮半暗」。
- **升级不缓存旧界面**:CSS/JS 链接带**内容指纹**(`app.css?v=<12 位哈希>`)。资源可以长缓存(一年、immutable),但样式或脚本一改、重新编译,URL 就会变,浏览器自动取新文件,不用手动清缓存;HTML 仍然 `no-store`。
---
## 4.2 套餐标签与批量开通
- **套餐标签**:套餐编辑页可以填一个标签(如「包月」)。日历顶部页签会**按标签合并**:同标签的套餐合成一个页签;勾了「在日历中独立显示」的套餐**额外**再开一个自己的页签。
- **批量开通**:`套餐 → 详情/批量`,勾选客户(表头有全选,已订阅该套餐的客户置灰),填开始日期与天数后一次开通;重复的会自动跳过并提示。
- **客户月历**:客户详情页「当月排单」,显示该客户当月每天几单、完成几单,可翻月,点某天直接跳到日历那天。
---
## 5. 数据、备份与导出
| 文件 | 说明 |
| --- | --- |
| `daili.db` | **全部数据都在这一个文件里**(SQLite,单文件回滚日志模式,运行中也不会产生 `-wal`/`-shm`) |
| `backup/daili-YYYYMMDD-HHMMSS.db` | 每天首次启动时自动备份一份,保留份数见 `backup_keep` |
| `logs/daili-YYYY-MM-DD.log` | 运行日志,按天切分 |
### 迁移与重置(只说一次,很重要)
- **迁移**:关闭程序 → 把 `daili.db` 复制到新电脑的新目录 → 放入 `daili.exe` 双击即可,账号、套餐、客户、订阅、追加记录、打卡、日志全部原样保留。
- **重置**:关闭程序 → 删除 `daili.db` → 重新打开,会回到「创建管理员」的全新状态。
- 套餐标签是 `packages.tag`;客户停用是 `customers.archived`(0/1);加急标记存在 `task_flags` 表;套餐的「独立日历」是 `packages.calendar_separate` 列,老版本数据库打开时会**自动加列**,不用手工迁移。
- **从 main 升级**:lite 版启动时会自动把老库里的「按次 / 通行证 / 深渊」统一折算成按日(详见文末差异说明),老库直接打开即可,无需手工改数据。
- `daili.ini`(监听/备份/日志配置)与 `backup/`、`logs/` 都是**独立于数据**的,迁移时可以不拷,程序会按默认值重新生成。
- 手动备份:`设置 → 立即备份`(同样遵守保留份数),备份文件本身也是可直接打开的单文件库;
- 导出:`设置 → 导出客户 / 订阅 / 日志 CSV`,UTF-8 BOM,Excel 直接打开不乱码。
---
## 6. 从源码构建
环境:Windows + Go 1.25(本项目在 `go1.25.6` 上验证)。
```powershell
.\build.ps1
```
`build.ps1` 会设置 `GOPROXY=off`、`GOCACHE=<项目>\.gocache`,用本地模块缓存离线构建,产物为 `daili.exe`(约 13.5 MB,内含 SQLite 引擎、时区库、模板与静态资源,不依赖任何 DLL)。
> **为什么有 `third_party/fileutil`**:SQLite 驱动 `modernc.org/sqlite` 的**测试代码**引用了 `modernc.org/fileutil`,但编译产物并不依赖它。本机无法访问任何 Go 模块源,因此用一个空模块占位,仅用于满足模块图解析。
>
> **为什么内嵌时区库**:Windows 没有 `/usr/share/zoneinfo`,为了 `timezone = Asia/Shanghai` 在任何机器上都可用,引入了 `time/tzdata`(约 0.5 MB)。
> 改了依赖之后,请在 Windows 上额外跑一次 `GOOS=linux go build ./...` 再提交 `go.sum`:
> 有些依赖(例如 `modernc.org/libc` 的 Linux 专用文件)只在 Linux 构建路径下被编译,
> 只在本机 Windows 构建会漏掉它们的 `go.sum` 条目,导致 CI 报 `missing go.sum entry`。
测试:
```powershell
$env:GOFLAGS='-mod=mod'; $env:GOPROXY='off'; $env:GOCACHE="$PWD\.gocache"
go test ./...
```
页面级冒烟(起一个隔离实例,把所有页面渲染一遍,并校验水印/品牌/已移除入口):
```powershell
powershell -ExecutionPolicy Bypass -File scripts\smoke-lite.ps1
```
目录结构:
```
main.go 程序入口(读配置、日志、时区、监听、备份)
internal/config daili.ini 的生成/解析/校验(含单测)
internal/domain 纯业务计算(日期、剩余天数、状态、追加换算)+ 单元测试
internal/store SQLite 连接、迁移、全部读写 + 单元测试
internal/logx 控制台 + 按天切分的运行日志
internal/pw 口令散列(PBKDF2-SHA256,标准库实现)
internal/web 路由、会话、模板渲染、处理器
internal/web/templates 页面与布局(embed 进二进制)
internal/web/static app.css / app.js
scripts/smoke-lite.ps1 lite 版页面级冒烟(渲染 + 水印 + 已移除入口 404)
scripts/acceptance.ps1 冒烟清单执行器(main 分支遗留,部分条目已不适用)
scripts/config-test.ps1 配置文件专项测试
scripts/seed.ps1 写入演示数据
scripts/dbtool.py 测试预处理(暂停回溯、批量造数、完整性检查)
.gitea/workflows/build.yml Gitea Actions:vet → test → 交叉编译 → 上传产物
.gitattributes 统一换行(.go/.yml 用 LF,.ps1 用 CRLF)
PRD.md 产品需求文档(本地文档,不入库)
TEST-CHECKLIST.md 冒烟测试清单 + 执行结果 + 缺陷记录(本地,不入库)
test-evidence/ 测试证据(本地,不入库)
daili.ini 首次运行自动生成
```
### 持续集成(Gitea Actions)
仓库:<https://git.code.cq.cn/chun_qiu/subtrack.git>,CI 配置在 `.gitea/workflows/build.yml`。
- 触发:推送到 `main` / `master`、打 `v*` 标签、PR、手动执行;
- 运行环境:`runs-on: ubuntu-latest`;Go 版本由 `actions/setup-go@v5` 读取 `go.mod`(1.25);
- 步骤:`go vet ./...` → `go test ./...` → 交叉编译 Windows / Linux 两个二进制 → `sha256sum` → 用 `actions/upload-artifact@v4` 上传;
- 上传步骤带 `env: ACTIONS_RESULTS_URL: https://git.code.cq.cn/`;
- 产物:`subtrack-<run_number>`,内含 `daili-windows-amd64.exe`、`daili-linux-amd64`、`SHA256SUMS.txt`,保留 14 天;
- 模块代理默认 `https://goproxy.cn,direct`,如在海外或内网构建,改 workflow 顶部的 `GOPROXY` 即可。
> 仓库只入库源码、构建脚本、CI 配置和本 README;PRD、测试清单、截图与测试证据都在 `.gitignore` 里排除。
---
## 7. 常见问题
**Windows 提示「已保护你的电脑」**
点「更多信息 → 仍要运行」。这是 Windows 对未签名 exe 的默认拦截。
**端口被占用 / 想换端口**
改 `daili.ini` 里的 `listen_port` 后重启;临时换端口可用 `daili.exe -addr :19000`。
**只想自己用,不让局域网访问**
把 `listen_ip` 改成 `127.0.0.1`,启动横幅会提示「仅本机可访问」。
**手机打不开局域网地址**
1. 确认 `listen_ip` 是 `0.0.0.0`;
2. 确认手机和电脑在同一个 WiFi;
3. Windows 防火墙首次会弹窗,选「允许访问」(专用网络);
4. 用启动时打印的那个 `192.168.x.x` 地址,不要用 `127.0.0.1`。
**忘记密码**
lite 版只有一个管理员,没有「新增管理员」可以绕过,只能二选一:删掉 `daili.db` 从零开始,或用任意 SQLite 工具把 `users` 表的 `password_hash` 清空后重新初始化。请务必记牢密码。
**数据会不会因为程序崩溃丢?**
单文件 SQLite + 每次操作即时落盘,正常使用不会丢;每天的自动备份是双保险。仍然建议定期把 `backup/` 拷到别的盘。
**能放到公网吗?**
不建议。程序按「局域网内可信环境」设计:会话 Cookie 有 HttpOnly / SameSite 保护,但没有 HTTPS、没有登录失败锁定。要放公网请套一层反向代理 + HTTPS + 访问控制。
---
## 8. 安全说明
- lite 版**只有一个管理员**,就是首次启动时创建的那个账户(`users.id = 1`),不提供新增、停用或编辑他人账户的入口;
- 改自己的密码用 `设置 → 修改我的密码`;
- 口令使用 PBKDF2-HMAC-SHA256(12 万次迭代 + 随机盐)存储,不可逆;
- 所有写操作记录操作人、时间、IP 与摘要,可在「操作日志」里查询;
- 服务端模板全部走 `html/template` 自动转义;
- 所有 POST 校验 `Origin`,会话 Cookie 使用 `SameSite=Lax` + `HttpOnly`;
- 配置文件里的数据库/备份/日志路径请自行确保目录权限,程序不会覆盖已有文件(备份重名会跳过)。
---
## 9. 盲水印
界面里带一处不可见的版权水印 `Powered by Baka春秋`:写在 `_layout.html` 里由 `.wm-blind` 样式隐藏(零尺寸、透明、`clip-path` 裁掉、不进入无障碍树),同时在 `app.css` 里留了同名注释。它不占位、不上色、不影响布局与打印,只用于源码/DOM 层面溯源,**请勿删除**。
页脚另有一行可见署名 `Powered by Baka春秋`。
---
## 10. 与 main 分支的差异
| 项目 | main | lite(本分支) |
| --- | --- | --- |
| 版本号 | `1.11.5` | `1.11.5-L`(`<title>`、启动横幅、日志前缀、页脚全站统一带 `-L`) |
| 品牌 | 代打台 | 代打台-L |
| 大屏 `/screen` | 有,`screen_public` 控制免登录 | **已移除**,路由返回 404 |
| 客户自助 `/customer` | 有,`portal_public` 控制免登录 | **已移除**,路由返回 404 |
| 套餐计费方式 | 按日 / 按次 / 通行证 / 深渊 | **只有按日** |
| 管理员 | 可新增多个,为主管可编辑他人 | **只有首次启动创建的那一个** |
| 打手归属 | 客户可指派打手,彩色竖条标识 | **已移除**(只剩一个人,颜色无意义) |
| 触摸小键盘 | 3×4 小键盘 + 触控/PC 切换 | **已移除** |
| 署名 | 无 | 页脚 + 盲水印 `Powered by Baka春秋` |
**老数据怎么折算成按日**(启动时自动完成,可重复执行):
- **通行证**:当年就是 `unit='day'` 且起止日期完整,清掉 `season` 标记即可,服务区间原样保留;
- **深渊**:同样已是 `unit='day'`,结束日 = 开始日 + 周期天数 × 周期数 − 1,清掉 `abyss/cycle_days` 标记,区间原样保留;
- **按次**:按「1 次 = 1 天」折算。有总次数但没结束日的,结束日 = 开始日 + 总次数 − 1 天;仍缺结束日的退回开始日;
- 折算只动数据,不删表、不删列:老库里的 `season / abyss / cycle_days / total_count / used_count` 等列会保留但不再使用,避免升级过程不可逆。
`screen_public` 与 `portal_public` 两个配置项在 lite 版仍然被解析(不报错),但不再有任何作用。