Skip to content

Latest commit

 

History

History
67 lines (47 loc) · 7.35 KB

File metadata and controls

67 lines (47 loc) · 7.35 KB

tech-design · v0.9 · 根路径公开落地页

配 PRD v0.9。零 schema、不碰认证逻辑、/login 行为不变。目标:把根路径首屏从「裸登录框」换成「公开介绍页」,既给项目对外门面,又消除 Chrome「Deceptive pages」误判的触发特征。

决策 108 · / 挂哪 + 怎么分流

  • 选定:改既有 common.HomeController(它本就 @GetMapping("/"),v0.7 FR-133 做已登录的 dashboard/onboarding 分流),在最前面加匿名分支:
    • me == null(匿名)→ return "landing"(公开落地页);
    • me != null → 沿用原逻辑:有周期+账户 → redirect:/dashboard,否则 → onboarding/index
  • ⚠ 教训(2026-06-25 真踩):我起初新建了 web/HomeController,与既有 common.HomeController 同简单类名 → 同默认 bean 名 homeControllerConflictingBeanDefinitionException,Spring 上下文启动失败、beta 崩溃重启循环。根因是我探落点时只 grep web/、漏了 common/ 里早有的 / 映射(正是「没核对既有逻辑就新增」)。改为在既有控制器上加分支,既不冲突、又自然保住 onboarding 行为。
  • 备选(否) SecurityConfig defaultSuccessUrl / forward:分流逻辑塞进安全配置隐晦难测。
  • 备选(否) 模板内 th:if 切登录态:已登录用户没必要加载落地页 DOM,redirect 更干净。

