WorkBuddy
|
83a1eed283
|
fix+polish(frontend): 预测弹窗打磨 + 全量语义色替换 + 前台交互小项
预测弹窗:
- 修复模板字符串直写 JSX,反引号原样显示在结果面板的 bug
- 入场动画(遮罩淡入+面板上浮);初始聚焦/Tab 焦点陷阱/关闭还焦;
打开时锁定背景滚动
- 「关闭窗口即取消」→ 如实描述:前端仅停止进度显示,后台任务
可能仍在执行并消耗额度
语义色:全部 emerald/amber/rose 裸色替换为 ok/warn/bad token
(Standings 分区与走势、KeyRing、定时任务、任务状态点、
数据完整性进度条、预测命中标记、净胜球)。
前台交互:
- 空态跳后台 a[href] → Link,不再整页刷新
- 联赛 tab 溢出右缘渐隐提示 + aria-current
- 状态行右侧计数组加细线分隔,小屏 wrap 后层级仍清晰
|
2026-09-22 16:56:19 +08:00 |
|
WorkBuddy
|
9c77efaa4b
|
refactor(frontend): 报头抽组件 Masthead + 回到顶部抽组件 BackTop
- Masthead:统一 HomePage/StandingsLayout 双份复制实现,当前版面
印报红高亮 + aria-current;⚙ 字符改线条 SVG;管理类入口加 title
提示需登录,访客不再被无声踢到登录页
- BackTop:两页各一份的浮动按钮收敛为共享组件,去 rounded-full/
shadow-lg,与全站方角无阴影语言对齐
|
2026-09-22 16:55:59 +08:00 |
|
WorkBuddy
|
b9beddacf7
|
feat(design): ok/warn/bad 语义色 token + btn-outline + 弹窗动效 keyframes
- tailwind 新增语义状态色三组(纸底文字级 600/700 对比度 ≥4.5:1,AA),
色相降饱和贴近墨色印刷感;后续替换全部裸 tailwind 原色(emerald/amber/rose)
- 补 btn-outline 定义:此前三处使用但从未定义,样式静默失效
- 弹窗入场动画 keyframes:遮罩淡入 0.18s + 面板上浮 0.2s,仅 opacity/transform
|
2026-09-22 16:55:58 +08:00 |
|
WorkBuddy
|
db3c64d49b
|
test: 修复 standings 约束断言的 flaky(匿名约束 name=None)
与 log-persistence 分支同款修复:constraints 集合含匿名约束(name=None),
any() 子串匹配撞上 None 直接 TypeError,集合顺序不定导致间歇失败。
先判真值再匹配。
|
2026-09-22 12:46:43 +08:00 |
|
WorkBuddy
|
d013407aa1
|
feat(admin): 采集任务历史列表 + 图标语言统一 + 存量样式修复
- dal 新增 fetchIngestJobs(接已有 GET /admin/ingest/jobs,此前前端未用)
- Collection 新增「最近任务」卡:最新 10 条,状态点/任务类型/结果摘要,
任务终态后自动刷新 —— 补齐采集失败后的回看入口
- 任务选择按钮修复:brand-* 色在 tailwind 中不存在(选中态样式失效),
改用全站 press/press-wash;去掉与报刊风方角语言不符的 rounded-lg
- 任务图标 ⚽🏆📊 → ◈◇▤⇉,与全站几何字符一致,不混用 emoji
- Logs 页描述补充 LOG_FILE 持久化边界说明
|
2026-09-22 12:44:29 +08:00 |
|
WorkBuddy
|
0ab838258c
|
feat(admin): Settings 分区 tab 化 + Dashboard 改待办驱动
- Settings(496 行五块内容单页)拆为 4 个 tab:数据源/LLM/定时任务/登录认证;
tab 状态写入 URL(?tab=llm)可深链;数据加载与 API 调用不变
- Dashboard 改待办驱动:死信待处理/联赛数据缺口/可结算预测,
有待办才显示,每项直达处理页;全部清零时显示状态行
- 三步工作流卡仅在库里无比赛时显示(首次引导,老用户不再占版面)
- 修文案漂移:「数据源健康」卡不再引用已不存在的「数据源」页;
「运行预测」链接语义修正(预测在前台比赛详情页发起)
- 数据源单源硬编码列表简化为一行;未配 Key 直达设置分区
- <a href> 全改 <Link>,不再整页刷新
|
2026-09-22 12:44:12 +08:00 |
|
WorkBuddy
|
9cedb874f7
|
refactor(admin): 导航按心智模型三组重排(数据/预测/系统)
- 预测历史/回测/评估移出「数据流水线」,组成「预测」组(生命周期闭环)
- 「评估与监控」拆开:评估归预测,监控归系统
- 删除 SIDEBAR_GROUP_TITLES 别名映射(分组名与面板名统一)
- 预测历史图标改 target(靶心),数据完整性改 eval(核对文档),不再与仪表盘共用 chart
|
2026-09-22 12:34:18 +08:00 |
|
WorkBuddy
|
1cdc6411f5
|
chore(admin): 删除合并后遗留的 4 个死代码页面(~1280 行)
DataSources/LLMConfig/Config 已合并进统一 Settings 页,
Predictions 已被 PredictionHistory 取代,均无任何路由或 import 引用。
|
2026-09-22 12:33:15 +08:00 |
|
WorkBuddy
|
f0c1cc1491
|
feat(logging): 日志持久化落盘(LOG_FILE 滚动文件)
- LOG_FILE 配置: 空(默认)保持 stdout + Admin 内存日志页(重启清零);
填路径后额外写 RotatingFileHandler(单文件 10MB × 5 份,utf-8)
- setup_logging 幂等挂载(按 abspath 判重,相对/绝对同文件算一个);
目录自动创建;文件基础设施失败只 warning 绝不拖垮启动
- docker-compose: api 挂 applogs 卷,默认 LOG_FILE=/app/logs/app.log,
容器重建日志不丢;.env.example/.gitignore/docs 同步
- tests/test_log_persistence.py: 落盘/幂等/空值禁用/自动建目录/失败降级
- test_p0_standings_cutoff: 匿名约束 name=None 使 any() 子串匹配 TypeError
(集合顺序不定 → flaky),先判真值再匹配(存量缺陷顺手加固)
|
2026-09-22 12:18:03 +08:00 |
|
WorkBuddy
|
6bdb1f8ae6
|
docs(deploy): 补充生产上线检查清单(README + docs/06)
- docs/06 新增「生产上线检查清单」10 项:APP_ENV / SECRET_KEY 强随机 /
管理鉴权二选一 / 数据库强密码 / HTTPS+Cookie Secure /
TRUST_PROXY_HEADERS+X-Forwarded-For / Nginx limit_req 限流前置
(应用内限流与 KeyRing 单进程限制) / uvicorn 单 worker /
/health 与 /health/ready / alembic 迁移已内置
- README 在部署说明后加同名精简清单,链接到 docs/06 详情
- 全部条目均对齐真实实现(security_check 弱值黑名单、auth.py
secure=production、deps.py TRUST_PROXY 解析、app.py 双健康检查、
compose/Dockerfile 内置迁移);纯文档,未改业务逻辑
|
2026-09-21 21:02:16 +08:00 |
|
WorkBuddy
|
80616cf459
|
docs: 全套文档与代码对齐(P1-1,数据源收敛 bzzoiro 后的存量漂移清理)
- 数据源:全部文档统一为 bzzoiro 唯一来源,删除 understat/api-football
作为现行数据源的表述(仅保留历史注记);.env.example/docs/06 移除
失效的 API_FOOTBALL_KEY
- 多 Agent:injuries 阵容完整性 → standings 联赛排名
(README/01/04 的专家表、数据流图、no_data 示例、agent_weights、
prompt 清单全部对齐 form/stats/home_away/standings/h2h)
- 数据库:6 张表 → 12 张表,补 standings/app_settings/schedules 与
4 张治理表说明(标注 raw_events/data_quality_checks/data_lineage
为预留未启用,ingest_failures 已启用),predictions 补 agent_weights 列
- API 文档(03):新增鉴权模型三档表;context/standings/leagues 公开端点
补全;predictions/eval/backtest 标注需管理员;ingest/bzzoiro 补
task/limit/season 参数契约,删除 understat/injuries 端点小节
- 目录树(README/01/07):与真实 src/、frontend/src 结构一致
(core 7 文件、data 7 文件、移除不存在的 retry.py)
- 采集命令(02/05):understat 回填 → task=stats/standings;
补管理员凭据提示;docs/08 文首标注历史/过时
|
2026-09-21 20:55:48 +08:00 |
|
WorkBuddy
|
a24017eb69
|
feat(api): 比赛上下文与联赛列表改公开只读(P1-2/P1-3)
- GET /matches/{id}/context 移除 require_admin:公开站详情页「近况/交锋」
数据来源,匿名 401 会导致前端静默空态;响应结构不变,不触发 LLM
- GET /leagues 改公开只读:公开站联赛筛选动态加载;仅返回
id/code/name/country 四个展示字段,不含配置或密钥信息
- 前端新增 useLeagues hook:优先请求 /api/v1/leagues,失败/空回退
本地五大联赛常量(已知代码保留中文标签,新联赛按 API 名称追加)
- 新增 tests/test_public_readonly_api.py(6 项):匿名 200/404、响应形状、
路由依赖声明检查(matches 路由无 require_admin)+ 判别力守卫
(ingest 路由必须检出 require_admin,防检测器恒真)
|
2026-09-21 20:55:26 +08:00 |
|
WorkBuddy
|
d9aeff2114
|
debt(D7): 限流/KeyRing 单进程限制说明 + 生产启动一次性告警
- app.py lifespan:APP_ENV=production 时 logger.warning 提醒
进程内 rate-limit/KeyRing 仅单进程有效(不 sys.exit、不引入 Redis)
- key_ring.py docstring 补充多 worker 不共享的后果说明
- deps.py 注释已具备(单进程有效,生产前置 Nginx),无需改动
|
2026-09-21 19:51:41 +08:00 |
|
WorkBuddy
|
17e99a4f9a
|
debt(D5): agent_outputs 类型标注对齐真实 JSON 形状(list[dict]|dict|None)
- Mapped[list[dict] | dict | None]:multi 模式存专家报告列表,
历史数据/兼容路径可能存 dict;加注释说明形状来源
- 仅类型标注修正,列类型(JSONB)与数据、迁移均不变
|
2026-09-21 19:51:38 +08:00 |
|
WorkBuddy
|
4514ef4e92
|
debt(D4): bzzoiro Team/League/Match 查找经 Repository 层,纠正空壳表述
- repositories.py 新增 MatchRepository.find_by_league_and_date_range /
find_finished_with_stats(含 stats 预加载),TeamRepository.get_or_create
支持 name_zh
- bzzoiro events/standings/stats 三条管线的联赛、球队、比赛查找与批量预载
全部改走 Repository;事务提交仍由调用方 UnitOfWork 控制
- Standing/RawEvent/Lineage 管线内私有读写保留在本模块(不强行 Repository 化)
- 模块 docstring 纠正为准确表述,移除「已全面 Repository 化」的误导性声明
|
2026-09-21 19:48:56 +08:00 |
|
WorkBuddy
|
e3cacc35e4
|
chore: 忽略本地审查/预览脚手架
.tools/ 与 .preview/ 为本机校验脚本与截图产物,不参与构建,不入库。
|
2026-09-15 17:45:21 +08:00 |
|
WorkBuddy
|
77d01450b1
|
feat(ui): 清爽专业风设计系统与界面重构
设计令牌(tailwind.config.js):
- brand 11 档蓝色阶、ink 11 档冷灰色阶、win/draw/loss 语义色
- 中文字体栈、tabular-nums 数字等宽、2xs 字号
- borderRadius card/pill、boxShadow card/raise/focus、fade-up/shimmer 动画
组件层(index.css):
- @layer base: 全局背景/字色/抗锯齿、统一焦点环、自定义滚动条,
尊重 prefers-reduced-motion
- @layer components: .card .btn .field .pill .skeleton .segment
界面:
- App.tsx: SVG 队徽替代 emoji、sticky 毛玻璃头部、服务状态指示、页脚免责声明
- Matches.tsx: 比赛列表卡片化;新增 TeamMark(队名哈希色相队标)、
EdgeBar(home_edge 双向可视化)、OutcomeCard、AgentCard(状态徽章+证据
列表+无数据说明)、骨架屏;SVG Spinner 替代 emoji 转圈
可用性修复:
- 预测面板拆为独立的胜平负/置信度/模式/耗时卡片,胜平负与比分各有
独立可视化条
- 移动端横向溢出:比赛行改为 flex-col -> sm:flex-row,元信息与操作按钮重排
- 无数据专家卡片补充说明文案,不再空白
保留原有契约:loadSeq/predictSeq 竞态防护、游标分页、全部类型定义。
验证:tsc --noEmit(strict)0 错误;vite build 成功;
360/390/414/768/1280px 五档实测 scrollWidth === viewport,零横向溢出。
|
2026-09-15 17:45:16 +08:00 |
|
WorkBuddy
|
acea7e699d
|
fix(frontend): 修复 postcss 配置误写导致 Tailwind 从未生效
postcss.config.js 中写入的是 Tailwind 的配置对象(content/theme/plugins),
缺少 PostCSS 插件注册所需的 plugins 字段,导致 PostCSS 从未加载 tailwindcss。
所有 className 上的 Tailwind 工具类一直是无效字符串,页面仅使用浏览器默认样式。
修复为标准写法 plugins: { tailwindcss: {}, autoprefixer: {} }。
验证:构建产物 CSS 由 1.81 kB 增至 24.66 kB,且不再残留未编译的
@tailwind / @apply / @layer 指令。
|
2026-09-15 17:45:10 +08:00 |
|
WorkBuddy
|
f965650c10
|
test+docs: P0 回归测试与文档同步
回归测试(P0 三项,不依赖 DB 的静态断言):
- tests/test_regressions.py:
· context_builder 三个查询函数 + backtest 必须 eager-load 关系
· Match.{stats,home_team,away_team,league} 必须 lazy="selectin"
· bzzoiro normalized_matches 必须携带并解包 (nm, raw) 配对
这些是「集成方式」缺陷,原先的单元测试(全 mock slice_fn/provider)
照不出来,故此专项静态锁定。
文档同步(修正与实际不符的表述):
- docs/01-architecture.md: app.py 标注改为「lifespan 仅验证连接,不建表」;
补 deps.py / repositories.py / unit_of_work.py / validation.py /
backtest.py / sources.py / http_client.py / retry.py;迁移范围改 0001~0006
- docs/07-development.md: 目录树修正 leagues.py→matches.py,补齐
backtest.py / deps.py 及 0003~0006 迁移文件
附带: src/core/retry.py 加 NOTE 说明其当前无调用点(P3),
避免「看似有重试实则未生效」的误判。
|
2026-09-15 16:55:54 +08:00 |
|
WorkBuddy
|
c89bfe2af7
|
fix(P2): no_data 结构化、权重校验、鉴权与前端竞态
P2-1 no_data 门控依赖文案子串(脆弱):
- context_builder 新增 SliceResult(text/has_data/n_records),
5 个切片函数改为显式声明 has_data
- base._slice_has_data() 优先取结构化结果,str 返回仍走文案回退
(兼容既有测试 mock 与自定义切片)
- build_context 的 has_stats/has_injuries 直接取切片声明
P2-2 agent_weights 无校验即落库:
- validation 新增 AgentWeightsSchema / validate_agent_weights:
未知专家名丢弃、越界值钳制、总和非 1 时归一化
- orchestrator 落库前对 agent_weights 做校验
P2-3 1x2 与比分不一致被静默修正:
- 仍以比分修正,但补 logger.warning 暴露 LLM 自相矛盾
P2-5/P2-6 prompt 缓存不可刷新 + 缓存键不含模板内容:
- 新增 clear_prompt_cache() 供改模板后显式失效
- 缓存键纳入模板内容 hash,模板一改缓存自动失效
P2-7 ingest/backtest/settle 接口无鉴权:
- 新增 require_admin_key 依赖(X-API-Key),
ADMIN_API_KEY 未设置时放行并告警(不破坏本地开发)
- 挂到 3 个 ingest 接口 + backtest + eval/settle
P2-8 前端请求竞态 + 未使用游标分页:
- Matches.tsx 用递增 seq 丢弃过期响应,避免旧筛选结果覆盖新筛选
- 接入后端已有的 cursor 分页 + 「加载更多」按钮
附带: .env.example 补齐 LLM_TIMEOUT / 分档模型 / ADMIN_API_KEY;
tests 新增 10 个用例覆盖 P2-1/2/3。
|
2026-09-15 16:54:05 +08:00 |
|
WorkBuddy
|
71bf723a10
|
fix(P1): 消除迁移漂移、回测缓存污染与历史数据越界风险
P1-1 schema/ORM 漂移:
- 新增 0006 迁移: 回填后 drop 幽灵列 predictions.cutoff_at,
并幂等重建 ix_match_stats_available_at / ix_predictions_cutoff_at
- models.py 为 MatchStats / Prediction 声明上述两个索引,
使 autogenerate 不再误建议删除
P1-2 迁移对历史越界数据不安全:
- 0004 在建 CHECK 约束前先 op.execute 清洗越界行
(负数进球/置信度越界/非法 1x2/mode),避免 ALTER 中途失败
导致迁移卡在半完成状态
P1-3 回测缓存污染:
- predict_match / _predict_single 新增 use_cache 参数,
use_cache=False 时既不读也不写进程内缓存
- run_backtest 显式传 use_cache=False,避免 settle 到旧记录
P1-5 understat 关系属性访问:
- MatchRepository.find_by_teams_and_date 加 selectinload(Match.stats)
P1-6 日期键构造不统一:
- bzzoiro 抽取 _to_date()/_match_key() 统一去重键构造,
消除 str(date) 与 isoformat() 的隐式格式依赖
|
2026-09-15 16:48:03 +08:00 |
|
WorkBuddy
|
235fb0de97
|
fix(P0): 修复三处静默失效的阻断缺陷
P0-1 backtest 会话生命周期:
- _get_historical_matches 加 selectinload(league/home_team/away_team),
并在 session 内物化为 BacktestCandidate 纯数据快照,避免 session
关闭后访问惰性关系抛 MissingGreenlet
- except 收窄并改用 logger.exception 保留堆栈
- 移除未使用的 and_ / League 导入
P0-2 切片函数关系属性 MissingGreenlet:
- models.py 为 Match.league/home_team/away_team/stats 声明 lazy=selectin
- context_builder 的 _get_form/_get_h2h/_get_home_away 显式 selectinload
(修复 form_slice/h2h_slice 恒定失败,被 fail-open 掩盖的问题)
- repositories.find_by_teams_and_date 加 selectinload(Match.stats)
P0-3 bzzoiro raw 变量泄漏导致血缘错乱:
- normalized_matches 改为携带 (nm, raw) 元组,内层循环解包
- source_event_id / source_record_id 现在取到正确 event id
- 已有比赛补建 stats 时补齐 source/source_event_id/retrieved_at/available_at
- 抽取 _match_key()/_to_date() 统一日期键构造 (P1-6)
|
2026-09-15 16:45:09 +08:00 |
|