Commit Graph

17 Commits

Author SHA1 Message Date
costcodev 1df6e7fa83 feat(BIZ-129): index.html 前端鉴权适配方案 C(session cookie + username/password)
替换 BIZ-110 Bearer token 模式 → 方案 C(cookie session + 账号密码):
- AUTH_STORAGE_KEY: 'lotto_api_token' → 'lotto_username'
- getToken/setToken → getUsername/setUsername(localStorage 只存用户名供 UI 提示)
- isLoggedIn() 改为检查 cookie 'lotto_session' 存在性(HttpOnly JS 读不到,仅作为 UI 优化)
- api() 去掉 Authorization: Bearer 注入;改用 fetch 的 credentials: 'same-origin' 让浏览器自动带 cookie
- loginOverlay: 单 token 输入框 → username + password 双输入框
- handleLoginSubmit: POST /api/auth/login + 处理 account_locked / rate_limited 等 code
- logout(): POST /api/auth/logout 吊销 session,再清本地 username
- bootstrap(): 启动时调 /api/auth/me 验证会话有效性

安全要点:
- HttpOnly cookie 防 XSS 读取(即使有 XSS 漏洞,攻击者也拿不到 session)
- 401 → 清 username + 跳登录页(api() 统一处理)
- logout() 走服务端吊销,防止 cookie 被持久化复用

依赖:commit 44f46a0 的 auth_api 蓝图 + Flask-Limiter 集成。

