diff --git a/README.md b/README.md index fc17369..ac87a59 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ -# 代打台 · 代打管理平台 +# 代打台-L · 代打管理平台(lite 版) -一个给代打工作室用的单文件管理程序:**套餐 → 订阅 → 追加时长 → 日历打卡 → 大屏**。 +一个给代打工作室用的单文件管理程序:**套餐 → 订阅 → 追加时长 → 日历打卡**。 整个程序就是一个 `daili.exe` + 一个 `daili.ini`,数据存在同目录的 `daili.db` 里,局域网内手机、电视都能打开。界面支持亮色 / 暗色切换,默认跟随系统。 +> **这是 lite 分支**:只保留「按日(包时)」一种套餐,去掉了大屏、客户自助查询、多管理员、触摸小键盘。 +> 版本号统一带 `-L` 后缀(当前 `1.11.5-L`),界面品牌为「代打台-L」,页脚署名 `Powered by Baka春秋`。 +> 与 `main` 分支的差异见文末「[10. 与 main 分支的差异](#10-与-main-分支的差异)」。 + --- ## 1. 快速开始 @@ -12,20 +16,18 @@ - 首次运行会自动生成配置文件 `daili.ini`; - 弹出一个黑色窗口,打印访问地址(默认 **17777** 端口); - 默认会自动打开浏览器; -3. 浏览器打开 `http://127.0.0.1:17777` → 第一次会让你**创建管理员账号**; +3. 浏览器打开 `http://127.0.0.1:17777` → 第一次会让你**创建管理员账号**(这是唯一的账号,之后不能再添加); 4. 之后按「套餐 → 客户 → 订阅」的顺序录入即可。 ``` ┌───────────────────────────────────────────────┐ - │ 代 打 台 · 代打管理平台 │ + │ 代 打 台 - L · 代打管理平台 │ └───────────────────────────────────────────────┘ - 版本 1.11.5 配置文件 daili.ini + 版本 1.11.5-L 配置文件 daili.ini 数据库 D:\代打台\daili.db 访问地址: 本机 http://127.0.0.1:17777 局域网 http://192.168.1.23:17777 - 大屏 http://127.0.0.1:17777/screen(免登录) - 注意:大屏免登录且监听所有网卡,同局域网内任何人都能打开 /screen 按 Ctrl+C 停止服务 ``` @@ -48,18 +50,19 @@ | `backup_keep` | `14` | 备份保留份数(1–365),超出自动清理 | | `timezone` | 空 | 留空跟随系统;可固定 `Asia/Shanghai`。**影响所有“自然日”计算** | | `open_browser` | `true` | 启动后自动打开浏览器 | -| `screen_public` | `true` | 大屏 `/screen` 免登录(投屏用);改 `false` 则需登录 | -| `portal_public` | `true` | 客户自助查询 `/customer` 免登录;改 `false` 则关闭该页(不影响大屏) | | `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 修好后重新启动即可(原文件未被修改) @@ -81,31 +84,31 @@ 示例:`daili.exe -addr 127.0.0.1:19000 -no-open` -> 业务参数(到期预警天数、大屏刷新秒数)不在配置文件里,仍在网页「设置」中修改。 +> 业务参数(到期预警天数)不在配置文件里,仍在网页「设置」中修改。 --- ## 3. 第一次使用(5 分钟走完) 1. **建套餐**:`套餐 → 新增套餐` - - 「包月日常」按日 30 天,色签绿; - - 「深渊十次」按次 10 次,色签紫。 + - 「包月日常」默认 30 天,色签绿; + - 只有「按日」一种计费方式:有开始日与结束日,每天一条待打任务。 > 以后要加套餐 D/E/F,直接再加一行就行,**不会影响任何已有客户数据**。 2. **建客户**:`客户 → 新建客户`,填手机尾号 + 客户名(这两项组合唯一)。 -3. **开通订阅**:进客户详情 → 选套餐、开始日、天数(按次则填次数)→ 开通订阅。 +3. **开通订阅**:进客户详情 → 选套餐、开始日、天数 → 开通订阅。 4. **日常使用**:打开「日历」,今天那格里就是这个客户今天要打的单,点「完成」打卡。 5. **续费**:客户转账后,点订阅上的「追加时长」→ 在日历上点新的终止日期 → 确认。 面板会实时显示「计算起点 / 原结束日 / 新结束日 / 本次追加几天 / 追加后剩余几天」,并在客户页留下追加记录。 -> 想先看效果再录自己的数据?程序启动后运行 `powershell -ExecutionPolicy Bypass -File scripts\seed.ps1`,会写入 8 个演示客户和 4 个套餐(默认账号 `admin` / `daili123`)。 +> 想先看效果再录自己的数据?程序启动后运行 `powershell -ExecutionPolicy Bypass -File scripts\seed.ps1`,会写入演示客户与套餐(默认账号 `admin` / `daili123`)。 --- ## 4. 业务规则(和 PRD 一致) **剩余时长** -- 按日:`剩余天数 = 结束日 − 今天 + 1`,结束日当天仍算服务日; -- 按次:`剩余次数 = 总次数 − 已用次数`;按次套餐的「有效期」可留空,留空就一直有效直到次数用完。 +- `剩余天数 = 结束日 − 今天 + 1`,结束日当天仍算服务日; +- 没有次数、没有周期:一个订阅就是从开始日到结束日的一段连续服务期。 **追加时长(核心)** - 打开追加面板,**在月历上点选新的终止日期**,系统自动反算; @@ -115,15 +118,14 @@ **暂停 / 恢复** - 暂停期间冻结剩余时长,日历上不出现待打任务; -- 恢复时按日套餐的结束日**自动顺延**暂停的自然日天数,时间轴上是灰色斜纹段。 +- 恢复时结束日**自动顺延**暂停的自然日天数,时间轴上是灰色斜纹段。 **打卡** -- 按日套餐:每天一条「完成 / 跳过」,只做记录不扣天数(天数按自然日流逝); -- 按次套餐:打卡「完成」自动扣 1 次,撤销打卡返还 1 次;次数为 0 时不允许再打; -- 按次套餐如果没设有效期,它不会出现在日历里 —— 直接在客户详情点订阅卡片上的「今日打卡」扣次。 +- 每天一条「完成 / 跳过」,只做记录不扣天数(天数按自然日流逝); +- 撤销打卡会删掉这条记录,不影响结束日。 **预警** -- 剩余 ≤3 天橙色、今天到期红色、已过期灰色;顶栏常驻「今日到期 / 3 天内」两个数字。 +- 剩余 ≤3 天橙色、今天到期红色、已过期灰色;顶栏常驻「今日到期 / 3 天内 / 7 天内」三个数字。 **查询** - 顶栏搜索框输入手机尾号即可定位客户(也支持姓名、游戏名、联系方式),按 `/` 快速聚焦。 @@ -135,51 +137,24 @@ - **按尾号快速完成**:日历右侧「当日待完成」顶部输入手机尾号 → 回车。 - 只匹配到 1 项:直接完成并提示; - 同一尾号有多个客户,或一个客户今天有多个服务:弹出勾选面板,默认全选,可多选后一次完成。 -- **加急**:每条任务右侧点「加急」,会加红色 `加急` 标签并排到最前;日历格子出现红点,大屏「今日待打」同步置顶。再点一次取消。 -- **按套餐分日历**:套餐编辑页勾选「在日历中独立显示」后,工作台与大屏顶部会出现套餐标签(全部 / 每日任务 / …),可以只看某类委托的排单。 -- **触摸小键盘**:日历右侧输入框下面有 3 列 4 行数字键盘,底行是 ⌫ / 0 / ✓,点 ✓ 直接提交;按键为自定义样式,不是浏览器原生按钮,触摸目标 52px 高; -- **日历格子**:不再显示排单缩略信息,只保留左侧打手色竖条 + 大字「待完成 / 总数」(如 1/2 = 还剩 1 单,共 2 单); -- **按钮收纳**:任务行只露出「完成」,跳过 / 加急 / 追加收进「⋯」菜单,点空白处自动收起; -- **停用客户**:客户详情页可「停用」(默认从列表、日历、大屏隐藏,订阅保留)或「彻底删除」(连订阅/打卡/追加记录一起删,需二次确认);列表右上角可切换到「显示已停用」再恢复; -- **7 天内到期**:顶栏新增徽标,固定统计未来 7 天内到期的订阅; -- **抽屉时钟**:右侧当日面板顶部有时钟,30px 大写、水平垂直居中,24 小时制精确到秒,下面一行是当天日期; -- **触控 / PC 模式**:尾号输入框右侧有「⌨ / 触控」开关。切到触控时展开 3×4 小键盘、输入框变只读并关闭系统输入法(`inputmode=none`),点键直接写值;切回 PC 收起键盘。选择记在浏览器里。 -- **格子计数**:工作台和大屏的日历格都显示「已完成 / 总数」,例如 2/5 = 5 单里做完 2 单。 -- **已完成折叠**:当日清单里已完成/已跳过的单子自动折叠成一行(尾号 + 姓名 + 套餐 + 状态),点这一行展开看剩余、打手,以及撤销/加急/追加;未完成的行不受影响; -- **打手颜色**:`设置 → 管理员` 里给每个管理员选一个颜色。客户详情或编辑页可以把客户指派给某个打手,之后这个客户的所有单子都用该颜色标识: - - 日历格左侧竖条 = 当天涉及的所有打手颜色(几种人几条,等分显示); - - 「当日待完成」每条前面有打手色条 + 名字,未指派的显示灰色「未指派」; - - 大屏的今日待打、悬停浮层、热力格竖条同样带颜色。 +- **加急**:每条任务右侧点「加急」,会加红色 `加急` 标签并排到最前;日历格子出现红点。再点一次取消。 +- **按套餐分日历**:套餐编辑页勾选「在日历中独立显示」后,工作台顶部会出现套餐标签(全部 / 每日任务 / …),可以只看某类委托的排单。 +- **日历格子**:只保留大字「已完成 / 总数」(如 1/2 = 2 单里做完 1 单)。 +- **按钮收纳**:任务行只露出「完成」,跳过 / 加急 / 追加收进「⋯」菜单,点空白处自动收起。 +- **停用客户**:客户详情页可「停用」(默认从列表与日历隐藏,订阅保留)或「彻底删除」(连订阅/打卡/追加记录一起删,需二次确认);列表右上角可切换到「显示已停用」再恢复; +- **7 天内到期**:顶栏徽标,固定统计未来 7 天内到期的订阅; +- **抽屉时钟**:右侧当日面板顶部有时钟,24 小时制精确到秒,下面一行是当天日期; +- **已完成折叠**:当日清单里已完成/已跳过的单子自动折叠成一行(尾号 + 姓名 + 套餐 + 状态),点这一行展开看剩余,以及撤销/加急/追加;未完成的行不受影响; - **主题**:顶栏右上角 ☾ / ☀ 切换亮 / 暗,偏好记在浏览器里;没选过时由页面在首屏按系统偏好自动决定。整套界面只认 `` 一个开关,不会出现「半亮半暗」。 - **升级不缓存旧界面**:CSS/JS 链接带**内容指纹**(`app.css?v=<12 位哈希>`)。资源可以长缓存(一年、immutable),但样式或脚本一改、重新编译,URL 就会变,浏览器自动取新文件,不用手动清缓存;HTML 仍然 `no-store`。 --- -## 4.4 客户自助查询 /customer - -- **独立开关**:`/customer` 由 `portal_public` 控制(默认 `true`),和大屏 `screen_public` 互不影响;关掉大屏不会连带关掉客户页。 -- **查法**:只接受 4–8 位数字手机尾号,精确匹配;同一尾号有多位客户时要求补姓名,并用「张* / Z***」这样的掩码提示,不直接列全名;每个 IP 每分钟最多 40 次查询。 -- **展示**:今日待完成数量、订阅列表(通行证 / 深渊 / 按次 / 按日,含已到期、暂停、已用尽状态)、当月排单。月历里日常任务用空心/实心点表示未完成/已完成,通行证和深渊用连续色块并显示 `1/1` 或 `1/4 周期`;前后月只做日期占位。页面不含备注与价格。 -- **隐私**:页面 `noindex`,HTML `no-store`;只显示排单与完成情况。若不需要客户自助,把 `portal_public` 改成 `false`。 - ---- - -## 4.3 通行证、深渊与按次有效期 - -- **通行证**(原「包赛季」):套餐上直接填通行证开始日/结束日。客户开通后,该套餐的独立日历会把整段有效期连续着色(用套餐色签的淡化色),每一格显示同一个「已完成 / 总单数」(0/1 或 1/1)。有效期内任意一天点「完成通行证」即整段完成,完成后色块保留并显示 1/1。 -- **深渊**:套餐填「周期天数 N」与「周期数 X」。开通时从开始日连续排出 X 个 N 天周期;独立日历按周期连续着色,颜色从约 34% 依次减淡到约 4%。每一格显示**该周期自己**的完成情况:`1/1` 或 `0/1`,不再重复全局的 `x/X`;工作台、大屏、客户自助页同一口径。周期内任意一天完成即算该周期完成(同周期重复完成不会重复计数),撤销该天打卡会退回该周期。 -- **通行证过期不可选**:今天已过通行证结束日的套餐,在客户开通下拉里会置灰并标「通行证已结束」,服务端也会拒绝开通。 -- **按次默认有效期**:按次套餐可设「默认有效期(天)」,开单时自动算出到期日(开始日 + 天数 − 1),仍可手动改。 -- **删除订阅**:已结束的订阅卡片上,在「重新启用」旁新增「删除」,二次确认后彻底删除该订阅及其打卡/追加记录。 - ---- - ## 4.2 套餐标签与批量开通 -- **套餐标签**:套餐编辑页可以填一个标签(如「包月」)。日历/大屏顶部页签会**按标签合并**:同标签的套餐合成一个页签;勾了「在日历中独立显示」的套餐**额外**再开一个自己的页签。 -- **批量开通**:`套餐 → 详情/批量`,勾选客户(表头有全选,已订阅该套餐的客户置灰),填开始日期与天数/次数后一次开通;重复的会自动跳过并提示。 -- **客户月历**:客户详情页新增「当月排单」,显示该客户当月每天几单、完成几单,可翻月,点某天直接跳到日历那天。 -- **按次到期**:按次订阅的「到期日」仍是选填;填了到期后状态显示「已到期」、不再进日历待排。 +- **套餐标签**:套餐编辑页可以填一个标签(如「包月」)。日历顶部页签会**按标签合并**:同标签的套餐合成一个页签;勾了「在日历中独立显示」的套餐**额外**再开一个自己的页签。 +- **批量开通**:`套餐 → 详情/批量`,勾选客户(表头有全选,已订阅该套餐的客户置灰),填开始日期与天数后一次开通;重复的会自动跳过并提示。 +- **客户月历**:客户详情页「当月排单」,显示该客户当月每天几单、完成几单,可翻月,点某天直接跳到日历那天。 --- @@ -194,8 +169,9 @@ ### 迁移与重置(只说一次,很重要) - **迁移**:关闭程序 → 把 `daili.db` 复制到新电脑的新目录 → 放入 `daili.exe` 双击即可,账号、套餐、客户、订阅、追加记录、打卡、日志全部原样保留。 -- **重置**:关闭程序 → 删除 `daili.db` → 重新打开,会回到「创建第一个管理员」的全新状态。 -- 通行证是 `packages.season/season_start/season_end` 与 `subscriptions.season/season_done`;深渊是 `packages.abyss/cycle_days` 与 `subscriptions.abyss/cycle_days`(`total_count` 存周期数);套餐标签是 `packages.tag`;客户停用是 `customers.archived`(0/1);打手颜色存在 `users.color`,客户归属打手是 `customers.assignee_id`;加急标记存在 `daili.db` 的 `task_flags` 表;套餐的「独立日历」是 `packages.calendar_separate` 列,老版本数据库打开时会**自动加列**,不用手工迁移; +- **重置**:关闭程序 → 删除 `daili.db` → 重新打开,会回到「创建管理员」的全新状态。 +- 套餐标签是 `packages.tag`;客户停用是 `customers.archived`(0/1);加急标记存在 `task_flags` 表;套餐的「独立日历」是 `packages.calendar_separate` 列,老版本数据库打开时会**自动加列**,不用手工迁移。 +- **从 main 升级**:lite 版启动时会自动把老库里的「按次 / 通行证 / 深渊」统一折算成按日(详见文末差异说明),老库直接打开即可,无需手工改数据。 - `daili.ini`(监听/备份/日志配置)与 `backup/`、`logs/` 都是**独立于数据**的,迁移时可以不拷,程序会按默认值重新生成。 - 手动备份:`设置 → 立即备份`(同样遵守保留份数),备份文件本身也是可直接打开的单文件库; @@ -203,22 +179,7 @@ --- -## 6. 大屏 - -- 地址:`http://<本机IP>:17777/screen` -- 默认 `screen_public = true`:**免登录**,电视/副屏直接打开即可(仅建议局域网使用); -- 改成 `screen_public = false` 后大屏回到登录保护; -- 「记分板」风格,亮 / 暗主题同样可切(右上角 ☾ / ☀): - - 顶部 KPI:客户 / 活跃订阅 / 今日到期 / 7 天内到期; - - 中部:**热力月历 + 即将到期 TOP10**。热力默认统计「每日待完成单量」,点右上角按钮可切回「到期热力」;鼠标悬停任意格子会浮出当日待办明细(含加急标记); - - 底部:**今日到期**(左)+ **今日待打**(右,加急置顶带红 tag); - - 最下方细条:最近追加流水; -- 套餐勾选「在日历中独立显示」后,大屏顶部会出现套餐标签,可切换只看某类委托; -- 默认 30 秒自动刷新(设置页可改),按 `F` 全屏。 - ---- - -## 7. 从源码构建 +## 6. 从源码构建 环境:Windows + Go 1.25(本项目在 `go1.25.6` 上验证)。 @@ -243,6 +204,12 @@ $env:GOFLAGS='-mod=mod'; $env:GOPROXY='off'; $env:GOCACHE="$PWD\.gocache" go test ./... ``` +页面级冒烟(起一个隔离实例,把所有页面渲染一遍,并校验水印/品牌/已移除入口): + +```powershell +powershell -ExecutionPolicy Bypass -File scripts\smoke-lite.ps1 +``` + 目录结构: ``` @@ -254,10 +221,10 @@ internal/logx 控制台 + 按天切分的运行日志 internal/pw 口令散列(PBKDF2-SHA256,标准库实现) internal/web 路由、会话、模板渲染、处理器 internal/web/templates 页面与布局(embed 进二进制) -internal/web/static app.css / screen.css / app.js / screen.js -scripts/acceptance.ps1 冒烟清单执行器(119 条断言) -scripts/config-test.ps1 配置文件专项测试(14 条断言) -scripts/smoke.ps1 端到端冒烟(早期版本,保留) +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 → 交叉编译 → 上传产物 @@ -283,7 +250,7 @@ daili.ini 首次运行自动生成 --- -## 8. 常见问题 +## 7. 常见问题 **Windows 提示「已保护你的电脑」** 点「更多信息 → 仍要运行」。这是 Windows 对未签名 exe 的默认拦截。 @@ -301,26 +268,55 @@ daili.ini 首次运行自动生成 4. 用启动时打印的那个 `192.168.x.x` 地址,不要用 `127.0.0.1`。 **忘记密码** -让另一位管理员在 `设置 → 管理员` 里新增一个账户即可;如果只剩一个管理员且忘了密码,只能删掉 `daili.db` 重来。 +lite 版只有一个管理员,没有「新增管理员」可以绕过,只能二选一:删掉 `daili.db` 从零开始,或用任意 SQLite 工具把 `users` 表的 `password_hash` 清空后重新初始化。请务必记牢密码。 **数据会不会因为程序崩溃丢?** 单文件 SQLite + 每次操作即时落盘,正常使用不会丢;每天的自动备份是双保险。仍然建议定期把 `backup/` 拷到别的盘。 **能放到公网吗?** -不建议。程序按「局域网内可信环境」设计:会话 Cookie 有 HttpOnly / SameSite 保护,但没有 HTTPS、没有登录失败锁定,且大屏和客户自助页默认免登录。要放公网请套一层反向代理 + HTTPS + 访问控制,并把 `screen_public`、`portal_public` 改成 `false`。 +不建议。程序按「局域网内可信环境」设计:会话 Cookie 有 HttpOnly / SameSite 保护,但没有 HTTPS、没有登录失败锁定。要放公网请套一层反向代理 + HTTPS + 访问控制。 --- -## 9. 安全说明 - -- **初始管理员**(第一个创建的账号,`id=1`)在 `设置 → 管理员` 每行可以点「编辑」,修改其他管理员的用户名、昵称,或重置密码; -- 重置密码会立刻清除对方的登录会话,他需要用新密码重新登录;昵称/用户名的修改不影响会话; -- 自己那一行不显示编辑(改自己的密码用「修改我的密码」),避免误操作把自己锁在门外; -- 其余管理员权限相同,但只能改自己的密码。 - +## 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`(`