5.7 KiB
服务端交付报告
实现目录:perler-beads-server,Go 1.25 + Gin + pgx PostgreSQL + Redis。全部 JSON 接口为 HTTP 200 与 {code,msg,data};code=403 未登录、401 未授权。所有计划接口已经注册。打印成功为 SVG 下载。
启动
先启动 PostgreSQL 和 Redis,使用 .env.example 配置 DATABASE_URL、REDIS_URL、ADMIN_USERNAME、至少 12 字符的 ADMIN_PASSWORD、APP_ENV,再执行 go run ./cmd/server。应用不会自动读取 .env,请通过容器环境或 shell export 注入。生产环境必须提供 WECHAT_APP_ID 和 WECHAT_APP_SECRET;仅 APP_ENV=development 接受 dev-guest。开发登录是共享演示身份,不应用于真实顾客。
启动时执行内嵌幂等迁移 internal/server/migrations/001_initial.sql。SEED_DEMO=true 在没有门店时写入 store-001、两个启用桌位、四张原创 7×7 像素图纸和十种 DEMO 色号。演示地址、营业时间和色号必须在上线前替换。生产环境不提供内存数据库降级。
数据与权限
业务数据持久化 PostgreSQL;Redis 只保存随机令牌会话与登录限流。令牌 SHA-256 哈希作为 Redis 键,有效期七天。微信 code2Session 请求只在服务端执行,openid 派生稳定用户 ID,平台密钥不返回。管理员账号从环境获取。默认不信任 X-Forwarded-For,以免伪造 IP 绕过限流;部署在代理后登录限流按代理源 IP 聚合,请在真实网络压力验证中评估并配置可信代理支持。
图纸大小限制 200×200,色板最多 512 色;色号必须唯一、hex 必须六位十六进制,cells 数量必须匹配尺寸,所有非透明格必须引用已定义颜色。保存图纸必须匹配当前门店维护色板,物料位置由服务端权威数据回填。顾客保存只能 private,所有者才能删除;私有图纸访问验证身份,工作人员只能访问所属门店。后台账号当前属于 store-001,无跨店授权。
桌位必须存在且已启用。服务请求从有效到店会话读取门店与桌位,提交时再次校验桌位;打印请求必须引用有访问权限且属于当前门店的图纸。requestId UUID 由数据库主键持久保证幂等,同用户并发请求通过事务锁串行;相同键不同 kind/note/patternId 返回 code=409。重试返回原始创建结果。员工状态只能 pending→accepted→completed,顾客仅取消自己的 pending。状态更新使用行锁,避免并发覆盖。
SVG 标注毫米尺寸、网格色号、坐标、色量、50 mm 校准标尺,pitchMm 为 2–10。文字转义,图纸 ID 不拼接文件路径。临时文件下载后一分钟清理。超大图注明打印机海报/分页模式并保持 100% 缩放。服务请求是人工打印任务,不驱动硬件。支付与储值 capability 恒为 enabled=false。
验证
先写图纸校验、授权状态机、幂等内容散列测试,执行时出现 undefined Pattern/Transition/Fingerprint 的预期失败,再实施。随后增加匿名 API 权限、私有图纸门店隔离、SVG 尺寸与转义检查。
实际执行通过:
GOPATH=/tmp/perler-go GOCACHE=/tmp/perler-go-cache GOMODCACHE=/tmp/perler-go-mod go test ./...
GOPATH=/tmp/perler-go GOCACHE=/tmp/perler-go-cache GOMODCACHE=/tmp/perler-go-mod go vet ./...
GOPATH=/tmp/perler-go GOCACHE=/tmp/perler-go-cache GOMODCACHE=/tmp/perler-go-mod go build -o /tmp/perler-beads-server ./cmd/server
TestPostgresPersistentIdempotency 无 TEST_DATABASE_URL 时跳过。执行该测试必须使用数据库名包含 test 的独立数据库;它检查数据库唯一键且回滚数据。初期系统没有 Docker/PostgreSQL/Redis,随后主任务使用临时 PostgreSQL/Redis 启动真实联调环境;独立 perler_test 数据库的集成测试实际通过。微信真实凭据、生产 DNS/TLS 和打印设备仍未验证。
审查修复
- 服务请求事务内读取图纸与桌位改用同一 tx,避免单连接池等待自己占用的连接;收藏列表采用单次 JOIN 查询,避免 rows 未释放时嵌套借连接。
- 启用 Gin ContextWithFallback,统一给请求 context 设置 15 秒业务操作期限;数据库、Redis 与微信请求继承客户端取消。事务回滚使用独立 3 秒清理 context。
- SVG 页面宽度至少 100 mm,50 mm 标尺完全位于页内;长标题按字符宽度换行,打印提示分行。图纸网格仍精确保持 pitchMm。新增最小图纸在 2/5/10 mm 的尺寸与文本边界测试。
- 演示数据扩为十色及四个原创像素图案(心、菱形、小花、星),全部 DEMO 标识。已初始化的库不会被 seed 覆盖,需要独立新库查看新演示数据。
- 新增 TestSingleConnectionHandlers:独立 TEST_DATABASE_URL 下将 pool MaxConns=1,有限时地验证收藏、打印服务创建与幂等重试;无数据库时明确跳过。
- 修复后 go test ./...、go vet ./...、go build 全部再次通过;随后主任务已使用独立 perler_test 数据库实际执行并通过 PostgreSQL 集成测试。
最终补充
WEBUI_DIR 可配置为后台构建输出目录(需包含 index.html),后端直接提供静态文件及非 API GET/HEAD 深层路由 SPA 回退。未知 /api 与 /api/* 仍返回 HTTP200/code404 JSON。路径验证限制在输出目录内,拒绝目录穿越与指向目录外的符号链接;新增临时目录静态文件、SPA、API 和符号链接测试。
自定义异常恢复统一返回 HTTP200/code500 JSON。SVG 标题、尺寸说明与坐标分开预留行,色号字体按长度缩放以限制在格内。上述修改的 go test/vet/build 已通过;主任务随后已切换到更新后的二进制,并再次通过 23 项 API 联调断言及最终真实数据库回归。