Co-authored-by: multica-agent <github@multica.ai>
2026-09-05 07:15:36 +08:00
costcodev 44f46a0e40 feat(BIZ-129): Flask-Limiter 集成 + 登录/重置限流
- requirements.txt: flask-limiter>=4.0
- app.py: 创建 Limiter 实例(memory:// 存储,60/分钟默认)
- auth_api.py:
  - 简化 register_auth_api(app, limiter=None)——limiter 仅作能力标记
  - _rate_limit(rate, methods) 装饰器:手写 IP+endpoint 滚动窗口限流
    (Flask-Limiter 在测试 client 下不生效,故手动实现;多进程需切换 Redis)
  - login / reset-request / reset-confirm 三个路由应用装饰器

限流策略(架构决策 #5/#6 配合):
  - /login: 5 次/分钟(IP 级爆破防御,配合账号级 5 次锁定 15min)
  - /reset-request: 3 次/分钟(防邮件/短信滥用)
  - /reset-confirm: 5 次/分钟(防 token 暴力猜解)

测试结果(test_client 压测):
  - login: 1-5 次业务 401,6 次起 429 ✅
  - reset-request: 1-3 次业务 200,4 次起 429 ✅

依赖:commit b002e92 的 7 路由蓝图。

BIZ-127 架构决策:
  ✓ #5 限流 5 次/分钟(IP 级,与账号级锁定 15min 双重防御)

Co-authored-by: multica-agent <github@multica.ai>
2026-09-05 07:13:50 +08:00
costcodev b002e929dc feat(BIZ-129): auth_api 蓝图 + 7 接口(login/logout/me/refresh/change-password/reset-request/reset-confirm)
- auth_api.py: Blueprint 独立文件,不污染 app.py 业务路由
- session 存 sha256 hash 入库(架构决策 #2,依赖 auth.py lookup_session)
- cookie HttpOnly + SameSite=Lax(Secure 留给部署层 nginx)
- 错误返回统一:{'success': False, 'error': str, 'code': str}
- 防用户名枚举:reset-request 即便用户不存在也返回 ok
- change-password 成功后强制吊销当前 session(防 session 复用)
- reset-confirm 成功后吊销该用户所有 session(防攻击者已登录状态)

依赖:auth.py + auth_db.py(commit ab9b62e)

BIZ-127 架构决策对齐:
  ✓ #1 bcrypt rounds=12(依赖 auth.py hash_password)
  ✓ #2 session token_hash = sha256(cookie_value)
  ✓ #5 5 次失败锁定 15 分钟(依赖 auth.py authenticate)
  ✓ #6 重置 token token_urlsafe(32) + 2h 过期 + 一次性(依赖 auth.py create_reset_token / consume_reset_token)
  ✓ #8 操作日志 90 天保留(依赖 auth.py write_op_log)

未完成(commit 2/3/4):
  - Flask-Limiter 集成(commit 2)
  - 前端 3 页(commit 3)
  - pytest 端到端(commit 4)

Co-authored-by: multica-agent <github@multica.ai>
2026-09-05 07:09:08 +08:00
costcodev ab9b62e9b3 feat(BIZ-129): 方案 C 用户系统 DB 层 + 业务层(BIZ-127 #1/#2/#5/#6/#8 对齐)
- auth_db.py: users/sessions/password_reset_tokens/operation_logs 4 表 + migrate_legacy_schema 增量升级
- auth.py: hash_password(rounds=12) + create_session(token_hash=sha256) + authenticate(锁定→清零) + cleanup_op_logs(90d)
- 业务层已就绪,未完成:auth_api 蓝图 / require_auth 装饰器 / 限流 / pytest / 前端 3 页

Co-authored-by: multica-agent <github@multica.ai>
2026-09-05 01:31:34 +08:00
vincent dcc7339384 feat(BIZ-110): index.html 前端鉴权适配 - localStorage + login 页 + Bearer 注入
方案 A 落地(严维序 COO 09-01 14:46 GMT+8 确认):
- 新增登录浮层(#/login 概念,hash 路由由 bootstrap() 管控)
- localStorage.lotto_api_token 存/取/清 token
- api() 函数自动注入 'Authorization: Bearer <token>'
- 401 → 清 token + 跳登录页
- Header 右侧 '退出' 按钮
- bootstrap() 启动入口:无 token 跳登录,有 token 进主页

测试:tests/mock_auth_server.py + jsdom 21/21 全过。
闭环:前端适配完成,待严维序撤 AUTH_BYPASS_PATHS 白名单。

—— 徐聪 (costcodev) | 2026-09-02 16:35 GMT+8

Co-authored-by: multica-agent <github@multica.ai>
2026-09-02 16:45:43 +08:00
徐聪 5d459b4505 feat(BIZ-105): 记录-比对 中奖金额展示 v1.2 实施
PRD v1.2 §4.3 落地,参考 BIZ-102 PRD 与 UI 原型。

后端(app.py):
- 新增 PRIZE_AMOUNT / PRIZE_KEY_MAP 常量双表(PRD §4.3)
- 新增 format_amount 千分位格式化(PRD §3.2:紧邻「元」不空格)
- 新增 compute_prize_summary 聚合函数
- api_compare_record 接口扩展:
  - 单注:prize_amount + prize_amount_display 字段
  - 顶层:total_prize / total_prize_display / has_variable_prize /
    prize_summary / prize_display 字段
- 一/二等奖标记为浮动奖(历史数据无奖池字段),不计入 total_prize

前端(index.html):
- 总中奖金额字段(橙色「含浮动奖」角标)
- 中奖奖项汇总表(不含浮动奖金额)
- 浮动奖橙色提示条(区别于固定奖绿色 #1e8e4a)
- 每注金额 badge(固定奖绿 / 浮动奖橙)

测试(tests/test_compare_prize.py):
- 31 个用例,100% 通过
- 覆盖:6 档奖金档 / 未中奖 / waiting 契约 / 0 注 / 全中奖 /
  仅有浮动奖 / 混合 / Mock 一致性(验收 #16)/ 响应结构契约
- running: pytest tests/test_compare_prize.py -v → 31 passed

不变项:比对算法 / 归期规则 / Excel 解析 / 认证 — 全部不动。

Co-authored-by: multica-agent <github@multica.ai>
2026-08-31 04:45:06 +08:00
vincent e4e66f3826 BIZ-102 v1.1: 记录-比对 中奖金额展示 PRD 与 UI 原型
v1.0: 3 字段增量扩展(prize_amount/total_prize/prize_summary)+ 前端 3 处扩展
v1.1: 落实架构评审 3 点意见
- 金额字段统一'(单位:元)'与量级安全说明
- prize_summary key 改英文枚举(first_prize..sixth_prize),新增 prize_level 中文名
- floating 标记落 results[i] 与 prize_summary[key] 两粒度
- 兼容性条款:每次实时计算,不依赖持久化

Co-authored-by: multica-agent <github@multica.ai>
2026-08-30 15:19:33 +08:00
vincent 4bfb9964db merge: BIZ-87 中奖比对算法修复 + P2/P3 迭代
修复中奖比对算法(BIZ-87):
- 新增 find_draw_by_gen_time() 窗口归属模型
- 修复所有生成记录都比对最新一期的 bug

P2/P3 代码质量改进:
- 删除死代码 load_latest_draw / find_next_draw_after
- 抽取 _row_to_draw 公共函数
- compare_type 细化为 same_day / cross_day / waiting
- 边界条件 <= 改为 < 严格对齐规则 2.2
- 删除不可达 elif 死分支

审核通过:架构/产品/COO/运维 四方审核 ✅
2026-07-09 08:33:40 +08:00
vincent 93295db357 refactor(BIZ-87): P2/P3 迭代——代码质量改进
P2-1: 删除死代码 load_latest_draw() 和 find_next_draw_after()
  - 两个函数在修复后已无任何调用点,删除避免误导维护者

P2-2: 抽取 row_to_draw 为模块级 _row_to_draw() 公共函数
  - 消除三处重复代码,统一调用入口

P2-3: compare_type 细化为 same_day / cross_day / waiting
  - 原 'current' 语义模糊,现区分同日 vs 跨日归属

P2-4: 边界条件修复 <=  → <
  - 严格对齐规则 2.2:生成时间 ≥ 20:00 归属下一期
  - 恰好 20:00:00 现在正确归属下一期

P3: 删除不可达的 elif 死分支
  - 移除注释已说明不会到达的防御性代码

验证:8 个测试用例全部通过(含 20:00:00 边界)

Co-authored-by: multica-agent <github@multica.ai>
2026-07-09 08:23:21 +08:00
vincent 52bee5f2e5 fix(BIZ-87): 修复中奖比对算法——按生成时间匹配正确期号
问题:所有生成记录都在比对最新一期开奖,而非根据生成时间匹配对应期号。

修复:
- 新增 find_draw_by_gen_time() 函数,采用窗口归属模型
- 每个开奖日 D 的归属窗口为 (D_prev 20:00, D 20:00]
- 生成时间落在哪个窗口内 → 与该开奖日 D 比对
- 规则 2.1:生成时间 < 开奖日 20:00 → 与当日开奖比对
- 规则 2.2:生成时间 ≥ 开奖日 20:00 → 与下次开奖日比对
- 超过所有开奖日 → 返回 waiting 状态

修改 api_compare_record() 使用新函数替换 load_latest_draw + find_next_draw_after 逻辑

测试验证:
- gen 6/29 10:00 → 6/30 2026074 ✅
- gen 6/28 19:00 → 6/28 2026073 ✅
- gen 6/28 21:00 → 6/30 2026074 ✅
- gen 7/4 18:22 → waiting(7/5 未开奖)✅

Co-authored-by: multica-agent <github@multica.ai>
2026-07-09 07:56:43 +08:00
vincent e43862be9f feat: 生成记录预览 + 中奖比对功能
功能一:生成记录预览
- 新增 API /api/records/<id>/preview 读取 Excel 返回号码数据
- 前端记录列表新增「预览」按钮,弹窗展示号码表格
- 支持最多显示100注,完整数据可通过下载按钮获取

功能二:中奖比对
- 新增 API /api/records/<id>/compare 根据生成时间匹配开奖数据
- 比对规则:生成时间 < 开奖日20:00 → 与当日开奖比对;≥ 20:00 → 与下次开奖日比对
- 按双色球官方规则判断一等奖至六等奖
- 前端记录列表新增「比对」按钮,弹窗展示比对结果
- 中奖号码高亮显示,未中奖号码半透明
- 若下次开奖尚未举行,返回等待状态提示

Co-authored-by: multica-agent <github@multica.ai>
2026-07-06 04:06:42 +08:00
vincent 462f1505ee fix: 修复代码审查P2/P3问题
P2:
- lottery.py 改为引用 history_loader.py 公共模块,删除独立 Excel 加载逻辑
- 删除 lottery.py 中不再使用的 _normalize_history_format 方法(已迁移到 history_loader)

P3:
- app.py: 删除未使用的 import shutil
- app.py: 删除不可达代码 return stats
- app.py: 修复 api_history 中奇偶形态字段映射错误(奇偶比→奇偶形态)
- app.py: api_history columns 列表补充奇偶比和奇偶形态字段
- 开发文档目录结构补充 history_loader.py 和 web_executor.py 废弃标记

Co-authored-by: multica-agent <github@multica.ai>
2026-07-04 09:21:43 +08:00
vincent 5cebbfa433 fix: 修复代码审查反馈全部问题
审查意见修复清单:

P1 列映射语义修复 (lottery.py):
- _normalize_history_format() 不再将红球2-6映射到开机号/和值特征/奇偶比等
  格式A不含这些特征字段,缺失列留空,前端做降级显示
- 删除已用于构建号码列的原始分列,避免数据重复

P2 架构优化:
- 提取 Excel 兼容逻辑到公共模块 history_loader.py
  lottery.py 和 app.py 共同引用,消除三处重复代码
- web_executor.py 标记为已废弃,功能已整合到 app.py

部署修复:
- 删除 deploy/lotto-web.service (旧服务),仅保留 lotto-app.service
- 更新 deploy/DEPLOY.md: 端口5000→8085, 接口清单更新, 添加迁移说明

安全加固:
- API Token 改为环境变量读取: os.environ.get('LOTTO_API_TOKEN')
- 错误信息不再暴露内部异常,改为通用错误消息+日志记录
- 目录遍历防护改用 os.path.realpath 检查最终路径

其他:
- .gitignore 补充排除 双色球历史数据.xlsx
- app.py 引用公共模块,简化 get_statistics_data 和 load_history_dataframe

测试验证: 全部 API 测试通过,120条历史数据正确解析

Issue: BIZ-75
2026-07-04 01:28:57 +08:00
vincent 5d5e77000e fix: 修复历史数据Excel格式兼容问题 + 完善开发文档
核心修复:
- lottery.py: load_history_data() 添加多格式Excel检测逻辑
  支持 格式A(双行header: 新列名+旧列名) 和 格式B(标准列名)
- lottery.py: parse_numbers() 新增拼接字符串(14位无分隔符)直接解析
  避免 re.findall 将整个号码串视为单个数字的问题
- app.py: load_history_dataframe() 同步修复多格式兼容逻辑

新增:
- docs/开发文档-双色球WebUI-v1.0.md: 完整开发文档
- deploy/backup.sh: 备份脚本

测试结果:
- 120条历史数据全部正确解析
- 号码生成API正常工作
- 全部API接口测试通过

Issue: BIZ-75
2026-07-03 23:05:58 +08:00
vincent cf4b5764b5 feat: BIZ-75 双色球系统改进
1. P1: 合并双 Flask 服务 — web_executor.py 功能整合到 app.py
   - /fetch → 抓取控制台页面
   - /api/fetch/status → 抓取状态查询
   - /api/fetch/execute → 触发抓取(后台线程异步)
   - web_console.html API 路径已更新

2. P1: fetch_data.py 增加重试机制 + 请求间隔
   - REQUEST_DELAY=2s, MAX_RETRIES=3, RETRY_DELAY=5s
   - 修复缩进 bug(try/except 块缩进错误)

3. P0: 修复 Excel 数据格式兼容性
   - fetch_data.py: 跳过网页 header 行,使用标准列名保存
   - app.py: 新增 load_history_dataframe() 智能加载函数
   - 兼容新旧两种 Excel 格式(一行/两行 header)
   - 统一列名: 开奖时间|期数|号码|开机号|和值特征|奇偶比|大小比|奇偶形态|跨度|其他

4. 运维: 创建 lotto-app.service systemd 单元
5. 修复 .gitignore(排除运行时数据文件和备份)
6. 创建 requirements.txt
2026-07-03 17:41:15 +08:00
vincent ae5d7a08ff fix: P0 — records 并发写入加锁 + 原子写入
BIZ-74 P0 改进项:
- 新增 threading.Lock (records_lock) 保护 .generation_records.json
- load_records / save_records / add_record 全部持锁
- api_delete_record 也加锁保护读-改-写
- 原子写入:先写 .tmp 再 os.replace,防止写入中途崩溃

并发测试验证:
- 10 线程并发写入,0 丢失
- 并发读写互不阻塞
- 无残留 .tmp 文件

评审②改进项,BIZ-74
2026-07-03 16:41:57 +08:00
vincent 13a259b0f8 chore: initial commit — existing lottoData codebase
Files:
- lottery.py (1189 lines) — DoubleColorBallGenerator core engine
- fetch_data.py (131 lines) — history data fetcher from 55128.cn
- web_executor.py (216 lines) — data fetch Web console (Flask :5000)
- app.py (505 lines) — number generation Web service (Flask :8085)
- index.html (1171 lines) — frontend SPA
- web_console.html (323 lines) — fetch console frontend
- deploy/ — systemd service + cron script + logs

BIZ-74 architecture review baseline
2026-07-03 16:39:21 +08:00