部署公网并修正 live smoke 角色导航(05-30-deploy-mobile-public-audit)
这个提交包含在:
@@ -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": {}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
在新工单中引用
屏蔽一个用户