部署并复检统一页面改造(05-30-deploy-unified-page-design-public-check)

这个提交包含在:
2026-05-30 20:18:21 +08:00
父节点 74db53b131
当前提交 f9e127ca56
共修改 8 个文件,包含 173 行新增和 12 行删除
+4 -4
查看文件
@@ -107,7 +107,7 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
### 3. Contracts
- `assertPortalLoaded(page)` must assert route and page content first:
`/portal` URL and the `今日任务` heading.
`/portal` URL and the `今日待处理` heading.
- Navigation-label assertions for the workbench link must accept both `工作台`
and `Portal` while older mocks still use English labels.
- Navigation assertions must be role-aware. Admin/manager smoke may assert
@@ -121,11 +121,11 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
### 4. Validation & Error Matrix
- Page reaches `/portal`, `今日任务` is visible, but nav label is `工作台` ->
- Page reaches `/portal`, `今日待处理` is visible, but nav label is `工作台` ->
smoke should pass.
- Employee reaches `/portal`, role-aware navigation hides `团队`, and employee
pages such as `项目` remain reachable -> smoke should pass.
- Page reaches `/portal`, but `今日任务` is missing -> smoke should fail.
- Page reaches `/portal`, but `今日待处理` is missing -> smoke should fail.
- Page stays on login, `/403`, error fallback or another route -> smoke should
fail.
- New shell label appears only in one live smoke test -> move the compatibility
@@ -133,7 +133,7 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
### 5. Good/Base/Bad Cases
- Good: `assertPortalLoaded` checks `/portal`, `今日任务`, and a workbench link
- Good: `assertPortalLoaded` checks `/portal`, `今日待处理`, and a workbench link
matching either `工作台` or `Portal`.
- Good: `assertMainNavVisible(page, { expectTeam: false })` is used for
ordinary employee live smoke.
@@ -1510,8 +1510,10 @@ onNeedRefresh() {
### 4. Login Entry Hierarchy
- Normal browser: account/password login is primary; Enterprise WeChat and OIDC
entries belong below the primary submit button as secondary methods.
- Normal browser: account/password or email-code login is primary. Enterprise
WeChat is shown as a boundary notice or secondary entry that tells users to
open OA from the Enterprise WeChat workbench; do not require a clickable OAuth
button in ordinary browsers.
- Enterprise WeChat WebView: Enterprise WeChat one-click entry is primary;
account/password login remains available as the fallback path.
- Enterprise WeChat entry changes must remain frontend entry hierarchy changes
@@ -1528,7 +1530,7 @@ onNeedRefresh() {
- keyboard Tab from the username/email field lands on the password input, not
the forgot-password link;
- password recovery request-code and reset flow on `/forgot-password`;
- normal-browser Enterprise WeChat entry as secondary CTA;
- normal-browser Enterprise WeChat boundary notice or secondary entry;
- WeCom WebView Enterprise WeChat entry as primary CTA with password fallback.
- UI design rules check and Playwright screenshot evidence when the auth layout
changes visibly.
@@ -0,0 +1,3 @@
{"file": ".trellis/spec/hyb-oa/project-governance.md", "reason": "Verify task metadata, scoped commits, approvals and finish requirements."}
{"file": ".trellis/spec/hyb-oa/deployment-trialrun.md", "reason": "Verify public deploy evidence, readiness, smoke and can-test declaration."}
{"file": ".trellis/spec/hyb-oa/frontend-react-vite-design.md", "reason": "Verify frontend test and visual baseline expectations."}
@@ -0,0 +1,3 @@
{"file": ".trellis/spec/hyb-oa/project-governance.md", "reason": "Trellis task, deployment approval, commit and solo-maintenance rules for this deploy/check task."}
{"file": ".trellis/spec/hyb-oa/deployment-trialrun.md", "reason": "Public deployment command, smoke, live readiness and rollback gates."}
{"file": ".trellis/spec/hyb-oa/frontend-react-vite-design.md", "reason": "Frontend deploy/test expectations and mature enterprise page baseline for visual checks."}
@@ -0,0 +1,18 @@
# 实施方案
## 步骤
1. 预检本地状态:确认工作区干净、当前 commit、基础前端构建和部署环境契约。
2. 安全部署:运行 `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`,不覆盖远端 `.env`。
3. 公网复检:检查 `/ready`、public bundle copy、live smoke readiness;如果凭据不足,保留脚本 SKIP 记录。
4. 页面抽查:用公网地址检查工作台、待我处理、采购、合同、项目、资产、应付账款等本轮重点页面是否反映统一设计规则。
5. 若发现阻断:只做最小修复并重新部署验证;涉及远端配置、真实数据或写企业微信时停止并单独确认。
6. 收尾:记录结果,提交任务文档或必要修复,明确是否可线上测试。
## 风险控制
- 不使用 `SYNC_ENV=1`。
- 不执行数据库 reset。
- 不执行真实付款、企业微信写入或外部系统写入。
- 不把 secret、账号、完整 payload 写入文档或回复。
- 发现历史 artifact / rsync 清理问题时,先保守排查并只清理部署排除目录或历史产物,不动业务数据目录。
@@ -0,0 +1,97 @@
# 部署最新页面改造并公网复检
## Goal
把已提交的统一页面设计改造部署到公网试运行环境,并完成公网可用性、关键页面冒烟和基础视觉/代码审计,明确当前线上是否可以让用户测试。
## What I already know
- 最新页面改造已提交:
- `d5ef818` 统一业务台账首屏设计(05-30-unified-page-design-review)
- `74db53b` 归档统一页面设计规范重审(05-30-unified-page-design-review)
- 上一轮本地验证通过:`git diff --check`、Trellis task validate、Trellis governance check。
- 用户要求继续推进;结合上一轮结论,本任务范围是部署和公网复检。
- 公网试运行环境是准生产环境,部署和公网检查必须记录命令、commit、`/ready`、smoke 结果和是否可测试。
- 默认部署命令是 `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`,不得覆盖远端 `.env`。
- 部署脚本已包含环境契约、远端 `/ready`、external deps smoke、runtime smoke 和公网 bundle 文案扫描。
## Assumptions
- 本轮只部署已提交代码,不修改生产配置、不重置数据库、不写企业微信、不执行真实付款。
- 如果缺少 live smoke 凭据或真实 OCR 样本,按脚本规则记录 SKIP,不阻塞基础部署结论。
- 如果部署或公网检查失败,先定位并修复最小问题,再重新验证;涉及真实配置变更时再单独请求确认。
## Scope
- 部署当前 `main` HEAD 到 `https://oa.hyinda.com` 对应试运行环境。
- 执行部署前本地质量门禁。
- 执行部署脚本的安全模式发布。
- 执行公网 `/ready`、公网 bundle copy、live smoke readiness。
- 对这次修改涉及的主要页面做公网视觉/交互抽查:
- 工作台
- 待我处理
- 采购
- 合同
- 项目
- 资产
- 应付账款
- 输出是否可以线上测试的结论。
## Out of Scope
- 不改业务功能范围。
- 不新增 UI 设计规则。
- 不做数据库重置、远端 `.env` 覆盖、企业微信写入或真实付款。
- 不导入真实业务数据。
## Requirements
- 部署前必须确认工作区状态,避免带未提交改动发布。
- 使用 `SYNC_ENV=0`,不覆盖远端 `.env`。
- 发布后必须检查公网 `/ready`。
- 面向用户可测试的声明必须以部署和公网检查通过为前提。
- 如果发现视觉或交互问题,优先判断是否属于本轮部署阻断;非阻断问题另建后续改造任务。
## Acceptance Criteria
- [x] 当前 commit 已部署到公网试运行环境。
- [x] `https://oa.hyinda.com/ready` 通过。
- [x] 部署脚本内置 smoke 通过或失败原因已记录。
- [x] `scripts/qa/run-live-smoke-readiness.sh` 通过。
- [x] 至少完成工作台、待我处理和本轮主要业务台账页面的公网抽查。
- [x] 最终结论明确:可以线上测试 / 已部署但仍有阻断 / 未部署成功。
## Test Plan
- `git status --short`
- `cd client && npm run typecheck`
- `cd client && npm run build`
- `node deploy/trialrun/validate-env-contract.js`
- `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
- `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`
- 必要时运行聚焦公网 Playwright 或截图检查。
- `git diff --check`
- `python3 ./.trellis/scripts/task.py validate 05-30-deploy-unified-page-design-public-check`
- `node scripts/qa/check-trellis-governance.mjs`
## Technical Notes
- 相关规范:
- `.trellis/spec/hyb-oa/project-governance.md`
- `.trellis/spec/hyb-oa/deployment-trialrun.md`
- `.trellis/spec/hyb-oa/frontend-react-vite-design.md`
- 相关脚本:
- `deploy/trialrun/deploy.sh`
- `scripts/qa/run-live-smoke-readiness.sh`
- `scripts/qa/check-public-bundle-copy.mjs`
- 本轮部署 commit:`74db53b`。
- 公网 readiness 报告:`artifacts/qa-flow/live-smoke-readiness-20260530-200338`。
- 公网页面抽查截图:`output/playwright/05-30-public-ledger-check/`。
- `r104-public-visual-acceptance` 原先在普通浏览器要求企业微信 OAuth 按钮;当前产品规则是普通浏览器展示企业微信工作台边界说明,企业微信 WebView UA 才展示一键进入按钮。本任务已同步修正断言。
## Definition of Done
- 公网部署与复检完成。
- 阻断问题已修复或明确记录。
- 任务文档、验证命令和提交记录完整。
- 最终回复包含 commit、部署状态、公网检查结果和可测试结论。
@@ -0,0 +1,26 @@
{
"id": "deploy-unified-page-design-public-check",
"name": "deploy-unified-page-design-public-check",
"title": "部署最新页面改造并公网复检",
"description": "部署最新统一页面设计改造到公网试运行环境,并完成公网 readiness、live smoke、视觉抽查和收尾审计。",
"status": "in_progress",
"dev_type": null,
"scope": "deploy,qa,frontend-test",
"package": null,
"priority": "P2",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-05-30",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -6,6 +6,8 @@ import { getExpectedWecomStatus, loginLiveUser, logoutLiveUser } from "./helpers
import { fetchWecomConfig } from "./helpers/smokeAssertions";
const screenshotDir = path.resolve(process.cwd(), "../output/playwright/r104");
const WECOM_UA =
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 wxwork/4.1.20 MicroMessenger/8.0.1 Language/zh_CN";
function screenshotPath(fileName: string) {
mkdirSync(screenshotDir, { recursive: true });
@@ -30,7 +32,8 @@ test.describe("R104 public visual acceptance", () => {
await expect(page.getByTestId("login-identifier")).toBeVisible();
await expect(page.getByTestId("login-password")).toBeVisible();
await expect(page.getByTestId("login-submit")).toBeVisible();
await expect(page.getByRole("button", { name: /企业微信/ })).toBeVisible();
await expect(page.getByTestId("wecom-entry-browser-notice")).toBeVisible();
await expect(page.getByTestId("wecom-entry-browser-notice")).toContainText("企业微信一键进入");
const wecomConfig = await fetchWecomConfig(page);
expect(wecomConfig.status).toBe(200);
@@ -42,15 +45,24 @@ test.describe("R104 public visual acceptance", () => {
await page.screenshot({ path: screenshotPath("desktop-login.png"), fullPage: true });
});
test("公网企业微信入口跳转到 OAuth 边界", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
test("公网企业微信 WebView 入口跳转到 OAuth 边界", async ({ browser }) => {
const context = await browser.newContext({
viewport: { width: 390, height: 844 },
isMobile: true,
hasTouch: true,
userAgent: WECOM_UA,
locale: "zh-CN",
serviceWorkers: "block",
});
const page = await context.newPage();
await page.goto("/login");
await expect(page.getByRole("button", { name: /企业微信/ })).toBeVisible();
await expect(page.getByTestId("wecom-entry-primary")).toBeVisible();
await page.screenshot({ path: screenshotPath("mobile-login-before-wecom.png"), fullPage: true });
await page.getByRole("button", { name: /企业微信/ }).click();
await page.getByTestId("wecom-entry-primary").click();
await expect(page).toHaveURL(/open\.weixin\.qq\.com|work\.weixin\.qq\.com|wechat_redirect/);
await context.close();
});
test("公网员工移动工作台与高频页面保持移动体验", async ({ page }) => {