部署公网并修正 live smoke 角色导航(05-30-deploy-mobile-public-audit)

这个提交包含在:
2026-05-30 15:42:43 +08:00
父节点 df56d6f1a0
当前提交 56faf3b71c
共修改 9 个文件,包含 157 行新增和 6 行删除
@@ -110,6 +110,9 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
`/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
team/admin entries such as `团队`, but ordinary employee smoke must not
require links that `NavLinks` intentionally hides for that role.
- Smoke tests must not require a copy string that is not a product contract for
public availability.
- If the product intentionally renames the primary workbench label again, update
@@ -120,6 +123,8 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
- 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 stays on login, `/403`, error fallback or another route -> smoke should
fail.
@@ -130,10 +135,14 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
- Good: `assertPortalLoaded` checks `/portal`, `今日任务`, and a workbench link
matching either `工作台` or `Portal`.
- Good: `assertMainNavVisible(page, { expectTeam: false })` is used for
ordinary employee live smoke.
- Base: mocked E2E fixtures may still emit `Portal`; live public navigation may
emit `工作台`.
- Bad: live smoke hard-codes only `Portal` and blocks deployment after a valid
Chinese navigation rollout.
- Bad: employee live smoke requires the `团队` sidebar link after role-aware
navigation intentionally hides it.
### 6. Tests Required
@@ -0,0 +1,41 @@
# 部署与审计记录
## Deployment
- 部署目标:`https://oa.hyinda.com`
- 部署代码基线:`df56d6f`,包含移动端工作提交 `ba7391e`
- 部署命令:`SYNC_ENV=0 LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash deploy/trialrun/deploy.sh`
- 环境边界:未同步本地 `.env`
- 远端结果:Docker build、Prisma migrate status、seed config/trialrun 完成;无待应用 migration
- 注意项:rsync 提示一个历史 artifact 非空目录无法删除,但未阻断构建、启动和 smoke
## Public Checks
- `node deploy/trialrun/validate-env-contract.js`:PASS
- `curl -fsS https://oa.hyinda.com/ready`:PASS
- `node scripts/qa/check-public-bundle-copy.mjs https://oa.hyinda.com`:PASS,`findings=0`
- `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`:PASS
- live smoke:3 passed
- live OCR sample:PASS,可解释终态为重复票据类别
- pending OCR 扫描:`SKIP missing DATABASE_URL`
## Mobile Visual Checks
- 公网资源移动专项:`E2E_BASE_URL=https://oa.hyinda.com npx playwright test tests/e2e/dg7-mobile-information-dedupe.spec.ts tests/e2e/r124-mobile-entry-matrix.spec.ts --workers=1 --reporter=list`
- 结果:6 passed
- 截图目录:
- `output/playwright/mobile-ia-flow-remediation/`
- `output/playwright/r124-mobile-entry-matrix/`
- 抽查页面:移动首页财务卡、待我处理 Tab、移动我的、移动报销。
## Code Audit Findings
- `ba7391e` 的移动端改动符合已确认边界:请假只读额度、财务卡条件出现、搜索标签 `当前列表搜索`、审批文案 `待我处理`、移动我的去掉无条件财务入口。
- `client/src` 中未发现旧用户文案 `待我审批`、`搜索报销`、`搜索采购` 或 `付款批次未启用` 仍作为当前 UI 输出。
- 发现 live smoke 旧断言:员工 smoke 仍强制要求侧栏 `团队`,但当前 `NavLinks` 已按角色隐藏员工团队入口。
- 已修正:`assertMainNavVisible` 支持 `expectTeam` 选项,管理员仍检查团队入口,员工断言团队入口不可见。
## Remaining Risk
- 本轮自动化覆盖公网浏览器和企业微信 UA 近似视口,但没有替代真实手机企业微信真机确认。
- `pending OCR` 只因本地缺 `DATABASE_URL` 记录 SKIP;不代表公网 OCR 队列异常。
@@ -0,0 +1,3 @@
{"file":".trellis/spec/hyb-oa/deployment-trialrun.md","reason":"检查部署证据、public smoke 和可测试声明是否满足公网门禁。"}
{"file":".trellis/spec/hyb-oa/frontend-react-vite-design.md","reason":"检查移动视觉和用户文案是否符合统一前端规则。"}
{"file":".trellis/spec/hyb-oa/security-privacy.md","reason":"检查报告和日志摘要不泄露敏感信息。"}
@@ -0,0 +1,3 @@
{"file":".trellis/spec/hyb-oa/deployment-trialrun.md","reason":"公网部署命令、SYNC_ENV=0 边界、部署后 ready/smoke/文案扫描验收。"}
{"file":".trellis/spec/hyb-oa/frontend-react-vite-design.md","reason":"移动端轻动作、视觉检查、条件卡片和当前列表搜索规则。"}
{"file":".trellis/spec/hyb-oa/security-privacy.md","reason":"公网验证和审计过程中避免输出敏感凭据、真实票据和原始 payload。"}
@@ -0,0 +1,21 @@
# Implementation Notes
## Execution Plan
1. 用 `SYNC_ENV=0` 执行既有公网部署脚本,不同步本地 `.env`。
2. 以部署脚本内置远端门禁作为第一层验收:env contract、compose config、Docker build、migration status、`/ready`、external deps smoke、runtime smoke、live smoke、public bundle copy scan。
3. 如部署脚本因非产品问题的 smoke 旧断言失败,先审计实际公网页面和断言来源;只修正测试口径,不改生产数据。
4. 用公网资源运行移动端专项 Playwright,覆盖本轮移动页面视觉和响应式基线。
5. 审计 `ba7391e` 的移动端页面、测试和 spec 变更,记录发现、修正和剩余风险。
## Boundaries
- 不同步本地 `.env`,不输出 Secret、密码或完整账号。
- 不做真实业务写入、真实付款、ERP 写入、银行流水导入或企业微信写入。
- 公网自动化和企业微信 UA 近似视口只作为自动化证据,不替代真实手机企业微信验收。
## Fix Applied During Audit
- live smoke 员工用例旧口径强制要求 `团队` 导航。
- 当前产品导航已经角色化,员工截图显示工作台、报销、采购、项目、资产、请假记录、待办,隐藏团队入口。
- 修正方式:`assertMainNavVisible` 增加 `expectTeam` 选项;管理员保留团队入口断言,员工断言团队入口不可见。
@@ -0,0 +1,46 @@
# 部署移动端收口并公网复检审计
## Goal
将已提交的移动端入口与文案收口改动部署到公网,并完成公网自动复检、代码审计和移动端视觉检查,给出是否可让用户在线测试的结论。
## Background
- 用户已批准“开始部署 公网复检”,并要求随后审计代码和视觉效果。
- 待部署工作提交为 `ba7391e 收口移动端入口与文案(05-30-mobile-confirmed-entry-copy-followups)`,归档提交为 `df56d6f chore(task): 归档移动端入口与文案收敛`。
- 公网是准生产环境,必须使用 `SYNC_ENV=0` 部署,避免覆盖远端 `.env`。
## Scope
- 执行当前 `main` 分支到公网的既有部署脚本。
- 公网复检:`/ready`、部署脚本内 smoke、public bundle 文案扫描、可用凭据条件下的 live smoke。
- 移动视觉检查:重点覆盖移动首页、移动请假、移动报销、移动采购、移动审批、移动我的。
- 代码审计:复核本次提交对移动入口、文案、条件显示、测试和 spec 的影响,列出风险与建议。
## Out Of Scope
- 不修改生产 `.env`。
- 不做真实付款、银行流水导入、ERP 写入或企业微信写入。
- 不新增业务功能或修复非本轮部署阻断之外的问题。
- 不把自动化企业微信 UA 验证等同于真实手机企业微信验收。
## Acceptance Criteria
- [ ] `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` 完成或给出明确阻断原因。
- [ ] 公网 `/ready` 通过。
- [ ] 公网只读 smoke / readiness 按可用凭据执行,缺凭据时记录 `SKIP` 而非伪装通过。
- [ ] 公网移动视觉截图或 Playwright 检查覆盖本轮变更页面。
- [ ] 代码审计覆盖 `ba7391e` 的前端页面、测试和 spec 变更。
- [ ] 给出是否可在线测试、剩余风险和下一步建议。
## Test Plan
- `node deploy/trialrun/validate-env-contract.js`
- `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
- `curl -fsS https://oa.hyinda.com/ready`
- `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`(凭据存在时)
- `cd client && E2E_BASE_URL=https://oa.hyinda.com npx playwright test tests/e2e/dg7-mobile-information-dedupe.spec.ts tests/e2e/r124-mobile-entry-matrix.spec.ts --workers=1 --reporter=list`
- 额外移动端截图或截图审查输出到 `output/playwright/05-30-deploy-mobile-public-audit/`
- `python3 ./.trellis/scripts/task.py validate 05-30-deploy-mobile-public-audit`
- `node scripts/qa/check-trellis-governance.mjs`
- `git diff --check`
@@ -0,0 +1,26 @@
{
"id": "deploy-mobile-public-audit",
"name": "deploy-mobile-public-audit",
"title": "部署移动端收口并公网复检审计",
"description": "部署已提交的移动端入口与文案收口改动,完成公网复检、代码审计和移动视觉检查。",
"status": "in_progress",
"dev_type": null,
"scope": "deployment-public-frontend-audit",
"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": {}
}
+7 -2
查看文件
@@ -32,9 +32,14 @@ export async function assertForbiddenPage(page: Page) {
await expect(page.getByRole("heading", { name: "无权限访问", exact: true })).toBeVisible();
}
export async function assertMainNavVisible(page: Page) {
export async function assertMainNavVisible(page: Page, options: { expectTeam?: boolean } = {}) {
const { expectTeam = true } = options;
await expect(page.getByRole("link", { name: "工作台", exact: true }).first()).toBeVisible();
await expect(page.getByRole("link", { name: "团队" }).first()).toBeVisible();
if (expectTeam) {
await expect(page.getByRole("link", { name: "团队" }).first()).toBeVisible();
} else {
await expect(page.getByRole("link", { name: "团队", exact: true })).toHaveCount(0);
}
await expect(page.getByRole("link", { name: "项目" }).first()).toBeVisible();
}
+1 -4
查看文件
@@ -115,10 +115,7 @@ test.describe("live smoke", () => {
userName = employee.name;
await assertPortalLoaded(page);
await assertMainNavVisible(page);
await page.goto("/team");
await expect(page.getByRole("heading", { name: "团队状态", exact: true })).toBeVisible();
await assertMainNavVisible(page, { expectTeam: false });
await page.goto("/projects");
await expect(page.getByRole("heading", { name: "项目", exact: true })).toBeVisible();