修复部署复检链路(05-31-deploy-unified-list-detail-reimbursement-public-check)
这个提交包含在:
@@ -168,5 +168,98 @@ await expect(
|
||||
page.getByRole("link", { name: "工作台", exact: true })
|
||||
.or(page.getByRole("link", { name: "Portal", exact: true }))
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
).toBeVisible();
|
||||
```
|
||||
|
||||
## Scenario: Trial Run Runtime Navigation Baseline
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: changing sidebar grouping, workspace module visibility defaults,
|
||||
`server/src/services/workspaceSettings.js`, or
|
||||
`server/src/scripts/smoke-trialrun-runtime.js`.
|
||||
- Purpose: keep deployment smoke aligned with the current small-team navigation
|
||||
model instead of blocking deployment on retired menu assumptions.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Runtime smoke command inside deployment:
|
||||
`docker compose exec -T server node src/scripts/smoke-trialrun-runtime.js`
|
||||
- Source of truth for sidebar grouping:
|
||||
`server/src/services/workspaceSettings.js`
|
||||
- Runtime smoke assertion file:
|
||||
`server/src/scripts/smoke-trialrun-runtime.js`
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- Runtime smoke must distinguish primary sidebar entries from collapsed
|
||||
`更多` entries.
|
||||
- Core day-to-day ledgers such as `报销`, `采购申请`, `合同台账`, `项目` and
|
||||
`资产` are expected in `main` when the active role has access.
|
||||
- Low-frequency entries such as `周报` and `公告` may be enabled but should be
|
||||
checked in `backup` / `更多`, not forced into `main`.
|
||||
- `待办` follows the current workspace and role-aware navigation baseline; do
|
||||
not move it between `main`, `backup` or hidden as part of a deployment-smoke
|
||||
fix unless a product task explicitly changes that rule.
|
||||
- Management pages should remain represented by the single `管理` entry unless
|
||||
a current task explicitly changes the management navigation model.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Core ledger missing from `main` -> runtime smoke fails with
|
||||
`系统导航 main 缺少 <key> 入口`.
|
||||
- `周报` or `公告` missing from `backup` while enabled -> runtime smoke fails
|
||||
with `系统导航 backup 缺少 <key> 入口`.
|
||||
- Management children are hidden behind `管理`, but smoke still expects
|
||||
`workspace`, `people`, `featureFlags` or `auditLogs` in `admin` -> update the
|
||||
smoke baseline; do not re-expand the sidebar for the smoke.
|
||||
- `systemSettings` missing from `admin` -> runtime smoke fails because the
|
||||
management entry is no longer reachable.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: `main` includes `portal`, `reimbursements`, `purchaseRequests`,
|
||||
`contractsRegistry`, `projects` and `assets`; `backup` includes
|
||||
`weeklyReports` and `announcements`; `admin` includes `systemSettings`.
|
||||
- Base: `待办` is in `main` for a configured admin/owner trial account; smoke
|
||||
accepts it there because it follows the current workspace baseline.
|
||||
- Bad: smoke expects `weeklyReports` in `main` after the sidebar rule moved
|
||||
low-frequency modules to `更多`.
|
||||
- Bad: smoke expects every management child as a sidebar row after the product
|
||||
rule consolidated management into one `管理` entry.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Run `node --check server/src/scripts/smoke-trialrun-runtime.js` after editing
|
||||
runtime smoke.
|
||||
- Run `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` for public deployment so the
|
||||
remote runtime smoke validates the actual workspace and feature-flag state.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```js
|
||||
const REQUIRED_MAIN_NAV_KEYS = [
|
||||
"portal",
|
||||
"reimbursements",
|
||||
"weeklyReports",
|
||||
"announcements",
|
||||
];
|
||||
const REQUIRED_ADMIN_NAV_KEYS = ["workspace", "people", "featureFlags"];
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```js
|
||||
const REQUIRED_MAIN_NAV_KEYS = [
|
||||
"portal",
|
||||
"reimbursements",
|
||||
"purchaseRequests",
|
||||
"contractsRegistry",
|
||||
"projects",
|
||||
"assets",
|
||||
];
|
||||
const REQUIRED_BACKUP_NAV_KEYS = ["weeklyReports", "announcements"];
|
||||
const REQUIRED_ADMIN_NAV_KEYS = ["systemSettings"];
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{"file": ".trellis/spec/hyb-oa/index.md", "reason": "hyb-oa spec entry and task governance read order"}
|
||||
{"file": ".trellis/spec/hyb-oa/project-governance.md", "reason": "Trellis task, approval, and commit rules for public deployment"}
|
||||
{"file": ".trellis/spec/hyb-oa/deployment-trialrun.md", "reason": "public deployment command, env contract, smoke, and reporting requirements"}
|
||||
{"file": ".trellis/spec/hyb-oa/docs-and-evidence.md", "reason": "evidence recording and documentation hygiene for public checks"}
|
||||
{"file": ".trellis/spec/hyb-oa/security-privacy.md", "reason": "secret and sensitive-data handling during deployment evidence collection"}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"file": ".trellis/spec/hyb-oa/index.md", "reason": "hyb-oa spec entry and task governance read order"}
|
||||
{"file": ".trellis/spec/hyb-oa/project-governance.md", "reason": "Trellis task, approval, and commit rules for public deployment"}
|
||||
{"file": ".trellis/spec/hyb-oa/deployment-trialrun.md", "reason": "public deployment command, env contract, smoke, and reporting requirements"}
|
||||
{"file": ".trellis/spec/hyb-oa/security-privacy.md", "reason": "secret and sensitive-data handling during deployment evidence collection"}
|
||||
@@ -0,0 +1,18 @@
|
||||
# Implementation Notes
|
||||
|
||||
## Plan
|
||||
|
||||
1. Run local deployment preflight checks and confirm the reimbursement page source no longer contains the old top task-card entry markers.
|
||||
2. Deploy with `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`, preserving the remote `.env`.
|
||||
3. If deploy verification is blocked by deployment tooling or smoke assertions that are inconsistent with current specs, fix only those deployment/checking assets.
|
||||
4. Rerun deployment until the remote env contract, `/ready`, external deps smoke, runtime smoke and public bundle scan pass.
|
||||
5. Record evidence and residual limitations in this task.
|
||||
|
||||
## Implemented Adjustments
|
||||
|
||||
- `deploy/trialrun/deploy.sh`: added explicit `/dev/null` stdin redirection to remote `docker compose exec -T` checks so the SSH here-doc continues through `/ready`, external deps smoke, runtime smoke and service-status steps.
|
||||
- `server/src/scripts/smoke-trialrun-runtime.js`: updated navigation smoke expectations to match the current sidebar model:
|
||||
- core business ledgers in `main`;
|
||||
- `周报` and `公告` in `backup` / `更多`;
|
||||
- one `systemSettings` management entry instead of many sidebar management children.
|
||||
- `.trellis/spec/hyb-oa/deployment-trialrun.md`: recorded the runtime navigation smoke baseline so future deploys do not regress to old menu assumptions.
|
||||
@@ -0,0 +1,61 @@
|
||||
# 部署统一列表详情改造并复检报销页
|
||||
|
||||
## Goal
|
||||
|
||||
将当前 `main` 的统一列表、详情页操作区和报销页改造部署到公网试运行环境,复检用户反馈的“报销页面顶部仍看到三个圆形按钮”是否已由最新版本修正。
|
||||
|
||||
## What I Already Know
|
||||
|
||||
- 用户已明确批准公网部署。
|
||||
- 本地 `main` 最新提交为 `efd6d28`,工作区干净。
|
||||
- 本地 `client/src/pages/ReimbursementsPage.tsx` 已不再保留旧的三张顶部快捷任务卡/圆形入口,当前顶部应为常规 `WorkspaceHeader`、`新建报销`、状态筛选和按权限显隐的 `台账工具`。
|
||||
- 公网试运行环境属于准生产环境,部署必须保留远端 `.env`,使用 `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`。
|
||||
|
||||
## Requirements
|
||||
|
||||
- 部署当前 `main` 到 `https://oa.hyinda.com` 对应公网试运行环境。
|
||||
- 不覆盖远端环境变量,不执行真实付款、真实业务写入或数据库重置。
|
||||
- 部署后验证 `/ready` 可用。
|
||||
- 部署后复检公网前端资源已更新到当前提交对应版本。
|
||||
- 尽量复检报销页视觉状态:报销页顶部不应再出现旧的三个圆形按钮或三张任务入口。
|
||||
- 如果本地缺少 `LIVE_SMOKE_*` 登录凭据,必须明确标注完整登录后页面检查的限制,并使用可用的公开 readiness/bundle 检查补足证据。
|
||||
|
||||
## Scope
|
||||
|
||||
- 公网试运行部署脚本、部署后 smoke、runtime smoke 基线和本任务证据记录。
|
||||
- 允许修复阻塞部署验证完成的部署脚本或 smoke 基线问题。
|
||||
- 不改变报销、列表、详情等业务页面实现。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] 本地部署前检查通过。
|
||||
- [x] `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` 成功完成。
|
||||
- [x] 公网 `/ready` 返回健康状态。
|
||||
- [x] 公网 bundle 检查通过或给出明确失败原因。
|
||||
- [x] 报告部署提交 hash、ready 结果、smoke/复检结果,以及用户是否可以测试。
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- 不新增或修改业务代码。
|
||||
- 不修改数据库结构和远端 `.env`。
|
||||
- 不执行真实付款、银行、ERP 或其他外部写入。
|
||||
- 不处理与本次部署无关的 UI 新问题。
|
||||
|
||||
## Test Plan
|
||||
|
||||
- `node deploy/trialrun/validate-env-contract.js`
|
||||
- `cd client && npm run build`
|
||||
- `node --check server/src/scripts/smoke-trialrun-runtime.js`
|
||||
- `bash -n deploy/trialrun/deploy.sh`
|
||||
- `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`
|
||||
- `node scripts/qa/check-public-bundle-copy.mjs https://oa.hyinda.com`
|
||||
- Public reimbursement bundle scan for absence of old task-card markers.
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- 部署规范:`.trellis/spec/hyb-oa/deployment-trialrun.md`
|
||||
- 部署命令:`SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
|
||||
- 环境契约检查:`node deploy/trialrun/validate-env-contract.js`
|
||||
- 公网目标:`https://oa.hyinda.com`
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# Deployment Evidence
|
||||
|
||||
## Deployment
|
||||
|
||||
- Date: 2026-05-31
|
||||
- Target: `https://oa.hyinda.com`
|
||||
- Command: `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
|
||||
- App baseline before deployment: `efd6d28`
|
||||
- Scope note: deployment included the current task's deployment-script/runtime-smoke fixes so the deploy command could complete the intended post-deploy checks.
|
||||
|
||||
## Findings During Deploy
|
||||
|
||||
- First full rerun showed that `docker compose exec -T` could consume the SSH here-doc and skip later deployment steps. Fixed by redirecting the affected remote `exec` commands from `/dev/null`.
|
||||
- Runtime smoke still assumed the old sidebar model. Updated it to match the current mature Ant Design-style navigation baseline:
|
||||
- core business ledgers in `main`;
|
||||
- `周报` and `公告` in `backup` / `更多`;
|
||||
- management represented by one `管理` entry.
|
||||
|
||||
## Verification
|
||||
|
||||
- `node deploy/trialrun/validate-env-contract.js`: PASS.
|
||||
- `cd client && npm run build`: PASS before deployment.
|
||||
- `node --check server/src/scripts/smoke-trialrun-runtime.js`: PASS.
|
||||
- `bash -n deploy/trialrun/deploy.sh`: PASS.
|
||||
- `git diff --check`: PASS.
|
||||
- Final deploy script run:
|
||||
- env contract: PASS;
|
||||
- Prisma migration status: up to date;
|
||||
- `/ready`: PASS;
|
||||
- external deps smoke: PASS;
|
||||
- runtime smoke: PASS;
|
||||
- public bundle copy scan: `scanned=93 focused=2 findings=0`.
|
||||
- Independent `/ready`: `{"ok":true}`.
|
||||
- `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`: PASS with live browser/OCR checks skipped because local `LIVE_SMOKE_*` credentials and OCR sample were not configured.
|
||||
|
||||
## Reimbursement Page Check
|
||||
|
||||
- Public asset `https://oa.hyinda.com/assets/ReimbursementsPage-DgJ0EYZV.js` is available.
|
||||
- The public reimbursement bundle contains the new header/action shape: `我的报销`, `新建报销`, status tabs and optional `台账工具`.
|
||||
- The public reimbursement bundle does not contain old task-card markers:
|
||||
- `reimbursements-task-card`
|
||||
- `被驳回待补正`
|
||||
- `草稿待完善`
|
||||
- `审批中进度`
|
||||
|
||||
## Residual Limits
|
||||
|
||||
- Full logged-in live smoke and logged-in visual inspection were skipped because this local environment does not have complete `LIVE_SMOKE_*` credentials.
|
||||
- Public readiness, deployment-in-container smoke, and public bundle checks passed, so the deployed app is available for user testing.
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"id": "deploy-unified-list-detail-reimbursement-public-check",
|
||||
"name": "deploy-unified-list-detail-reimbursement-public-check",
|
||||
"title": "部署统一列表详情改造并复检报销页",
|
||||
"description": "Deploy the unified business list/detail UI changes to the public trial run environment and verify the reimbursement page no longer serves the old top task-card buttons.",
|
||||
"status": "in_progress",
|
||||
"dev_type": null,
|
||||
"scope": "deployment-trialrun",
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "shunli",
|
||||
"assignee": "shunli",
|
||||
"createdAt": "2026-05-31",
|
||||
"completedAt": null,
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [
|
||||
"deploy/trialrun/deploy.sh",
|
||||
"server/src/scripts/smoke-trialrun-runtime.js",
|
||||
".trellis/spec/hyb-oa/deployment-trialrun.md"
|
||||
],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ echo ">>> 4.4 执行 init"
|
||||
"${compose_cmd[@]}" run --rm -T --entrypoint sh init -lc 'cd /app/server && npm run prisma:migrate:deploy && npm run seed:config && npm run seed:trialrun' </dev/null
|
||||
|
||||
echo ">>> 4.5 校验 Prisma migration status"
|
||||
if ! "${compose_cmd[@]}" exec -T server sh -lc 'cd /app/server && npx prisma migrate status'; then
|
||||
if ! "${compose_cmd[@]}" exec -T server sh -lc 'cd /app/server && npx prisma migrate status' </dev/null; then
|
||||
echo "[ERROR] Prisma migration status 未通过,停止部署" >&2
|
||||
"${compose_cmd[@]}" logs --tail=200 server || true
|
||||
exit 1
|
||||
@@ -164,14 +164,14 @@ if [[ "$ready_ok" != "1" ]]; then
|
||||
fi
|
||||
|
||||
echo ">>> 4.7 执行 external deps smoke"
|
||||
if ! "${compose_cmd[@]}" exec -T server node src/scripts/smoke-external-deps.js; then
|
||||
if ! "${compose_cmd[@]}" exec -T server node src/scripts/smoke-external-deps.js </dev/null; then
|
||||
echo "[ERROR] external deps smoke 失败,输出最近 server 日志" >&2
|
||||
"${compose_cmd[@]}" logs --tail=200 server || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ">>> 4.8 执行 runtime smoke"
|
||||
if ! "${compose_cmd[@]}" exec -T server node src/scripts/smoke-trialrun-runtime.js; then
|
||||
if ! "${compose_cmd[@]}" exec -T server node src/scripts/smoke-trialrun-runtime.js </dev/null; then
|
||||
echo "[ERROR] runtime smoke 失败,输出最近 server 日志" >&2
|
||||
"${compose_cmd[@]}" logs --tail=200 server || true
|
||||
exit 1
|
||||
|
||||
@@ -43,17 +43,19 @@ const REQUIRED_MAIN_NAV_KEYS = [
|
||||
'leaveRequests',
|
||||
'reimbursements',
|
||||
'assets',
|
||||
'weeklyReports',
|
||||
'contractsRegistry',
|
||||
'purchaseRequests',
|
||||
'projects',
|
||||
'todos',
|
||||
];
|
||||
|
||||
const REQUIRED_BACKUP_NAV_KEYS = [
|
||||
'weeklyReports',
|
||||
'announcements',
|
||||
];
|
||||
|
||||
const REQUIRED_ADMIN_NAV_KEYS = [
|
||||
'workspace',
|
||||
'people',
|
||||
'systemSettings',
|
||||
'featureFlags',
|
||||
'auditLogs',
|
||||
];
|
||||
|
||||
const REQUIRED_WORKSPACE_VISIBILITY = {
|
||||
@@ -137,14 +139,18 @@ function assertNavigation(navigation) {
|
||||
|
||||
const mainKeys = new Set((navigation.sections.main ?? []).map((item) => item.key));
|
||||
const adminKeys = new Set((navigation.sections.admin ?? []).map((item) => item.key));
|
||||
const backupKeys = new Set((navigation.sections.backup ?? []).map((item) => item.key));
|
||||
|
||||
for (const key of REQUIRED_MAIN_NAV_KEYS) {
|
||||
assertCondition(mainKeys.has(key), `系统导航 main 缺少 ${key} 入口`);
|
||||
}
|
||||
for (const key of REQUIRED_BACKUP_NAV_KEYS) {
|
||||
assertCondition(backupKeys.has(key), `系统导航 backup 缺少 ${key} 入口`);
|
||||
}
|
||||
for (const key of REQUIRED_ADMIN_NAV_KEYS) {
|
||||
assertCondition(adminKeys.has(key), `系统导航 admin 缺少 ${key} 入口`);
|
||||
}
|
||||
console.log(`[PASS] 导航构建成功,main=${navigation.sections.main.length} admin=${navigation.sections.admin.length}`);
|
||||
console.log(`[PASS] 导航构建成功,main=${navigation.sections.main.length} backup=${navigation.sections.backup.length} admin=${navigation.sections.admin.length}`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
在新工单中引用
屏蔽一个用户