- 设置 → 管理员:仅 id=1 的账户在“其他人”那一行看到「编辑」入口,自己那行不显示
- 新增编辑页 /settings/users/{id}/edit:可改用户名、显示名称,密码留空则不修改
- 重置密码后立即清除该账户的所有登录会话,强制对方用新密码重新登录;只改昵称/用户名不影响会话
- 服务端三重校验:非 id=1 拒绝、不能编辑自己、用户名 3-32 字符且唯一、密码 ≥6 位且两次一致
- 所有修改写入操作日志(旧用户名/昵称 → 新值,是否重置密码)
- 设置页根据当前账户显示不同提示:超管看到可编辑说明,其他人只看到“只有初始管理员可以修改”
数据库:无需变更(复用 users 表与 sessions 表)
测试:
- 新增单测 TestUpdateUserByAdmin:昵称更新、会话保留、改密后旧会话失效、新密码可登录旧密码失效、重名返回 ErrDuplicate、空用户名拒绝
- 功能验证 20 项:入口显示/隐藏、普通管理员与自我编辑被拒、改密踢下线、新旧密码、长度/一致性/重名校验、留空密码、日志记录
- 原有 119 项验收回归全绿;go vet / go test ./... 通过;版本号 1.4.0
16 KiB
代打台 · 代打管理平台
一个给代打工作室用的单文件管理程序:套餐 → 订阅 → 追加时长 → 日历打卡 → 大屏。
整个程序就是一个 daili.exe + 一个 daili.ini,数据存在同目录的 daili.db 里,局域网内手机、电视都能打开。界面支持亮色 / 暗色切换,默认跟随系统。
1. 快速开始
- 把
daili.exe放进任意文件夹(建议路径不含空格,例如D:\代打台\); - 双击
daili.exe:- 首次运行会自动生成配置文件
daili.ini; - 弹出一个黑色窗口,打印访问地址(默认 17777 端口);
- 默认会自动打开浏览器;
- 首次运行会自动生成配置文件
- 浏览器打开
http://127.0.0.1:17777→ 第一次会让你创建管理员账号; - 之后按「套餐 → 客户 → 订阅」的顺序录入即可。
┌───────────────────────────────────────────────┐
│ 代 打 台 · 代打管理平台 │
└───────────────────────────────────────────────┘
版本 1.1.0 配置文件 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 停止服务
关掉黑窗口 = 停止服务。数据已经落盘,下次打开继续用。
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 |
启动后自动打开浏览器 |
screen_public |
true |
大屏 /screen 免登录(投屏用);改 false 则需登录 |
log_enabled |
true |
运行日志写文件(控制台始终输出) |
log_dir |
logs |
日志目录,按天切分 daili-YYYY-MM-DD.log |
行为约定
- 文件不存在 → 自动生成默认配置,照常启动;
- 文件写错 → 报错退出并指出行号,原文件不会被修改。例如:
[代打台] 配置文件有问题,已停止启动:
第 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 分钟走完)
- 建套餐:
套餐 → 新增套餐- 「包月日常」按日 30 天,色签绿;
- 「深渊十次」按次 10 次,色签紫。
以后要加套餐 D/E/F,直接再加一行就行,不会影响任何已有客户数据。
- 建客户:
客户 → 新建客户,填手机尾号 + 客户名(这两项组合唯一)。 - 开通订阅:进客户详情 → 选套餐、开始日、天数(按次则填次数)→ 开通订阅。
- 日常使用:打开「日历」,今天那格里就是这个客户今天要打的单,点「完成」打卡。
- 续费:客户转账后,点订阅上的「追加时长」→ 在日历上点新的终止日期 → 确认。 面板会实时显示「计算起点 / 原结束日 / 新结束日 / 本次追加几天 / 追加后剩余几天」,并在客户页留下追加记录。
想先看效果再录自己的数据?程序启动后运行
powershell -ExecutionPolicy Bypass -File scripts\seed.ps1,会写入 8 个演示客户和 4 个套餐(默认账号admin/daili123)。
4. 业务规则(和 PRD 一致)
剩余时长
- 按日:
剩余天数 = 结束日 − 今天 + 1,结束日当天仍算服务日; - 按次:
剩余次数 = 总次数 − 已用次数;按次套餐的「有效期」可留空,留空就一直有效直到次数用完。
追加时长(核心)
- 打开追加面板,在月历上点选新的终止日期,系统自动反算;
- 未到期的订阅:从原结束日的次日起算,追加天数 = 新结束日 − 原结束日;
- 已到期的订阅:从今天起算(不会把中间空档的天数白送给客户);
- 追加记录保留:计算起点、旧结束日、新结束日、天数、备注、操作人、时间。
暂停 / 恢复
- 暂停期间冻结剩余时长,日历上不出现待打任务;
- 恢复时按日套餐的结束日自动顺延暂停的自然日天数,时间轴上是灰色斜纹段。
打卡
- 按日套餐:每天一条「完成 / 跳过」,只做记录不扣天数(天数按自然日流逝);
- 按次套餐:打卡「完成」自动扣 1 次,撤销打卡返还 1 次;次数为 0 时不允许再打;
- 按次套餐如果没设有效期,它不会出现在日历里 —— 直接在客户详情点订阅卡片上的「今日打卡」扣次。
预警
- 剩余 ≤3 天橙色、今天到期红色、已过期灰色;顶栏常驻「今日到期 / 3 天内」两个数字。
查询
- 顶栏搜索框输入手机尾号即可定位客户(也支持姓名、游戏名、联系方式),按
/快速聚焦。
4.1 工作台速用
- 按尾号快速完成:日历右侧「当日待完成」顶部输入手机尾号 → 回车。
- 只匹配到 1 项:直接完成并提示;
- 同一尾号有多个客户,或一个客户今天有多个服务:弹出勾选面板,默认全选,可多选后一次完成。
- 加急:每条任务右侧点「加急」,会加红色
加急标签并排到最前;日历格子出现红点,大屏「今日待打」同步置顶。再点一次取消。 - 按套餐分日历:套餐编辑页勾选「在日历中独立显示」后,工作台与大屏顶部会出现套餐标签(全部 / 每日任务 / …),可以只看某类委托的排单。
- 打手颜色:
设置 → 管理员里给每个管理员选一个颜色。客户详情或编辑页可以把客户指派给某个打手,之后这个客户的所有单子都用该颜色标识:- 日历格左侧竖条 = 当天涉及的所有打手颜色(几种人几条,等分显示);
- 「当日待完成」每条前面有打手色条 + 名字,未指派的显示灰色「未指派」;
- 大屏的今日待打、悬停浮层、热力格竖条同样带颜色。
- 主题:顶栏右上角 ☾ / ☀ 切换亮 / 暗,偏好记在浏览器里;不选时跟随系统。
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→ 重新打开,会回到「创建第一个管理员」的全新状态。 -
打手颜色存在
users.color,客户归属打手是customers.assignee_id;加急标记存在daili.db的task_flags表;套餐的「独立日历」是packages.calendar_separate列,老版本数据库打开时会自动加列,不用手工迁移; -
daili.ini(监听/备份/日志配置)与backup/、logs/都是独立于数据的,迁移时可以不拷,程序会按默认值重新生成。 -
手动备份:
设置 → 立即备份(同样遵守保留份数),备份文件本身也是可直接打开的单文件库; -
导出:
设置 → 导出客户 / 订阅 / 日志 CSV,UTF-8 BOM,Excel 直接打开不乱码。
6. 大屏
- 地址:
http://<本机IP>:17777/screen - 默认
screen_public = true:免登录,电视/副屏直接打开即可(仅建议局域网使用); - 改成
screen_public = false后大屏回到登录保护; - 「记分板」风格,亮 / 暗主题同样可切(右上角 ☾ / ☀):
- 顶部 KPI:客户 / 活跃订阅 / 今日到期 / 7 天内到期;
- 中部:热力月历 + 即将到期 TOP10。热力默认统计「每日待完成单量」,点右上角按钮可切回「到期热力」;鼠标悬停任意格子会浮出当日待办明细(含加急标记);
- 底部:今日到期(左)+ 今日待打(右,加急置顶带红 tag);
- 最下方细条:最近追加流水;
- 套餐勾选「在日历中独立显示」后,大屏顶部会出现套餐标签,可切换只看某类委托;
- 默认 30 秒自动刷新(设置页可改),按
F全屏。
7. 从源码构建
环境:Windows + Go 1.25(本项目在 go1.25.6 上验证)。
.\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。
测试:
$env:GOFLAGS='-mod=mod'; $env:GOPROXY='off'; $env:GOCACHE="$PWD\.gocache"
go test ./...
目录结构:
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 / screen.css / app.js / screen.js
scripts/acceptance.ps1 冒烟清单执行器(119 条断言)
scripts/config-test.ps1 配置文件专项测试(14 条断言)
scripts/smoke.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里排除。
8. 常见问题
Windows 提示「已保护你的电脑」 点「更多信息 → 仍要运行」。这是 Windows 对未签名 exe 的默认拦截。
端口被占用 / 想换端口
改 daili.ini 里的 listen_port 后重启;临时换端口可用 daili.exe -addr :19000。
只想自己用,不让局域网访问
把 listen_ip 改成 127.0.0.1,启动横幅会提示「仅本机可访问」。
手机打不开局域网地址
- 确认
listen_ip是0.0.0.0; - 确认手机和电脑在同一个 WiFi;
- Windows 防火墙首次会弹窗,选「允许访问」(专用网络);
- 用启动时打印的那个
192.168.x.x地址,不要用127.0.0.1。
忘记密码
让另一位管理员在 设置 → 管理员 里新增一个账户即可;如果只剩一个管理员且忘了密码,只能删掉 daili.db 重来。
数据会不会因为程序崩溃丢?
单文件 SQLite + 每次操作即时落盘,正常使用不会丢;每天的自动备份是双保险。仍然建议定期把 backup/ 拷到别的盘。
能放到公网吗?
不建议。程序按「局域网内可信环境」设计:会话 Cookie 有 HttpOnly / SameSite 保护,但没有 HTTPS、没有登录失败锁定,且大屏默认免登录。要放公网请套一层反向代理 + HTTPS + 访问控制,并把 screen_public 改成 false。
9. 安全说明
-
初始管理员(第一个创建的账号,
id=1)在设置 → 管理员每行可以点「编辑」,修改其他管理员的用户名、昵称,或重置密码; -
重置密码会立刻清除对方的登录会话,他需要用新密码重新登录;昵称/用户名的修改不影响会话;
-
自己那一行不显示编辑(改自己的密码用「修改我的密码」),避免误操作把自己锁在门外;
-
其余管理员权限相同,但只能改自己的密码。
-
口令使用 PBKDF2-HMAC-SHA256(12 万次迭代 + 随机盐)存储,不可逆;
-
所有写操作记录操作人、时间、IP 与摘要,可在「操作日志」里查询;
-
服务端模板全部走
html/template自动转义; -
所有 POST 校验
Origin,会话 Cookie 使用SameSite=Lax+HttpOnly; -
配置文件里的数据库/备份/日志路径请自行确保目录权限,程序不会覆盖已有文件(备份重名会跳过)。