决策 109 · Security 放行 /

  • SecurityConfig.authorizeHttpRequestsrequestMatchers(...permitAll) 清单里加 精确根 "/"(不是 /**)。匿名 GET / 才能命中 HomeController 返回 landing,而非被 anyRequest().authenticated() 重定向到 /login
  • 安全自检:只加 "/" 一条;anyRequest().authenticated() 不动;/dashboard/reports/accounts 等仍需登录(QA v09-LAND-4 回归守护)。

决策 110 · 落地页模板:复用 layout head,零外部 CDN

  • templates/landing.html:<head th:replace="~{fragments/layout :: head('家庭账房 · Family Ledger')}"> —— 复用既有骨架(自托管 /vendor/tailwind.js + 三套字体 + style.css,无任何外部 CDN,正好契合自托管/隐私定位)。body 移植 preview 六块,用既有 .eyebrow + Tailwind 工具类(bg-card/border-rule/text-brass-deep/text-forest/text-rust,均映射到 style.css 的 CSS 变量),全 inline SVG、无 emoji(承 [[feedback_no_emoji]]),文案非技术家人可懂(承 [[feedback_user_friendly_naming]])。
  • 匿名访问不传 nav(无家庭)→ head 的 favicon 命中 nav != null 守卫、回落预设 icon2;buildVersionGlobalModelAdvice 全局注入,匿名页同样有。

决策 111 · 截图静态资源:落本地,不外链

  • docs/screenshots/feature_summary_total.jpg 复制到 src/main/resources/static/img/feature_summary_total.jpg,模板用 @{/img/feature_summary_total.jpg(v=${buildVersion})}
  • 备选(否) 沿用 preview 的 jsDelivr 外链:自托管产品的对外首页依赖外部 CDN 不合适(离线可用性 / 隐私 / 墙),且 /img/** 已在 permitAll。代价:jar +~0.47MB,可接受。

向后兼容 / 验收

  • 零 schema;不改认证、会话、/login;已登录访问 / 仍直达 dashboard。
  • QA(qa-run v09-LAND-*):① 匿名 GET /=200 且含定位文案 + GitHub 全 URL + 截图引用;② 匿名 / 不再 302 /login;③ 已登录 /→302 /dashboard;④ 回归:匿名 /dashboard 仍要求登录(permitAll 没放过头)。
  • 降钓鱼信号(FR-162):首屏 HTML 即品牌 + 说明 + 开源链接的真实页。
  • 文档同步:prd/v0.9 + 本文件 + docs/qa-cases + CHANGELOG + README(承 [[feedback_doc_sync]])。

v0.9.1 · 落地页精修(决策 112)

  • 布局:参考 brew.sh / ohmyzsh.sh 改居中单列;landing.html 复用 layout head(自托管 tailwind/字体/css),v0.9.1 专属 CSS 内联在模板 <style>(grain / github-corner / reveal / cmd-block / ghstar / q-item / pillar-hover),全 inline SVG、无 emoji、prefers-reduced-motion 静默。
  • 快速开始改真实 4 步:git clone → cd → bash deploy/docker-init.sh → docker compose up -d(取自 README,不假装一键)。
  • GitHub-Star / star 数:客户端 api.github.comstargazers_count,0/失败 → 数字段 :empty 隐藏(不露怯)。

决策 112 · 工程数字带「发版联动」(本次新增的硬门)

  • 问题:landing 的 4 个数字(版本/单测/迁移/黑盒)若写死,会随迭代过时(用户选 B:保留精确数字)。
  • 选定:不在运行时算(应用不知道自己的测试数),而在发版预检钉一道硬门 —— release-prod skill preflight 计算 版本=prd/v0.*.md 个数、迁移=db/migration/V*.sql 个数(确定可算),单测/黑盒 取 README 的「N 单元 / N 黑盒回归」(发版本就要更 README,landing 跟它走);校验 landing.htmldata-stat(version/tests/migrations/blackbox)与之一致,任一过时 → die 给出应改值。qa-run v09-LAND-6 同口径日常守护。
  • 备选(否) 构建期注入 / 运行时计算:加构建复杂度,且"测试数/黑盒数"运行时不可得;发版门 + README 单一事实源最省心可靠。
  • 备选(否) 数字 round 成约数(A 方案):用户要精确,改走联动。
  • landing 用 data-stat="version|tests|migrations|blackbox" 锚点,校验脚本据此精确取值(非顺序依赖)。

v0.9.3 · 表单缺项前置拦截(决策 113)

决策 113 · 条件必填:声明式 data-require-when 通用助手 vs 逐表单内联 JS

  • 背景:全站写表单审计后,多数必填项直接挂原生 required 即可;但有几处是条件必填——应急金 fixedBaseline 仅当 autoBaseline=false 时必填、自选股 costBasis 仅当 deductCash 勾选时必填。HTML 的 required 是静态的,无法表达「某控件命中才必填」。
  • 备选 A(否)· 每个条件字段写一段内联 JS:监听控制控件、toggle 目标字段 required。直观但散落、重复、易漏绑;新增一处就复制一段。
  • 备选 B(否)· 后端校验为主:提交后服务端报错回 toast。但本次目标就是「别发请求」——后端校验是兜底不是前置,且体验差(往返一次才知道)。
  • 选定 C · 声明式 data-require-when="控件名=值" + layout footer 全站一处助手:字段上声明依赖,助手统一扫描并绑定。
    • 取值规则:同表单内名为「控件名」的控件——radio 取 :checked 的 value、checkbox 勾选→其 value(默认 'true')未勾→'false'、其它取 .value;== 指定值则 required,否则摘除。
    • change 实时同步;htmx:load 对换入片段再绑(本站条件字段都是整页表单,这是冗余保险)。
    • 为何更好:声明式、零重复、未来任意表单加一个属性即生效;摘除逻辑天然避免「对隐藏/不适用字段误挂 required → 提交被卡死且无可见报错」这个 HTML5 校验经典坑(承 data-searchable 下拉因 display:none 不可挂 required 的既有教训)。
  • 原生 required 仍是主力:HTMX 尊重 HTML5 校验,提交前即拦,无需额外代码;data-require-when 只补「条件」这一维。
  • 防回归:qa-run v09-FORM-1~6 静态扫模板断言各字段挂载到位 + 助手就位;区分「故意可选」与「漏挂必填」写进 qa-cases 注释,防后人误删或误加。
  • 纯模板 + 1 处全站脚本,零 schema、向后兼容(承 [[feedback_prod_backward_compat]] [[feedback_doc_sync]])。