From a01d2ab7087980fa67af5e340285eb1d08376039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=95?= Date: Tue, 21 Jul 2026 17:15:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=A9=E5=B1=95=E6=B0=A2=E8=83=BD=E7=AB=99?= =?UTF-8?q?=E7=82=B9=E3=80=81=E5=8A=A0=E6=B0=A2=E8=AE=B0=E5=BD=95=E4=B8=8E?= =?UTF-8?q?=E5=8F=B0=E8=B4=A6=E5=8E=9F=E5=9E=8B=E9=93=BE=E8=B7=AF=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=B7=A5=E4=BD=9C=E5=8F=B0=E3=80=81=E8=BD=A6?= =?UTF-8?q?=E8=BE=86=E8=B5=84=E4=BA=A7=20H5=E3=80=81=E8=87=AA=E8=90=A5?= =?UTF-8?q?=E7=89=A9=E6=B5=81=E7=AD=89=E5=8E=9F=E5=9E=8B=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=AF=BC=E8=88=AA=E6=B3=A8=E5=86=8C=E3=80=81?= =?UTF-8?q?PRD=20=E8=B5=84=E6=BA=90=E4=B8=8E=E4=BA=A4=E4=BB=98=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .agents/skills/user-story-mapping/SKILL.md | 297 + .../user-story-mapping/examples/sample.md | 77 + .agents/skills/user-story-mapping/template.md | 41 + .axhub/make/sidebar-tree.json | 173 +- .claude/skills/AutoVUL/SKILL.md | 139 + .claude/skills/AutoVUL/examples.md | 77 + .claude/skills/AutoVUL/input-template.md | 69 + .claude/skills/AutoVUL/module-role-mapping.md | 60 + .../AutoVUL/references/yunxiao-sprint.md | 81 + .claude/skills/oneos-autoprd/SKILL.md | 183 + .../references/annotation-sync.md | 98 + .../references/granularity-example.md | 58 + .../references/release-changelog.md | 78 + .../oneos-autoprd/references/template.md | 163 + .../references/yunxiao-stage-tasks.md | 44 + .cursor/rules/oneos-autoprd-sync.mdc | 42 + .../yunxiao-record-requirement-fast-path.mdc | 17 + .cursor/skills/AutoVUL/SKILL.md | 139 + .cursor/skills/AutoVUL/examples.md | 77 + .cursor/skills/AutoVUL/input-template.md | 69 + .cursor/skills/AutoVUL/module-role-mapping.md | 60 + .../AutoVUL/references/yunxiao-sprint.md | 81 + .cursor/skills/oneos-autoprd/SKILL.md | 183 + .../references/annotation-sync.md | 98 + .../references/granularity-example.md | 58 + .../references/release-changelog.md | 78 + .../oneos-autoprd/references/template.md | 163 + .../references/yunxiao-stage-tasks.md | 44 + .../yunxiao-requirement-lifecycle/SKILL.md | 157 + .../agents/openai.yaml | 4 + .../assets/oneos-pc-runtime-ids.json | 209 + .../assets/oneos-pc-tag-catalog.md | 50 + .../oneos-project-profile.template.json | 119 + .../assets/project-profile.template.json | 148 + .../references/auto-stage-task.md | 62 + .../references/codeup-integration.md | 60 + .../references/lifecycle-rules.md | 103 + .../references/live-operations.md | 133 + .../references/oneos-pc-fast-path.md | 117 + .../oneos-pc-record-requirement-prompt.md | 139 + .../references/oneos-terminal-flow.md | 192 + .../references/pipeline-release.md | 66 + .../references/report-template.md | 97 + .../references/role-trigger-prompts.md | 499 + .../references/simple-role-commands.md | 417 + .../references/task-lifecycle.md | 87 + .../references/test-defect-closure.md | 62 + .../scripts/oneos_profile_tool.py | 314 + .../scripts/profile_tool.py | 536 + AGENTS.md | 1 + .../plans/2026-07-20-h5-vehicle-assets.md | 65 + .../2026-07-20-h5-vehicle-assets-design.md | 189 + .../2026-07-21-h5-mileage-task-card-design.md | 16 + .../2026-07-21-vehicle-assets-ui-measure.md | 110 + .../2026-07-21-vehicle-assets-ui-measure.png | Bin 0 -> 467875 bytes src/common/oneos-app-shell/OneOsAppShell.tsx | 413 + src/common/oneos-app-shell/README.md | 12 + .../oneos-app-shell/ShellNoticeCenter.tsx | 364 + src/common/oneos-app-shell/index.ts | 50 + src/common/oneos-app-shell/nav-bridge.ts | 29 + .../oneos-app-shell/nav-from-prototypes.ts | 104 + src/common/oneos-app-shell/notice-bridge.ts | 60 + .../oneos-app-shell/oneos-app-shell.css | 1564 + .../oneos-app-shell/oneos-theme-content.css | 163 + .../oneos-app-shell/release-demo-bridge.ts | 64 + src/common/oneos-app-shell/theme.ts | 122 + src/common/selfOperatedLogisticsBridge.js | 567 + .../.spec/requirements-prd.md | 40 + .../.spec/sector-data-source.md | 60 + .../annotation-source.json | 69 + .../.spec/requirements-prd.md | 376 + .../annotation-source.json | 9 +- .../oneos-h5-h2-order/.spec/flows.md | 68 + .../oneos-h5-h2-order/.spec/key-logic.md | 52 + .../.spec/prototype-review.md | 64 + .../oneos-h5-h2-order/.spec/roles.md | 38 + .../oneos-h5-h2-order/.spec/user-stories.md | 84 + .../components/CreateWizard.tsx | 18 - .../.spec/biz-timeline.md | 38 + .../.spec/demo-biz-records.md | 25 + .../.spec/expire-remain-shanghai.md | 31 + .../.spec/insurance-records.md | 37 + .../.spec/lease-records.md | 42 + .../.spec/license-certs.md | 33 + .../.spec/mileage-task.md | 77 + .../.spec/more-filters.md | 38 + .../.spec/requirements-prd.md | 276 + .../annotation-source.json | 293 + .../components/AppToast.tsx | 37 + .../components/BizTimelinePanels.tsx | 283 + .../components/FilterSheet.tsx | 206 + .../components/Icons.tsx | 512 + .../components/InsuranceRecordsPanel.tsx | 368 + .../components/LeaseRecordsPanel.tsx | 129 + .../components/LicenseCertPanel.tsx | 308 + .../components/NativeDatePickerSheet.tsx | 259 + .../components/NativePickerSheet.tsx | 392 + .../components/PhoneShell.tsx | 126 + .../components/RecordTimeline.tsx | 79 + .../components/RecordsEmpty.tsx | 31 + .../components/TimelineDateFilter.tsx | 189 + .../components/VehicleCard.tsx | 161 + .../components/VehicleDetail.tsx | 136 + .../components/VehicleList.tsx | 402 + .../components/detailTabs.tsx | 235 + .../oneos-h5-vehicle-assets/index.tsx | 383 + .../scripts/sync-annotation-directory.mjs | 152 + .../oneos-h5-vehicle-assets/styles/index.css | 3415 ++ .../utils/demo-biz-records.ts | 366 + .../oneos-h5-vehicle-assets/utils/filter.ts | 386 + .../utils/mileage-task.ts | 158 + .../16f6ecbd-234d-408d-8d06-54b5d99a8142.png | Bin 0 -> 1354805 bytes .../aa9ad84f-ff14-4b35-b6d9-2814f695baa9.png | Bin 0 -> 1208665 bytes .../.spec/requirements-prd.md | 28 + .../annotation-source.json | 52 + src/prototypes/oneos-prototype-demo/index.tsx | 319 + src/prototypes/oneos-prototype-demo/style.css | 7 + .../oneos-prototype-nav/nav-menu.json | 30 +- .../prototype-registry.json | 4017 +- .../pages/05-业务部台账.jsx | 74 +- .../.spec/requirements-prd-list.md | 3 +- .../.spec/requirements-prd.md | 10 +- .../.spec/station-batch-export.md | 118 + .../annotation-source.json | 53 +- .../pages/03-站点信息.jsx | 31 + .../scripts/build-annotation-source.mjs | 10 +- .../utils/h2-station-batch-export.ts | 281 + .../.spec/feature-create.md | 44 + .../.spec/feature-daily-stats.md | 15 + .../.spec/feature-export.md | 25 + .../.spec/feature-list.md | 34 + .../.spec/feature-manual-ledger.md | 63 + .../.spec/fleet-plate-tag.md | 33 + .../.spec/fleet-vehicle-verify.md | 62 + .../oneos-web-h2-station/.spec/flows.md | 58 + .../oneos-web-h2-station/.spec/key-logic.md | 53 + .../.spec/reconcile-linkage.md | 48 + .../oneos-web-h2-station/.spec/roles.md | 39 + .../.spec/user-stories.md | 85 + .../scripts/build-annotation-source.mjs | 140 + .../.spec/approval-embed.md | 115 + .../.spec/notice-center.md | 38 + .../.spec/oneos-app-shell.md | 7 + .../.spec/payment-unlinked-kpi.md | 48 + .../00a55813-caa3-422d-b93d-180ec36746a2.png | Bin 0 -> 1179984 bytes .../07dbfb15-4c95-444f-b320-6295cbd13c6e.png | Bin 0 -> 132161 bytes .../2c22b2c2-2423-4fe6-beee-c597526a8cd4.png | Bin 0 -> 1118554 bytes .../b2d784e5-43dd-4b59-8e3b-67780bd6f7cd.png | Bin 0 -> 203022 bytes .../.spec/release-notes-popup.md | 87 + .../.spec/requirements-prd.md | 420 + .../.spec/role-insights.md | 185 + .../.spec/role-visibility.md | 65 + .../.spec/task-analytics.md | 40 + .../.spec/task-flow.md | 32 + .../.spec/todo-biz-admin.md | 110 + .../.spec/urge-channels.md | 70 + .../annotation-source.json | 337 + .../components/AnalyticsBoard.tsx | 79 + .../components/ApprovalPanel.tsx | 236 + .../components/BizAdminCockpit.tsx | 420 + .../components/HeroSummary.tsx | 95 + .../components/KpiGrid.tsx | 141 + .../components/NoticePanel.tsx | 160 + .../components/OpsCockpit.tsx | 445 + .../components/QuickEntry.tsx | 410 + .../components/ReleaseNotesModal.tsx | 213 + .../components/RoleInsightsPanel.tsx | 662 + .../components/TaskDetailDrawer.tsx | 211 + .../components/TodoLineMultiSelect.tsx | 185 + .../components/TodoLineTable.tsx | 373 + .../components/TodoPanel.tsx | 135 + .../components/TodoUrgencySelect.tsx | 92 + .../components/Topbar.tsx | 65 + .../components/UrgeModal.tsx | 120 + .../components/WorkbenchPage.tsx | 552 + .../oneos-web-workbench-new/data/analytics.ts | 85 + .../oneos-web-workbench-new/data/approvals.ts | 448 + .../oneos-web-workbench-new/data/kpi.ts | 558 + .../oneos-web-workbench-new/data/notices.ts | 176 + .../data/ops-cockpit.ts | 129 + .../data/quick-actions.ts | 88 + .../data/release-notes.ts | 190 + .../data/role-insights.ts | 324 + .../oneos-web-workbench-new/data/roles.ts | 219 + .../oneos-web-workbench-new/data/tasks.ts | 826 + .../data/todo-lines.ts | 214 + .../oneos-web-workbench-new/index.tsx | 40 + .../scripts/build-annotation-source.mjs | 102 + .../oneos-web-workbench-new/style.css | 8449 +++ .../utils/analytics-store.ts | 95 + .../utils/fleet-brand-model-stats.ts | 72 + .../utils/fleet-contract-delivery.ts | 99 + .../utils/fleet-project-stats.ts | 53 + .../utils/fleet-region-dist.ts | 98 + .../utils/fleet-region-stats.ts | 62 + .../oneos-web-workbench-new/utils/helpers.ts | 63 + .../utils/ops-vehicle-status.ts | 28 + .../utils/release-seen.ts | 64 + .../utils/urge-permission.ts | 26 + .../payment-records/.spec/payment-notify.md | 43 + .../payment-records/.spec/requirements-prd.md | 42 + .../data/notify-customer-map.json | 27 + .../utils/notify-recipients.ts | 136 + .../.spec/ledger-auto-gen.md | 50 + .../.spec/contract-rules.md | 55 + .../.spec/requirements-prd.md | 60 + .../SelfOperatedContractPage.jsx | 321 + .../annotation-source.json | 92 + .../self-operated-contract/index.tsx | 71 + .../self-operated-contract/styles/index.css | 40 + .../.spec/dispatch-rules.md | 68 + .../.spec/requirements-prd.md | 59 + .../SelfOperatedDispatchTaskPage.jsx | 567 + .../annotation-source.json | 92 + .../self-operated-dispatch-task/index.tsx | 71 + .../styles/index.css | 59 + .../vehicle-h2-fee-ledger/styles/index.css | 35 + .../vehicle-management/.spec/data-source.md | 20 + .../.spec/requirements-prd-list.md | 2 +- .../data/lease-records.json | 36 + .../vehicle-management/data/vehicles.json | 47937 ++++++++++++++-- .../data/vehicles.sample-100.json.bak | 4402 ++ .../vehicle-management/utils/vehicle.ts | 2 + .../.spec/requirements-prd.md | 247 + .../annotation-source.json | 130 + .../colloquial-commands.ts | 561 + .../yunxiao-pipeline-handbook/content.ts | 586 + .../yunxiao-colloquial-commands-onepage.md | 83 + .../docs/yunxiao-config-handbook.md | 622 + .../yunxiao-pipeline-handbook/index.tsx | 1406 + .../yunxiao-pipeline-handbook/pm-skills.ts | 127 + .../yunxiao-pipeline-handbook/simulation.ts | 402 + .../yunxiao-pipeline-handbook/style.css | 1989 + .../OneOS-R1-用户故事清单.xlsx | Bin 0 -> 15398 bytes .../oneos-story-map/r1-user-stories.md | 309 + .../oneos-story-map/user-story-map.md | 394 + .../prd/insurance-procurement-autoprd.md | 376 + .../prd/oneos-h5-vehicle-assets-autoprd.md | 276 + .../prd/yunxiao-pipeline-handbook-autoprd.md | 241 + .../self-operated-logistics/assumptions.md | 38 + vite-plugins/clientPreviewPlugin.ts | 5 +- vite.config.ts | 3 + 242 files changed, 102296 insertions(+), 4866 deletions(-) create mode 100644 .agents/skills/user-story-mapping/SKILL.md create mode 100644 .agents/skills/user-story-mapping/examples/sample.md create mode 100644 .agents/skills/user-story-mapping/template.md create mode 100644 .claude/skills/AutoVUL/SKILL.md create mode 100644 .claude/skills/AutoVUL/examples.md create mode 100644 .claude/skills/AutoVUL/input-template.md create mode 100644 .claude/skills/AutoVUL/module-role-mapping.md create mode 100644 .claude/skills/AutoVUL/references/yunxiao-sprint.md create mode 100644 .claude/skills/oneos-autoprd/SKILL.md create mode 100644 .claude/skills/oneos-autoprd/references/annotation-sync.md create mode 100644 .claude/skills/oneos-autoprd/references/granularity-example.md create mode 100644 .claude/skills/oneos-autoprd/references/release-changelog.md create mode 100644 .claude/skills/oneos-autoprd/references/template.md create mode 100644 .claude/skills/oneos-autoprd/references/yunxiao-stage-tasks.md create mode 100644 .cursor/rules/oneos-autoprd-sync.mdc create mode 100644 .cursor/rules/yunxiao-record-requirement-fast-path.mdc create mode 100644 .cursor/skills/AutoVUL/SKILL.md create mode 100644 .cursor/skills/AutoVUL/examples.md create mode 100644 .cursor/skills/AutoVUL/input-template.md create mode 100644 .cursor/skills/AutoVUL/module-role-mapping.md create mode 100644 .cursor/skills/AutoVUL/references/yunxiao-sprint.md create mode 100644 .cursor/skills/oneos-autoprd/SKILL.md create mode 100644 .cursor/skills/oneos-autoprd/references/annotation-sync.md create mode 100644 .cursor/skills/oneos-autoprd/references/granularity-example.md create mode 100644 .cursor/skills/oneos-autoprd/references/release-changelog.md create mode 100644 .cursor/skills/oneos-autoprd/references/template.md create mode 100644 .cursor/skills/oneos-autoprd/references/yunxiao-stage-tasks.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/SKILL.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/agents/openai.yaml create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-runtime-ids.json create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-tag-catalog.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-project-profile.template.json create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/assets/project-profile.template.json create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/auto-stage-task.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/codeup-integration.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/lifecycle-rules.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/live-operations.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-fast-path.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-record-requirement-prompt.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/oneos-terminal-flow.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/pipeline-release.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/report-template.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/role-trigger-prompts.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/simple-role-commands.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/task-lifecycle.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/references/test-defect-closure.md create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/scripts/oneos_profile_tool.py create mode 100644 .cursor/skills/yunxiao-requirement-lifecycle/scripts/profile_tool.py create mode 100644 docs/superpowers/plans/2026-07-20-h5-vehicle-assets.md create mode 100644 docs/superpowers/specs/2026-07-20-h5-vehicle-assets-design.md create mode 100644 docs/superpowers/specs/2026-07-21-h5-mileage-task-card-design.md create mode 100644 docs/superpowers/specs/2026-07-21-vehicle-assets-ui-measure.md create mode 100644 docs/superpowers/specs/2026-07-21-vehicle-assets-ui-measure.png create mode 100644 src/common/oneos-app-shell/OneOsAppShell.tsx create mode 100644 src/common/oneos-app-shell/README.md create mode 100644 src/common/oneos-app-shell/ShellNoticeCenter.tsx create mode 100644 src/common/oneos-app-shell/index.ts create mode 100644 src/common/oneos-app-shell/nav-bridge.ts create mode 100644 src/common/oneos-app-shell/nav-from-prototypes.ts create mode 100644 src/common/oneos-app-shell/notice-bridge.ts create mode 100644 src/common/oneos-app-shell/oneos-app-shell.css create mode 100644 src/common/oneos-app-shell/oneos-theme-content.css create mode 100644 src/common/oneos-app-shell/release-demo-bridge.ts create mode 100644 src/common/oneos-app-shell/theme.ts create mode 100644 src/common/selfOperatedLogisticsBridge.js create mode 100644 src/prototypes/business-dept-ledger/.spec/requirements-prd.md create mode 100644 src/prototypes/business-dept-ledger/.spec/sector-data-source.md create mode 100644 src/prototypes/business-dept-ledger/annotation-source.json create mode 100644 src/prototypes/insurance-procurement/.spec/requirements-prd.md create mode 100644 src/prototypes/oneos-h5-h2-order/.spec/flows.md create mode 100644 src/prototypes/oneos-h5-h2-order/.spec/key-logic.md create mode 100644 src/prototypes/oneos-h5-h2-order/.spec/prototype-review.md create mode 100644 src/prototypes/oneos-h5-h2-order/.spec/roles.md create mode 100644 src/prototypes/oneos-h5-h2-order/.spec/user-stories.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/biz-timeline.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/demo-biz-records.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/expire-remain-shanghai.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/insurance-records.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/lease-records.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/license-certs.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/mileage-task.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/more-filters.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/.spec/requirements-prd.md create mode 100644 src/prototypes/oneos-h5-vehicle-assets/annotation-source.json create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/AppToast.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/BizTimelinePanels.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/FilterSheet.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/Icons.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/InsuranceRecordsPanel.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/LeaseRecordsPanel.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/LicenseCertPanel.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/NativeDatePickerSheet.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/NativePickerSheet.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/PhoneShell.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/RecordTimeline.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/RecordsEmpty.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/TimelineDateFilter.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/VehicleCard.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/VehicleDetail.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/VehicleList.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/components/detailTabs.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/index.tsx create mode 100644 src/prototypes/oneos-h5-vehicle-assets/scripts/sync-annotation-directory.mjs create mode 100644 src/prototypes/oneos-h5-vehicle-assets/styles/index.css create mode 100644 src/prototypes/oneos-h5-vehicle-assets/utils/demo-biz-records.ts create mode 100644 src/prototypes/oneos-h5-vehicle-assets/utils/filter.ts create mode 100644 src/prototypes/oneos-h5-vehicle-assets/utils/mileage-task.ts create mode 100644 src/prototypes/oneos-prototype-demo/.spec/prototype-comment-assets/16f6ecbd-234d-408d-8d06-54b5d99a8142.png create mode 100644 src/prototypes/oneos-prototype-demo/.spec/prototype-comment-assets/aa9ad84f-ff14-4b35-b6d9-2814f695baa9.png create mode 100644 src/prototypes/oneos-prototype-demo/.spec/requirements-prd.md create mode 100644 src/prototypes/oneos-prototype-demo/annotation-source.json create mode 100644 src/prototypes/oneos-prototype-demo/index.tsx create mode 100644 src/prototypes/oneos-prototype-demo/style.css create mode 100644 src/prototypes/oneos-web-h2-station-site/.spec/station-batch-export.md create mode 100644 src/prototypes/oneos-web-h2-station-site/utils/h2-station-batch-export.ts create mode 100644 src/prototypes/oneos-web-h2-station/.spec/feature-create.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/feature-daily-stats.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/feature-export.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/feature-list.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/feature-manual-ledger.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/fleet-plate-tag.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/fleet-vehicle-verify.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/flows.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/key-logic.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/reconcile-linkage.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/roles.md create mode 100644 src/prototypes/oneos-web-h2-station/.spec/user-stories.md create mode 100644 src/prototypes/oneos-web-h2-station/scripts/build-annotation-source.mjs create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/approval-embed.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/notice-center.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/oneos-app-shell.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/payment-unlinked-kpi.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/prototype-comment-assets/00a55813-caa3-422d-b93d-180ec36746a2.png create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/prototype-comment-assets/07dbfb15-4c95-444f-b320-6295cbd13c6e.png create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/prototype-comment-assets/2c22b2c2-2423-4fe6-beee-c597526a8cd4.png create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/prototype-comment-assets/b2d784e5-43dd-4b59-8e3b-67780bd6f7cd.png create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/release-notes-popup.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/requirements-prd.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/role-insights.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/role-visibility.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/task-analytics.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/task-flow.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/todo-biz-admin.md create mode 100644 src/prototypes/oneos-web-workbench-new/.spec/urge-channels.md create mode 100644 src/prototypes/oneos-web-workbench-new/annotation-source.json create mode 100644 src/prototypes/oneos-web-workbench-new/components/AnalyticsBoard.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/ApprovalPanel.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/BizAdminCockpit.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/HeroSummary.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/KpiGrid.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/NoticePanel.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/OpsCockpit.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/QuickEntry.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/ReleaseNotesModal.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/RoleInsightsPanel.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/TaskDetailDrawer.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/TodoLineMultiSelect.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/TodoLineTable.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/TodoPanel.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/TodoUrgencySelect.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/Topbar.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/UrgeModal.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/components/WorkbenchPage.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/data/analytics.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/approvals.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/kpi.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/notices.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/ops-cockpit.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/quick-actions.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/release-notes.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/role-insights.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/roles.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/tasks.ts create mode 100644 src/prototypes/oneos-web-workbench-new/data/todo-lines.ts create mode 100644 src/prototypes/oneos-web-workbench-new/index.tsx create mode 100644 src/prototypes/oneos-web-workbench-new/scripts/build-annotation-source.mjs create mode 100644 src/prototypes/oneos-web-workbench-new/style.css create mode 100644 src/prototypes/oneos-web-workbench-new/utils/analytics-store.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/fleet-brand-model-stats.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/fleet-contract-delivery.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/fleet-project-stats.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/fleet-region-dist.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/fleet-region-stats.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/helpers.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/ops-vehicle-status.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/release-seen.ts create mode 100644 src/prototypes/oneos-web-workbench-new/utils/urge-permission.ts create mode 100644 src/prototypes/payment-records/.spec/payment-notify.md create mode 100644 src/prototypes/payment-records/.spec/requirements-prd.md create mode 100644 src/prototypes/payment-records/data/notify-customer-map.json create mode 100644 src/prototypes/payment-records/utils/notify-recipients.ts create mode 100644 src/prototypes/self-operated-business-ledger/.spec/ledger-auto-gen.md create mode 100644 src/prototypes/self-operated-contract/.spec/contract-rules.md create mode 100644 src/prototypes/self-operated-contract/.spec/requirements-prd.md create mode 100644 src/prototypes/self-operated-contract/SelfOperatedContractPage.jsx create mode 100644 src/prototypes/self-operated-contract/annotation-source.json create mode 100644 src/prototypes/self-operated-contract/index.tsx create mode 100644 src/prototypes/self-operated-contract/styles/index.css create mode 100644 src/prototypes/self-operated-dispatch-task/.spec/dispatch-rules.md create mode 100644 src/prototypes/self-operated-dispatch-task/.spec/requirements-prd.md create mode 100644 src/prototypes/self-operated-dispatch-task/SelfOperatedDispatchTaskPage.jsx create mode 100644 src/prototypes/self-operated-dispatch-task/annotation-source.json create mode 100644 src/prototypes/self-operated-dispatch-task/index.tsx create mode 100644 src/prototypes/self-operated-dispatch-task/styles/index.css create mode 100644 src/prototypes/vehicle-h2-fee-ledger/styles/index.css create mode 100644 src/prototypes/vehicle-management/.spec/data-source.md create mode 100644 src/prototypes/vehicle-management/data/vehicles.sample-100.json.bak create mode 100644 src/prototypes/yunxiao-pipeline-handbook/.spec/requirements-prd.md create mode 100644 src/prototypes/yunxiao-pipeline-handbook/annotation-source.json create mode 100644 src/prototypes/yunxiao-pipeline-handbook/colloquial-commands.ts create mode 100644 src/prototypes/yunxiao-pipeline-handbook/content.ts create mode 100644 src/prototypes/yunxiao-pipeline-handbook/docs/yunxiao-colloquial-commands-onepage.md create mode 100644 src/prototypes/yunxiao-pipeline-handbook/docs/yunxiao-config-handbook.md create mode 100644 src/prototypes/yunxiao-pipeline-handbook/index.tsx create mode 100644 src/prototypes/yunxiao-pipeline-handbook/pm-skills.ts create mode 100644 src/prototypes/yunxiao-pipeline-handbook/simulation.ts create mode 100644 src/prototypes/yunxiao-pipeline-handbook/style.css create mode 100644 src/resources/oneos-story-map/OneOS-R1-用户故事清单.xlsx create mode 100644 src/resources/oneos-story-map/r1-user-stories.md create mode 100644 src/resources/oneos-story-map/user-story-map.md create mode 100644 src/resources/prd/insurance-procurement-autoprd.md create mode 100644 src/resources/prd/oneos-h5-vehicle-assets-autoprd.md create mode 100644 src/resources/prd/yunxiao-pipeline-handbook-autoprd.md create mode 100644 src/resources/self-operated-logistics/assumptions.md diff --git a/.agents/skills/user-story-mapping/SKILL.md b/.agents/skills/user-story-mapping/SKILL.md new file mode 100644 index 0000000..026aa57 --- /dev/null +++ b/.agents/skills/user-story-mapping/SKILL.md @@ -0,0 +1,297 @@ +--- +name: user-story-mapping +argument-hint: "[product or workflow]" +description: Create a user story map that lays out activities, steps, tasks, and release slices. Use when planning a workflow, backlog, or MVP around the user journey. +intent: >- + Visualize the user journey by creating a hierarchical map that breaks down high-level activities into steps and tasks, organized left-to-right as a narrative flow. Use this to build shared understanding across product, design, and engineering, prioritize features based on user workflows, and identify gaps or opportunities in the user experience. +type: component +--- + + +## Purpose +Visualize the user journey by creating a hierarchical map that breaks down high-level activities into steps and tasks, organized left-to-right as a narrative flow. Use this to build shared understanding across product, design, and engineering, prioritize features based on user workflows, and identify gaps or opportunities in the user experience. + +This is not a backlog—it's a strategic artifact that shows *how* users accomplish their goals, which then informs *what* to build. + +## Input + +**Works best with:** The product or user workflow being mapped. +**Also useful:** The primary user, the end-to-end narrative as you understand it, existing backlog items to place, and release goals. + +Anything supplied with the invocation itself — text after the skill name, a pasted context dump, or an appended `ARGUMENTS:` line — counts as answers already given. Use it and skip whatever it covers; don't re-ask. + +**Arriving empty-handed? That works too.** The skill asks whose journey you're mapping and what they're trying to get done, then builds backbone → tasks → slices. + +**Example invocation:** `Story map for our expense-reporting flow, from receipt capture to reimbursement, with an MVP slice for the pilot.` + +## Key Concepts + +### The Jeff Patton Story Mapping Framework +Invented by Jeff Patton, story mapping organizes work into a 2D structure: + +**Horizontal axis (left-to-right):** User journey over time +- **Backbone:** High-level activities the user performs +- **Steps:** Specific actions within each activity +- **Tasks:** Detailed work required to complete each step + +**Vertical axis (top-to-bottom):** Priority and releases +- **Top rows:** Essential tasks (MVP / Release 1) +- **Lower rows:** Nice-to-have tasks (Future releases) + +### Story Map Structure + +``` +Segment → Persona → Narrative (User's goal) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +[Activity 1] → [Activity 2] → [Activity 3] → [Activity 4] → [Activity 5] + ↓ ↓ ↓ ↓ ↓ + [Step 1.1] [Step 2.1] [Step 3.1] [Step 4.1] [Step 5.1] + [Step 1.2] [Step 2.2] [Step 3.2] [Step 4.2] [Step 5.2] + [Step 1.3] [Step 2.3] [Step 3.3] [Step 4.3] [Step 5.3] + ↓ ↓ ↓ ↓ ↓ + [Task 1.1.1] [Task 2.1.1] [Task 3.1.1] [Task 4.1.1] [Task 5.1.1] + [Task 1.1.2] [Task 2.1.2] [Task 3.1.2] [Task 4.1.2] [Task 5.1.2] + [Task 1.1.3] [Task 2.1.3] [Task 3.1.3] [Task 4.1.3] [Task 5.1.3] + ... ... ... ... ... +``` + +### Why This Works +- **User-centric:** Organizes work around user goals, not engineering modules +- **Shared understanding:** Product, design, engineering all see the same journey +- **Prioritization clarity:** Top tasks = MVP, lower tasks = future iterations +- **Gap identification:** Missing steps or tasks become obvious +- **Release planning:** Draw horizontal "release lines" to define scope + +### Anti-Patterns (What This Is NOT) +- **Not a Gantt chart:** This isn't project management—it's user journey visualization +- **Not a feature list:** Activities aren't features—they're user behaviors +- **Not static:** Story maps evolve as you learn more about users + +### When to Use This +- Kicking off a new product or major feature +- Aligning stakeholders on user workflow +- Prioritizing backlog based on user needs +- Identifying MVP vs. future releases +- Onboarding new team members to the product vision + +### When NOT to Use This +- For trivial features (don't map what you already understand) +- When user workflows are constantly changing (map stabilizes workflows) +- As a replacement for user stories (the map informs stories, doesn't replace them) + +--- + +## Application + +### Step 1: Define the Context + +Use `template.md` for the full fill-in structure. + +#### Segment +Who are you building for? + +```markdown +### Segment: +- [Specify the target segment, e.g., "Small business owners using DIY accounting software"] +``` + +**Quality checks:** +- **Specific:** Not "users" but "enterprise IT admins" or "freelance designers" + +--- + +#### Persona +Provide details about the persona within this segment (reference `skills/proto-persona/SKILL.md`). + +```markdown +### Persona: +- [Describe the persona: demographics, behaviors, pains, goals] +``` + +**Example:** +- "Sarah, 35-year-old freelance graphic designer, manages 5-10 client projects at once, struggles with invoicing and payment tracking, wants to spend less time on admin and more time designing" + +--- + +### Step 2: Define the Narrative +What is the user trying to accomplish? Frame this as a Jobs-to-be-Done statement (reference `skills/jobs-to-be-done/SKILL.md`). + +```markdown +### Narrative: +- [Concise narrative of the persona's objective, e.g., "Complete a client project from kickoff to final payment"] +``` + +**Quality checks:** +- **Outcome-focused:** Not "use the product" but "deliver a client project on time and get paid" +- **One sentence:** If it takes more than one sentence, the scope may be too broad + +--- + +### Step 3: Identify Activities (Backbone) +List 3-5 high-level activities the persona engages in to fulfill the narrative. These form the backbone of your map. + +```markdown +### Activities: +1. [Activity 1, e.g., "Negotiate project scope and pricing"] +2. [Activity 2, e.g., "Execute design work"] +3. [Activity 3, e.g., "Deliver final assets to client"] +4. [Activity 4, e.g., "Send invoice and receive payment"] +5. [Activity 5, optional] +``` + +**Quality checks:** +- **Sequential:** Activities happen in order (left-to-right) +- **User actions:** Describe what the user *does*, not what the product *provides* +- **3-5 activities:** Too few = oversimplified, too many = overwhelming + +--- + +### Step 4: Break Activities into Steps +For each activity, list 3-5 steps that detail how the activity is carried out. + +```markdown +### Steps: + +**For Activity 1: [Activity Name]** +- Step 1: [Detail step 1, e.g., "Review client brief"] +- Step 2: [Detail step 2, e.g., "Draft project proposal"] +- Step 3: [Detail step 3, e.g., "Negotiate timeline and budget"] +- Step 4: [Optional step 4] +- Step 5: [Optional step 5] + +**For Activity 2: [Activity Name]** +- Step 1: [Detail step 1] +- Step 2: [Detail step 2] +... +``` + +**Quality checks:** +- **Actionable:** Each step is something the user does +- **Observable:** You could watch someone perform this step +- **Logical sequence:** Steps follow a natural order + +--- + +### Step 5: Break Steps into Tasks +For each step, list 5-7 tasks that must be completed. + +```markdown +### Tasks: + +**For Activity 1, Step 1: [Step Name]** +- Task 1: [Detail task 1, e.g., "Read client brief document"] +- Task 2: [Detail task 2, e.g., "Identify key deliverables"] +- Task 3: [Detail task 3, e.g., "Note budget constraints"] +- Task 4: [Detail task 4, e.g., "Clarify timeline expectations"] +- Task 5: [Detail task 5, e.g., "List open questions for client"] +- Task 6: [Optional task 6] +- Task 7: [Optional task 7] + +**For Activity 1, Step 2: [Step Name]** +- Task 1: [Detail task 1] +... +``` + +**Quality checks:** +- **Granular:** Tasks are small, specific actions +- **User-facing or behind-the-scenes:** Include both (e.g., "Send email" and "Receive confirmation") +- **Prioritizable:** You'll prioritize tasks vertically (top = essential, bottom = nice-to-have) + +--- + +### Step 6: Prioritize Vertically +Arrange tasks top-to-bottom by priority: +- **Top rows:** MVP / Release 1 (must-have) +- **Middle rows:** Release 2 (important but not critical) +- **Bottom rows:** Future / Nice-to-have + +Draw horizontal "release lines" to demarcate scope. + +--- + +### Step 7: Identify Gaps and Opportunities +Review the map and ask: +- Are there missing steps or tasks? +- Are there pain points we're not addressing? +- Are there opportunities to delight users? +- Do all activities flow logically? + +--- + +## Examples + +See `examples/sample.md` for a full story map example. + +--- + +## Common Pitfalls + +### Pitfall 1: Activities Are Features, Not User Behaviors +**Symptom:** "Activity 1: Use the dashboard. Activity 2: Generate reports." + +**Consequence:** You've mapped the product, not the user journey. + +**Fix:** Reframe as user actions: "Activity 1: Monitor project progress. Activity 2: Summarize work for stakeholders." + +--- + +### Pitfall 2: Too Many Activities +**Symptom:** 10+ activities across the backbone + +**Consequence:** Map becomes overwhelming and loses focus. + +**Fix:** Consolidate. If you have 10 activities, you're likely mixing activities with steps. Aim for 3-5 high-level activities. + +--- + +### Pitfall 3: Tasks Are Too Vague +**Symptom:** "Task 1: Do the thing" + +**Consequence:** Can't prioritize or estimate vague tasks. + +**Fix:** Be specific: "Task 1: Enter client email address in the 'Bill To' field." + +--- + +### Pitfall 4: Ignoring Vertical Prioritization +**Symptom:** All tasks at the same level—no MVP vs. future releases defined + +**Consequence:** No clarity on what to build first. + +**Fix:** Explicitly prioritize. Draw release lines. Force hard choices about what's MVP. + +--- + +### Pitfall 5: Mapping in Isolation +**Symptom:** PM creates the map alone, then presents it to the team + +**Consequence:** No shared ownership or understanding. + +**Fix:** Map collaboratively. Run a story mapping workshop with product, design, and engineering. + +--- + +## References + +### Related Skills +- `skills/proto-persona/SKILL.md` — Defines the persona for the story map +- `skills/jobs-to-be-done/SKILL.md` — Informs the narrative and activities +- `skills/user-story/SKILL.md` — Tasks from the map become user stories +- `skills/problem-statement/SKILL.md` — Problem statement frames the narrative + +### External Frameworks +- Jeff Patton, *User Story Mapping* (2014) — Origin of the story mapping technique +- Teresa Torres, *Continuous Discovery Habits* (2021) — Opportunity solution trees (complementary to story maps) + +### Dean's Work +- User Story Mapping Prompt (adapted from Jeff Patton's methodology) + +### Provenance +- Adapted from `prompts/user-story-mapping.md` in the `https://github.com/deanpeters/product-manager-prompts` repo. + +--- + +**Skill type:** Component +**Suggested filename:** `user-story-mapping.md` +**Suggested placement:** `/skills/components/` +**Dependencies:** References `skills/proto-persona/SKILL.md`, `skills/jobs-to-be-done/SKILL.md`, `skills/user-story/SKILL.md`, `skills/problem-statement/SKILL.md` diff --git a/.agents/skills/user-story-mapping/examples/sample.md b/.agents/skills/user-story-mapping/examples/sample.md new file mode 100644 index 0000000..06213e3 --- /dev/null +++ b/.agents/skills/user-story-mapping/examples/sample.md @@ -0,0 +1,77 @@ +# User Story Mapping Example + +## Example: Story Map for Freelance Invoicing Product + +```markdown +## User Story Map: Freelance Invoicing + +### Who + +#### Segment: +- Freelance creative professionals (designers, writers, photographers) + +#### Persona: +- Sarah, 35, freelance graphic designer +- Manages 5-10 clients at once +- Struggles with invoicing, payment tracking, and follow-ups +- Wants to spend less time on admin, more time designing +- Currently uses Excel + email, which is error-prone and time-consuming + +### Backbone + +#### Narrative: +- Complete a client project from kickoff to final payment without admin hassle + +#### Activities: +1. Negotiate project scope and pricing +2. Execute design work +3. Deliver final assets +4. Send invoice and receive payment +5. Follow up on late payments + +### Steps: + +**For Activity 1: Negotiate project scope and pricing** +- Step 1: Review client brief +- Step 2: Draft project proposal +- Step 3: Negotiate timeline and budget + +**For Activity 2: Execute design work** +- Step 1: Create initial concepts +- Step 2: Share concepts for feedback +- Step 3: Iterate based on feedback +- Step 4: Finalize design + +**For Activity 3: Deliver final assets** +- Step 1: Export final files in client-requested formats +- Step 2: Upload files to shared folder or email +- Step 3: Confirm client receipt + +**For Activity 4: Send invoice and receive payment** +- Step 1: Create invoice with project details +- Step 2: Send invoice to client +- Step 3: Track payment status +- Step 4: Confirm payment received + +**For Activity 5: Follow up on late payments** +- Step 1: Identify overdue invoices +- Step 2: Send payment reminder +- Step 3: Escalate if still unpaid + +### Tasks (Sample for Activity 4, Step 1: Create invoice): + +**MVP (Release 1):** +- Task 1: Enter client name and contact info +- Task 2: Add line items (description, hours, rate) +- Task 3: Calculate total automatically +- Task 4: Preview invoice before sending + +**Release 2:** +- Task 5: Add logo and custom branding +- Task 6: Save invoice templates for repeat clients +- Task 7: Auto-populate line items from project notes + +**Future:** +- Task 8: Generate invoices from time tracking data +- Task 9: Multi-currency support +``` diff --git a/.agents/skills/user-story-mapping/template.md b/.agents/skills/user-story-mapping/template.md new file mode 100644 index 0000000..7029373 --- /dev/null +++ b/.agents/skills/user-story-mapping/template.md @@ -0,0 +1,41 @@ +# User Story Map Template + +Use this template to create a complete user story map (segment, persona, backbone, steps, and tasks). + +## Provenance +Adapted from `prompts/user-story-mapping.md` in the `https://github.com/deanpeters/product-manager-prompts` repo. + +## Template +```markdown +## User Story Map Template + +### Who + +#### Segment: +- [Specify the target segment] + +#### Persona: +- [Describe the persona and their key characteristics] + +### Backbone + +#### Narrative: +- [Insert the concise narrative of the persona's objective] + +#### Activities: +1. [Describe Activity 1] +2. [Describe Activity 2] +3. [Continue as necessary for up to 5 activities] + +#### Steps: +For [Activity 1]: +- Step 1: [Detail Step 1 for Activity 1] +- Step 2: [Detail Step 2 for Activity 1] +- Step 3: [Detail Step 3 for Activity 1] + +#### Tasks: +For [Activity 1, Step 1]: +- Task 1: [Detail Task 1 for Step 1 of Activity 1] +- Task 2: [Detail Task 2 for Step 1 of Activity 1] +- Task 3: [Detail Task 3 for Step 1 of Activity 1] +``` diff --git a/.axhub/make/sidebar-tree.json b/.axhub/make/sidebar-tree.json index 3df5294..05b4337 100644 --- a/.axhub/make/sidebar-tree.json +++ b/.axhub/make/sidebar-tree.json @@ -1,79 +1,18 @@ { "version": 1, - "updatedAt": "2026-07-20T07:33:31.296Z", + "updatedAt": "2026-07-21T08:13:15.367Z", "prototypes": [ - { - "id": "item:prototypes:oneos-prototype-demo", - "kind": "item", - "title": "原型演示", - "itemKey": "prototypes/oneos-prototype-demo" - }, - { - "id": "folder-1783875936186-27raza", - "kind": "folder", - "title": "旧ONEOS", - "children": [ - { - "id": "item:prototypes:oneos-web-business", - "kind": "item", - "title": "业务管理", - "itemKey": "prototypes/oneos-web-business" - }, - { - "id": "item:prototypes:oneos-web-data-analysis", - "kind": "item", - "title": "数据分析", - "itemKey": "prototypes/oneos-web-data-analysis" - }, - { - "id": "item:prototypes:oneos-web-finance", - "kind": "item", - "title": "财务管理(合包)", - "itemKey": "prototypes/oneos-web-finance" - }, - { - "id": "item:prototypes:oneos-web-help-center", - "kind": "item", - "title": "帮助中心", - "itemKey": "prototypes/oneos-web-help-center" - }, - { - "id": "item:prototypes:oneos-web-lease-contract", - "kind": "item", - "title": "车辆租赁合同", - "itemKey": "prototypes/oneos-web-lease-contract" - }, - { - "id": "item:prototypes:oneos-web-ledger-data", - "kind": "item", - "title": "台账数据", - "itemKey": "prototypes/oneos-web-ledger-data" - }, - { - "id": "item:prototypes:oneos-web-ops", - "kind": "item", - "title": "运维管理", - "itemKey": "prototypes/oneos-web-ops" - }, - { - "id": "item:prototypes:oneos-web-procurement", - "kind": "item", - "title": "采购管理", - "itemKey": "prototypes/oneos-web-procurement" - } - ] - }, - { - "id": "item:prototypes:oneos-prototype-nav", - "kind": "item", - "title": "原型导航", - "itemKey": "prototypes/oneos-prototype-nav" - }, { "id": "folder-1782874576229-r8atbu", "kind": "folder", "title": "OneOS", "children": [ + { + "id": "item:prototypes:oneos-prototype-demo", + "kind": "item", + "title": "原型演示", + "itemKey": "prototypes/oneos-prototype-demo" + }, { "id": "item:prototypes:oneos-web-workbench-new", "kind": "item", @@ -185,12 +124,6 @@ "title": "站点信息", "itemKey": "prototypes/oneos-web-h2-station-site" }, - { - "id": "item:prototypes:oneos-h5-h2-order", - "kind": "item", - "title": "加氢记录(H5)", - "itemKey": "prototypes/oneos-h5-h2-order" - }, { "id": "item:prototypes:oneos-web-h2-station", "kind": "item", @@ -204,9 +137,9 @@ "itemKey": "prototypes/oneos-web-h2-station-weekly" }, { - "id": "item-prototypes-oneos-web-h2-station-analysis", + "id": "item:prototypes:oneos-web-h2-station-analysis", "kind": "item", - "title": "oneos web h2 station analysis", + "title": "加氢站分析", "itemKey": "prototypes/oneos-web-h2-station-analysis" } ] @@ -328,8 +261,94 @@ "kind": "item", "title": "业务条线说明", "itemKey": "prototypes/lease-business-line-overview" + }, + { + "id": "item:prototypes:yunxiao-pipeline-handbook", + "kind": "item", + "title": "云效生产线手册", + "itemKey": "prototypes/yunxiao-pipeline-handbook" } ] + }, + { + "id": "folder-1784563868850-xi0guc", + "kind": "folder", + "title": "小羚羚", + "children": [ + { + "id": "item:prototypes:oneos-h5-vehicle-assets", + "kind": "item", + "title": "车辆资产(H5)", + "itemKey": "prototypes/oneos-h5-vehicle-assets" + }, + { + "id": "item:prototypes:oneos-h5-h2-order", + "kind": "item", + "title": "加氢记录(H5)", + "itemKey": "prototypes/oneos-h5-h2-order" + } + ] + }, + { + "id": "folder-1783875936186-27raza", + "kind": "folder", + "title": "旧ONEOS", + "children": [ + { + "id": "item:prototypes:oneos-web-business", + "kind": "item", + "title": "业务管理", + "itemKey": "prototypes/oneos-web-business" + }, + { + "id": "item:prototypes:oneos-web-data-analysis", + "kind": "item", + "title": "数据分析", + "itemKey": "prototypes/oneos-web-data-analysis" + }, + { + "id": "item:prototypes:oneos-web-finance", + "kind": "item", + "title": "财务管理(合包)", + "itemKey": "prototypes/oneos-web-finance" + }, + { + "id": "item:prototypes:oneos-web-help-center", + "kind": "item", + "title": "帮助中心", + "itemKey": "prototypes/oneos-web-help-center" + }, + { + "id": "item:prototypes:oneos-web-lease-contract", + "kind": "item", + "title": "车辆租赁合同", + "itemKey": "prototypes/oneos-web-lease-contract" + }, + { + "id": "item:prototypes:oneos-web-ledger-data", + "kind": "item", + "title": "台账数据", + "itemKey": "prototypes/oneos-web-ledger-data" + }, + { + "id": "item:prototypes:oneos-web-ops", + "kind": "item", + "title": "运维管理", + "itemKey": "prototypes/oneos-web-ops" + }, + { + "id": "item:prototypes:oneos-web-procurement", + "kind": "item", + "title": "采购管理", + "itemKey": "prototypes/oneos-web-procurement" + } + ] + }, + { + "id": "item:prototypes:oneos-prototype-nav", + "kind": "item", + "title": "原型导航", + "itemKey": "prototypes/oneos-prototype-nav" } ], "docs": [], @@ -1116,4 +1135,4 @@ "templates": [], "components": [], "canvas": [] -} +} \ No newline at end of file diff --git a/.claude/skills/AutoVUL/SKILL.md b/.claude/skills/AutoVUL/SKILL.md new file mode 100644 index 0000000..41c2365 --- /dev/null +++ b/.claude/skills/AutoVUL/SKILL.md @@ -0,0 +1,139 @@ +--- +name: AutoVUL +description: >- + Generates OneOS PC version update logs from a Yunxiao (云效) iteration name by + loading all linked requirements, or from a pasted changelog list. Use when testers + or PMs say AutoVUL, 版本更新日志, 按迭代生成更新日志, 发版公告, or give a 迭代名称. +--- + +# AutoVUL(OneOS PC 版本更新日志) + +面向**测试人员发版**:输入云效**迭代名称** → 拉取该迭代关联需求 → 自动生成对外版本更新日志。 + +也可手动粘贴变更清单(兜底)。**预计维护时长**由人工单独通知,不写进日志。 + +## 何时使用 + +- 测试说:`按 $AutoVUL` / `生成版本更新日志` / `按迭代 xxx 出更新日志` +- 输入含云效「迭代名称」 +- 发版前需要统一文案口径 + +## 主流程(测试 · 按迭代,默认) + +完整取数步骤见 [references/yunxiao-sprint.md](references/yunxiao-sprint.md)。 + +```text +1. 确认云效项目(缺省:统一运营管理平台PC端;可改) +2. 向用户索取「迭代名称」(若口令里已带则直接用) +3. 在云效查找该迭代 → 立即反馈成功或失败(见下) +4. 成功:列出迭代内关联「需求」清单(编号 + 标题 + 状态)供用户快速核对 +5. 从每条需求提取变更素材(优先级见「素材优先级」) +6. 按本文格式分类、合并、润色 → 输出成稿正文 +``` + +### 迭代读取 · 成功 / 失败反馈(强制) + +查找迭代后**必须先反馈**,再继续或停止: + +| 结果 | 反馈文案(可微调,语义不变) | 下一步 | +|------|------------------------------|--------| +| 精确命中 1 个迭代 | `✅ 已找到迭代「{名称}」(状态:{状态}),关联需求 {N} 条。` | 继续拉需求并生成 | +| 0 个匹配 | `❌ 未找到迭代「{输入}」。请核对后重新输入迭代名称(可复制云效迭代标题)。` | **停止生成**;等待用户重输 | +| 多个模糊匹配 | `⚠️ 找到多个相似迭代:1)… 2)… 请回复序号或完整精确名称。` | 等用户选定后再继续 | +| 登录/权限失败 | `❌ 无法访问云效(未登录或无权限)。请在浏览器登录云效后重试,或改用手动清单。` | 停止;提示登录或兜底 | +| 迭代存在但 0 条需求 | `⚠️ 迭代「{名称}」已找到,但暂无关联需求。请确认规划后再试,或改用手动清单。` | 停止生成对外正文 | + +**失败后**:用户只需再发一次迭代名称(或纠正项目名),从步骤 2 重跑;不要要求用户重装 Skill。 + +### 素材优先级(每条需求) + +| 顺序 | 来源 | 用法 | +|------|------|------| +| 1 | 需求描述中的「更新内容」段(非「更新内容·历史」) | 首选,直接改写成日志条目 | +| 2 | AutoPRD「功能变更记录」中可对外部分 | 次选 | +| 3 | 需求标题 + 简短描述摘要 | 仅当前两者皆空时;价值句可合理补全 | +| — | 纯技术债、内部监控、未对用户开放 | **不写入**对外日志 | + +Bug 类需求(类型/标签含缺陷修复、或更新内容写「修复」)归入【Bug修复】;其余归【新功能】。边界不清优先 Bug修复。 + +### 版本号与更新时间 + +| 字段 | 规则 | +|------|------| +| 版本号 | 优先从迭代名解析(如含 `V1.1.5` / `1.1.5`);解析不到再问用户 | +| 更新时间 | 用户提供则用;未提供可写迭代计划开始日的 `MM月DD日HH:MM`,或先问 | +| 产品线 | 默认 `OneOS PC版` | + +## 兜底流程(手动清单) + +无云效权限、或迭代为空时:用 [input-template.md](input-template.md) 粘贴清单,跳过迭代步骤,直接分类润色。 + +## 固定输出格式 + +```text +OneOS PC版V{主.次.修订}更新日志: +更新时间:{MM}月{DD}日{HH}:{MM} +---------------------------------------------------- +【新功能】 +1「{模块名}」{做了什么},{责任部门}可{用户价值 / 解决的问题} +2「{模块名}」{…} + +【Bug修复】 +3「{模块名}」{修复了什么},{责任部门}可{恢复的能力 / 避免的影响} +``` + +### 版式硬约束 + +| 项 | 规则 | +|----|------| +| 标题 | `OneOS PC版V` + 版本号 + `更新日志:`(如 `V1.1.5`,无空格) | +| 更新时间 | 仅开始时刻;`MM月DD日HH:MM`;**禁止**「预计x分钟内完成」 | +| 分隔线 | `----------------------------------------------------` | +| 分区 | 仅 `【新功能】` / `【Bug修复】`;空类整段省略 | +| 序号 | 全篇连续,从 1 起 | +| 模块名 | `「」`;与产品菜单正式名一致 | +| 对外正文 | **不写**需求号、缺陷号、迭代 ID(核对清单可另附) | +| 结尾 | 无签名、无「感谢使用」、无技术附录 | + +## 分类 / 合并 / 文案(成稿规则) + +| 归入【新功能】 | 归入【Bug修复】 | 不写进对外日志 | +|----------------|-----------------|----------------| +| 新增能力、新入口、新字段/流程、可感知体验优化 | 原有能力异常、报错、数据错、交互失效 | 技术债、无感性能、未开放灰度、仅内部监控 | + +- **同分区同模块**合并为一条,分点用 `;` +- **跨分区**同模块分栏各写一条 +- 每条含:做什么 + 用户价值 + 责任部门自然嵌入(`{部门}人员可…`) +- **禁止**「供…条线…使用」;部门对照 [module-role-mapping.md](module-role-mapping.md) +- 禁止编造迭代内不存在的能力;存疑跳过或标待确认(不进正式成稿) +- 单条建议 ≤100 字 + +排序:先新功能后修复;同分区全员影响优先;资金/合规修复置修复区最前。 + +## 输出纪律 + +1. **先**输出迭代读取反馈(✅/❌/⚠️) +2. 成功时:可先给「本迭代需求清单」短表(含编号,便于测试核对) +3. 再输出**纯文本成稿正文**(默认不要 Markdown 大标题包裹成稿) +4. 用户明确要求时,可在正文后附「分类依据 / 跳过项」 + +## 发布前自检 + +- [ ] 已反馈迭代查找结果;失败时未强行编造成稿 +- [ ] 成稿条目均可追溯到本迭代关联需求素材 +- [ ] 标题、时间行、分隔线正确;无预计时长 +- [ ] 同模块合并正确;每条含价值 + 部门自然嵌入 +- [ ] 对外正文无需求号/缺陷号/技术黑话;序号连续 + +## 给测试同事的口令 + +```text +按 $AutoVUL 生成版本更新日志。 +项目:统一运营管理平台PC端 +迭代名称:V1.1.5发版迭代 +更新时间:07月16日16:00 +``` + +迭代名写错时,按提示改完再发一次即可。 + +手动兜底见 [input-template.md](input-template.md);成稿范例见 [examples.md](examples.md)。 diff --git a/.claude/skills/AutoVUL/examples.md b/.claude/skills/AutoVUL/examples.md new file mode 100644 index 0000000..eef8c46 --- /dev/null +++ b/.claude/skills/AutoVUL/examples.md @@ -0,0 +1,77 @@ +# AutoVUL 成稿与交互范例 + +## 1. 迭代读取失败 → 重输 + +**用户:** + +```text +按 $AutoVUL,迭代名称:V1.1.5 +``` + +**Agent:** + +```text +❌ 未找到迭代「V1.1.5」。请核对后重新输入迭代名称(可复制云效迭代标题)。 +``` + +**用户:** + +```text +V1.1.5发版迭代 +``` + +**Agent:** + +```text +✅ 已找到迭代「V1.1.5发版迭代」(进行中),关联需求 3 条: +1. ONEOSP-201 车辆管理运营城市展示优化 +2. ONEOSP-202 还车应结款租金计算修复 +3. ONEOSP-203 业务管理新增租赁物流统计 + +OneOS PC版V1.1.5更新日志: +更新时间:07月16日16:00 +---------------------------------------------------- +【新功能】 +1「车辆管理」优化车辆「运营城市」展示,运维部人员可快速辨认车辆所属运营区域及数据来源 +2「业务管理」新增租赁、物流业务统计模块,业务管理组、业务员可查看租赁经营数据,业务服务组、财务部可跟踪自营物流经营情况 + +【Bug修复】 +3「还车应结款」修复租金费用项重复及计算错误,业务管理组、安全部、运维部、能源部人员还车结算金额更准确 +``` + +## 2. 完整成稿风格基准(手动/迭代素材均可) + +> 责任部门对照:`module-role-mapping.md`(OneOS 业务条线说明) + +```text +OneOS PC版V1.1.5更新日志: +更新时间:07月16日16:00 +---------------------------------------------------- +【新功能】 +1「加氢站管理」升级新版加氢站模块,采购部可统一维护签约站与非签约站信息,签约加氢站可便捷管理站点日常运营 +2「车辆管理」优化车辆「运营城市」展示,运维部人员可快速辨认车辆所属运营区域及数据来源 +3「审批中心」支持按客户名称搜索关联合同,业务员、法务部审批时可快速定位合同;还车应结款添加照片附件后,业务管理组、安全部、运维部、能源部会签人员可在审批流中查看完整附件 +4「保险采购」支持保比价单按车辆单独勾选并分开提交审批,运维部、安全部人员可按车辆分批推进投保审批 +5「故障管理」增加「故障来源」字段展示,运维部人员可区分上报渠道并跟进处理 +6「证照管理」特种设备使用登记证/使用标识照片支持OCR识别,运维部、安全部人员可减少手工录入、加快证照建档 +7「全局显示」统一各模块列表分页样式与交互,各模块操作人员跨页面浏览时体验一致、上手更快 +8「交还车管理」支持交车/还车签章文件在线预览,运维部、业务管理组、司机可现场核对签章文件;提交时增加胎纹数据必填校验,减少漏填导致的后续结算争议 +9「还车应结款」调整安全部待办区域权限,安全部人员仅处理职责范围内的还车应结款审批 +10「业务管理」新增租赁、物流业务统计模块,业务管理组、业务员可查看租赁经营数据,业务服务组、财务部可跟踪自营物流经营情况 + +【Bug修复】 +11「还车管理」修复签字单与还车检查单/还车应结款轮胎数据不一致,运维部、业务管理组人员还车验收与结算依据保持一致 +12「租赁合同」修复编辑合同提交时异常提示内容冗余,业务员、法务部人员可快速定位真正错误原因 +13「交还车管理」修复交车/还车签章客户名称关联错误,避免运维部、业务管理组、司机现场核对时客户信息张冠李戴 +14「车辆氢费明细」修复同客户不同生效时段、以及不同客户成本单价显示异常,能源部人员可准确核对氢费核算单价 +15「还车应结款」修复租金费用项重复及计算错误,业务管理组、安全部、运维部、能源部人员还车结算金额更准确 +``` + +## 对照要点 + +| 规则 | 体现 | +|------|------| +| 先反馈迭代结果 | 失败可重输;成功列出需求再成稿 | +| 无预计时长 | 更新时间仅 `07月16日16:00` | +| 对外无需求号 | 成稿正文不出现 ONEOSP-xxx | +| 部门自然嵌入 | `运维部人员可…`,无「供…条线使用」 | diff --git a/.claude/skills/AutoVUL/input-template.md b/.claude/skills/AutoVUL/input-template.md new file mode 100644 index 0000000..ab42bb7 --- /dev/null +++ b/.claude/skills/AutoVUL/input-template.md @@ -0,0 +1,69 @@ +# AutoVUL · 输入模板 + +## A. 测试人员 · 按云效迭代(推荐) + +复制下面口令发给 AI(改迭代名即可): + +```text +按 $AutoVUL 生成版本更新日志。 +项目:统一运营管理平台PC端 +迭代名称: +更新时间: +``` + +### 期望反馈 + +- 成功:`✅ 已找到迭代「…」,关联需求 N 条` → 随后输出成稿 +- 失败:`❌ 未找到迭代「…」。请重新输入迭代名称` → 改名后再发一次即可 + +**说明**:预计维护时长由人工单独通知,不写入版本更新日志。 + +--- + +## B. 手动清单(云效不可用时兜底) + +### 版本元信息 + +| 字段 | 填写 | 示例 | +|------|------|------| +| 产品线 | | OneOS PC版 | +| 版本号 | | V1.1.5 | +| 更新开始时间 | | 07月16日16:00 | +| 发布范围 | 全量 / 灰度(说明) | 全量 | +| 是否对外发布 | 是 / 否 | 是 | + +### 新功能(原始材料,可粗糙) + +| 模块 | 做了什么 | 解决什么问题 / 用户故事 | 业务条线 | 责任部门 | 是否写入日志 | +|------|----------|-------------------------|----------|----------|--------------| +| 例:加氢站管理 | 升级新版模块 | 统一维护站点 | 能源业务条线 | 采购部、加氢站 | 是 | +| | | | | | | + +### Bug修复(原始材料,可粗糙) + +| 模块 | 原问题现象 | 修复后表现 | 对用户的影响 | 业务条线 | 责任部门 | 是否写入日志 | +|------|------------|------------|--------------|----------|----------|--------------| +| 例:还车应结款 | 租金费用项重复 | 金额正确 | 避免结算偏差 | 租赁业务条线 | 业务管理组、安全部、运维部、业务管理组-能源部 | 是 | +| | | | | | | | + +### 明确不要写入对外日志 + +- (如:重构、依赖升级、仅内部监控、未上线配置) + +### 粘贴给 AI + +```text +请按 $AutoVUL 用下方手动清单生成成稿(跳过云效迭代)。 +要求:同模块合并;每条含「做什么 + 用户价值」;责任部门自然融入;只输出正文。 + +【版本元信息】 +- 产品线:OneOS PC版 +- 版本号:V +- 更新开始时间: + +【新功能】 +(粘贴) + +【Bug修复】 +(粘贴) +``` diff --git a/.claude/skills/AutoVUL/module-role-mapping.md b/.claude/skills/AutoVUL/module-role-mapping.md new file mode 100644 index 0000000..2deea13 --- /dev/null +++ b/.claude/skills/AutoVUL/module-role-mapping.md @@ -0,0 +1,60 @@ +# 模块 → 业务条线 / 责任部门对照 + +> 数据源:OneOS「业务条线说明」页责任部门定义(租赁 / 能源 / 运维 / 安全 / 自营) +> 生成更新日志时,**适用对象**须从此表取 `roles`,不得自造部门名。 + +## 五条业务条线 + +| ID | 条线名称 | +|----|----------| +| lease | 租赁业务条线 | +| energy | 能源业务条线 | +| ops | 运维管理条线 | +| safety | 安全管理条线 | +| self-operated | 自营业务条线 | + +## 本版变更模块对照 + +| 更新日志模块名 | 业务条线 | lines.ts 模块 | 责任部门(roles,原文) | +|----------------|----------|---------------|-------------------------| +| 加氢站管理 | 能源业务条线 | 加氢站管理 | 采购部、加氢站 | +| 车辆管理 | 运维管理条线 | 验车入库 / 车辆管理(资产底座) | 运维部、采购部 | +| 审批中心 | 跨条线 | 租赁合同 + 还车应结款(审批场景) | 业务员、法务部、业务管理组、安全部、运维部、业务管理组-能源部 | +| 保险采购 | 运维管理条线 | 证照 / 保险(车辆资产关联) | 运维部、安全部 | +| 故障管理 | 运维管理条线 | 故障管理 | 运维部、司机 | +| 租赁合同 | 租赁业务条线 | 租赁合同 | 业务员、法务部 | +| 证照管理 | 运维管理条线 | 证照管理 | 运维部、安全部 | +| 全局显示 | 全系统 | 系统介绍(五条条线) | 业务管理组、运维部、采购部、安全部、法务部、财务部、业务管理组-能源部、业务服务组 | +| 交还车管理 | 运维管理条线 | 交车管理 + 还车管理 | 运维部、业务管理组、司机 | +| 还车应结款 | 租赁业务条线 | 还车应结款 | 业务管理组、安全部、运维部、业务管理组-能源部 | +| 还车管理 | 运维管理条线 | 还车管理 | 运维部、业务管理组 | +| 车辆氢费明细 | 能源业务条线 | 车辆氢费明细 | 业务管理组-能源部 | +| 业务管理 · 租赁统计 | 租赁业务条线 | 租赁业务台账 / 客户管理 | 业务管理组、业务员 | +| 业务管理 · 物流统计 | 自营业务条线 | 自营台账 | 业务服务组、财务部 | + +## 写法规则 + +- 部门名与 `roles` **完全一致**(`业务管理组-能源部` 对外可简写「能源部人员」) +- **禁止**「供…业务条线…使用」「供…人员使用」 +- **推荐**:`{部门}人员可…` / `{部门}可…` / `{部门}审批时可…` +- 同模块合并后,多部门用顿号或分号自然串联 +- 找不到模块时:查 `lines.ts`;仍无则标「待产品确认」不进成稿 + +## 自然表述示例 + +| 模块 | 生硬(禁止) | 自然(推荐) | +|------|--------------|--------------| +| 车辆管理 | 供运维管理条线运维部使用 | 运维部人员可快速辨认车辆所属运营区域 | +| 加氢站管理 | 供能源业务条线采购部、加氢站使用 | 采购部可统一维护站点信息,加氢站可便捷管理日常运营 | +| 还车应结款 | 供租赁业务条线业务管理组…使用 | 业务管理组、安全部、运维部、能源部还车结算金额更准确 | + +## 常见错误(禁止) + +| 错误写法 | 正确写法 | +|----------|----------| +| 氢能运营业务人员 | 采购部、加氢站(能源 · 加氢站管理) | +| 车辆运营及资产管理业务人员 | 运维部、采购部(运维 · 车辆管理) | +| 各业务条线审批人员 | 业务员、法务部、…(按审批场景枚举) | +| 保险采购业务人员 | 运维部、安全部 | +| 还车结算业务人员 | 业务管理组、安全部、运维部、业务管理组-能源部 | +| 经营分析及管理部门 | 业务管理组、业务服务组(分租赁 / 自营) | diff --git a/.claude/skills/AutoVUL/references/yunxiao-sprint.md b/.claude/skills/AutoVUL/references/yunxiao-sprint.md new file mode 100644 index 0000000..93fc195 --- /dev/null +++ b/.claude/skills/AutoVUL/references/yunxiao-sprint.md @@ -0,0 +1,81 @@ +# 云效迭代 → 需求清单(AutoVUL 取数) + +测试人员只提供**迭代名称**;Agent 负责在云效定位迭代、拉取关联需求、抽出更新素材。 + +## 安全边界(强制) + +- **禁止**在对话、Skill、日志、成稿中写入 Token、密码、Cookie、私钥。 +- 优先复用**已登录浏览器会话**操作云效 Projex(与 `yunxiao-requirement-lifecycle` 一致)。 +- 若团队已配置本机环境变量访问 OpenAPI,可走 API;Token 只读自环境,永不回显。 + +建议环境变量(可选,名称可按团队调整): + +| 变量 | 用途 | +|------|------| +| `YUNXIAO_TOKEN` | 个人访问令牌(`x-yunxiao-token`) | +| `YUNXIAO_DOMAIN` | 服务接入点域名 | +| `YUNXIAO_ORG_ID` | 组织 ID(中心版) | +| `YUNXIAO_PROJECT_ID` | 默认项目 ID(或运行时按项目名解析) | + +## 默认项目 + +口令未指定时:`统一运营管理平台PC端`。项目名不精确时先问清再查迭代。 + +## 判定顺序:解析迭代 + +| 顺序 | 条件 | 结果 | +|------|------|------| +| 1 | 用户未给迭代名称 | 询问:「请输入云效迭代名称(可复制标题)」;不继续 | +| 2 | 项目未确定 | 询问精确项目名;不继续 | +| 3 | 云效不可访问 | `❌ 无法访问云效…`;不继续 | +| 4 | 名称精确匹配 1 条迭代 | `✅ 已找到迭代…`;进入拉需求 | +| 5 | 0 条 | `❌ 未找到迭代…请重新输入`;不继续 | +| 6 | >1 条相似 | `⚠️ 多个相似…请回复序号或精确名称`;等用户选定 | +| 7 | 命中但关联需求 0 | `⚠️ …暂无关联需求`;不生成对外正文 | + +匹配规则:先**全等**(去首尾空格);全等失败再做包含/去空格模糊;模糊多条必须让用户确认,禁止擅自挑一个。 + +## 拉取关联需求 + +只收集工作项类型为**需求(Req)**且正式关联到该迭代的项。 + +- **浏览器路径**:打开项目 → 迭代 → 该迭代规划/工作项列表 → 筛选类型=需求 → 导出或逐条打开描述。 +- **OpenAPI 路径(示意)**: + 1. `ListSprints`:按项目列迭代,用 `name` 匹配用户输入,得到 `sprintId` + 2. `SearchWorkitems`:`category=Req`,`spaceId=项目Id`,`conditions` 中按字段 `sprint` CONTAINS `[sprintId]`(以租户实际 fieldIdentifier 为准;可先在需求列表页过滤后复制 `workitem/list` 的 conditions) + 3. 分页直到收齐(关注响应头 `x-total`) + 4. 需要正文时再 `GetWorkitem` 取 description + +每条需求最少记录: + +| 字段 | 说明 | +|------|------| +| serialNumber | 如 ONEOSP-123(仅用于核对清单,**不进对外成稿**) | +| subject | 标题 | +| status | 状态展示名 | +| description | 用于抽取「更新内容」 | +| category / type / labels | 辅助判断新功能 vs 修复 | + +## 从描述抽「更新内容」 + +1. 定位标题「更新内容」或「## 更新内容」段落 +2. **不要**把「更新内容·历史」并入本版素材(历史仅备查) +3. 若无「更新内容」:用标题 + 需求说明摘要;仍无有效产品语义则跳过并在内部备注 + +## 成功反馈后的核对清单(推荐短表) + +在生成成稿前输出,例如: + +```text +✅ 已找到迭代「V1.1.5发版迭代」(进行中),关联需求 5 条: +1. ONEOSP-101 保险采购比价分车提交 +2. ONEOSP-102 交还车签章预览 +… +正在按 AutoVUL 格式生成更新日志… +``` + +用户若立刻纠正「某条不要进日志」,从素材中剔除后再出成稿。 + +## 与发版任务关系 + +本 Skill **只读**迭代与需求,**不创建**【发版】任务、不改云效状态。发版挂链仍用 `yunxiao-requirement-lifecycle`(如「准备发布」口令 / A08)。 diff --git a/.claude/skills/oneos-autoprd/SKILL.md b/.claude/skills/oneos-autoprd/SKILL.md new file mode 100644 index 0000000..a10d84f --- /dev/null +++ b/.claude/skills/oneos-autoprd/SKILL.md @@ -0,0 +1,183 @@ +--- +name: oneos-autoprd +description: >- + Generates and keeps in sync OneOS AutoPRD (PM-facing requirements) plus Axhub + Make annotation directory Markdown/PRD; on 需求定稿/定稿/确认定稿/本次定稿 appends + functional release changelog below the PRD since last baseline. When a Yunxiao + requirement advances to 分析中/设计中/待开发, creates a same-titled linked task + (tag+create-time follow the requirement; 分析中/设计中 assignee=creator, 待开发 + assignee=何斐). Use eagerly for prototypes, AutoPRD, or Yunxiao requirement + description/更新内容/stage advance—do not skip annotation sync, 定稿 changelog, + or stage-task creation. +--- + +# OneOS AutoPRD + +为 OneOS 业务模块生成**产品经理可读、可评审、可排期**的需求说明,并**自动挂到 Axhub Make 标注工具 → 原型目录**。 + +另支持:**需求定稿**时汇总功能/逻辑变更记录;与云效组合时提供「需求说明 + 更新内容」;需求进入**分析中 / 设计中 / 待开发**时**自动创建与需求同名的关联任务**(规则见下,写在本 Skill,不改云效 Skill)。 + +颗粒度对齐「保险采购」全模块 PRD:讲清做什么、谁用、故事点、正逆向、流程图与关键业务逻辑;**不写**表结构、接口、字段代码名、文件路径、实现清单。 + +## 何时使用(含自动触发) + +**主动调用时** + +- AutoPRD、OneOS 需求说明、整模块 PRD、故事点 + 流程图 + +**改原型时必须自动跟进(全局规则)** + +- 正在修改 `src/prototypes//` 下页面、交互、文案、判定、验收相关内容 +- 同一轮交付内同步更新 PRD Markdown + 标注目录,不得只改代码 + +**需求定稿时(强制)** + +- 用户回复含:`需求定稿` / `定稿` / `确认定稿` / `本次定稿` +- 执行 [references/release-changelog.md](references/release-changelog.md):写第 10 章 + 更新基线 + +**云效建需求 / 完善需求时(强制组合)** + +- 与 `$yunxiao-requirement-lifecycle` 一起使用时:先跑本 Skill,再写云效描述 +- **需求说明** ← PRD 正文(或浓缩交付口径 + 关键章节) +- **更新内容** ← 第 10 章中**自上次定稿以来**的条目(无增量则「首版定稿 / 本轮无功能增量」) + +**云效需求推进至分析中 / 设计中 / 待开发时(强制)** + +- 无论口令来自本 Skill 还是云效 Skill,只要本轮把需求推到上述状态,就执行 [references/yunxiao-stage-tasks.md](references/yunxiao-stage-tasks.md) +- 自动建**与需求同名**的任务并正式关联;标签与创建时间口径沿用需求;分析中/设计中负责人=创建人,待开发负责人=**何斐** + +不要用本 Skill 替代:需求探索访谈、设计比稿、纯样式微调(无产品语义变化时可跳过全量重写)。 + +## 工作流 + +### 主流程(写/同步 PRD) + +1. **定模块**:确认 OneOS 模块名与 `src/prototypes//`。 +2. **读上下文(只取产品语义)** + - 用户说明、已确认口径、原型标注、`.spec/`、业务条线说明(`lines.ts`) + - 忽略实现细节;字段名/接口改写成业务语言。 +3. **收敛边界**:做什么 / 不做什么、外部依赖、与其它模块关系。 +4. **按模板成文**:下方「输出结构」;缺关键信息最多问 1~2 个问题,其余写「假设」。 +5. **落盘 + 标注同步(强制)** — 见 [references/annotation-sync.md](references/annotation-sync.md)。 + + | 顺序 | 动作 | + |------|------| + | A | 写/更新 `src/prototypes//.spec/requirements-prd.md` | + | B | 写/更新 `src/resources/prd/-autoprd.md` | + | C | 更新 `annotation-source.json` **顶层** `directory.nodes`(PRD 全文 + 推荐分章);禁止只写 `data.directory` | + | D | 若存在 `scripts/sync-annotation-directory.mjs`,执行之 | + +6. **交付说明**:路径、标注目录入口、故事点合计、开放问题/假设。 + +### 定稿流程(关键字触发) + +见 [references/release-changelog.md](references/release-changelog.md)。摘要: + +1. 读 PRD + `.spec/autoprd-baseline.json` +2. 汇总自上次定稿以来的**功能/逻辑**变更(排除样式/UI/表结构) +3. 追加到 `## 10. 功能变更记录`(最新在上) +4. 更新基线 JSON + 标注目录 +5. 若同时发云效:本次定稿块 →「更新内容」;旧段 →「更新内容·历史」 + +### 云效交接格式(给 yunxiao skill 直接粘贴) + +```markdown +## 原型链接 +<对象存储或预览 URL> + +## 需求说明 + + +## 更新内容 +<第 10 章本次定稿块;无则「首版定稿」或「本轮无功能/逻辑增量」> + +## 更新内容·历史 +<以往更新内容倒序,勿删除> +``` + +### 云效阶段任务(状态推进时强制) + +见 [references/yunxiao-stage-tasks.md](references/yunxiao-stage-tasks.md)。摘要: + +| 需求状态 | 任务标签 | 负责人 | 标题 | +|----------|----------|--------|------| +| 分析中 | 分析 | 需求创建人 | 与需求同名 | +| 设计中 | 设计 | 需求创建人 | 与需求同名 | +| 待开发 | 交付(或开发) | 何斐 | 与需求同名 | + +正式关联需求;创建时间口径沿用需求;同阶段未取消任务不重复建。 + +## 写作硬约束 + +**必须写** + +- 一句话定位 + 目标 / 非目标 +- 模块边界(含 mermaid 总览更好) +- 角色与目标(角色名优先对齐业务条线说明) +- **用户故事**(业务条线说明口径)+ Epic 级**故事点(SP)**粗估 +- 分功能**正向**与**逆向/边界** +- 至少 1~2 个 **mermaid** 流程图 +- **关键业务逻辑**(业务话) +- 验收清单 + 「交付口径」一段话 +- 定稿后:**功能变更记录**(第 10 章) + +**禁止写** + +- 数据库表、字段名、接口路径、代码路径、组件名、存储 key +- 研发实现指令(可写「正式环境由审批中心回写」这类业务依赖) +- 在变更记录里写样式/UI/表结构优化 + +## 用户故事口径(强制 · 对齐业务条线说明) + +真相源:原型 **业务条线说明**(`lease-business-line-overview` / `lines.ts`)。 +每条能力用「责任部门 → **起点** → **怎么运作** → **闭环**」叙述;**不要**用「作为…我希望…」宽表作主叙述。 + +| 块 | 写什么 | +|----|--------| +| 角色 | 谁负责、谁协同 | +| 起点 | 谁在什么前提下启动 | +| 怎么运作 | 有序步骤,含跨角色协作 | +| 关键结果 | 可选标签 | +| 闭环 | 业务终点与可追溯性 | + +可选:`US-xx`、压缩句「作为…我想…以便…」、规模 S/M/L 或 SP(仅排期,不替代主叙述)。 + +## 输出结构 + +完整模板:[references/template.md](references/template.md)。 + +```markdown +# <模块名> · 产品需求说明(全模块) + +## 1. 一句话与目标 +## 2. 模块边界(最重要) +## 3. 用户与角色 +## 4. 用户故事与故事点(业务条线说明口径) +## 5. 功能模块说明(正向 / 逆向) +## 6. 关键业务逻辑(必须对齐) +## 7. 总览流程图 +## 8. 验收清单 +## 9. 交付口径 +## 10. 功能变更记录 ← 定稿后维护;日常改原型不强制每改必写 +``` + +## 质量自检 + +- [ ] 产品经理不看代码也能评审 +- [ ] 用户故事为起点 / 怎么运作 / 闭环 +- [ ] `.spec/requirements-prd.md` 已更新 +- [ ] 标注目录「产品需求说明(PRD)」已同步且正文一致 +- [ ] 定稿时:第 10 章 + `autoprd-baseline.json` 已更新 +- [ ] 推进至分析中/设计中/待开发时:同名任务已创建或复用,正式关联,负责人正确 +- [ ] 无表结构 / 接口 / 代码路径;变更记录无样式/UI 废话 + +## 参考 + +- 定稿变更日志:[references/release-changelog.md](references/release-changelog.md) +- 云效阶段任务:[references/yunxiao-stage-tasks.md](references/yunxiao-stage-tasks.md) +- 标注同步细则:[references/annotation-sync.md](references/annotation-sync.md) +- 章节模板:[references/template.md](references/template.md) +- 故事示例:[references/granularity-example.md](references/granularity-example.md) +- 业务条线:`src/prototypes/lease-business-line-overview/lines.ts` +- 复杂判定规格:配合项目规则 `business-logic-documentation` +- 云效组合:`$yunxiao-requirement-lifecycle`(建需求时先本 Skill) diff --git a/.claude/skills/oneos-autoprd/references/annotation-sync.md b/.claude/skills/oneos-autoprd/references/annotation-sync.md new file mode 100644 index 0000000..2cdd7c6 --- /dev/null +++ b/.claude/skills/oneos-autoprd/references/annotation-sync.md @@ -0,0 +1,98 @@ +# Axhub 标注目录同步(强制) + +使用 OneOS AutoPRD 时,除资源库归档外,**必须**把 PRD 挂到 Axhub Make 标注工具的「原型目录」里,用户才能在右侧面板直接打开。 + +## 落点(三份必须一致) + +| # | 路径 | 作用 | +|---|------|------| +| 1 | `src/prototypes//.spec/requirements-prd.md` | **主真相**:产品可读 PRD 全文 | +| 2 | `src/prototypes//annotation-source.json` → **顶层** `directory.nodes` | **标注目录入口**:面板里可见的 Markdown/PRD | +| 3 | `src/resources/prd/-autoprd.md` | 资源库归档(可选但默认写) | + +可选:复杂判定另写 `.spec/.md`,目录里再挂一条,并在 PRD 里链过去(与 `business-logic-documentation` 一致)。 + +## ⚠️ 目录必须写在文档顶层(常见踩坑) + +Wire format(与 `prototype-annotation` 一致): + +```json +{ + "documentVersion": 1, + "format": "axhub-annotation-source", + "data": { "version": 2, "prototypeName": "...", "nodes": [] }, + "markdownMap": {}, + "assetMap": {}, + "directory": { "nodes": [] } +} +``` + +| 正确 | 错误 | +|------|------| +| 顶层 `directory.nodes` | 只写在 `data.directory` | +| `PrototypeAnnotationHost` / Make 读的是 `source.directory` | 写在 `data` 下时右侧「原型目录」为空 | + +若误写在 `data.directory`:迁移到顶层 `directory`,并删除 `data.directory`,避免双源。 + +## 目录节点写法(推荐) + +在说明根 `folder` 的 `children` **靠前**插入或更新: + +```json +{ + "type": "markdown", + "id": "-doc-prd", + "title": "产品需求说明(PRD)", + "markdownPath": ".spec/requirements-prd.md", + "markdown": "<与 .spec/requirements-prd.md 相同的全文>" +} +``` + +### 分章节 PRD(推荐,便于右侧目录浏览) + +在「产品需求说明(PRD)」旁增加文件夹 **「PRD 分章」**,按 PRD 的 `##` 一级标题拆成多个 `markdown` 子节点(标题用章节名,正文为该章全文)。专题规格(`.spec/.md`)放在 **「专题规格」** 文件夹。 + +参考结构: + +```text +folder <模块>说明 + markdown 产品需求说明(PRD) ← 全文 + folder PRD 分章 + markdown 1. 一句话与目标 + markdown 2. 模块边界 + … + folder 专题规格 + markdown +``` + +有 `scripts/sync-annotation-directory.mjs` 的原型:改 PRD / `.spec` 后**必须执行**该脚本再生目录。 + +定稿后第 10 章「功能变更记录」必须进入 PRD 全文节点;若有「PRD 分章」,同步该章节点。详见 [release-changelog.md](release-changelog.md)。 + +约定: + +- `id`:`<短前缀>-doc-prd`(如 `ipc-doc-prd`、`wb-doc-prd`、`h5-va-doc-prd`) +- `title`:优先「产品需求说明(PRD)」;已有「PRD」可保留不改名 +- **同时写** `markdownPath` + `markdown`:构建可内联路径,运行时目录也能直接读到正文 +- 若已有同 id / 同标题节点:只更新正文与 path,不新建重复入口 +- 遵守 `prototype-annotation-layout`:目录以 `markdown` 为主,不要用 `link`/`route` 顶替 PRD + +## 增量改原型时怎么更新 + +1. 根据改动的文件路径确定 ``。 +2. 读现有 `.spec/requirements-prd.md`(没有则按 AutoPRD 模板新建)。 +3. 只改受影响章节(故事、正逆向、关键逻辑、验收);其余保留。 +4. 回写 `.spec` + `resources/prd` + **顶层** `annotation-source.json` → `directory`(正文与文件一致;含分章节点)。 +5. 若原型有 `scripts/sync-annotation-directory.mjs`,改完后执行它。 + +## 可跳过全量重写的情况 + +纯样式、无文案/无流程/无判定变化的微调:不必重写整份 PRD,但若改了用户可见文案或交互结果,仍须同步对应段落与目录节点。 + +## 验收 + +- [ ] `.spec/requirements-prd.md` 存在且为最新 +- [ ] `annotation-source.json` **顶层**有 `directory.nodes`(不是只在 `data` 下) +- [ ] 标注目录能打开「产品需求说明(PRD)」且内容与文件一致 +- [ ] 若有「PRD 分章」:各章可单独打开 +- [ ] 用户故事仍为业务条线说明口径(起点 / 怎么运作 / 闭环) diff --git a/.claude/skills/oneos-autoprd/references/granularity-example.md b/.claude/skills/oneos-autoprd/references/granularity-example.md new file mode 100644 index 0000000..5126671 --- /dev/null +++ b/.claude/skills/oneos-autoprd/references/granularity-example.md @@ -0,0 +1,58 @@ +# 颗粒度标杆与用户故事口径 + +## 用户故事:对齐「业务条线说明」页面 + +页面:`/prototypes/lease-business-line-overview` +数据:`src/prototypes/lease-business-line-overview/lines.ts` + +每个能力卡片的产品叙述结构: + +```text +责任部门(roles) + → 起点(start) + → 怎么运作(process[],有序) + → 关键结果(outcomes,可选) + → 闭环(closure) +``` + +AutoPRD 第 4 章必须用同一套结构写用户故事,而不是「作为…我希望…」宽表。 + +### 示例(保险采购 · 交车合规,示意) + +#### US · 保险有效才允许交车 +- **角色**:采购部、运维部 +- **起点**:采购或运营维护车辆交强险与商业险台账,作为交车前置合规数据。 +- **怎么运作**: + 1. 在保险采购维护一车一档险种台账(录入 / 识别 / 导入)。 + 2. 交车时系统校验交强 + 商业是否均为正常或临期。 + 3. 不合规则拦截交车并给出可理解原因。 +- **关键结果**:`允许交车` / `禁止交车 · 保险异常` +- **闭环**:交车合规与台账一致、可追溯;异常车辆不能进入履约运营。 +- **排期(可选)**:US-17 · 规模 M · 作为采购/运维,我想维护交强+商业险并在交车时校验,以便不合规车辆无法交车。 + +### 反例(不要作为主叙述) + +| 编号 | 用户故事 | SP | +|------|----------|----| +| B1 | 作为运营,我能看到失败原因 | 2 | + +↑ 信息太扁,缺少起点、协作步骤与闭环结果。 + +--- + +## AutoPRD 其余层次(仍要对齐) + +1. 一句话 + 目标/非目标 +2. 模块边界(独立业务线写清「不自动同步」) +3. 角色表(命名对齐业务条线) +4. **用户故事:起点 / 怎么运作 / 闭环** + SP/规模 +5. 分模块正逆向 +6. 关键业务逻辑 +7. mermaid 流程图 +8. 验收清单 +9. 交付口径 + +## 刻意不写 + +- 表结构、接口、代码路径 +- 用实现指令代替业务闭环描述 diff --git a/.claude/skills/oneos-autoprd/references/release-changelog.md b/.claude/skills/oneos-autoprd/references/release-changelog.md new file mode 100644 index 0000000..998e9bf --- /dev/null +++ b/.claude/skills/oneos-autoprd/references/release-changelog.md @@ -0,0 +1,78 @@ +# 功能变更记录(定稿触发) + +用户回复**需求定稿关键字**后,必须生成「自上次定稿以来」的功能变更日志,追加到 PRD **最下方**,并更新定稿基线。 + +## 触发关键字(命中任一) + +`需求定稿` · `定稿` · `确认定稿` · `本次定稿` + +(可带模块名,如「保险采购需求定稿」。) + +## 写什么 / 不写什么 + +| 要写(产品发版视角) | 不写 | +|----------------------|------| +| 哪个功能做了什么修改 | 布局、页面样式、UI 设计优化 | +| 变更了什么业务逻辑 / 流程 / 验收口径 | 「优化了哪些表」「改了哪些字段/接口」 | +| 用户可感知的交互结果变化 | 纯样式、动效、无语义文案微调 | + +条目口吻示例: + +- 「识别失败」:失败数可点击查看失败文件与原因;仅失败时也可打开明细 +- 「比价单」:审批通过后仍须在保单管理另行录入正式保单(强调不自动同步) + +## 落点 + +1. **PRD 文末章节**(强制) + `src/prototypes//.spec/requirements-prd.md` → `## 10. 功能变更记录` + 新定稿块插在该章**最上方**(倒序:最新在上);历史块保留。 + +2. **定稿基线**(强制) + `src/prototypes//.spec/autoprd-baseline.json` + +```json +{ + "prototypeId": "insurance-procurement", + "lastConfirmedAt": "2026-07-21T12:00:00+08:00", + "lastConfirmedLabel": "定稿 · 2026-07-21", + "summaryBullets": ["…", "…"] +} +``` + +3. **标注目录**:同步更新「产品需求说明(PRD)」全文节点;若有「PRD 分章」,增加或更新「功能变更记录」分章。 + +4. **云效交接**(若本轮同时走云效):把**本次定稿块**正文作为需求描述「更新内容」;旧「更新内容」挪到「更新内容·历史」。 + +## 定稿工作流 + +1. 确认 ``(从对话 / 打开文件 / 用户指名推断)。 +2. 读现有 PRD 与 `autoprd-baseline.json`(无基线则本轮视为**首版定稿**)。 +3. 收集自 `lastConfirmedAt` 以来的产品语义变更: + 对话确认口径 → 批注/操作说明 → PRD 新旧差异 →(可选)相关提交说明。 + 过滤掉样式/表结构类改动。 +4. 若无功能增量:仍写定稿块,正文为「本轮无功能/逻辑增量(仅样式或未达产品语义变更)」或「首版定稿」。 +5. 追加 `### 定稿 · YYYY-MM-DD` + 条目列表到第 10 章顶部。 +6. 更新 `autoprd-baseline.json`;执行 annotation-sync。 +7. 向用户回报:基线时间、条目数、PRD 路径;若需发云效,提示可用口令。 + +## PRD 章节格式 + +```markdown +## 10. 功能变更记录 + +> 产品经理原型发版记录。仅记功能与业务逻辑变更;不含样式/UI/表结构。 + +### 定稿 · 2026-07-21 + +- 「能力名」:做了什么 / 逻辑如何变 +- … + +### 定稿 · 2026-07-10 + +- … +``` + +## 与日常改原型的关系 + +- **改原型过程中**:按 AutoPRD 主流程增量更新第 1–9 章;**不必**每改一次就写第 10 章。 +- **用户说定稿时**:才汇总写入第 10 章并刷新基线。 diff --git a/.claude/skills/oneos-autoprd/references/template.md b/.claude/skills/oneos-autoprd/references/template.md new file mode 100644 index 0000000..449b8bf --- /dev/null +++ b/.claude/skills/oneos-autoprd/references/template.md @@ -0,0 +1,163 @@ +# OneOS AutoPRD 输出模板 + +复制下列结构填写。方括号为占位。**第 4 章用户故事必须用业务条线说明口径。** + +```markdown +# <模块名> · 产品需求说明(全模块) + +## 1. 一句话与目标 + +**一句话** +<用一句话说明模块解决什么> + +**要解决的问题** +- … + +**本期目标** +1. … +2. … + +**非目标(本期不做)** +- … + +## 2. 模块边界(最重要) + +(可选 mermaid:模块与外部系统关系) + +| 业务线 / 子域 | 做什么 | 不做什么 | +|---------------|--------|----------| +| … | … | … | + +**外部依赖(产品口径)** +| 依赖方 | 交互方式 | 产品要求 | +|--------|----------|----------| +| … | … | … | + +**关键约束** +- … + +## 3. 用户与角色 + +| 角色 | 主要目标 | +|------|----------| +| … | … | + +> 角色名优先与「业务条线说明」一致(如业务管理组、采购部、运维部)。 + +## 4. 用户故事与故事点(业务条线说明口径) + +> 故事点 / 规模供排期参考,可按团队基准调整。合计约 **N SP**(或 S/M/L 汇总说明)。 + +按 Epic 或能力单元分组。**每一条**按下面四块写(对齐业务条线说明页:责任部门 → 起点 → 怎么运作 → 闭环): + +### Epic A · <名称>(约 N SP) + +#### A1 · <能力标题> +- **角色**:… +- **起点**:… +- **怎么运作**: + 1. … + 2. … + 3. … +- **关键结果**(可选):`可…` / `禁止…` +- **闭环**:… +- **排期(可选)**:US-xx · 规模 S/M/L · 「作为…,我想…,以便…」 + +#### A2 · <能力标题> +… + +### Epic B · … + +## 5. 功能模块说明(正向 / 逆向) + +### 5.x <功能名> + +**正向** +1. … +2. … + +**逆向 / 边界** + +| 情况 | 系统表现 | +|------|----------| +| … | … | + +## 6. 关键业务逻辑(必须对齐) + +### 6.x <规则名> + +用编号优先级、表格或短列表写清业务规则。 + +## 7. 总览流程图 + +```mermaid +flowchart TB + home[入口] --> a[能力A] + home --> b[能力B] +``` + +## 8. 验收清单(产品 / 测试) + +**子域 A** +- [ ] … + +## 9. 交付口径 + +> <一段可贴需求单开头的浓缩描述> + +## 10. 功能变更记录 + +> 产品经理原型发版记录。仅记功能与业务逻辑变更;不含样式/UI/表结构。 +> 由「需求定稿」关键字触发追加;日常改原型不强制每改必写。详见 [release-changelog.md](release-changelog.md)。 + +### 定稿 · YYYY-MM-DD + +- 「<能力名>」:<做了什么 / 逻辑如何变> +- … +``` + +成文后**必须**同步 Axhub 标注目录(见 [annotation-sync.md](annotation-sync.md)): + +1. `src/prototypes//.spec/requirements-prd.md` +2. `annotation-source.json` → 目录节点「产品需求说明(PRD)」(`markdownPath` + `markdown`) +3. `src/resources/prd/-autoprd.md` +4. 定稿时另写:`src/prototypes//.spec/autoprd-baseline.json` + +## 用户故事写法(对照业务条线说明) + +业务条线说明页字段 → AutoPRD 字段: + +| 页面 | AutoPRD | +|------|---------| +| 责任部门标签 | **角色** | +| 起点 | **起点** | +| 怎么运作(有序列表) | **怎么运作** | +| 结果色签 | **关键结果**(可选) | +| 闭环 | **闭环** | + +示例口吻(摘自业务条线风格,非照抄某模块): + +- **起点**:采购部维护交强与商业险台账,作为交车合规的前置数据。 +- **怎么运作**:1. 运营录入或识别保单… 2. 运维交车时校验… +- **闭环**:核心险种有效则允许交车;异常则拦截并提示原因,过程可追溯。 + +**不要**用下面这种宽表作为第 4 章唯一形态: + +| 编号 | 作为…我希望… | SP | +|------|--------------|----| + +「作为…我想…以便…」只允许作为**排期可选一行**,主叙述必须是起点/怎么运作/闭环。 + +## 正逆向写法约定 + +- **正向**:用户成功完成任务的最短路径 +- **逆向**:取消、关闭、禁用、冲突状态、不可操作项 +- 审批类:写清「本页只读 / 在外部系统办理」 + +## 规模与故事点 + +| 规模 | 建议含义 | 约 SP | +|------|----------|-------| +| S | 轻交互 / 列表展示 | 1~2 | +| M | 标准流程 + 筛选详情 | 3~5 | +| L | 状态机 / 跨模块 / 批量识别 | 6+ | diff --git a/.claude/skills/oneos-autoprd/references/yunxiao-stage-tasks.md b/.claude/skills/oneos-autoprd/references/yunxiao-stage-tasks.md new file mode 100644 index 0000000..09cfe27 --- /dev/null +++ b/.claude/skills/oneos-autoprd/references/yunxiao-stage-tasks.md @@ -0,0 +1,44 @@ +# 云效阶段任务自动创建(AutoPRD 负责) + +本规则写在 **oneos-autoprd**,不依赖改写 `yunxiao-requirement-lifecycle`。 +凡本 Skill 参与的云效操作中,需求推进到下列状态时,**同一轮必须**建同名任务并正式关联需求。 + +## 触发状态 + +| 需求推进至 | 任务标签(右侧基础字段) | 负责人 | 任务标题 | +|------------|--------------------------|--------|----------| +| **分析中** | `分析` | **需求创建人**姓名 | 与需求标题**完全同名** | +| **设计中** | `设计` | **需求创建人**姓名 | 与需求标题**完全同名** | +| **待开发** | `交付`(若项目无「交付」标签则用 `开发`) | **何斐** | 与需求标题**完全同名** | + +## 强制字段与关系 + +1. **正式关联需求**:父子或关联项;只挂标题不算关联。创建后必须在界面上能看到需求 ↔ 任务关系。 +2. **标签**:写入任务「标签」基础字段(见上表);标题前缀不能代替标签。 +3. **创建时间沿用需求**:任务的创建时间(及标签侧可见的创建时间口径)尽量与**需求创建时间**一致。 + - 平台允许改创建时间 / 用 API 指定时:写成需求的创建时间。 + - 平台不允许:在任务描述首行注明 `创建时间口径=需求创建时间(YYYY-MM-DD HH:mm)`,并在回报里说明「平台限制未改系统创建时间」。 +4. **幂等**:同一需求、同一阶段标签下,已存在**未取消**且已正式关联的同名任务 → **不新建**,只校验负责人/标签/关联是否正确,缺则补齐。 +5. **顺序**:先确认需求已进入目标状态(或本轮动作将写入该状态),再创建/补齐任务;回报时给出任务编号与链接。 + +## 执行步骤(apply 时) + +```text +1. 读取需求:标题、创建人、创建时间、当前/目标状态、已关联任务列表 +2. 按上表确定本轮阶段标签与负责人 +3. 查重(需求ID + 阶段标签 + 未取消) +4. 无则创建:标题=需求标题;标签=阶段标签;负责人=上表;创建时间口径=需求创建时间;正式关联需求 +5. 有则校验并修补标签/负责人/关联 +6. 回报:需求状态、任务编号、负责人、是否新建/复用 +``` + +## 不在本规则内 + +- 不自动建「测试 / 发版」任务(除非用户另行要求)。 +- 不因样式类 PRD 变更触发建任务;仅**需求状态**进入分析中/设计中/待开发时触发。 +- 云效生命周期细则、流水线、缺陷闭环仍可由 `$yunxiao-requirement-lifecycle` 配合执行;**本建任务逻辑以 AutoPRD 本文为准**。 + +## 与定稿 / 需求说明的关系 + +- 写「需求说明 / 更新内容」仍按 AutoPRD 主流程与定稿流程。 +- 推进到待开发且需要描述时:先 AutoPRD 文档,再改状态,再按本文件建任务(负责人何斐)。 diff --git a/.cursor/rules/oneos-autoprd-sync.mdc b/.cursor/rules/oneos-autoprd-sync.mdc new file mode 100644 index 0000000..a679800 --- /dev/null +++ b/.cursor/rules/oneos-autoprd-sync.mdc @@ -0,0 +1,42 @@ +--- +description: 改原型跟进 AutoPRD;定稿写功能变更;云效建需求先跑 AutoPRD;状态推进建同名任务 +alwaysApply: true +--- + +# OneOS AutoPRD 全局同步 + +## 改原型 + +修改 `src/prototypes/**` 且涉及**行为、文案、流程、判定或验收**时,同一轮必须跟进 **oneos-autoprd**: + +1. 更新 `.spec/requirements-prd.md`(用户故事:起点 → 怎么运作 → 闭环)。 +2. 更新 `annotation-source.json` **顶层** `directory.nodes` 中「产品需求说明(PRD)」。 +3. 同步 `src/resources/prd/-autoprd.md`(若有)。 + +纯样式且无产品语义变化:可跳过全量重写。 + +## 需求定稿 + +用户回复含 `需求定稿` / `定稿` / `确认定稿` / `本次定稿` 时: + +1. 按 skill `references/release-changelog.md` 汇总**自上次定稿**的功能/逻辑变更。 +2. 追加到 PRD `## 10. 功能变更记录`(最新在上);更新 `.spec/autoprd-baseline.json`。 +3. **不要**写布局/UI/表结构类条目。 + +## 云效建需求 + +使用 `yunxiao-requirement-lifecycle` **创建或完善**需求描述时,**先**跑 `oneos-autoprd`: + +- 需求说明 ← AutoPRD 正文 +- 更新内容 ← 第 10 章自上次定稿以来的条目(无则首版/无增量) +- 旧更新内容 →「更新内容·历史」 + +## 云效状态 → 同名任务(AutoPRD 规则,不改云效 Skill) + +需求推进至 **分析中 / 设计中 / 待开发** 时,按 `references/yunxiao-stage-tasks.md`: + +- 建与需求**同名**任务并正式关联 +- 标签:分析 / 设计 / 交付(或开发) +- 创建时间口径沿用需求 +- 分析中、设计中负责人=创建人;待开发负责人=**何斐** +- 同阶段已有未取消关联任务则复用,不重复建 diff --git a/.cursor/rules/yunxiao-record-requirement-fast-path.mdc b/.cursor/rules/yunxiao-record-requirement-fast-path.mdc new file mode 100644 index 0000000..d250731 --- /dev/null +++ b/.cursor/rules/yunxiao-record-requirement-fast-path.mdc @@ -0,0 +1,17 @@ +--- +description: 云效「记录需求」须先点选优先级/推进至;统一运营管理平台走快路径,禁止默认参数与盲目探测 +globs: +alwaysApply: true +--- + +# 云效记录需求 · 参数门禁与快路径 + +当用户使用 `$yunxiao-requirement-lifecycle` / 「记录需求」,且涉及优先级、推进至时: + +1. **必须先拿到明确选择**:优先级(紧急/高/中/低)、推进至(分析中/设计中/设计完成/开发中)、**标签(从云效标签 catalog 点选,可多选)**。优先 `AskQuestion`;不可用则用 Plan 确认并由用户写明 A+B+C。 +2. **禁止**在未选择时默认「中 + 分析中」并自动建单。「批准/Implement 计划」≠ 已完成点选。 +3. 项目为**统一运营管理平台**(含 PC 端别名)时:先读 + `.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-fast-path.md` 与 + `.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-runtime-ids.json`(标签清单见同目录 `oneos-pc-tag-catalog.md`),按已验证 API/ID 执行;禁止重复探测 create URL。 +4. **标签禁止**按 `lines.ts` / 业务条线说明自动推断;只能用用户点选的 catalog 名。API 打标失败时 UI 兜底;仍失败则停下请用户回复标签名,不得声称已成功。 +5. 优先 API(创建时写入描述;标签用 `PATCH … propertyKey=tag`);浏览器仅作标签失败或状态连跳兜底;少截图、同失败不循环重试。 diff --git a/.cursor/skills/AutoVUL/SKILL.md b/.cursor/skills/AutoVUL/SKILL.md new file mode 100644 index 0000000..41c2365 --- /dev/null +++ b/.cursor/skills/AutoVUL/SKILL.md @@ -0,0 +1,139 @@ +--- +name: AutoVUL +description: >- + Generates OneOS PC version update logs from a Yunxiao (云效) iteration name by + loading all linked requirements, or from a pasted changelog list. Use when testers + or PMs say AutoVUL, 版本更新日志, 按迭代生成更新日志, 发版公告, or give a 迭代名称. +--- + +# AutoVUL(OneOS PC 版本更新日志) + +面向**测试人员发版**:输入云效**迭代名称** → 拉取该迭代关联需求 → 自动生成对外版本更新日志。 + +也可手动粘贴变更清单(兜底)。**预计维护时长**由人工单独通知,不写进日志。 + +## 何时使用 + +- 测试说:`按 $AutoVUL` / `生成版本更新日志` / `按迭代 xxx 出更新日志` +- 输入含云效「迭代名称」 +- 发版前需要统一文案口径 + +## 主流程(测试 · 按迭代,默认) + +完整取数步骤见 [references/yunxiao-sprint.md](references/yunxiao-sprint.md)。 + +```text +1. 确认云效项目(缺省:统一运营管理平台PC端;可改) +2. 向用户索取「迭代名称」(若口令里已带则直接用) +3. 在云效查找该迭代 → 立即反馈成功或失败(见下) +4. 成功:列出迭代内关联「需求」清单(编号 + 标题 + 状态)供用户快速核对 +5. 从每条需求提取变更素材(优先级见「素材优先级」) +6. 按本文格式分类、合并、润色 → 输出成稿正文 +``` + +### 迭代读取 · 成功 / 失败反馈(强制) + +查找迭代后**必须先反馈**,再继续或停止: + +| 结果 | 反馈文案(可微调,语义不变) | 下一步 | +|------|------------------------------|--------| +| 精确命中 1 个迭代 | `✅ 已找到迭代「{名称}」(状态:{状态}),关联需求 {N} 条。` | 继续拉需求并生成 | +| 0 个匹配 | `❌ 未找到迭代「{输入}」。请核对后重新输入迭代名称(可复制云效迭代标题)。` | **停止生成**;等待用户重输 | +| 多个模糊匹配 | `⚠️ 找到多个相似迭代:1)… 2)… 请回复序号或完整精确名称。` | 等用户选定后再继续 | +| 登录/权限失败 | `❌ 无法访问云效(未登录或无权限)。请在浏览器登录云效后重试,或改用手动清单。` | 停止;提示登录或兜底 | +| 迭代存在但 0 条需求 | `⚠️ 迭代「{名称}」已找到,但暂无关联需求。请确认规划后再试,或改用手动清单。` | 停止生成对外正文 | + +**失败后**:用户只需再发一次迭代名称(或纠正项目名),从步骤 2 重跑;不要要求用户重装 Skill。 + +### 素材优先级(每条需求) + +| 顺序 | 来源 | 用法 | +|------|------|------| +| 1 | 需求描述中的「更新内容」段(非「更新内容·历史」) | 首选,直接改写成日志条目 | +| 2 | AutoPRD「功能变更记录」中可对外部分 | 次选 | +| 3 | 需求标题 + 简短描述摘要 | 仅当前两者皆空时;价值句可合理补全 | +| — | 纯技术债、内部监控、未对用户开放 | **不写入**对外日志 | + +Bug 类需求(类型/标签含缺陷修复、或更新内容写「修复」)归入【Bug修复】;其余归【新功能】。边界不清优先 Bug修复。 + +### 版本号与更新时间 + +| 字段 | 规则 | +|------|------| +| 版本号 | 优先从迭代名解析(如含 `V1.1.5` / `1.1.5`);解析不到再问用户 | +| 更新时间 | 用户提供则用;未提供可写迭代计划开始日的 `MM月DD日HH:MM`,或先问 | +| 产品线 | 默认 `OneOS PC版` | + +## 兜底流程(手动清单) + +无云效权限、或迭代为空时:用 [input-template.md](input-template.md) 粘贴清单,跳过迭代步骤,直接分类润色。 + +## 固定输出格式 + +```text +OneOS PC版V{主.次.修订}更新日志: +更新时间:{MM}月{DD}日{HH}:{MM} +---------------------------------------------------- +【新功能】 +1「{模块名}」{做了什么},{责任部门}可{用户价值 / 解决的问题} +2「{模块名}」{…} + +【Bug修复】 +3「{模块名}」{修复了什么},{责任部门}可{恢复的能力 / 避免的影响} +``` + +### 版式硬约束 + +| 项 | 规则 | +|----|------| +| 标题 | `OneOS PC版V` + 版本号 + `更新日志:`(如 `V1.1.5`,无空格) | +| 更新时间 | 仅开始时刻;`MM月DD日HH:MM`;**禁止**「预计x分钟内完成」 | +| 分隔线 | `----------------------------------------------------` | +| 分区 | 仅 `【新功能】` / `【Bug修复】`;空类整段省略 | +| 序号 | 全篇连续,从 1 起 | +| 模块名 | `「」`;与产品菜单正式名一致 | +| 对外正文 | **不写**需求号、缺陷号、迭代 ID(核对清单可另附) | +| 结尾 | 无签名、无「感谢使用」、无技术附录 | + +## 分类 / 合并 / 文案(成稿规则) + +| 归入【新功能】 | 归入【Bug修复】 | 不写进对外日志 | +|----------------|-----------------|----------------| +| 新增能力、新入口、新字段/流程、可感知体验优化 | 原有能力异常、报错、数据错、交互失效 | 技术债、无感性能、未开放灰度、仅内部监控 | + +- **同分区同模块**合并为一条,分点用 `;` +- **跨分区**同模块分栏各写一条 +- 每条含:做什么 + 用户价值 + 责任部门自然嵌入(`{部门}人员可…`) +- **禁止**「供…条线…使用」;部门对照 [module-role-mapping.md](module-role-mapping.md) +- 禁止编造迭代内不存在的能力;存疑跳过或标待确认(不进正式成稿) +- 单条建议 ≤100 字 + +排序:先新功能后修复;同分区全员影响优先;资金/合规修复置修复区最前。 + +## 输出纪律 + +1. **先**输出迭代读取反馈(✅/❌/⚠️) +2. 成功时:可先给「本迭代需求清单」短表(含编号,便于测试核对) +3. 再输出**纯文本成稿正文**(默认不要 Markdown 大标题包裹成稿) +4. 用户明确要求时,可在正文后附「分类依据 / 跳过项」 + +## 发布前自检 + +- [ ] 已反馈迭代查找结果;失败时未强行编造成稿 +- [ ] 成稿条目均可追溯到本迭代关联需求素材 +- [ ] 标题、时间行、分隔线正确;无预计时长 +- [ ] 同模块合并正确;每条含价值 + 部门自然嵌入 +- [ ] 对外正文无需求号/缺陷号/技术黑话;序号连续 + +## 给测试同事的口令 + +```text +按 $AutoVUL 生成版本更新日志。 +项目:统一运营管理平台PC端 +迭代名称:V1.1.5发版迭代 +更新时间:07月16日16:00 +``` + +迭代名写错时,按提示改完再发一次即可。 + +手动兜底见 [input-template.md](input-template.md);成稿范例见 [examples.md](examples.md)。 diff --git a/.cursor/skills/AutoVUL/examples.md b/.cursor/skills/AutoVUL/examples.md new file mode 100644 index 0000000..eef8c46 --- /dev/null +++ b/.cursor/skills/AutoVUL/examples.md @@ -0,0 +1,77 @@ +# AutoVUL 成稿与交互范例 + +## 1. 迭代读取失败 → 重输 + +**用户:** + +```text +按 $AutoVUL,迭代名称:V1.1.5 +``` + +**Agent:** + +```text +❌ 未找到迭代「V1.1.5」。请核对后重新输入迭代名称(可复制云效迭代标题)。 +``` + +**用户:** + +```text +V1.1.5发版迭代 +``` + +**Agent:** + +```text +✅ 已找到迭代「V1.1.5发版迭代」(进行中),关联需求 3 条: +1. ONEOSP-201 车辆管理运营城市展示优化 +2. ONEOSP-202 还车应结款租金计算修复 +3. ONEOSP-203 业务管理新增租赁物流统计 + +OneOS PC版V1.1.5更新日志: +更新时间:07月16日16:00 +---------------------------------------------------- +【新功能】 +1「车辆管理」优化车辆「运营城市」展示,运维部人员可快速辨认车辆所属运营区域及数据来源 +2「业务管理」新增租赁、物流业务统计模块,业务管理组、业务员可查看租赁经营数据,业务服务组、财务部可跟踪自营物流经营情况 + +【Bug修复】 +3「还车应结款」修复租金费用项重复及计算错误,业务管理组、安全部、运维部、能源部人员还车结算金额更准确 +``` + +## 2. 完整成稿风格基准(手动/迭代素材均可) + +> 责任部门对照:`module-role-mapping.md`(OneOS 业务条线说明) + +```text +OneOS PC版V1.1.5更新日志: +更新时间:07月16日16:00 +---------------------------------------------------- +【新功能】 +1「加氢站管理」升级新版加氢站模块,采购部可统一维护签约站与非签约站信息,签约加氢站可便捷管理站点日常运营 +2「车辆管理」优化车辆「运营城市」展示,运维部人员可快速辨认车辆所属运营区域及数据来源 +3「审批中心」支持按客户名称搜索关联合同,业务员、法务部审批时可快速定位合同;还车应结款添加照片附件后,业务管理组、安全部、运维部、能源部会签人员可在审批流中查看完整附件 +4「保险采购」支持保比价单按车辆单独勾选并分开提交审批,运维部、安全部人员可按车辆分批推进投保审批 +5「故障管理」增加「故障来源」字段展示,运维部人员可区分上报渠道并跟进处理 +6「证照管理」特种设备使用登记证/使用标识照片支持OCR识别,运维部、安全部人员可减少手工录入、加快证照建档 +7「全局显示」统一各模块列表分页样式与交互,各模块操作人员跨页面浏览时体验一致、上手更快 +8「交还车管理」支持交车/还车签章文件在线预览,运维部、业务管理组、司机可现场核对签章文件;提交时增加胎纹数据必填校验,减少漏填导致的后续结算争议 +9「还车应结款」调整安全部待办区域权限,安全部人员仅处理职责范围内的还车应结款审批 +10「业务管理」新增租赁、物流业务统计模块,业务管理组、业务员可查看租赁经营数据,业务服务组、财务部可跟踪自营物流经营情况 + +【Bug修复】 +11「还车管理」修复签字单与还车检查单/还车应结款轮胎数据不一致,运维部、业务管理组人员还车验收与结算依据保持一致 +12「租赁合同」修复编辑合同提交时异常提示内容冗余,业务员、法务部人员可快速定位真正错误原因 +13「交还车管理」修复交车/还车签章客户名称关联错误,避免运维部、业务管理组、司机现场核对时客户信息张冠李戴 +14「车辆氢费明细」修复同客户不同生效时段、以及不同客户成本单价显示异常,能源部人员可准确核对氢费核算单价 +15「还车应结款」修复租金费用项重复及计算错误,业务管理组、安全部、运维部、能源部人员还车结算金额更准确 +``` + +## 对照要点 + +| 规则 | 体现 | +|------|------| +| 先反馈迭代结果 | 失败可重输;成功列出需求再成稿 | +| 无预计时长 | 更新时间仅 `07月16日16:00` | +| 对外无需求号 | 成稿正文不出现 ONEOSP-xxx | +| 部门自然嵌入 | `运维部人员可…`,无「供…条线使用」 | diff --git a/.cursor/skills/AutoVUL/input-template.md b/.cursor/skills/AutoVUL/input-template.md new file mode 100644 index 0000000..ab42bb7 --- /dev/null +++ b/.cursor/skills/AutoVUL/input-template.md @@ -0,0 +1,69 @@ +# AutoVUL · 输入模板 + +## A. 测试人员 · 按云效迭代(推荐) + +复制下面口令发给 AI(改迭代名即可): + +```text +按 $AutoVUL 生成版本更新日志。 +项目:统一运营管理平台PC端 +迭代名称: +更新时间: +``` + +### 期望反馈 + +- 成功:`✅ 已找到迭代「…」,关联需求 N 条` → 随后输出成稿 +- 失败:`❌ 未找到迭代「…」。请重新输入迭代名称` → 改名后再发一次即可 + +**说明**:预计维护时长由人工单独通知,不写入版本更新日志。 + +--- + +## B. 手动清单(云效不可用时兜底) + +### 版本元信息 + +| 字段 | 填写 | 示例 | +|------|------|------| +| 产品线 | | OneOS PC版 | +| 版本号 | | V1.1.5 | +| 更新开始时间 | | 07月16日16:00 | +| 发布范围 | 全量 / 灰度(说明) | 全量 | +| 是否对外发布 | 是 / 否 | 是 | + +### 新功能(原始材料,可粗糙) + +| 模块 | 做了什么 | 解决什么问题 / 用户故事 | 业务条线 | 责任部门 | 是否写入日志 | +|------|----------|-------------------------|----------|----------|--------------| +| 例:加氢站管理 | 升级新版模块 | 统一维护站点 | 能源业务条线 | 采购部、加氢站 | 是 | +| | | | | | | + +### Bug修复(原始材料,可粗糙) + +| 模块 | 原问题现象 | 修复后表现 | 对用户的影响 | 业务条线 | 责任部门 | 是否写入日志 | +|------|------------|------------|--------------|----------|----------|--------------| +| 例:还车应结款 | 租金费用项重复 | 金额正确 | 避免结算偏差 | 租赁业务条线 | 业务管理组、安全部、运维部、业务管理组-能源部 | 是 | +| | | | | | | | + +### 明确不要写入对外日志 + +- (如:重构、依赖升级、仅内部监控、未上线配置) + +### 粘贴给 AI + +```text +请按 $AutoVUL 用下方手动清单生成成稿(跳过云效迭代)。 +要求:同模块合并;每条含「做什么 + 用户价值」;责任部门自然融入;只输出正文。 + +【版本元信息】 +- 产品线:OneOS PC版 +- 版本号:V +- 更新开始时间: + +【新功能】 +(粘贴) + +【Bug修复】 +(粘贴) +``` diff --git a/.cursor/skills/AutoVUL/module-role-mapping.md b/.cursor/skills/AutoVUL/module-role-mapping.md new file mode 100644 index 0000000..2deea13 --- /dev/null +++ b/.cursor/skills/AutoVUL/module-role-mapping.md @@ -0,0 +1,60 @@ +# 模块 → 业务条线 / 责任部门对照 + +> 数据源:OneOS「业务条线说明」页责任部门定义(租赁 / 能源 / 运维 / 安全 / 自营) +> 生成更新日志时,**适用对象**须从此表取 `roles`,不得自造部门名。 + +## 五条业务条线 + +| ID | 条线名称 | +|----|----------| +| lease | 租赁业务条线 | +| energy | 能源业务条线 | +| ops | 运维管理条线 | +| safety | 安全管理条线 | +| self-operated | 自营业务条线 | + +## 本版变更模块对照 + +| 更新日志模块名 | 业务条线 | lines.ts 模块 | 责任部门(roles,原文) | +|----------------|----------|---------------|-------------------------| +| 加氢站管理 | 能源业务条线 | 加氢站管理 | 采购部、加氢站 | +| 车辆管理 | 运维管理条线 | 验车入库 / 车辆管理(资产底座) | 运维部、采购部 | +| 审批中心 | 跨条线 | 租赁合同 + 还车应结款(审批场景) | 业务员、法务部、业务管理组、安全部、运维部、业务管理组-能源部 | +| 保险采购 | 运维管理条线 | 证照 / 保险(车辆资产关联) | 运维部、安全部 | +| 故障管理 | 运维管理条线 | 故障管理 | 运维部、司机 | +| 租赁合同 | 租赁业务条线 | 租赁合同 | 业务员、法务部 | +| 证照管理 | 运维管理条线 | 证照管理 | 运维部、安全部 | +| 全局显示 | 全系统 | 系统介绍(五条条线) | 业务管理组、运维部、采购部、安全部、法务部、财务部、业务管理组-能源部、业务服务组 | +| 交还车管理 | 运维管理条线 | 交车管理 + 还车管理 | 运维部、业务管理组、司机 | +| 还车应结款 | 租赁业务条线 | 还车应结款 | 业务管理组、安全部、运维部、业务管理组-能源部 | +| 还车管理 | 运维管理条线 | 还车管理 | 运维部、业务管理组 | +| 车辆氢费明细 | 能源业务条线 | 车辆氢费明细 | 业务管理组-能源部 | +| 业务管理 · 租赁统计 | 租赁业务条线 | 租赁业务台账 / 客户管理 | 业务管理组、业务员 | +| 业务管理 · 物流统计 | 自营业务条线 | 自营台账 | 业务服务组、财务部 | + +## 写法规则 + +- 部门名与 `roles` **完全一致**(`业务管理组-能源部` 对外可简写「能源部人员」) +- **禁止**「供…业务条线…使用」「供…人员使用」 +- **推荐**:`{部门}人员可…` / `{部门}可…` / `{部门}审批时可…` +- 同模块合并后,多部门用顿号或分号自然串联 +- 找不到模块时:查 `lines.ts`;仍无则标「待产品确认」不进成稿 + +## 自然表述示例 + +| 模块 | 生硬(禁止) | 自然(推荐) | +|------|--------------|--------------| +| 车辆管理 | 供运维管理条线运维部使用 | 运维部人员可快速辨认车辆所属运营区域 | +| 加氢站管理 | 供能源业务条线采购部、加氢站使用 | 采购部可统一维护站点信息,加氢站可便捷管理日常运营 | +| 还车应结款 | 供租赁业务条线业务管理组…使用 | 业务管理组、安全部、运维部、能源部还车结算金额更准确 | + +## 常见错误(禁止) + +| 错误写法 | 正确写法 | +|----------|----------| +| 氢能运营业务人员 | 采购部、加氢站(能源 · 加氢站管理) | +| 车辆运营及资产管理业务人员 | 运维部、采购部(运维 · 车辆管理) | +| 各业务条线审批人员 | 业务员、法务部、…(按审批场景枚举) | +| 保险采购业务人员 | 运维部、安全部 | +| 还车结算业务人员 | 业务管理组、安全部、运维部、业务管理组-能源部 | +| 经营分析及管理部门 | 业务管理组、业务服务组(分租赁 / 自营) | diff --git a/.cursor/skills/AutoVUL/references/yunxiao-sprint.md b/.cursor/skills/AutoVUL/references/yunxiao-sprint.md new file mode 100644 index 0000000..93fc195 --- /dev/null +++ b/.cursor/skills/AutoVUL/references/yunxiao-sprint.md @@ -0,0 +1,81 @@ +# 云效迭代 → 需求清单(AutoVUL 取数) + +测试人员只提供**迭代名称**;Agent 负责在云效定位迭代、拉取关联需求、抽出更新素材。 + +## 安全边界(强制) + +- **禁止**在对话、Skill、日志、成稿中写入 Token、密码、Cookie、私钥。 +- 优先复用**已登录浏览器会话**操作云效 Projex(与 `yunxiao-requirement-lifecycle` 一致)。 +- 若团队已配置本机环境变量访问 OpenAPI,可走 API;Token 只读自环境,永不回显。 + +建议环境变量(可选,名称可按团队调整): + +| 变量 | 用途 | +|------|------| +| `YUNXIAO_TOKEN` | 个人访问令牌(`x-yunxiao-token`) | +| `YUNXIAO_DOMAIN` | 服务接入点域名 | +| `YUNXIAO_ORG_ID` | 组织 ID(中心版) | +| `YUNXIAO_PROJECT_ID` | 默认项目 ID(或运行时按项目名解析) | + +## 默认项目 + +口令未指定时:`统一运营管理平台PC端`。项目名不精确时先问清再查迭代。 + +## 判定顺序:解析迭代 + +| 顺序 | 条件 | 结果 | +|------|------|------| +| 1 | 用户未给迭代名称 | 询问:「请输入云效迭代名称(可复制标题)」;不继续 | +| 2 | 项目未确定 | 询问精确项目名;不继续 | +| 3 | 云效不可访问 | `❌ 无法访问云效…`;不继续 | +| 4 | 名称精确匹配 1 条迭代 | `✅ 已找到迭代…`;进入拉需求 | +| 5 | 0 条 | `❌ 未找到迭代…请重新输入`;不继续 | +| 6 | >1 条相似 | `⚠️ 多个相似…请回复序号或精确名称`;等用户选定 | +| 7 | 命中但关联需求 0 | `⚠️ …暂无关联需求`;不生成对外正文 | + +匹配规则:先**全等**(去首尾空格);全等失败再做包含/去空格模糊;模糊多条必须让用户确认,禁止擅自挑一个。 + +## 拉取关联需求 + +只收集工作项类型为**需求(Req)**且正式关联到该迭代的项。 + +- **浏览器路径**:打开项目 → 迭代 → 该迭代规划/工作项列表 → 筛选类型=需求 → 导出或逐条打开描述。 +- **OpenAPI 路径(示意)**: + 1. `ListSprints`:按项目列迭代,用 `name` 匹配用户输入,得到 `sprintId` + 2. `SearchWorkitems`:`category=Req`,`spaceId=项目Id`,`conditions` 中按字段 `sprint` CONTAINS `[sprintId]`(以租户实际 fieldIdentifier 为准;可先在需求列表页过滤后复制 `workitem/list` 的 conditions) + 3. 分页直到收齐(关注响应头 `x-total`) + 4. 需要正文时再 `GetWorkitem` 取 description + +每条需求最少记录: + +| 字段 | 说明 | +|------|------| +| serialNumber | 如 ONEOSP-123(仅用于核对清单,**不进对外成稿**) | +| subject | 标题 | +| status | 状态展示名 | +| description | 用于抽取「更新内容」 | +| category / type / labels | 辅助判断新功能 vs 修复 | + +## 从描述抽「更新内容」 + +1. 定位标题「更新内容」或「## 更新内容」段落 +2. **不要**把「更新内容·历史」并入本版素材(历史仅备查) +3. 若无「更新内容」:用标题 + 需求说明摘要;仍无有效产品语义则跳过并在内部备注 + +## 成功反馈后的核对清单(推荐短表) + +在生成成稿前输出,例如: + +```text +✅ 已找到迭代「V1.1.5发版迭代」(进行中),关联需求 5 条: +1. ONEOSP-101 保险采购比价分车提交 +2. ONEOSP-102 交还车签章预览 +… +正在按 AutoVUL 格式生成更新日志… +``` + +用户若立刻纠正「某条不要进日志」,从素材中剔除后再出成稿。 + +## 与发版任务关系 + +本 Skill **只读**迭代与需求,**不创建**【发版】任务、不改云效状态。发版挂链仍用 `yunxiao-requirement-lifecycle`(如「准备发布」口令 / A08)。 diff --git a/.cursor/skills/oneos-autoprd/SKILL.md b/.cursor/skills/oneos-autoprd/SKILL.md new file mode 100644 index 0000000..a10d84f --- /dev/null +++ b/.cursor/skills/oneos-autoprd/SKILL.md @@ -0,0 +1,183 @@ +--- +name: oneos-autoprd +description: >- + Generates and keeps in sync OneOS AutoPRD (PM-facing requirements) plus Axhub + Make annotation directory Markdown/PRD; on 需求定稿/定稿/确认定稿/本次定稿 appends + functional release changelog below the PRD since last baseline. When a Yunxiao + requirement advances to 分析中/设计中/待开发, creates a same-titled linked task + (tag+create-time follow the requirement; 分析中/设计中 assignee=creator, 待开发 + assignee=何斐). Use eagerly for prototypes, AutoPRD, or Yunxiao requirement + description/更新内容/stage advance—do not skip annotation sync, 定稿 changelog, + or stage-task creation. +--- + +# OneOS AutoPRD + +为 OneOS 业务模块生成**产品经理可读、可评审、可排期**的需求说明,并**自动挂到 Axhub Make 标注工具 → 原型目录**。 + +另支持:**需求定稿**时汇总功能/逻辑变更记录;与云效组合时提供「需求说明 + 更新内容」;需求进入**分析中 / 设计中 / 待开发**时**自动创建与需求同名的关联任务**(规则见下,写在本 Skill,不改云效 Skill)。 + +颗粒度对齐「保险采购」全模块 PRD:讲清做什么、谁用、故事点、正逆向、流程图与关键业务逻辑;**不写**表结构、接口、字段代码名、文件路径、实现清单。 + +## 何时使用(含自动触发) + +**主动调用时** + +- AutoPRD、OneOS 需求说明、整模块 PRD、故事点 + 流程图 + +**改原型时必须自动跟进(全局规则)** + +- 正在修改 `src/prototypes//` 下页面、交互、文案、判定、验收相关内容 +- 同一轮交付内同步更新 PRD Markdown + 标注目录,不得只改代码 + +**需求定稿时(强制)** + +- 用户回复含:`需求定稿` / `定稿` / `确认定稿` / `本次定稿` +- 执行 [references/release-changelog.md](references/release-changelog.md):写第 10 章 + 更新基线 + +**云效建需求 / 完善需求时(强制组合)** + +- 与 `$yunxiao-requirement-lifecycle` 一起使用时:先跑本 Skill,再写云效描述 +- **需求说明** ← PRD 正文(或浓缩交付口径 + 关键章节) +- **更新内容** ← 第 10 章中**自上次定稿以来**的条目(无增量则「首版定稿 / 本轮无功能增量」) + +**云效需求推进至分析中 / 设计中 / 待开发时(强制)** + +- 无论口令来自本 Skill 还是云效 Skill,只要本轮把需求推到上述状态,就执行 [references/yunxiao-stage-tasks.md](references/yunxiao-stage-tasks.md) +- 自动建**与需求同名**的任务并正式关联;标签与创建时间口径沿用需求;分析中/设计中负责人=创建人,待开发负责人=**何斐** + +不要用本 Skill 替代:需求探索访谈、设计比稿、纯样式微调(无产品语义变化时可跳过全量重写)。 + +## 工作流 + +### 主流程(写/同步 PRD) + +1. **定模块**:确认 OneOS 模块名与 `src/prototypes//`。 +2. **读上下文(只取产品语义)** + - 用户说明、已确认口径、原型标注、`.spec/`、业务条线说明(`lines.ts`) + - 忽略实现细节;字段名/接口改写成业务语言。 +3. **收敛边界**:做什么 / 不做什么、外部依赖、与其它模块关系。 +4. **按模板成文**:下方「输出结构」;缺关键信息最多问 1~2 个问题,其余写「假设」。 +5. **落盘 + 标注同步(强制)** — 见 [references/annotation-sync.md](references/annotation-sync.md)。 + + | 顺序 | 动作 | + |------|------| + | A | 写/更新 `src/prototypes//.spec/requirements-prd.md` | + | B | 写/更新 `src/resources/prd/-autoprd.md` | + | C | 更新 `annotation-source.json` **顶层** `directory.nodes`(PRD 全文 + 推荐分章);禁止只写 `data.directory` | + | D | 若存在 `scripts/sync-annotation-directory.mjs`,执行之 | + +6. **交付说明**:路径、标注目录入口、故事点合计、开放问题/假设。 + +### 定稿流程(关键字触发) + +见 [references/release-changelog.md](references/release-changelog.md)。摘要: + +1. 读 PRD + `.spec/autoprd-baseline.json` +2. 汇总自上次定稿以来的**功能/逻辑**变更(排除样式/UI/表结构) +3. 追加到 `## 10. 功能变更记录`(最新在上) +4. 更新基线 JSON + 标注目录 +5. 若同时发云效:本次定稿块 →「更新内容」;旧段 →「更新内容·历史」 + +### 云效交接格式(给 yunxiao skill 直接粘贴) + +```markdown +## 原型链接 +<对象存储或预览 URL> + +## 需求说明 + + +## 更新内容 +<第 10 章本次定稿块;无则「首版定稿」或「本轮无功能/逻辑增量」> + +## 更新内容·历史 +<以往更新内容倒序,勿删除> +``` + +### 云效阶段任务(状态推进时强制) + +见 [references/yunxiao-stage-tasks.md](references/yunxiao-stage-tasks.md)。摘要: + +| 需求状态 | 任务标签 | 负责人 | 标题 | +|----------|----------|--------|------| +| 分析中 | 分析 | 需求创建人 | 与需求同名 | +| 设计中 | 设计 | 需求创建人 | 与需求同名 | +| 待开发 | 交付(或开发) | 何斐 | 与需求同名 | + +正式关联需求;创建时间口径沿用需求;同阶段未取消任务不重复建。 + +## 写作硬约束 + +**必须写** + +- 一句话定位 + 目标 / 非目标 +- 模块边界(含 mermaid 总览更好) +- 角色与目标(角色名优先对齐业务条线说明) +- **用户故事**(业务条线说明口径)+ Epic 级**故事点(SP)**粗估 +- 分功能**正向**与**逆向/边界** +- 至少 1~2 个 **mermaid** 流程图 +- **关键业务逻辑**(业务话) +- 验收清单 + 「交付口径」一段话 +- 定稿后:**功能变更记录**(第 10 章) + +**禁止写** + +- 数据库表、字段名、接口路径、代码路径、组件名、存储 key +- 研发实现指令(可写「正式环境由审批中心回写」这类业务依赖) +- 在变更记录里写样式/UI/表结构优化 + +## 用户故事口径(强制 · 对齐业务条线说明) + +真相源:原型 **业务条线说明**(`lease-business-line-overview` / `lines.ts`)。 +每条能力用「责任部门 → **起点** → **怎么运作** → **闭环**」叙述;**不要**用「作为…我希望…」宽表作主叙述。 + +| 块 | 写什么 | +|----|--------| +| 角色 | 谁负责、谁协同 | +| 起点 | 谁在什么前提下启动 | +| 怎么运作 | 有序步骤,含跨角色协作 | +| 关键结果 | 可选标签 | +| 闭环 | 业务终点与可追溯性 | + +可选:`US-xx`、压缩句「作为…我想…以便…」、规模 S/M/L 或 SP(仅排期,不替代主叙述)。 + +## 输出结构 + +完整模板:[references/template.md](references/template.md)。 + +```markdown +# <模块名> · 产品需求说明(全模块) + +## 1. 一句话与目标 +## 2. 模块边界(最重要) +## 3. 用户与角色 +## 4. 用户故事与故事点(业务条线说明口径) +## 5. 功能模块说明(正向 / 逆向) +## 6. 关键业务逻辑(必须对齐) +## 7. 总览流程图 +## 8. 验收清单 +## 9. 交付口径 +## 10. 功能变更记录 ← 定稿后维护;日常改原型不强制每改必写 +``` + +## 质量自检 + +- [ ] 产品经理不看代码也能评审 +- [ ] 用户故事为起点 / 怎么运作 / 闭环 +- [ ] `.spec/requirements-prd.md` 已更新 +- [ ] 标注目录「产品需求说明(PRD)」已同步且正文一致 +- [ ] 定稿时:第 10 章 + `autoprd-baseline.json` 已更新 +- [ ] 推进至分析中/设计中/待开发时:同名任务已创建或复用,正式关联,负责人正确 +- [ ] 无表结构 / 接口 / 代码路径;变更记录无样式/UI 废话 + +## 参考 + +- 定稿变更日志:[references/release-changelog.md](references/release-changelog.md) +- 云效阶段任务:[references/yunxiao-stage-tasks.md](references/yunxiao-stage-tasks.md) +- 标注同步细则:[references/annotation-sync.md](references/annotation-sync.md) +- 章节模板:[references/template.md](references/template.md) +- 故事示例:[references/granularity-example.md](references/granularity-example.md) +- 业务条线:`src/prototypes/lease-business-line-overview/lines.ts` +- 复杂判定规格:配合项目规则 `business-logic-documentation` +- 云效组合:`$yunxiao-requirement-lifecycle`(建需求时先本 Skill) diff --git a/.cursor/skills/oneos-autoprd/references/annotation-sync.md b/.cursor/skills/oneos-autoprd/references/annotation-sync.md new file mode 100644 index 0000000..2cdd7c6 --- /dev/null +++ b/.cursor/skills/oneos-autoprd/references/annotation-sync.md @@ -0,0 +1,98 @@ +# Axhub 标注目录同步(强制) + +使用 OneOS AutoPRD 时,除资源库归档外,**必须**把 PRD 挂到 Axhub Make 标注工具的「原型目录」里,用户才能在右侧面板直接打开。 + +## 落点(三份必须一致) + +| # | 路径 | 作用 | +|---|------|------| +| 1 | `src/prototypes//.spec/requirements-prd.md` | **主真相**:产品可读 PRD 全文 | +| 2 | `src/prototypes//annotation-source.json` → **顶层** `directory.nodes` | **标注目录入口**:面板里可见的 Markdown/PRD | +| 3 | `src/resources/prd/-autoprd.md` | 资源库归档(可选但默认写) | + +可选:复杂判定另写 `.spec/.md`,目录里再挂一条,并在 PRD 里链过去(与 `business-logic-documentation` 一致)。 + +## ⚠️ 目录必须写在文档顶层(常见踩坑) + +Wire format(与 `prototype-annotation` 一致): + +```json +{ + "documentVersion": 1, + "format": "axhub-annotation-source", + "data": { "version": 2, "prototypeName": "...", "nodes": [] }, + "markdownMap": {}, + "assetMap": {}, + "directory": { "nodes": [] } +} +``` + +| 正确 | 错误 | +|------|------| +| 顶层 `directory.nodes` | 只写在 `data.directory` | +| `PrototypeAnnotationHost` / Make 读的是 `source.directory` | 写在 `data` 下时右侧「原型目录」为空 | + +若误写在 `data.directory`:迁移到顶层 `directory`,并删除 `data.directory`,避免双源。 + +## 目录节点写法(推荐) + +在说明根 `folder` 的 `children` **靠前**插入或更新: + +```json +{ + "type": "markdown", + "id": "-doc-prd", + "title": "产品需求说明(PRD)", + "markdownPath": ".spec/requirements-prd.md", + "markdown": "<与 .spec/requirements-prd.md 相同的全文>" +} +``` + +### 分章节 PRD(推荐,便于右侧目录浏览) + +在「产品需求说明(PRD)」旁增加文件夹 **「PRD 分章」**,按 PRD 的 `##` 一级标题拆成多个 `markdown` 子节点(标题用章节名,正文为该章全文)。专题规格(`.spec/.md`)放在 **「专题规格」** 文件夹。 + +参考结构: + +```text +folder <模块>说明 + markdown 产品需求说明(PRD) ← 全文 + folder PRD 分章 + markdown 1. 一句话与目标 + markdown 2. 模块边界 + … + folder 专题规格 + markdown +``` + +有 `scripts/sync-annotation-directory.mjs` 的原型:改 PRD / `.spec` 后**必须执行**该脚本再生目录。 + +定稿后第 10 章「功能变更记录」必须进入 PRD 全文节点;若有「PRD 分章」,同步该章节点。详见 [release-changelog.md](release-changelog.md)。 + +约定: + +- `id`:`<短前缀>-doc-prd`(如 `ipc-doc-prd`、`wb-doc-prd`、`h5-va-doc-prd`) +- `title`:优先「产品需求说明(PRD)」;已有「PRD」可保留不改名 +- **同时写** `markdownPath` + `markdown`:构建可内联路径,运行时目录也能直接读到正文 +- 若已有同 id / 同标题节点:只更新正文与 path,不新建重复入口 +- 遵守 `prototype-annotation-layout`:目录以 `markdown` 为主,不要用 `link`/`route` 顶替 PRD + +## 增量改原型时怎么更新 + +1. 根据改动的文件路径确定 ``。 +2. 读现有 `.spec/requirements-prd.md`(没有则按 AutoPRD 模板新建)。 +3. 只改受影响章节(故事、正逆向、关键逻辑、验收);其余保留。 +4. 回写 `.spec` + `resources/prd` + **顶层** `annotation-source.json` → `directory`(正文与文件一致;含分章节点)。 +5. 若原型有 `scripts/sync-annotation-directory.mjs`,改完后执行它。 + +## 可跳过全量重写的情况 + +纯样式、无文案/无流程/无判定变化的微调:不必重写整份 PRD,但若改了用户可见文案或交互结果,仍须同步对应段落与目录节点。 + +## 验收 + +- [ ] `.spec/requirements-prd.md` 存在且为最新 +- [ ] `annotation-source.json` **顶层**有 `directory.nodes`(不是只在 `data` 下) +- [ ] 标注目录能打开「产品需求说明(PRD)」且内容与文件一致 +- [ ] 若有「PRD 分章」:各章可单独打开 +- [ ] 用户故事仍为业务条线说明口径(起点 / 怎么运作 / 闭环) diff --git a/.cursor/skills/oneos-autoprd/references/granularity-example.md b/.cursor/skills/oneos-autoprd/references/granularity-example.md new file mode 100644 index 0000000..5126671 --- /dev/null +++ b/.cursor/skills/oneos-autoprd/references/granularity-example.md @@ -0,0 +1,58 @@ +# 颗粒度标杆与用户故事口径 + +## 用户故事:对齐「业务条线说明」页面 + +页面:`/prototypes/lease-business-line-overview` +数据:`src/prototypes/lease-business-line-overview/lines.ts` + +每个能力卡片的产品叙述结构: + +```text +责任部门(roles) + → 起点(start) + → 怎么运作(process[],有序) + → 关键结果(outcomes,可选) + → 闭环(closure) +``` + +AutoPRD 第 4 章必须用同一套结构写用户故事,而不是「作为…我希望…」宽表。 + +### 示例(保险采购 · 交车合规,示意) + +#### US · 保险有效才允许交车 +- **角色**:采购部、运维部 +- **起点**:采购或运营维护车辆交强险与商业险台账,作为交车前置合规数据。 +- **怎么运作**: + 1. 在保险采购维护一车一档险种台账(录入 / 识别 / 导入)。 + 2. 交车时系统校验交强 + 商业是否均为正常或临期。 + 3. 不合规则拦截交车并给出可理解原因。 +- **关键结果**:`允许交车` / `禁止交车 · 保险异常` +- **闭环**:交车合规与台账一致、可追溯;异常车辆不能进入履约运营。 +- **排期(可选)**:US-17 · 规模 M · 作为采购/运维,我想维护交强+商业险并在交车时校验,以便不合规车辆无法交车。 + +### 反例(不要作为主叙述) + +| 编号 | 用户故事 | SP | +|------|----------|----| +| B1 | 作为运营,我能看到失败原因 | 2 | + +↑ 信息太扁,缺少起点、协作步骤与闭环结果。 + +--- + +## AutoPRD 其余层次(仍要对齐) + +1. 一句话 + 目标/非目标 +2. 模块边界(独立业务线写清「不自动同步」) +3. 角色表(命名对齐业务条线) +4. **用户故事:起点 / 怎么运作 / 闭环** + SP/规模 +5. 分模块正逆向 +6. 关键业务逻辑 +7. mermaid 流程图 +8. 验收清单 +9. 交付口径 + +## 刻意不写 + +- 表结构、接口、代码路径 +- 用实现指令代替业务闭环描述 diff --git a/.cursor/skills/oneos-autoprd/references/release-changelog.md b/.cursor/skills/oneos-autoprd/references/release-changelog.md new file mode 100644 index 0000000..998e9bf --- /dev/null +++ b/.cursor/skills/oneos-autoprd/references/release-changelog.md @@ -0,0 +1,78 @@ +# 功能变更记录(定稿触发) + +用户回复**需求定稿关键字**后,必须生成「自上次定稿以来」的功能变更日志,追加到 PRD **最下方**,并更新定稿基线。 + +## 触发关键字(命中任一) + +`需求定稿` · `定稿` · `确认定稿` · `本次定稿` + +(可带模块名,如「保险采购需求定稿」。) + +## 写什么 / 不写什么 + +| 要写(产品发版视角) | 不写 | +|----------------------|------| +| 哪个功能做了什么修改 | 布局、页面样式、UI 设计优化 | +| 变更了什么业务逻辑 / 流程 / 验收口径 | 「优化了哪些表」「改了哪些字段/接口」 | +| 用户可感知的交互结果变化 | 纯样式、动效、无语义文案微调 | + +条目口吻示例: + +- 「识别失败」:失败数可点击查看失败文件与原因;仅失败时也可打开明细 +- 「比价单」:审批通过后仍须在保单管理另行录入正式保单(强调不自动同步) + +## 落点 + +1. **PRD 文末章节**(强制) + `src/prototypes//.spec/requirements-prd.md` → `## 10. 功能变更记录` + 新定稿块插在该章**最上方**(倒序:最新在上);历史块保留。 + +2. **定稿基线**(强制) + `src/prototypes//.spec/autoprd-baseline.json` + +```json +{ + "prototypeId": "insurance-procurement", + "lastConfirmedAt": "2026-07-21T12:00:00+08:00", + "lastConfirmedLabel": "定稿 · 2026-07-21", + "summaryBullets": ["…", "…"] +} +``` + +3. **标注目录**:同步更新「产品需求说明(PRD)」全文节点;若有「PRD 分章」,增加或更新「功能变更记录」分章。 + +4. **云效交接**(若本轮同时走云效):把**本次定稿块**正文作为需求描述「更新内容」;旧「更新内容」挪到「更新内容·历史」。 + +## 定稿工作流 + +1. 确认 ``(从对话 / 打开文件 / 用户指名推断)。 +2. 读现有 PRD 与 `autoprd-baseline.json`(无基线则本轮视为**首版定稿**)。 +3. 收集自 `lastConfirmedAt` 以来的产品语义变更: + 对话确认口径 → 批注/操作说明 → PRD 新旧差异 →(可选)相关提交说明。 + 过滤掉样式/表结构类改动。 +4. 若无功能增量:仍写定稿块,正文为「本轮无功能/逻辑增量(仅样式或未达产品语义变更)」或「首版定稿」。 +5. 追加 `### 定稿 · YYYY-MM-DD` + 条目列表到第 10 章顶部。 +6. 更新 `autoprd-baseline.json`;执行 annotation-sync。 +7. 向用户回报:基线时间、条目数、PRD 路径;若需发云效,提示可用口令。 + +## PRD 章节格式 + +```markdown +## 10. 功能变更记录 + +> 产品经理原型发版记录。仅记功能与业务逻辑变更;不含样式/UI/表结构。 + +### 定稿 · 2026-07-21 + +- 「能力名」:做了什么 / 逻辑如何变 +- … + +### 定稿 · 2026-07-10 + +- … +``` + +## 与日常改原型的关系 + +- **改原型过程中**:按 AutoPRD 主流程增量更新第 1–9 章;**不必**每改一次就写第 10 章。 +- **用户说定稿时**:才汇总写入第 10 章并刷新基线。 diff --git a/.cursor/skills/oneos-autoprd/references/template.md b/.cursor/skills/oneos-autoprd/references/template.md new file mode 100644 index 0000000..449b8bf --- /dev/null +++ b/.cursor/skills/oneos-autoprd/references/template.md @@ -0,0 +1,163 @@ +# OneOS AutoPRD 输出模板 + +复制下列结构填写。方括号为占位。**第 4 章用户故事必须用业务条线说明口径。** + +```markdown +# <模块名> · 产品需求说明(全模块) + +## 1. 一句话与目标 + +**一句话** +<用一句话说明模块解决什么> + +**要解决的问题** +- … + +**本期目标** +1. … +2. … + +**非目标(本期不做)** +- … + +## 2. 模块边界(最重要) + +(可选 mermaid:模块与外部系统关系) + +| 业务线 / 子域 | 做什么 | 不做什么 | +|---------------|--------|----------| +| … | … | … | + +**外部依赖(产品口径)** +| 依赖方 | 交互方式 | 产品要求 | +|--------|----------|----------| +| … | … | … | + +**关键约束** +- … + +## 3. 用户与角色 + +| 角色 | 主要目标 | +|------|----------| +| … | … | + +> 角色名优先与「业务条线说明」一致(如业务管理组、采购部、运维部)。 + +## 4. 用户故事与故事点(业务条线说明口径) + +> 故事点 / 规模供排期参考,可按团队基准调整。合计约 **N SP**(或 S/M/L 汇总说明)。 + +按 Epic 或能力单元分组。**每一条**按下面四块写(对齐业务条线说明页:责任部门 → 起点 → 怎么运作 → 闭环): + +### Epic A · <名称>(约 N SP) + +#### A1 · <能力标题> +- **角色**:… +- **起点**:… +- **怎么运作**: + 1. … + 2. … + 3. … +- **关键结果**(可选):`可…` / `禁止…` +- **闭环**:… +- **排期(可选)**:US-xx · 规模 S/M/L · 「作为…,我想…,以便…」 + +#### A2 · <能力标题> +… + +### Epic B · … + +## 5. 功能模块说明(正向 / 逆向) + +### 5.x <功能名> + +**正向** +1. … +2. … + +**逆向 / 边界** + +| 情况 | 系统表现 | +|------|----------| +| … | … | + +## 6. 关键业务逻辑(必须对齐) + +### 6.x <规则名> + +用编号优先级、表格或短列表写清业务规则。 + +## 7. 总览流程图 + +```mermaid +flowchart TB + home[入口] --> a[能力A] + home --> b[能力B] +``` + +## 8. 验收清单(产品 / 测试) + +**子域 A** +- [ ] … + +## 9. 交付口径 + +> <一段可贴需求单开头的浓缩描述> + +## 10. 功能变更记录 + +> 产品经理原型发版记录。仅记功能与业务逻辑变更;不含样式/UI/表结构。 +> 由「需求定稿」关键字触发追加;日常改原型不强制每改必写。详见 [release-changelog.md](release-changelog.md)。 + +### 定稿 · YYYY-MM-DD + +- 「<能力名>」:<做了什么 / 逻辑如何变> +- … +``` + +成文后**必须**同步 Axhub 标注目录(见 [annotation-sync.md](annotation-sync.md)): + +1. `src/prototypes//.spec/requirements-prd.md` +2. `annotation-source.json` → 目录节点「产品需求说明(PRD)」(`markdownPath` + `markdown`) +3. `src/resources/prd/-autoprd.md` +4. 定稿时另写:`src/prototypes//.spec/autoprd-baseline.json` + +## 用户故事写法(对照业务条线说明) + +业务条线说明页字段 → AutoPRD 字段: + +| 页面 | AutoPRD | +|------|---------| +| 责任部门标签 | **角色** | +| 起点 | **起点** | +| 怎么运作(有序列表) | **怎么运作** | +| 结果色签 | **关键结果**(可选) | +| 闭环 | **闭环** | + +示例口吻(摘自业务条线风格,非照抄某模块): + +- **起点**:采购部维护交强与商业险台账,作为交车合规的前置数据。 +- **怎么运作**:1. 运营录入或识别保单… 2. 运维交车时校验… +- **闭环**:核心险种有效则允许交车;异常则拦截并提示原因,过程可追溯。 + +**不要**用下面这种宽表作为第 4 章唯一形态: + +| 编号 | 作为…我希望… | SP | +|------|--------------|----| + +「作为…我想…以便…」只允许作为**排期可选一行**,主叙述必须是起点/怎么运作/闭环。 + +## 正逆向写法约定 + +- **正向**:用户成功完成任务的最短路径 +- **逆向**:取消、关闭、禁用、冲突状态、不可操作项 +- 审批类:写清「本页只读 / 在外部系统办理」 + +## 规模与故事点 + +| 规模 | 建议含义 | 约 SP | +|------|----------|-------| +| S | 轻交互 / 列表展示 | 1~2 | +| M | 标准流程 + 筛选详情 | 3~5 | +| L | 状态机 / 跨模块 / 批量识别 | 6+ | diff --git a/.cursor/skills/oneos-autoprd/references/yunxiao-stage-tasks.md b/.cursor/skills/oneos-autoprd/references/yunxiao-stage-tasks.md new file mode 100644 index 0000000..09cfe27 --- /dev/null +++ b/.cursor/skills/oneos-autoprd/references/yunxiao-stage-tasks.md @@ -0,0 +1,44 @@ +# 云效阶段任务自动创建(AutoPRD 负责) + +本规则写在 **oneos-autoprd**,不依赖改写 `yunxiao-requirement-lifecycle`。 +凡本 Skill 参与的云效操作中,需求推进到下列状态时,**同一轮必须**建同名任务并正式关联需求。 + +## 触发状态 + +| 需求推进至 | 任务标签(右侧基础字段) | 负责人 | 任务标题 | +|------------|--------------------------|--------|----------| +| **分析中** | `分析` | **需求创建人**姓名 | 与需求标题**完全同名** | +| **设计中** | `设计` | **需求创建人**姓名 | 与需求标题**完全同名** | +| **待开发** | `交付`(若项目无「交付」标签则用 `开发`) | **何斐** | 与需求标题**完全同名** | + +## 强制字段与关系 + +1. **正式关联需求**:父子或关联项;只挂标题不算关联。创建后必须在界面上能看到需求 ↔ 任务关系。 +2. **标签**:写入任务「标签」基础字段(见上表);标题前缀不能代替标签。 +3. **创建时间沿用需求**:任务的创建时间(及标签侧可见的创建时间口径)尽量与**需求创建时间**一致。 + - 平台允许改创建时间 / 用 API 指定时:写成需求的创建时间。 + - 平台不允许:在任务描述首行注明 `创建时间口径=需求创建时间(YYYY-MM-DD HH:mm)`,并在回报里说明「平台限制未改系统创建时间」。 +4. **幂等**:同一需求、同一阶段标签下,已存在**未取消**且已正式关联的同名任务 → **不新建**,只校验负责人/标签/关联是否正确,缺则补齐。 +5. **顺序**:先确认需求已进入目标状态(或本轮动作将写入该状态),再创建/补齐任务;回报时给出任务编号与链接。 + +## 执行步骤(apply 时) + +```text +1. 读取需求:标题、创建人、创建时间、当前/目标状态、已关联任务列表 +2. 按上表确定本轮阶段标签与负责人 +3. 查重(需求ID + 阶段标签 + 未取消) +4. 无则创建:标题=需求标题;标签=阶段标签;负责人=上表;创建时间口径=需求创建时间;正式关联需求 +5. 有则校验并修补标签/负责人/关联 +6. 回报:需求状态、任务编号、负责人、是否新建/复用 +``` + +## 不在本规则内 + +- 不自动建「测试 / 发版」任务(除非用户另行要求)。 +- 不因样式类 PRD 变更触发建任务;仅**需求状态**进入分析中/设计中/待开发时触发。 +- 云效生命周期细则、流水线、缺陷闭环仍可由 `$yunxiao-requirement-lifecycle` 配合执行;**本建任务逻辑以 AutoPRD 本文为准**。 + +## 与定稿 / 需求说明的关系 + +- 写「需求说明 / 更新内容」仍按 AutoPRD 主流程与定稿流程。 +- 推进到待开发且需要描述时:先 AutoPRD 文档,再改状态,再按本文件建任务(负责人何斐)。 diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/SKILL.md b/.cursor/skills/yunxiao-requirement-lifecycle/SKILL.md new file mode 100644 index 0000000..4dede21 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/SKILL.md @@ -0,0 +1,157 @@ +--- +name: yunxiao-requirement-lifecycle +description: >- + Audit, design, configure, migrate, test, troubleshoot, and document Alibaba Cloud + Yunxiao/Projex requirement lifecycle automation. Use for short conversational Chinese + commands such as 记录需求、确认需求、需求定稿、开始分析、开始开发、提交测试、测试完成、 + 发布成功 and 验收通过; OneOS delivery or legacy stage-task model. When creating or + refreshing OneOS product requirements, first call oneos-autoprd for 需求说明 and + 更新内容 (functional changelog since last 定稿), then write Yunxiao description. + Also covers role handoffs, Codeup, Testhub, pipelines, release acceptance, migration, + and Chinese runbooks without exposing credentials or changing production pipelines unexpectedly. +--- + +# Yunxiao Requirement Lifecycle + +Use this as the single entry skill. Load only the internal modules needed for the request. Installing the skill does not change Yunxiao; live transitions start only after an authorized `apply` and verified `test` run. + +## Route to modules + +Read each selected file completely before acting: + +| Request scope | Required module | +|---|---| +| OneOS终态模型、`【交付】`主任务、多开发子任务、迭代测试/发版、Y01–Y40、A01–A10、旧规则迁移 | [references/oneos-terminal-flow.md](references/oneos-terminal-flow.md) | +| Lifecycle statuses, requirement labels, related tasks, R01–R12, stage entry/completion | [references/lifecycle-rules.md](references/lifecycle-rules.md) | +| Design/development/test task creation, task states, rollups, TK01–TK08 | [references/task-lifecycle.md](references/task-lifecycle.md) | +| Test plans, case results, failed cases, bugs, retest, TC01–TC03, DF01–DF03 | [references/test-defect-closure.md](references/test-defect-closure.md) | +| Repositories, `dev`/`develop`, branches, commits, MRs, multi-repository gate, CI01 | [references/codeup-integration.md](references/codeup-integration.md) | +| test/prod pipelines, Docker callback, release changes, acceptance, CI02–CI03, L01–L02 | [references/pipeline-release.md](references/pipeline-release.md) | +| Live audit/apply/test, browser execution, evidence, diagnosis, rollback, governance | [references/live-operations.md](references/live-operations.md) | +| 日常口语化操作,使用“记录需求、开始分析、安排开发、提交测试、复测通过、发布成功”等短口令 | [references/simple-role-commands.md](references/simple-role-commands.md) | +| **统一运营管理平台**日常「记录需求」加速执行(已验证 ID / API / 状态三跳) | [references/oneos-pc-fast-path.md](references/oneos-pc-fast-path.md) + [assets/oneos-pc-runtime-ids.json](assets/oneos-pc-runtime-ids.json) | +| **统一运营管理平台**「记录需求到云效」完整可复制提示词(A/B/C 门禁 + 30 标签) | [references/oneos-pc-record-requirement-prompt.md](references/oneos-pc-record-requirement-prompt.md) | +| 需求进入分析中/设计中/待开发时自动建同名任务、关联需求、负责人与时间沿用 | [references/auto-stage-task.md](references/auto-stage-task.md) | +| 产品、分析、设计、开发、评审、测试、缺陷、运维、验收等角色的节点沟通话术和交接回报 | [references/role-trigger-prompts.md](references/role-trigger-prompts.md) | +| Final Chinese report | [references/report-template.md](references/report-template.md) | + +## 记录需求 · 参数门禁与加速(强制) + +当口令为「记录需求」且项目为统一运营管理平台(含 PC 端别名)时: + +1. **先读** [references/oneos-pc-fast-path.md](references/oneos-pc-fast-path.md) 与 [assets/oneos-pc-runtime-ids.json](assets/oneos-pc-runtime-ids.json),按快路径执行,禁止重复探测 create URL / 优先级 ID。 +2. 若用户要求 Plan/单选优先级、推进至与标签:必须先拿到明确 **A+B+C**;**禁止**未选时默认「中 + 分析中」并建单。「批准计划」≠ 已选 A+B+C。完整用户口令见 [references/oneos-pc-record-requirement-prompt.md](references/oneos-pc-record-requirement-prompt.md)。 +3. 优先 API 建单(创建时写入 `document`)、同名任务与打标签(`PATCH` `propertyKey=tag`);浏览器仅用于标签 API 失败或状态连跳兜底。统一运营管理平台标签从 `assets/oneos-pc-tag-catalog.md` 选择器点选,禁止按 `lines.ts` 自动映射。 + +## AutoPRD 组合(强制 · OneOS 产品需求) + +当请求涉及**创建产品类需求、写入/刷新需求描述、快轨待开发、完善需求说明/更新内容**,或用户口令含「记录需求(带模块/原型)」「需求已经确定」「需求定稿」并要进云效时: + +1. **先**加载并执行项目/全局的 **`oneos-autoprd`** skill(勿跳过)。 +2. 用其产出填充云效需求描述: + - **需求说明** ← AutoPRD 正文(产品语言;不写表结构/接口/代码) + - **更新内容** ← AutoPRD 第 10 章「功能变更记录」中自上次定稿以来的条目;无则「首版定稿」或「本轮无功能/逻辑增量」 + - 已有「更新内容」时:旧段挪到 **更新内容·历史**(倒序追加,勿删) +3. 原型发版增量以 AutoPRD 功能变更记录为准;PC 整包发版公告仍可另用 `AutoVUL`。 +4. 对象存储链接、主任务、状态推进等仍按 `oneos-terminal-flow.md` / `simple-role-commands.md`。 + +`A02` / `A02B` 在 OneOS 项目上的默认实现方式即为调用 AutoPRD,不得只写占位文案。 + +## 阶段同名任务(强制 · AI apply) + +凡将需求推进到 **分析中 / 设计中 / 待开发**(含口令「开始分析」「开始设计」「快速开发」「需求已经确定…待开发」等),必须先读并执行 [references/auto-stage-task.md](references/auto-stage-task.md): + +| 状态 | 任务 | 负责人 | +|---|---|---| +| 分析中 | 与需求**同名**;标签`分析`;正式关联需求 | 需求**创建人** | +| 设计中 | 与需求**同名**;标签`设计`;正式关联需求 | 需求**创建人** | +| 待开发 | 与需求**同名**;标签`交付`(或阶段模型下`开发`);正式关联需求 | 固定**何斐** | + +时间:任务计划开始(及可写的创建时间)**沿用需求**的计划开始/创建时间。查重幂等,禁止同阶段重复建单。 + +First identify `flow_model`: + +- `stage_tasks`: use R01–R12 and TK01–TK08; read the five domain modules plus `live-operations.md` for a full audit. +- `oneos_delivery`: read `oneos-terminal-flow.md`, `codeup-integration.md`, `test-defect-closure.md`, `pipeline-release.md`, and `live-operations.md`. + +For a narrow diagnosis, read the relevant domain module plus `live-operations.md`. Do not blend both models in one project or load unrelated modules merely because they exist. + +## Classify authority + +- `audit`: inspect and report only. +- `plan`: prepare a project profile and change plan only. +- `apply`: create or modify only explicitly named projects, labels, rules, and test assets. +- `test`: exercise approved rules with isolated artifacts and collect evidence. +- `document`: produce a Chinese runbook from verified facts. + +Treat ambiguous requests as `audit` or `plan`. Documentation never authorizes live changes. + +When the user uses a write verb defined in `simple-role-commands.md`—for example `记录需求`、`确认需求`、`开始分析`、`开始设计`、`开始开发`、`提交测试`、`复测通过`、`发布成功` or `验收通过`—treat it as `apply` authorization only for the current exact project and named work item. If the project is not exact, ask only for the project before writing. Query verbs such as `查状态`、`为什么没流转`、`下一步` and `给我方案` remain `audit` or `plan`. + +Whenever apply changes a requirement status to `分析中`、`设计中` or `待开发`, also apply [references/auto-stage-task.md](references/auto-stage-task.md) in the same turn (same-name task, formal link, assignee and time rules) before reporting success. + +## Build the project profile + +1. Choose exactly one template and copy it outside the skill: + - `stage_tasks`: `assets/project-profile.template.json`. + - `oneos_delivery`: `assets/oneos-project-profile.template.json`. +2. Keep one independent object in `projects` for every target project; duplicate the template object when needed. +3. Replace observed statuses, rule instances, repositories, branches, pipelines, test plans, defect workflows, release evidence, and control dispositions. +4. Never add usernames, passwords, cookies, tokens, OTPs, Webhook secrets, or private keys. +5. Validate before applying: + +```text +python scripts/profile_tool.py validate +python scripts/profile_tool.py render --output + +python scripts/oneos_profile_tool.py validate +python scripts/oneos_profile_tool.py render --output +``` + +Do not apply while placeholders remain or validation fails. + +## Completeness contract + +For `stage_tasks`, account for every control for every project independently: + +- `R01`–`R12`: requirement lifecycle and manual gates. +- `TK01`–`TK08`: stage-task creation, task-state transitions, requirement rollups, and idempotency. +- `TC01`–`TC03`: test-case execution, failed-case/defect association, and retest result update. +- `DF01`–`DF03`: fixed handoff, tester-pass closure, and tester-fail reopen. +- `CI01`–`CI03`: multi-repository coverage, callback safeguards, and environment/release separation. +- `L01`–`L02`: legacy immediate-close rule disposition. + +Record the actual R01–R12 `rule_instances`, TK01–TK08 `task_rule_instances`, and all 31 `control_coverage` entries. Valid modes are `manual`, `native_rule`, `integration_bridge`, `disabled_legacy`, and `not_supported`. `not_supported` needs an owner and fallback and is never equivalent to automated. + +Completeness covers the requirement lifecycle, stage-task lifecycle and rollups, Testhub closure, Codeup assets, pipeline/release evidence, and legacy-close controls. Notifications, SLA reminders, generic automatic assignment, and unrelated custom-field synchronization remain outside scope unless explicitly added to `adjunct_automations`. + +For `oneos_delivery`, account for all 48 controls independently: + +- `Y01`–`Y16`, `Y20`–`Y22`, `Y30`–`Y35`, `Y40`: native/mirrored lifecycle controls. +- `A01`, `A02`, `A02B`, `A03`–`A10`: AI/Webhook bridge controls. +- `TC01`–`TC03`, `DF01`–`DF03`, `CI01`–`CI03`, `L01`–`L02`: test, defect, code/release separation, and legacy-close controls. + +Do not disable the old stage rules until the OneOS migration readiness gate passes: required requirement statuses exist, the main-task identity is filterable or procedurally exclusive, task workflows are ready, bridge owners and idempotency exist, and rollback evidence is saved. `L01` and `L02` remain disabled because they bypass acceptance. + +## Global safety rules + +- Reuse the authenticated browser session or approved semantic connector. +- Preserve unrelated rules and user edits. +- Never modify an existing production pipeline unless the user authorizes that exact change. +- Clone a test pipeline before callback experiments and modify only the clone. +- Require the expected current state in every automatic transition. +- Separate observed evidence from proposals and inference. +- Wait 5–30 seconds for asynchronous events and inspect execution logs. +- Stop at CAPTCHA, OTP, login approval, permission elevation, protected-branch ambiguity, or indistinguishable similarly named resources. + +## Required outputs + +Return only the applicable artifacts: + +1. Observed inventory and current-versus-target matrix. +2. Per-project R01–R12 and TK01–TK08 rule-instance inventory with a 31-control ledger. +3. Live change log with reopen verification. +4. Test result per transition: `通过`、`异步通过`、`未触发`、`误触发` or `阻塞`. +5. Remaining risks, manual gates, and rollback steps. +6. When requested, a role-and-trigger communication playbook with copyable prompts, required evidence, next owner, and stop conditions. +7. For daily commands, answer briefly with the work item, actual state change, created/linked asset, blocker if any, and the exact next short command. diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/agents/openai.yaml b/.cursor/skills/yunxiao-requirement-lifecycle/agents/openai.yaml new file mode 100644 index 0000000..ca6e380 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "云效需求生命周期自动化" + short_description: "使用口语化短口令驱动云效需求开发测试发布闭环并自动核查" + default_prompt: "使用 $yunxiao-requirement-lifecycle,把当前项目设为统一运营管理平台PC端,然后通过“记录需求、开始开发、提交测试、验收通过”等短口令协作。" diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-runtime-ids.json b/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-runtime-ids.json new file mode 100644 index 0000000..2787bf4 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-runtime-ids.json @@ -0,0 +1,209 @@ +{ + "schema_version": 2, + "verified_at": "2026-07-21", + "verified_by_example": { + "requirement_serial": "ONEOS-88", + "requirement_identifier": "a34e700286d456ee74ddc16e58", + "note": "标签 API:PATCH /workitem/workitem/{id} propertyKey=tag operateType=COVER;全量标签来自 space/tag/search(spaceType=Space)" + }, + "project": { + "name_aliases": ["统一运营管理平台", "统一运营管理平台PC端", "统一运营管理平台 PC 端"], + "spaceIdentifier": "1280be963a5a2cc126a4118dca", + "customCode": "ONEOS", + "spaceType": "Project" + }, + "people": { + "wangmian": { + "displayName": "王冕", + "identifier": "6811df000601d2fea60144a9" + }, + "hefei": { + "displayName": "何斐", + "identifier": "695f0400562f09713f9c3a93" + } + }, + "workitem_types": { + "product_req": { + "name": "产品类需求", + "category": "Req", + "identifier": "9uy29901re573f561d69jn40" + }, + "task": { + "name": "任务", + "category": "Task", + "identifier": "ba102e46bc6a8483d9b7f25c" + } + }, + "priority": { + "紧急": "646004e97f54bb77fec7b455df", + "高": "95b89e0a524d9693e1f335ffe5", + "中": "fa155d1214f9f8db222d39db3b", + "低": "92924feff9c1085891e7511872" + }, + "fields": { + "plan_start": { + "fieldIdentifier": "79", + "name": "计划开始时间", + "value_shape": "epoch_ms_string_china_noon", + "example": "Date.parse('YYYY-MM-DDT12:00:00+08:00') 转 String,避免 00:00 显示成前一天" + }, + "plan_end": { + "fieldIdentifier": "80", + "name": "计划完成时间" + }, + "tag": { + "fieldIdentifier": "tag", + "name": "标签", + "propertyKey": "tag" + } + }, + "status_ui_paths": { + "from_待处理": { + "分析中": ["分析中"], + "设计中": ["设计中"], + "设计完成": ["设计完成"], + "开发中": ["设计完成", "待开发", "开发中"], + "note": "待处理下拉无「开发中」;须按路径连跳。状态 identifier 未在 UI 路径中固化,优先 UI 三跳;有可靠 API 后再补 statusIdentifier。" + } + }, + "assignee_rules": { + "分析中": "creator", + "设计中": "creator", + "设计完成": "creator", + "开发中": "hefei", + "待开发": "hefei" + }, + "api": { + "create_workitem": { + "url": "https://devops.aliyun.com/projex/api/workitem/workitem?_input_charset=utf-8", + "methods": ["POST", "PUT"], + "do_not_use": ["/projex/api/workitem/workitem/create"], + "required_create_fields": [ + "subject", + "spaceType", + "spaceIdentifier", + "category", + "categoryIdentifier", + "workitemTypeIdentifier", + "workitemType", + "assignedTo" + ], + "document_on_create": { + "supported": true, + "shape": { "content": "", "formatType": "RICHTEXT" }, + "note": "创建时一并写入描述,避免再开 Cangjie 编辑器" + } + }, + "get_workitem": { + "url_template": "https://devops.aliyun.com/projex/api/workitem/workitem/{identifier}?_input_charset=utf-8" + }, + "list_workitem": { + "url": "https://devops.aliyun.com/projex/api/workitem/workitem/list?_input_charset=utf-8", + "method": "POST" + }, + "update_field_value": { + "url": "https://devops.aliyun.com/projex/api/workitem/workitem/updateWorkitemFieldValue?_input_charset=utf-8", + "method": "PATCH", + "note": "部分会话返回 InvalidWorkitem.NotFound;失败则改用详情页 UI onChange / 下拉点选,勿盲目重试超过 1 次" + }, + "update_document": { + "preferred": "create 时写入 document;若需补写,用详情页 onWorkItemEditorSubmit(html, 'RICHTEXT')", + "avoid": "对 Cangjie 编辑器直接改 DOM / 错误 onChange 形状(曾导致页面崩溃)" + }, + "list_tags": { + "url": "https://devops.aliyun.com/projex/api/workspace/space/tag/search?spaceType=Space&spaceIdentifier=1280be963a5a2cc126a4118dca&q=&_input_charset=utf-8", + "method": "GET", + "note": "注意 spaceType=Space(不是 Project)。刷新全量标签时用此接口覆盖 tags.catalog" + }, + "apply_tags": { + "url_template": "https://devops.aliyun.com/projex/api/workitem/workitem/{identifier}?_input_charset=utf-8", + "method": "PATCH", + "body_shape": { + "workitemIdentifier": "{identifier}", + "propertyKey": "tag", + "propertyValue": "{comma_separated_tag_identifiers}", + "operateType": "COVER" + }, + "note": "propertyValue 为标签 identifier 逗号拼接;COVER=覆盖整组。已验证于 ONEOS-88。" + } + }, + "tags": { + "selection_mode": "plan_or_askquestion_selector", + "do_not_match_from": ["lease-business-line-overview/lines.ts", "业务条线说明自动推断"], + "apply_strategy": "用户从 catalog 点选标签名 → 用 identifier 调 apply_tags API;失败 1 次再 UI 兜底;仍失败则停并请用户回标签名,禁止假装已打上", + "refresh_hint": "标签库变更时重新 GET list_tags 并更新本 catalog / fetched_at", + "fetched_at": "2026-07-21", + "source": "GET /projex/api/workspace/space/tag/search?spaceType=Space&spaceIdentifier=1280be963a5a2cc126a4118dca&q=", + "count": 30, + "by_name": { + "运维管理条线": "a611b2f2b7c3fd3623ba2d5f52", + "报表中心": "8428d15f45a5b34173202db758", + "能源管理": "3396ebb527358fe6f89fe42849", + "维修站管理": "e7b4da7984801e75630645f437", + "充电站管理": "6ea830485f28a0d8a6e14851a8", + "还车应结款": "4204960ce658c94b17abb7c5f6", + "交车应收款": "6e7a9127d0f70281ca65d335d9", + "加氢站管理": "6cdacae673f9e1fba96296f2ad", + "保险管理": "578ed5ba12bfcf274721e956f2", + "合同管理": "23873be81931cb0dc1bf87a456", + "租赁账单": "3e9433f571b20e80b02626d2de", + "供应商管理": "7b86b7c1f3b3da21b814c5a39d", + "客户管理": "dbf959a70bc979106dc78afa90", + "还车任务": "763de323cefee45e5270e4661e", + "安全培训": "76988b5f73ef515de5930d59b9", + "备件管理": "5d09c864767d898a4f8c0ca804", + "停车场管理": "7c522e2fb29213d7d390e78b97", + "备车管理": "22c6a3ed0e4bf95fd99bf95695", + "异动管理": "84b4333ec7c76d0fa2adcc47bf", + "调拨管理": "fa88c16c152c6fb5978e3b4339", + "上牌管理": "990af2fb716dd85568bc7019bf", + "替换车管理": "8d1decf30259b941016cadc9d2", + "还车管理": "1e0fce2d6929ff4ac13b310e97", + "交车管理": "b6947b5aca82a8c759612ba039", + "车辆管理": "cb71b6db9373d6d8c7aae452a5", + "审批中心": "338add796f2221bbc36dd77d35", + "故障管理": "ceb526a7343995577645317e9a", + "车辆年审": "5193165dad1e91a161502b54c2", + "维修管理": "f5202633870ff409e0ebc16d5f", + "工作台": "b62d21beac55c0b43389915eab" + }, + "catalog": [ + { "name": "运维管理条线", "identifier": "a611b2f2b7c3fd3623ba2d5f52", "color": "#49AEAC" }, + { "name": "报表中心", "identifier": "8428d15f45a5b34173202db758", "color": "#49AEAC" }, + { "name": "能源管理", "identifier": "3396ebb527358fe6f89fe42849", "color": "#49AEAC" }, + { "name": "维修站管理", "identifier": "e7b4da7984801e75630645f437", "color": "#49AEAC" }, + { "name": "充电站管理", "identifier": "6ea830485f28a0d8a6e14851a8", "color": "#49AEAC" }, + { "name": "还车应结款", "identifier": "4204960ce658c94b17abb7c5f6", "color": "#49AEAC" }, + { "name": "交车应收款", "identifier": "6e7a9127d0f70281ca65d335d9", "color": "#49AEAC" }, + { "name": "加氢站管理", "identifier": "6cdacae673f9e1fba96296f2ad", "color": "#49AEAC" }, + { "name": "保险管理", "identifier": "578ed5ba12bfcf274721e956f2", "color": "#49AEAC" }, + { "name": "合同管理", "identifier": "23873be81931cb0dc1bf87a456", "color": "#49AEAC" }, + { "name": "租赁账单", "identifier": "3e9433f571b20e80b02626d2de", "color": "#49AEAC" }, + { "name": "供应商管理", "identifier": "7b86b7c1f3b3da21b814c5a39d", "color": "#49AEAC" }, + { "name": "客户管理", "identifier": "dbf959a70bc979106dc78afa90", "color": "#49AEAC" }, + { "name": "还车任务", "identifier": "763de323cefee45e5270e4661e", "color": "#49AEAC" }, + { "name": "安全培训", "identifier": "76988b5f73ef515de5930d59b9", "color": "#49AEAC" }, + { "name": "备件管理", "identifier": "5d09c864767d898a4f8c0ca804", "color": "#49AEAC" }, + { "name": "停车场管理", "identifier": "7c522e2fb29213d7d390e78b97", "color": "#49AEAC" }, + { "name": "备车管理", "identifier": "22c6a3ed0e4bf95fd99bf95695", "color": "#49AEAC" }, + { "name": "异动管理", "identifier": "84b4333ec7c76d0fa2adcc47bf", "color": "#49AEAC" }, + { "name": "调拨管理", "identifier": "fa88c16c152c6fb5978e3b4339", "color": "#49AEAC" }, + { "name": "上牌管理", "identifier": "990af2fb716dd85568bc7019bf", "color": "#49AEAC" }, + { "name": "替换车管理", "identifier": "8d1decf30259b941016cadc9d2", "color": "#49AEAC" }, + { "name": "还车管理", "identifier": "1e0fce2d6929ff4ac13b310e97", "color": "#49AEAC" }, + { "name": "交车管理", "identifier": "b6947b5aca82a8c759612ba039", "color": "#49AEAC" }, + { "name": "车辆管理", "identifier": "cb71b6db9373d6d8c7aae452a5", "color": "#49AEAC" }, + { "name": "审批中心", "identifier": "338add796f2221bbc36dd77d35", "color": "#49AEAC" }, + { "name": "故障管理", "identifier": "ceb526a7343995577645317e9a", "color": "#49AEAC" }, + { "name": "车辆年审", "identifier": "5193165dad1e91a161502b54c2", "color": "#49AEAC" }, + { "name": "维修管理", "identifier": "f5202633870ff409e0ebc16d5f", "color": "#49AEAC" }, + { "name": "工作台", "identifier": "b62d21beac55c0b43389915eab", "color": "#49AEAC" } + ] + }, + "performance_rules": { + "prefer_api_over_browser": true, + "max_endpoint_probes_per_step": 1, + "screenshot_only_at": ["after_create", "after_final_status"], + "browser_fallback_for": ["tag_apply_when_api_fails", "status_multi_hop_when_api_unknown"] + } +} diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-tag-catalog.md b/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-tag-catalog.md new file mode 100644 index 0000000..f01c22f --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-pc-tag-catalog.md @@ -0,0 +1,50 @@ +# 统一运营管理平台 · 云效标签清单(选择器用) + +来源:`GET /projex/api/workspace/space/tag/search?spaceType=Space&spaceIdentifier=1280be963a5a2cc126a4118dca&q=` +抓取时间:2026-07-21 +机器可读:同目录 `oneos-pc-runtime-ids.json` → `tags` + +## 使用约定 + +- **记录需求**时标签由用户用选择器点选(`AskQuestion` / Plan ○),**不要**再按 `lines.ts` 业务条线说明自动推断。 +- Agent 用点选到的 `name` 查 `tags.by_name` → `identifier`,再 `PATCH` 打标。 +- 完整建单提示词(含 A/B/C 与 30 标签枚举):[references/oneos-pc-record-requirement-prompt.md](../references/oneos-pc-record-requirement-prompt.md) + +## 全量标签(30) + +| # | 标签名 | identifier | +|---|---|---| +| 1 | 运维管理条线 | `a611b2f2b7c3fd3623ba2d5f52` | +| 2 | 报表中心 | `8428d15f45a5b34173202db758` | +| 3 | 能源管理 | `3396ebb527358fe6f89fe42849` | +| 4 | 维修站管理 | `e7b4da7984801e75630645f437` | +| 5 | 充电站管理 | `6ea830485f28a0d8a6e14851a8` | +| 6 | 还车应结款 | `4204960ce658c94b17abb7c5f6` | +| 7 | 交车应收款 | `6e7a9127d0f70281ca65d335d9` | +| 8 | 加氢站管理 | `6cdacae673f9e1fba96296f2ad` | +| 9 | 保险管理 | `578ed5ba12bfcf274721e956f2` | +| 10 | 合同管理 | `23873be81931cb0dc1bf87a456` | +| 11 | 租赁账单 | `3e9433f571b20e80b02626d2de` | +| 12 | 供应商管理 | `7b86b7c1f3b3da21b814c5a39d` | +| 13 | 客户管理 | `dbf959a70bc979106dc78afa90` | +| 14 | 还车任务 | `763de323cefee45e5270e4661e` | +| 15 | 安全培训 | `76988b5f73ef515de5930d59b9` | +| 16 | 备件管理 | `5d09c864767d898a4f8c0ca804` | +| 17 | 停车场管理 | `7c522e2fb29213d7d390e78b97` | +| 18 | 备车管理 | `22c6a3ed0e4bf95fd99bf95695` | +| 19 | 异动管理 | `84b4333ec7c76d0fa2adcc47bf` | +| 20 | 调拨管理 | `fa88c16c152c6fb5978e3b4339` | +| 21 | 上牌管理 | `990af2fb716dd85568bc7019bf` | +| 22 | 替换车管理 | `8d1decf30259b941016cadc9d2` | +| 23 | 还车管理 | `1e0fce2d6929ff4ac13b310e97` | +| 24 | 交车管理 | `b6947b5aca82a8c759612ba039` | +| 25 | 车辆管理 | `cb71b6db9373d6d8c7aae452a5` | +| 26 | 审批中心 | `338add796f2221bbc36dd77d35` | +| 27 | 故障管理 | `ceb526a7343995577645317e9a` | +| 28 | 车辆年审 | `5193165dad1e91a161502b54c2` | +| 29 | 维修管理 | `f5202633870ff409e0ebc16d5f` | +| 30 | 工作台 | `b62d21beac55c0b43389915eab` | + +## 选择器文案(C 段) + +已并入 [oneos-pc-record-requirement-prompt.md](../references/oneos-pc-record-requirement-prompt.md)「用户口令」;此处仅作 identifier 对照表。 diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-project-profile.template.json b/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-project-profile.template.json new file mode 100644 index 0000000..633af24 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/assets/oneos-project-profile.template.json @@ -0,0 +1,119 @@ +{ + "schema_version": 1, + "flow_model": "oneos_delivery", + "organization": "请填写组织名称", + "work_item_type": "产品类需求", + "target_lifecycle": [ + "待处理", "已确认", "分析中", "分析完成", "设计中", "设计完成", + "待开发", "开发中", "开发完成", "待测试", "测试中", "测试完成", + "发布中", "发布完成", "发布失败", "已关闭" + ], + "task_labels": ["交付", "开发", "测试", "发版"], + "policies": { + "require_formal_relations": true, + "require_current_state_condition": true, + "allow_automatic_final_close_without_acceptance": false, + "require_nonzero_development_tasks": true, + "test_success_is_production_release": false, + "bridge_idempotency_key_components": ["项目ID", "需求ID", "动作类型"], + "require_signed_timestamped_replay_protected_callbacks": true + }, + "projects": [ + { + "name": "请填写项目名称", + "migration_phase": "P0", + "requirement_statuses_observed": ["请填写当前全部需求状态"], + "task_workflow_mode": "B", + "task_identity_mode": "task_label", + "native_rule_can_filter_related_task_identity": false, + "main_task_prefix": "【交付】", + "development_task_prefix": "【开发】", + "test_task_prefix": "【测试】", + "release_task_prefix": "【发版】", + "repositories": [ + { + "name": "请填写仓库名称", + "role": "frontend", + "integration_branch": "develop", + "codeup_integrated": true, + "required_for_mr_gate": true, + "evidence": "请填写观察证据" + } + ], + "test_plan": { + "name": "请填写测试计划名称", + "iteration_formally_related": true, + "cases_partitioned_by_requirement": true, + "fixed_defect_is_terminal": false, + "tester_retest_required": true, + "evidence": "请填写观察证据" + }, + "release": { + "release_task_formally_related_to_iteration": true, + "production_evidence_separate_from_test": true, + "acceptance_required_after_release": true, + "production_pipeline_unchanged": true, + "evidence": "请填写观察证据" + }, + "legacy_rules": [ + { + "name": "请填写旧规则显示名", + "disposition": "keep_until_ready", + "enabled": true, + "reason": "请填写保留、停用或替换原因", + "evidence": "请填写观察证据" + } + ], + "controls": [ + {"id":"Y01","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接创建并关联唯一主任务","evidence":"请填写观察证据"}, + {"id":"Y02","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接或人工同步分析完成","evidence":"请填写观察证据"}, + {"id":"Y03","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接或人工同步设计中","evidence":"请填写观察证据"}, + {"id":"Y04","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接或人工同步设计完成","evidence":"请填写观察证据"}, + {"id":"Y05","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接或人工同步待开发","evidence":"请填写观察证据"}, + {"id":"Y06","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接或人工同步开发中","evidence":"请填写观察证据"}, + {"id":"Y07","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接或人工同步开发完成","evidence":"请填写观察证据"}, + {"id":"Y08","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接或人工同步待测试","evidence":"请填写观察证据"}, + {"id":"Y09","mode":"not_supported","enabled":false,"owner":"请填写负责人","fallback":"桥接或人工同步测试中","evidence":"请填写观察证据"}, + {"id":"Y10","mode":"not_supported","enabled":false,"owner":"请填写测试负责人","fallback":"测试负责人按用例和缺陷闭环同步测试完成","evidence":"请填写观察证据"}, + {"id":"Y11","mode":"not_supported","enabled":false,"owner":"请填写发布负责人","fallback":"发版任务提交后人工同步发布中","evidence":"请填写观察证据"}, + {"id":"Y12","mode":"not_supported","enabled":false,"owner":"请填写发布负责人","fallback":"核对生产发布证据后人工同步发布完成","evidence":"请填写观察证据"}, + {"id":"Y13","mode":"not_supported","enabled":false,"owner":"请填写发布负责人","fallback":"人工记录发布失败及原因","evidence":"请填写观察证据"}, + {"id":"Y14","mode":"manual","enabled":true,"owner":"请填写产品负责人","fallback":"产品验收后人工关闭","evidence":"请填写观察证据"}, + {"id":"Y15","mode":"not_supported","enabled":false,"owner":"请填写项目管理员","fallback":"只保留一个权威镜像方向","evidence":"请填写观察证据"}, + {"id":"Y16","mode":"not_supported","enabled":false,"owner":"请填写产品负责人","fallback":"快轨时桥接同步主任务","evidence":"请填写观察证据"}, + {"id":"Y20","mode":"native_rule","enabled":true,"owner":"请填写研发负责人","fallback":"开发负责人人工开始任务和需求","evidence":"请填写观察证据"}, + {"id":"Y21","mode":"integration_bridge","enabled":false,"owner":"请填写研发负责人","fallback":"人工核对全部开发任务和MR","evidence":"请填写观察证据"}, + {"id":"Y22","mode":"integration_bridge","enabled":false,"owner":"请填写项目管理员","fallback":"人工创建并关联唯一测试任务","evidence":"请填写观察证据"}, + {"id":"Y30","mode":"integration_bridge","enabled":false,"owner":"请填写测试负责人","fallback":"人工同步待测试","evidence":"请填写观察证据"}, + {"id":"Y31","mode":"manual","enabled":true,"owner":"请填写测试负责人","fallback":"测试负责人开始测试","evidence":"请填写观察证据"}, + {"id":"Y32","mode":"integration_bridge","enabled":false,"owner":"请填写测试负责人","fallback":"按需求用例包人工验收","evidence":"请填写观察证据"}, + {"id":"Y33","mode":"integration_bridge","enabled":false,"owner":"请填写发布负责人","fallback":"人工提交发版任务并同步范围","evidence":"请填写观察证据"}, + {"id":"Y34","mode":"integration_bridge","enabled":false,"owner":"请填写流水线负责人","fallback":"人工核对生产发布成功","evidence":"请填写观察证据"}, + {"id":"Y35","mode":"integration_bridge","enabled":false,"owner":"请填写流水线负责人","fallback":"人工记录生产发布失败","evidence":"请填写观察证据"}, + {"id":"Y40","mode":"manual","enabled":true,"owner":"请填写产品负责人","fallback":"人工关联迭代","evidence":"请填写观察证据"}, + {"id":"A01","mode":"not_supported","enabled":false,"owner":"请填写产品负责人","fallback":"人工上传并回写链接","evidence":"请填写观察证据"}, + {"id":"A02","mode":"not_supported","enabled":false,"owner":"请填写产品负责人","fallback":"人工维护更新内容历史","evidence":"请填写观察证据"}, + {"id":"A02B","mode":"not_supported","enabled":false,"owner":"请填写产品负责人","fallback":"人工维护需求说明","evidence":"请填写观察证据"}, + {"id":"A03","mode":"integration_bridge","enabled":false,"owner":"请填写项目管理员","fallback":"人工建需求和唯一主任务","evidence":"请填写观察证据"}, + {"id":"A04","mode":"integration_bridge","enabled":false,"owner":"请填写研发负责人","fallback":"人工拆分开发任务","evidence":"请填写观察证据"}, + {"id":"A05","mode":"integration_bridge","enabled":false,"owner":"请填写研发负责人","fallback":"人工核对MR并完成开发任务","evidence":"请填写观察证据"}, + {"id":"A06","mode":"integration_bridge","enabled":false,"owner":"请填写项目管理员","fallback":"人工建唯一测试任务","evidence":"请填写观察证据"}, + {"id":"A07","mode":"integration_bridge","enabled":false,"owner":"请填写测试负责人","fallback":"人工核对用例与缺陷闭环","evidence":"请填写观察证据"}, + {"id":"A08","mode":"integration_bridge","enabled":false,"owner":"请填写发布负责人","fallback":"人工建发版任务和更新说明","evidence":"请填写观察证据"}, + {"id":"A09","mode":"integration_bridge","enabled":false,"owner":"请填写流水线负责人","fallback":"人工回写发布成败","evidence":"请填写观察证据"}, + {"id":"A10","mode":"manual","enabled":true,"owner":"请填写产品负责人","fallback":"产品验收后人工关闭","evidence":"请填写观察证据"}, + {"id":"TC01","mode":"manual","enabled":true,"owner":"请填写测试负责人","fallback":"逐条执行并记录用例结果","evidence":"请填写观察证据"}, + {"id":"TC02","mode":"manual","enabled":true,"owner":"请填写测试负责人","fallback":"人工正式关联失败用例和缺陷","evidence":"请填写观察证据"}, + {"id":"TC03","mode":"manual","enabled":true,"owner":"请填写测试负责人","fallback":"复测后更新原用例状态","evidence":"请填写观察证据"}, + {"id":"DF01","mode":"manual","enabled":true,"owner":"请填写测试负责人","fallback":"已修复继续等待复测","evidence":"请填写观察证据"}, + {"id":"DF02","mode":"manual","enabled":true,"owner":"请填写测试负责人","fallback":"复测通过后关闭缺陷并通过用例","evidence":"请填写观察证据"}, + {"id":"DF03","mode":"manual","enabled":true,"owner":"请填写测试负责人","fallback":"复测失败后重开缺陷并保持用例失败","evidence":"请填写观察证据"}, + {"id":"CI01","mode":"manual","enabled":true,"owner":"请填写研发负责人","fallback":"人工核对全部前后端MR","evidence":"请填写观察证据"}, + {"id":"CI02","mode":"not_supported","enabled":false,"owner":"请填写流水线负责人","fallback":"使用test副本评审回调POC","evidence":"请填写观察证据"}, + {"id":"CI03","mode":"manual","enabled":true,"owner":"请填写发布负责人","fallback":"人工区分test、生产发布和验收","evidence":"请填写观察证据"}, + {"id":"L01","mode":"disabled_legacy","enabled":false,"owner":"请填写项目管理员","fallback":"保持发布完成等待验收","evidence":"请填写观察证据"}, + {"id":"L02","mode":"disabled_legacy","enabled":false,"owner":"请填写项目管理员","fallback":"保持发布完成等待验收","evidence":"请填写观察证据"} + ] + } + ] +} diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/assets/project-profile.template.json b/.cursor/skills/yunxiao-requirement-lifecycle/assets/project-profile.template.json new file mode 100644 index 0000000..3f6c25a --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/assets/project-profile.template.json @@ -0,0 +1,148 @@ +{ + "schema_version": 3, + "organization": "请填写组织名称", + "work_item_type": "产品类需求", + "lifecycle": [ + "待处理", + "已确认", + "分析中", + "分析完成", + "设计中", + "设计完成", + "待开发", + "开发中", + "开发完成", + "测试中", + "测试完成", + "完成发布", + "已完成" + ], + "requirement_labels": ["分析", "设计", "开发", "测试"], + "automation_policy": { + "require_current_state_condition": true, + "inspect_rule_order_and_cascades": true, + "allow_automatic_final_close_without_acceptance": false, + "zero_related_items_are_complete": false, + "development_start_event_candidates": ["添加关联分支", "正式关联代码资产"], + "require_code_assets_linked_to_requirement_and_development_task": true, + "stage_task_creation_idempotency_key_components": ["项目ID", "需求ID", "阶段类型"] + }, + "test_closure_policy": { + "accepted_case_states": ["已通过"], + "rejected_case_states": ["未执行", "待测试", "阻塞", "未通过"], + "defect_fixed_is_terminal": false, + "require_tester_retest": true + }, + "projects": [ + { + "name": "请填写项目名称", + "work_item_type": "产品类需求", + "release_status": "完成发布", + "final_status": "已完成", + "labels_observed": ["分析", "设计", "开发", "测试"], + "repositories": [ + { + "name": "请填写仓库名称", + "role": "frontend", + "integration_branch": "dev", + "codeup_integrated": true, + "required_for_mr_gate": true, + "evidence": "请填写仓库与需求正式关联的观察证据" + } + ], + "test_pipeline": { + "name": "请填写test流水线名称", + "environment": "test", + "is_clone_for_callback_poc": true, + "original_pipeline_unchanged": true, + "docker_success_callback_enabled": false, + "callback_security": { + "signed": false, + "timestamp_checked": false, + "replay_protected": false, + "idempotent_by_execution_id": false, + "failure_retry_tested": false + }, + "evidence": "请填写流水线观察证据" + }, + "release_evidence": { + "mode": "native_release_change", + "target_environment": "请填写目标发布环境", + "test_success_is_production_release": false, + "evidence": "请填写发布证据" + }, + "test_plan": { + "name": "请填写测试计划名称", + "requirement_formally_related": true, + "case_states_observed": ["未执行", "已通过", "未通过", "阻塞"], + "evidence": "请填写测试计划与用例状态证据" + }, + "defect_workflow": { + "fixed_status": "已修复", + "fixed_is_terminal": false, + "tester_retest_required": true, + "closed_status": "请填写缺陷真实关闭状态", + "reopen_status": "请填写缺陷复测失败后的状态", + "evidence": "请填写缺陷工作流证据" + }, + "rule_instances": [ + {"id": "R01", "mode": "manual", "actual_rule_name": "人工门禁:需求确认", "trigger": "负责人确认", "source_status": "待处理", "target_status": "已确认", "conditions": ["范围、负责人和验收口径明确"], "action": "人工变更状态为已确认", "order": 1, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R02", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "添加关联任务工作项", "source_status": "已确认", "target_status": "分析中", "conditions": ["当前状态=已确认", "需求自身标签包含分析"], "action": "变更状态为分析中", "order": 2, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R03", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "关联任务工作项状态变化", "source_status": "分析中", "target_status": "分析完成", "conditions": ["当前状态=分析中", "本阶段分析任务全部完成;若平台不能过滤阶段则记录替代策略"], "action": "变更状态为分析完成", "order": 3, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R04", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "添加关联任务工作项", "source_status": "分析完成", "target_status": "设计中", "conditions": ["当前状态=分析完成", "需求自身标签包含设计"], "action": "变更状态为设计中", "order": 4, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R05", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "关联任务工作项状态变化", "source_status": "设计中", "target_status": "设计完成", "conditions": ["当前状态=设计中", "本阶段设计任务全部完成;若平台不能过滤阶段则记录替代策略"], "action": "变更状态为设计完成", "order": 5, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R06", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "添加关联任务工作项", "source_status": "设计完成", "target_status": "待开发", "conditions": ["当前状态=设计完成", "需求自身标签包含开发"], "action": "变更状态为待开发", "order": 6, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R07", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "添加关联分支或正式关联代码资产", "source_status": "待开发", "target_status": "开发中", "conditions": ["当前状态=待开发", "仓库已集成", "代码资产正式关联需求"], "action": "变更状态为开发中", "order": 7, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R08", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "关联合并请求状态变化", "source_status": "开发中", "target_status": "开发完成", "conditions": ["当前状态=开发中", "全部相关前后端合并请求已合并"], "action": "变更状态为开发完成", "order": 8, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R09", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "添加关联任务工作项", "source_status": "开发完成", "target_status": "测试中", "conditions": ["当前状态=开发完成", "需求自身标签包含测试"], "action": "变更状态为测试中", "order": 9, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R10", "mode": "manual", "actual_rule_name": "人工门禁:测试验收", "trigger": "测试验收任务完成或获批回调", "source_status": "测试中", "target_status": "测试完成", "conditions": ["当前状态=测试中", "范围内用例全部通过", "失败用例已复测通过", "范围内缺陷已由测试复测并关闭"], "action": "变更状态为测试完成", "order": 10, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R11", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "关联发布变更完成或获批回调", "source_status": "测试完成", "target_status": "完成发布", "conditions": ["当前状态=测试完成", "目标环境发布证据满足组织口径"], "action": "变更状态为完成发布", "order": 11, "enabled": false, "has_current_state_condition": true, "evidence": "请填写观察证据"}, + {"id": "R12", "mode": "manual", "actual_rule_name": "人工门禁:业务验收", "trigger": "业务验收", "source_status": "完成发布", "target_status": "已完成", "conditions": ["当前状态=完成发布", "正式验收证据有效"], "action": "人工变更状态为已完成", "order": 12, "enabled": true, "has_current_state_condition": true, "evidence": "请填写观察证据"} + ], + "task_rule_instances": [ + {"id": "TK01", "mode": "manual", "actual_rule_name": "人工门禁:设计任务真实开工", "trigger": "负责人开始处理设计任务", "scope": "设计任务", "source_state": "待处理", "conditions": ["任务标签=设计", "任务正式关联需求"], "actions": ["设计任务变为处理中"], "order": 101, "enabled": true, "evidence": "请填写观察证据"}, + {"id": "TK02", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "设计任务状态变为已完成", "scope": "产品需求", "source_state": "设计中", "conditions": ["本阶段设计任务非零且全部完成"], "actions": ["需求变为设计完成"], "order": 102, "enabled": true, "evidence": "请填写观察证据"}, + {"id": "TK03", "mode": "integration_bridge", "actual_rule_name": "桥接:设计完成后创建开发任务", "trigger": "需求状态变为设计完成", "scope": "产品需求", "source_state": "设计完成", "conditions": ["不存在同需求同阶段未取消开发任务", "幂等键=项目ID+需求ID+开发"], "actions": ["创建并关联开发任务", "开发任务标签=开发", "开发任务状态=待处理"], "order": 103, "enabled": false, "evidence": "请填写观察证据"}, + {"id": "TK04", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "开发任务创建并正式关联需求", "scope": "产品需求", "source_state": "设计完成", "conditions": ["需求标签包含开发", "开发任务关系可见"], "actions": ["需求变为待开发"], "order": 104, "enabled": true, "evidence": "请填写观察证据"}, + {"id": "TK05", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "添加正式关联分支或代码资产", "scope": "产品需求和开发任务", "source_state": "需求=待开发;开发任务=待处理", "conditions": ["代码资产同时关联需求和开发任务", "仓库已接入项目"], "actions": ["需求变为开发中", "开发任务变为处理中"], "order": 105, "enabled": true, "evidence": "请填写观察证据"}, + {"id": "TK06", "mode": "native_rule", "actual_rule_name": "请填写云效规则显示名", "trigger": "关联合并请求状态变化", "scope": "产品需求和开发任务", "source_state": "需求=开发中;开发任务=处理中", "conditions": ["全部相关前后端MR已合并到真实dev/develop", "MR同时关联需求和开发任务"], "actions": ["需求变为开发完成", "开发任务变为已完成"], "order": 106, "enabled": true, "evidence": "请填写观察证据"}, + {"id": "TK07", "mode": "integration_bridge", "actual_rule_name": "桥接:test部署成功后创建测试任务", "trigger": "test流水线部署成功", "scope": "产品需求", "source_state": "开发完成", "conditions": ["流水线执行ID未处理", "不存在同需求同阶段未取消测试任务", "回调签名和防重放验证通过"], "actions": ["创建并关联测试任务", "测试任务变为处理中", "关联测试计划和用例", "需求变为测试中"], "order": 107, "enabled": false, "evidence": "请填写观察证据"}, + {"id": "TK08", "mode": "manual", "actual_rule_name": "人工门禁:测试任务验收闭环", "trigger": "测试验收完成", "scope": "产品需求和测试任务", "source_state": "需求=测试中;测试任务=处理中", "conditions": ["用例全部通过", "失败用例已复测", "范围内缺陷已由测试关闭"], "actions": ["测试任务变为已完成", "需求变为测试完成"], "order": 108, "enabled": true, "evidence": "请填写观察证据"} + ], + "control_coverage": [ + {"id": "R01", "mode": "manual", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "人工确认需求范围与验收口径"}, + {"id": "R02", "mode": "native_rule", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "人工进入分析中"}, + {"id": "R03", "mode": "native_rule", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "人工核对本阶段分析任务"}, + {"id": "R04", "mode": "native_rule", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "人工进入设计中"}, + {"id": "R05", "mode": "native_rule", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "人工核对本阶段设计任务"}, + {"id": "R06", "mode": "native_rule", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "人工进入待开发"}, + {"id": "R07", "mode": "native_rule", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "开发人员真实开工时人工开始开发"}, + {"id": "R08", "mode": "native_rule", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "人工核对全部相关MR"}, + {"id": "R09", "mode": "native_rule", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写负责人", "fallback": "人工进入测试中"}, + {"id": "R10", "mode": "manual", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写测试负责人", "fallback": "测试负责人验收任务"}, + {"id": "R11", "mode": "native_rule", "enabled": false, "evidence": "请填写观察证据", "owner": "请填写发布负责人", "fallback": "人工核对发布变更"}, + {"id": "R12", "mode": "manual", "enabled": true, "evidence": "请填写观察证据", "owner": "请填写产品负责人", "fallback": "产品或业务负责人验收"}, + {"id": "TK01", "mode": "manual", "enabled": true, "evidence": "请填写设计任务开工证据", "owner": "请填写设计负责人", "fallback": "负责人真实开工时人工改为处理中"}, + {"id": "TK02", "mode": "native_rule", "enabled": true, "evidence": "请填写设计任务完成汇总证据", "owner": "请填写产品负责人", "fallback": "人工核对非零设计任务全部完成"}, + {"id": "TK03", "mode": "integration_bridge", "enabled": false, "evidence": "请填写自动创建开发任务能力证据", "owner": "请填写项目管理员", "fallback": "从需求中人工新建并关联唯一开发任务"}, + {"id": "TK04", "mode": "native_rule", "enabled": true, "evidence": "请填写开发任务关联证据", "owner": "请填写项目管理员", "fallback": "人工核对关系后将需求改为待开发"}, + {"id": "TK05", "mode": "native_rule", "enabled": true, "evidence": "请填写需求和开发任务双对象开工证据", "owner": "请填写研发负责人", "fallback": "开发人员真实开工时人工更新任务和需求"}, + {"id": "TK06", "mode": "native_rule", "enabled": true, "evidence": "请填写需求和开发任务双对象完成证据", "owner": "请填写研发负责人", "fallback": "人工核对全部相关MR后完成任务和需求"}, + {"id": "TK07", "mode": "integration_bridge", "enabled": false, "evidence": "请填写test部署回调和自动创建测试任务证据", "owner": "请填写流水线负责人", "fallback": "test部署成功后人工创建并关联测试任务"}, + {"id": "TK08", "mode": "manual", "enabled": true, "evidence": "请填写测试任务与需求双闭环证据", "owner": "请填写测试负责人", "fallback": "测试负责人验收后同步完成测试任务和需求"}, + {"id": "TC01", "mode": "manual", "enabled": true, "evidence": "请填写用例执行状态证据", "owner": "请填写测试负责人", "fallback": "逐条执行并记录用例结果"}, + {"id": "TC02", "mode": "manual", "enabled": true, "evidence": "请填写失败用例关联缺陷证据", "owner": "请填写测试负责人", "fallback": "人工核对失败用例与缺陷正式关系"}, + {"id": "TC03", "mode": "manual", "enabled": true, "evidence": "请填写用例复测更新证据", "owner": "请填写测试负责人", "fallback": "复测后更新原用例状态"}, + {"id": "DF01", "mode": "manual", "enabled": true, "evidence": "请填写已修复交接证据", "owner": "请填写测试负责人", "fallback": "已修复继续等待测试复测"}, + {"id": "DF02", "mode": "manual", "enabled": true, "evidence": "请填写复测通过关闭证据", "owner": "请填写测试负责人", "fallback": "测试复测通过后关闭缺陷"}, + {"id": "DF03", "mode": "manual", "enabled": true, "evidence": "请填写复测失败重开证据", "owner": "请填写测试负责人", "fallback": "测试复测失败后重开缺陷"}, + {"id": "CI01", "mode": "native_rule", "enabled": true, "evidence": "请填写多仓库关联证据", "owner": "请填写研发负责人", "fallback": "人工核对全部前后端MR"}, + {"id": "CI02", "mode": "not_supported", "enabled": false, "evidence": "请填写流水线能力证据", "owner": "请填写流水线负责人", "fallback": "复制test流水线后评审回调POC"}, + {"id": "CI03", "mode": "manual", "enabled": true, "evidence": "请填写环境与发布证据", "owner": "请填写发布负责人", "fallback": "人工区分test部署、生产发布与业务验收"}, + {"id": "L01", "mode": "disabled_legacy", "enabled": false, "evidence": "请填写旧规则盘点证据", "owner": "请填写项目管理员", "fallback": "使用独立业务验收任务"}, + {"id": "L02", "mode": "disabled_legacy", "enabled": false, "evidence": "请填写旧规则盘点证据", "owner": "请填写项目管理员", "fallback": "保持发布完成状态等待业务验收"} + ], + "adjunct_automations": [] + } + ] +} diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/auto-stage-task.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/auto-stage-task.md new file mode 100644 index 0000000..82abe72 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/auto-stage-task.md @@ -0,0 +1,62 @@ +# 需求进阶段自动建同名任务(强制 · AI 桥接) + +当**产品类需求**推进到下列状态时,AI(`apply`)必须查重后创建/复用**与需求同名**的任务,并**正式关联**该需求。不得只改需求状态却不建任务。 + +## 触发状态与字段规则 + +| 需求进入状态 | 任务标题 | 任务标签(右侧基础字段) | 负责人 | 时间字段 | +|---|---|---|---|---| +| **分析中** | = 需求标题(同名) | `分析` | **需求创建人**姓名 | 沿用需求(见下) | +| **设计中** | = 需求标题(同名) | `设计` | **需求创建人**姓名 | 沿用需求(见下) | +| **待开发** | = 需求标题(同名) | `交付`(OneOS 主任务);若项目仍用阶段开发标签则用 `开发` | 固定 **何斐** | 沿用需求(见下) | + +### 时间沿用(「标签创建时间沿用需求的」) + +按平台能力尽量对齐,优先级: + +1. 任务 **计划开始时间** = 需求的计划开始时间;若需求无计划开始,则用需求 **创建时间** 的日期。 +2. 若平台允许写入任务创建时间:任务创建时间 = 需求创建时间。 +3. 若都不能改创建时间:仍写计划开始;并在任务描述首行注明:`创建时间沿用需求:YYYY-MM-DD HH:mm`。 + +### 正式关联(强制) + +- 使用云效**父子项或关联项**把任务挂到**该产品需求**。 +- 仅标题写需求编号、或只关联其他任务而不关联需求 → **不合格**,必须补关联后再回报成功。 +- 标题前缀(如 `【分析】`)**不是**关联;本规则默认标题与需求**完全同名**;若项目强制标题前缀,允许 `【分析】{需求标题}`,但仍须正式关联 + 正确标签。 + +## 幂等与查重 + +幂等键: + +```text +项目ID + 需求ID + 阶段标签(分析|设计|交付/开发) +``` + +执行顺序: + +1. 查询该需求下是否已有**未取消**、同阶段标签、同名(或同名前缀)任务。 +2. **已存在一条** → 复用:核对正式关联、标签、负责人(待开发须为何斐)、时间字段;缺则补齐;**不新建第二条**。 +3. **存在多条冲突** → 停止创建,列出任务编号,请人合并后再继续。 +4. **不存在** → 创建并正式关联,再改需求状态(若口令是「推进到某状态」且任务尚未建好,先建任务再改状态,避免有状态无任务)。 + +快轨直接到 **待开发**(跳过分析/设计):只建/复用 **待开发** 那一条同名任务(负责人何斐),不要为跳过的阶段补建分析/设计任务,除非用户明确要求。 + +## 与 OneOS 终态模型对齐 + +- `oneos_delivery`:待开发阶段的同名任务即唯一 **【交付】主任务**(标题优先与需求同名;标签=`交付`)。分析中/设计中另建同名阶段任务(标签分析/设计),**不**再额外建第二条交付主任务。 +- `stage_tasks`:分析/设计/开发阶段任务按上表;待开发对应标签=`开发` 的同名任务,负责人何斐(若项目配置了其他默认开发负责人,以用户当次口令为准,口令未写则何斐)。 + +## AI 回报模板(短) + +```text +需求:ONEOSP-xxx「标题」→ 状态:分析中/设计中/待开发 +任务:TASK-xxx「同名」;标签:…;负责人:…;计划开始:…;已正式关联需求 +查重:新建 / 复用 +``` + +## 负向(不得误做) + +- 需求仅到 `已确认` / `待处理`:不自动建分析/设计/待开发任务。 +- 不得把样式类口头变更当成建任务触发。 +- 不得在未查重时重复创建同阶段同名任务。 +- 不得把负责人设错:分析中/设计中≠何斐(除非创建人就是何斐);待开发必须何斐(除非用户当次明确改派)。 diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/codeup-integration.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/codeup-integration.md new file mode 100644 index 0000000..ac12a75 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/codeup-integration.md @@ -0,0 +1,60 @@ +# Codeup研发资产集成 + +## 目录 + +1. 仓库覆盖 +2. 分支策略 +3. 正式关联 +4. 提交与合并请求 +5. CI01多仓库门禁 +6. 验证场景 + +## 1. 仓库覆盖 + +前端、后端均可触发研发自动化,前提是仓库接入正确项目,分支/提交/MR正式关联需求,规则没有错误限定到单一仓库。 + +逐仓库记录:仓库名、角色、项目集成状态、Webhook、实际集成分支、是否纳入全部MR门禁和观察证据。 + +## 2. 分支策略 + +- 检查真实集成分支:存在`dev`则优先`dev`,否则使用存在的`develop`。 +- 从集成分支创建`feature/`或`fix/`。 +- 一个需求涉及多个仓库时,每个仓库可有一个同名活动分支。 +- 快速迭代仍使用短生命周期分支;合并后是否删除按仓库策略和用户授权处理。 +- 不直接推送保护分支,不为自动化方便创建不存在的`dev`/`develop`。 + +## 3. 正式关联 + +关联优先级: + +1. 从需求“代码”区域创建分支并回到需求确认资产数量。 +2. 在Codeup创建分支/MR时显式选择工作项;存在开发任务时同时关联需求和开发任务。 +3. 使用以工作项ID结尾的分支名,并在需求页确认自动关联。 +4. 提交说明关键字仅作兜底,必须回需求页验证。 + +需求编号、分支名或MR标题有关键字,不等同已正式关联。 + +## 4. 提交与合并请求 + +- 提交可带工作项ID增强追溯,但不判定开发完成。 +- MR目标必须是仓库真实集成分支。 +- MR显式关联需求和对应开发任务并完成评审。 +- 只有全部相关MR合并后才观察R08。 +- 首次提交时间不作为真实开发开始时间;人工“开始开发”为效能主口径。 + +## 5. CI01多仓库门禁 + +CI01要求所有相关前后端仓库的分支、提交和MR都正式关联需求与对应开发任务。只遗漏一个仓库或只关联需求,平台的“全部MR已合并”就可能只看到部分资产、提前完成需求,或完全无法完成开发任务。 + +如果无法可靠聚合所有仓库,保留人工核对门禁,不能声称R08已完全自动化。 + +## 6. 验证场景 + +| 场景 | 操作 | 期望 | +|---|---|---| +| 正式关联分支 | 从需求代码区创建真实仓库分支 | 异步进入开发中 | +| 非关联分支 | 仅本地创建/推送无关系分支 | 不触发R07 | +| 仅有提交 | 提交代码但不合并MR | 不进入开发完成 | +| 单仓库MR | 所有相关仓库只有一个MR合并 | 仍保持开发中 | +| 多仓库全部合并 | 所有相关MR正式关联并合并 | 进入开发完成 | +| 错误集成分支 | MR指向非真实集成分支 | 阻塞并修正,不伪造通过 | diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/lifecycle-rules.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/lifecycle-rules.md new file mode 100644 index 0000000..71904a5 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/lifecycle-rules.md @@ -0,0 +1,103 @@ +# 需求生命周期与项目规则 + +## 目录 + +1. 状态参数化 +2. R01–R12规则矩阵 +3. 阶段入口和阶段完成 +4. 开发开始与开发完成 +5. 标签、任务和关键字 +6. 规则实例与限制 + +## 1. 状态参数化 + +典型流程为: + +```text +待处理 → 已确认 → 分析中 → 分析完成 → 设计中 → 设计完成 → 待开发 +→ 开发中 → 开发完成 → 测试中 → 测试完成 → 完成发布/发布完成 → 已完成/已关闭 +``` + +逐项目读取真实状态。`完成发布`与`发布完成`、`已完成`与`已关闭`是候选映射,不能静默互换或创建近义重复状态。 + +## 2. R01–R12规则矩阵 + +| ID | 流转 | 触发事件 | 必要条件 | 动作 | 方式 | +|---|---|---|---|---|---| +| R01 | 待处理 → 已确认 | 负责人确认 | 范围、负责人、优先级、验收口径明确 | 改为已确认 | 人工 | +| R02 | 已确认 → 分析中 | 添加关联任务工作项 | 当前状态;需求自身标签包含`分析` | 改为分析中 | 原生规则 | +| R03 | 分析中 → 分析完成 | 关联任务状态变化 | 当前状态;本阶段分析任务全部完成 | 改为分析完成 | 原生/条件化 | +| R04 | 分析完成 → 设计中 | 添加关联任务工作项 | 当前状态;需求自身标签包含`设计` | 改为设计中 | 原生规则 | +| R05 | 设计中 → 设计完成 | 关联任务状态变化 | 当前状态;本阶段设计任务全部完成 | 改为设计完成 | 原生/条件化 | +| R06 | 设计完成 → 待开发 | 添加关联任务工作项 | 当前状态;需求自身标签包含`开发` | 改为待开发 | 原生规则 | +| R07 | 待开发 → 开发中 | 添加关联分支或正式关联代码资产 | 当前状态;仓库已集成;资产正式关联 | 改为开发中 | 自动兜底 | +| R08 | 开发中 → 开发完成 | 关联合并请求状态变化 | 当前状态;全部相关MR已合并 | 改为开发完成 | 原生规则 | +| R09 | 开发完成 → 测试中 | 添加关联任务工作项 | 当前状态;需求自身标签包含`测试` | 改为测试中 | 原生规则 | +| R10 | 测试中 → 测试完成 | 测试验收任务或获批回调 | 当前状态;用例、失败复测、缺陷均闭环 | 改为测试完成 | 人工/桥接 | +| R11 | 测试完成 → 发布完成状态 | 发布变更完成或获批回调 | 当前状态;目标环境发布证据有效 | 改为真实发布状态 | 原生/桥接 | +| R12 | 发布完成状态 → 项目终态 | 业务验收 | 当前状态;验收证据有效 | 改为真实终态 | 默认人工 | + +R05–R10与阶段任务自身状态的双向联动见`task-lifecycle.md`;R10的用例和缺陷口径见`test-defect-closure.md`;R11、R12见`pipeline-release.md`;R07、R08的代码资产口径见`codeup-integration.md`。 + +## 3. 阶段入口和阶段完成 + +R02、R04、R06、R09同时要求: + +1. 从需求中“新建并关联”或“关联已有”任务工作项。 +2. 当前状态等于前一阶段完成状态。 +3. 需求自身包含目标阶段标签。 + +如果规则编辑器不能读取需求自身标签,标签只能用于筛选和报表;保留人工入口或使用当前状态+显式关联事件,不能声称标签已成为技术门槛。 + +R03、R05首选“本阶段任务全部完成”。若平台只能判断“全部关联任务工作项”,选择一种明确策略: + +- 阶段开始时才创建/关联本阶段任务。 +- 使用不同工作项类型,并确认规则能按类型过滤。 +- 使用父子需求拆分阶段。 +- 保留人工完成动作。 + +不得把零个关联任务当作全部完成。使用关联项状态变化触发,并执行零关联项负向测试。 + +## 4. 开发开始与开发完成 + +R07采用双轨口径: + +- 效能主口径:开发人员真实开工时执行“开始开发”。 +- 自动兜底:创建或正式关联需求分支/代码资产后进入`开发中`。 + +首次提交可能晚于真实开工,不能作为权威开始时间。自动规则必须要求当前状态=`待开发`。 + +R08推荐定义: + +```text +事件 = 关联合并请求状态变化 +且当前状态 = 开发中 +且全部相关合并请求 = 已合并 +则状态 = 开发完成 +``` + +提交活动不能证明开发完成。全部相关前端、后端MR都必须正式关联并合并到实际集成分支。 + +## 5. 标签、任务和关键字 + +`分析`、`设计`、`开发`、`测试`标签加在产品类需求自身。 + +“添加关联任务工作项”依赖云效正式关系,不依赖标题关键字。有效方式: + +- 在需求详情新建并关联任务。 +- 在需求详情关联已有任务。 +- 在任务详情选择该需求为父项/关联项,并在页面确认关系。 + +仅写需求编号、粘贴链接或添加同名标签都不能替代正式关联。编号仍建议出现在标题中,方便检索和审计。 + +## 6. 规则实例与限制 + +每个项目分别记录R01–R12的实际规则显示名/人工门禁、精确触发、源状态、目标状态、全部条件、动作、顺序、启用状态、执行账号和证据。 + +已知限制: + +- 规则通常异步执行,5–30秒内不要过早判失败。 +- “全部关联项”只能看到已集成并正式关联的资产。 +- 提前创建未来阶段任务可能阻塞“全部任务完成”。 +- 需求状态规则不会自动更新阶段任务状态;必须同时配置TK01–TK08。 +- 平台能力因模板、版本和权限不同,以当前UI和执行日志为准。 diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/live-operations.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/live-operations.md new file mode 100644 index 0000000..0c8271d --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/live-operations.md @@ -0,0 +1,133 @@ +# 线上实施、验证与治理 + +## 目录 + +1. 安全边界 +2. 项目盘点 +3. 规则应用 +4. 隔离验证 +5. 故障诊断 +6. 回滚 +7. 运行治理 + +## 1. 安全边界 + +- 只操作明确授权的组织、项目、工作项类型和测试资产。 +- 复用已登录会话,不记录账号、密码、Cookie、Token、OTP、私钥或签名材料。 +- 不修改现有prod流水线;回调实验只改test副本。 +- 不删除测试需求、分支、MR、流水线或规则,除非另有授权。 +- 保留无关规则和用户修改。 + +## 2. 项目盘点 + +先记录`flow_model=stage_tasks`或`flow_model=oneos_delivery`。发现两套规则同时启用时判定为迁移中,不按规则名称猜测主模型。 + +对每个项目独立记录: + +| 类别 | 必查内容 | +|---|---| +| 项目 | 精确项目名、项目ID、工作项类型 | +| 工作流 | 全部状态、顺序、发布状态、终态 | +| 阶段任务 | 设计/开发/测试任务类型、状态、标签、父子/关联关系、自动创建能力 | +| 标签 | `分析`、`设计`、`开发`、`测试` | +| 规则 | 名称、事件、条件、动作、顺序、启用、执行账号、后继规则 | +| Codeup | 前后端仓库、集成、Webhook、真实`dev/develop` | +| 流水线 | test/prod名称、环境、部署阶段、副本能力 | +| 发布 | 发布变更关系、目标环境、完成状态 | +| 测试 | 计划范围、用例状态枚举、需求关系 | +| 缺陷 | 已修复、复测、关闭态、重开态、权限 | + +两个项目只复制逻辑,不复制内部ID、仓库、分支、流水线、规则ID或观察证据。 + +OneOS终态迁移还要盘点:`待测试/发布中/发布失败`状态缺口、主任务身份方式、任务工作流方案A/B、原生规则能否过滤关联任务身份、Y/A控制负责人和幂等键。门禁未齐时保留可用旧规则,只停用会绕过验收或造成已确认误触发的规则。 + +## 3. 规则应用 + +1. 导出或截图现有规则和启用状态。 +2. 识别后继规则和可能的连锁路径。 +3. 创建缺失阶段标签。 +4. 一次只创建或修改一条规则。 +5. 设置精确触发、当前状态、需求标签、其他条件和目标动作。 +6. 保存后重新打开,核对全部字段、顺序、启用状态和执行账号。 +7. 把真实字段写回该项目`rule_instances`。 +8. 触发最小事件,等待5–30秒。 +9. 查看状态、研发资产、执行日志和二次流转。 +10. 记录证据后再处理下一条。 + +手工推进只能标记为“测试准备动作”,不能伪造规则通过。 + +## 4. 隔离验证 + +测试需求命名示例: + +```text +[自动化测试] 产品类需求生命周期验证-YYYYMMDD +``` + +| 用例 | 操作 | 期望 | 负向检查 | +|---|---|---|---| +| T01 | 已确认+分析标签+关联任务 | 分析中 | 无标签不流转 | +| T02 | 完成分析任务 | 分析完成 | 零任务不误触发 | +| T03 | 设计标签+关联任务 | 设计中 | 仅标题关键字不触发 | +| T04 | 完成设计任务 | 设计完成 | 未完成任务阻塞 | +| T04a | 设计完成后触发TK03 | 创建且只创建一个开发任务 | 重复事件不得重复创建 | +| T04b | 开发任务正式关联需求 | 需求待开发、任务待处理 | 仅标题编号不触发 | +| T05 | 开发标签+关联任务 | 待开发 | 错误当前状态不触发 | +| T06 | 正式关联真实仓库分支 | 开发中 | 非关联分支不触发 | +| T06a | 同一代码资产同时关联需求和开发任务 | 需求开发中、开发任务处理中 | 只关联需求不得声称任务已联动 | +| T07 | 前后端各关联MR,只合并一个 | 保持开发中 | 不得提前完成 | +| T08 | 全部相关MR合并 | 开发完成 | 仅提交不得完成 | +| T08a | 全部相关MR同时关联开发任务并合并 | 开发任务已完成 | 部分仓库MR未合并时阻塞 | +| T09 | 测试标签+关联测试任务 | 测试中 | 未来任务不应误阻塞 | +| T09a | test流水线部署成功触发TK07 | 创建且只创建一个测试任务并进入处理中 | 流水线失败或重复回调不得创建 | +| T10a | 执行全部用例并记录结果 | 每条有明确状态 | 未执行/阻塞/失败阻塞 | +| T10b | 失败用例关联缺陷,改为已修复 | 保持测试中 | 未复测不得完成 | +| T10c | 复测通过/失败 | 关闭并通过/重开并失败 | 用例缺陷状态一致 | +| T11 | 发布变更完成/获批回调 | 发布完成状态 | 重复回调不重复推进 | +| T12a | 进入发布完成后等待异步窗口 | 停留发布完成 | L01/L02不得误关闭 | +| T12b | 完成正式业务验收 | 项目终态 | test或发布不能替代验收 | + +结果只使用:`通过`、`异步通过`、`未触发`、`误触发`、`阻塞`。 + +## 5. 故障诊断 + +按顺序检查: + +1. 事件是否真实发生,而不是只有相似标题/标签。 +2. 当前状态是否精确匹配。 +3. 标签是否加在需求自身。 +4. 任务、分支、MR、发布变更是否正式关联。 +5. 仓库是否接入正确项目,Webhook是否有效。 +6. 全部相关MR是否合并到真实集成分支。 +7. 规则是否启用,执行账号是否有权限。 +8. 执行日志是未匹配、失败还是排队。 +9. 用例是否仍有未执行、阻塞或失败。 +10. 缺陷是否仅到已修复,尚未复测关闭。 +11. 目标状态是否触发后继/重复规则。 +12. 是否发生`测试完成 → 发布完成 → 终态`瞬时连锁。 +13. 是否等待合理异步窗口。 +14. 是否误用另一个项目的内部ID或证据。 +15. 缺陷与原用例在复测后的状态是否一致。 +16. 需求状态变化是否遗漏对应阶段任务状态,或任务变化是否遗漏需求汇总。 +17. 自动创建下一阶段任务是否具有需求ID+阶段类型幂等门禁。 +18. 项目是否混用R/TK阶段模型与Y/A终态模型。 +19. OneOS主任务是否唯一且可被规则可靠识别。 +20. 是否缺少`待测试`、`发布中`、`发布失败`或重试流转边。 + +## 6. 回滚 + +- 先禁用本次新增规则,再恢复修改前条件和动作。 +- 保留变更前截图/导出和规则ID。 +- 回调POC通过禁用副本回滚;删除需单独授权。 +- 分支/MR等测试资产按仓库策略清理;删除需单独授权。 +- 不覆盖与本次变更无关的规则。 + +## 7. 运行治理 + +- 每季度复核规则账号、权限、启用状态和失败日志。 +- 新增仓库时同步验证项目集成、Webhook和多仓库门禁。 +- 状态、标签或工作项类型重命名后逐条复核依赖规则。 +- 保护`master/main`和真实`dev/develop`。 +- 监控超过7天无活动的需求分支,未经授权不删除。 +- 分开统计自动流转时间与真实业务开始时间。 +- 持续检查回调签名、时间戳、防重放、幂等和失败重试。 diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-fast-path.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-fast-path.md new file mode 100644 index 0000000..be9a309 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-fast-path.md @@ -0,0 +1,117 @@ +# 统一运营管理平台 · 记录需求快路径 + +真相源运行时 ID:[assets/oneos-pc-runtime-ids.json](../assets/oneos-pc-runtime-ids.json) +验证样例:ONEOS-84「测试需求」(2026-07-21)。 + +**用户可复制完整口令(A/B/C 门禁 + 30 标签):** [oneos-pc-record-requirement-prompt.md](oneos-pc-record-requirement-prompt.md) + +本文件只服务**日常 `记录需求` apply**。全量审计/规则配置仍读 `live-operations.md` 等模块。 + +## 0. 执行门禁(强制) + +用户口令要求 Plan / 单选「优先级」「推进至」「标签」时: + +1. **必须**先拿到明确的 A(紧急/高/中/低)、B(分析中/设计中/设计完成/开发中)、**C(标签,可多选)**。 +2. 标签 **C** 只能从 [assets/oneos-pc-tag-catalog.md](../assets/oneos-pc-tag-catalog.md) / `tags.catalog` 点选;**禁止**按 `lines.ts` 或业务条线说明自动推断。 +3. 来源优先:`AskQuestion` 点选 → Plan 批准时写明的 A+B+C → 用户一句话点明。 +4. **禁止**:未选时默认「中 + 分析中」并继续建单;**禁止**未选标签时自作主张打标。 +5. 「批准/Implement 计划」≠ 已选 A+B+C;计划正文若仍是空 ○,必须先问清再执行。 + +多条需求名称时:每条或整批共用的优先级/推进至/标签仍须用户点选后再写云效。 + +## 1. 加载配方(禁止重探) + +对项目别名含「统一运营管理平台」时: + +1. 读取 `assets/oneos-pc-runtime-ids.json`。 +2. **禁止**再探测 create URL(不要用 `/workitem/create`)。 +3. **禁止**对优先级 ID 做猜测重试超过配方表。 + +## 2. 标准执行顺序(目标 1~3 分钟) + +```text +AutoPRD → API 建需求(含描述) → API 建阶段任务 → API 打标签(COVER) → 状态连跳 → 一次校验回报 +``` + +### 2.1 AutoPRD + +- 先跑 `$oneos-autoprd`(模块/原型来自口令或对话页)。 +- 描述 HTML 最小块:`【原型】` + `【目标】` + `【非目标】` + `【更新内容】`(无变更记录则「首版定稿」)。 + +### 2.2 API 建产品类需求 + +`POST`(失败再试一次 `PUT`) +`https://devops.aliyun.com/projex/api/workitem/workitem?_input_charset=utf-8` + +要点: + +- `workitemTypeIdentifier` **与** `workitemType` 都设为产品类需求 ID。 +- `document: { content: html, formatType: 'RICHTEXT' }` 创建时写入。 +- `fieldValueList` 带 `priority`、`assignedTo`、可选 `79`(计划开始,中国时区中午 epoch 字符串)。 +- 开发中:需求负责人用何斐 ID;分析中/设计中/设计完成:创建人 ID。 + +### 2.3 API 建同名阶段任务 + +同一 create 接口,`category=Task`,`parentIdentifier=需求ID`,标题与需求**完全同名**。 + +| 推进至 | 任务标签目标 | 负责人 | +|---|---|---| +| 分析中 | 分析 | 创建人 | +| 设计中 | 设计 | 创建人 | +| 开发中 / 待开发 | 开发或交付 | 何斐 | + +查重:同需求 + 同阶段标签 + 未取消 → 复用,不建第二条。详见 [auto-stage-task.md](auto-stage-task.md)。 + +### 2.4 状态连跳(开发中) + +从「待处理」到「开发中」UI 固定路径: + +```text +待处理 → 设计完成 → 待开发 → 开发中 +``` + +每跳点开状态按钮选目标;不要在「待处理」下拉里找「开发中」(没有)。 + +其它推进至: + +- 分析中 / 设计中 / 设计完成:待处理下拉通常可直达。 + +### 2.5 标签(选择器点选 + API) + +1. 用户已从 `tags.catalog` 点选标签名(见门禁 C);用 `tags.by_name` 得到 identifier。 +2. **优先 API**(已验证): + +```http +PATCH https://devops.aliyun.com/projex/api/workitem/workitem/{identifier}?_input_charset=utf-8 +Content-Type: application/json + +{ + "workitemIdentifier": "{identifier}", + "propertyKey": "tag", + "propertyValue": "{id1,id2}", + "operateType": "COVER" +} +``` + +3. API 失败 1 次 → UI 标签选择器勾选 → **确定**;仍失败 → **停止并请用户回复标签名**,禁止报「已打上」。 +4. **禁止**对照 `lines.ts` / 业务条线说明自动映射;刷新全量标签:`GET` `api.list_tags`(`spaceType=Space`)。 + +### 2.6 校验与回报(只做一次) + +核对:编号、链接、状态、优先级、负责人、计划开始、描述含原型、任务父项、标签。 +截图最多:建单后、终态后各一次。 + +## 3. 性能红线 + +| 允许 | 禁止 | +|---|---| +| 配方内 ID / URL | 每步 3+ 端点盲探 | +| create 带描述 | 默认先开富文本再粘贴 | +| 状态固定三跳 | React fiber 乱调 onChange(易白屏) | +| 标签失败问用户 | 标签失败仍声称成功 | +| API 失败 1 次改 UI | 同失败 payload 循环重试 | + +## 4. 浏览器会话 + +- 复用已登录 `devops.aliyun.com`;不记录 Cookie/Token。 +- 优先已打开的「统一运营管理平台」项目页;锁 tab 后执行,结束解锁。 diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-record-requirement-prompt.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-record-requirement-prompt.md new file mode 100644 index 0000000..0ee6812 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-pc-record-requirement-prompt.md @@ -0,0 +1,139 @@ +# 统一运营管理平台 · 记录需求到云效(完整提示词) + +复制下方「用户口令」整段发给 Agent;Agent 须先弹出 Plan / 单选卡让你点选 **A 优先级 + B 推进至 + C 标签**,**未点选前不得建单**。 + +--- + +## 用户口令(复制整段) + +```text +使用 $yunxiao-requirement-lifecycle + $oneos-autoprd +记录需求到云效 · 统一运营管理平台 + +【需求名】 +(填写 1 条或多条,例:车辆资产功能优化) + +【描述来源】 +$oneos-autoprd,原型/模块:(填写,例:oneos-h5-vehicle-assets) + +【已锁定】 +- 项目:统一运营管理平台 +- 计划开始:今日 +- 负责人:分析中 / 设计中 / 设计完成 → 创建人;开发中 → 何斐 + +【请你先 Plan 点选,未选完禁止写云效】 + +A. 优先级(单选) +○ 紧急 +○ 高 +○ 中 +○ 低 + +B. 推进至(单选) +○ 分析中(建同名「分析」任务,负责人=创建人) +○ 设计中(建同名「设计」任务,负责人=创建人) +○ 设计完成(只改状态,负责人=创建人;不强制建阶段任务) +○ 开发中(建同名开发/交付任务,负责人=何斐;状态连跳:待处理→设计完成→待开发→开发中) + +C. 标签(可多选;只能从下列 30 个云效标签中选,禁止自造、禁止按业务条线/lines.ts 自动推断) +○ 运维管理条线 +○ 报表中心 +○ 能源管理 +○ 维修站管理 +○ 充电站管理 +○ 还车应结款 +○ 交车应收款 +○ 加氢站管理 +○ 保险管理 +○ 合同管理 +○ 租赁账单 +○ 供应商管理 +○ 客户管理 +○ 还车任务 +○ 安全培训 +○ 备件管理 +○ 停车场管理 +○ 备车管理 +○ 异动管理 +○ 调拨管理 +○ 上牌管理 +○ 替换车管理 +○ 还车管理 +○ 交车管理 +○ 车辆管理 +○ 审批中心 +○ 故障管理 +○ 车辆年审 +○ 维修管理 +○ 工作台 + +【我确认 A+B+C 后你再执行】 +批准 / Implement 计划时,我会写明所选值,例如: +优先级:高;推进至:设计完成;标签:运维管理条线 + +【执行要求(Agent)】 +1. 读快路径:references/oneos-pc-fast-path.md + assets/oneos-pc-runtime-ids.json +2. 先 $oneos-autoprd 生成描述(【原型】【目标】【非目标】【更新内容】;无增量则「首版定稿」) +3. API 建产品类需求(POST /workitem/workitem,创建时写入 document) +4. 计划开始=今日;按 B 推进状态;按规则建/复用同名阶段任务 +5. 标签:用 tags.by_name 查 identifier,PATCH propertyKey=tag operateType=COVER;API 失败 1 次再 UI 兜底 +6. 一次校验回报:编号、链接、状态、优先级、负责人、计划开始、标签、描述、阶段任务 +7. 禁止默认「中+分析中」;禁止未选标签时自作主张打标;标签失败不得报「已成功」 +``` + +--- + +## 单条需求示例(填好即用) + +```text +使用 $yunxiao-requirement-lifecycle + $oneos-autoprd +记录需求到云效 · 统一运营管理平台 + +【需求名】 +测试需求3 + +【描述来源】 +$oneos-autoprd,原型/模块:oneos-h5-vehicle-assets + +【已锁定】 +- 项目:统一运营管理平台 +- 计划开始:今日 +- 负责人:分析中/设计中/设计完成=创建人;开发中=何斐 + +请先 Plan 让我点选 A 优先级、B 推进至、C 标签(30 项 catalog,禁止 lines.ts 推断)。 +我确认后再执行快路径建单。 +``` + +确认回复示例: + +```text +优先级:高;推进至:设计完成;标签:运维管理条线 +``` + +--- + +## Agent 执行清单(内部,勿省略) + +| 步 | 动作 | +|---|---| +| 0 | 门禁:A+B+C 已明确;Implement ≠ 已选 | +| 1 | `$oneos-autoprd` → HTML 描述 | +| 2 | `POST …/workitem/workitem` 建需求 + document | +| 3 | 字段:priority、assignedTo、79=今日 | +| 4 | 若 B∈{分析中,设计中,开发中} → 同名阶段任务(查重复用) | +| 5 | 状态推进至 B(开发中三跳) | +| 6 | `PATCH …/workitem/{id}` 打标签 COVER | +| 7 | 回报 ONEOS-xxx + 链接 + 核对表 | + +标签 API 形态: + +```json +{ + "workitemIdentifier": "{id}", + "propertyKey": "tag", + "propertyValue": "{identifier1},{identifier2}", + "operateType": "COVER" +} +``` + +参考:`assets/oneos-pc-tag-catalog.md`、`assets/oneos-pc-runtime-ids.json` → `tags.by_name` diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-terminal-flow.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-terminal-flow.md new file mode 100644 index 0000000..269f36a --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/oneos-terminal-flow.md @@ -0,0 +1,192 @@ +# OneOS终态交付模型与迁移规则 + +## 目录 + +1. 模型选择与边界 +2. 工作项和状态 +3. 原生Y规则 +4. AI与Webhook桥接 +5. 迁移就绪门禁 +6. 分阶段实施 +7. 验收与负向测试 +8. 已知风险和回滚 + +## 1. 模型选择与边界 + +OneOS终态模型以需求为阶段看板唯一真相: + +```text +迭代 +├─ 多个产品类需求 +├─ 按需求分包的测试计划 +└─ 一条【发版】任务 + +产品类需求 +└─ 一条【交付】主任务 + ├─ 多条【开发】任务 + └─ 一条【测试】任务 +``` + +每个项目只能选择一个主模型: + +- `stage_tasks`:分析、设计、开发、测试各建阶段任务,使用R/TK控制。 +- `oneos_delivery`:一条交付主任务镜像需求,多开发任务汇总,使用Y/A控制。 + +不得同时启用两套进场规则。若项目仍有分析/设计标签驱动的R规则,先记录为`legacy_active`,完成迁移就绪检查后再停用。 + +## 2. 工作项和状态 + +### 2.1 任务身份 + +| 任务 | 数量 | 必须关系 | 识别优先级 | +|---|---:|---|---| +| `【交付】`主任务 | 每需求1条 | 正式关联需求 | 独立工作项类型 > 任务标签`交付` > 标题前缀流程约束 | +| `【开发】`任务 | 每需求1..N条 | 正式关联需求和主任务 | 独立类型 > 标签`开发` > 标题前缀 | +| `【测试】`任务 | 每需求1条 | 正式关联需求和主任务 | 独立类型 > 标签`测试` > 标题前缀 | +| `【发版】`任务 | 每迭代1条 | 正式关联迭代和本批需求 | 独立类型 > 标签`发版` > 标题前缀 | + +标题关键字本身不构成正式关系。规则编辑器不能读取关联任务的类型、标签或标题时,不得声称原生规则能区分主任务和开发任务;使用桥接或人工门禁。 + +### 2.2 需求状态 + +目标状态顺序: + +```text +待处理 → 已确认 → 分析中 → 分析完成 → 设计中 → 设计完成 +→ 待开发 → 开发中 → 开发完成 → 待测试 → 测试中 → 测试完成 +→ 发布中 → 发布完成 / 发布失败 → 已关闭 +``` + +允许的快轨至少包括`已确认 → 待开发`和`设计完成 → 待开发`。`发布失败 → 发布中`用于重试。已有近义状态先映射,禁止静默创建`完成发布/发布完成`、`已完成/已关闭`等重复状态。 + +### 2.3 任务状态 + +- 主任务:首选与需求同名镜像状态。 +- 开发任务:`待处理 → 处理中 → 已完成`,另有取消态。 +- 测试任务:`待处理/待测试 → 测试中 → 已完成`。 +- 发版任务:`待处理 → 发布中 → 发布完成/发布失败`。 + +若任务工作流不能承载主任务全量状态,不配置Y02–Y16原生镜像;由桥接更新需求并记录主任务显示口径。 + +## 3. 原生Y规则 + +统一规则前缀:`[OneOS终态] Yxx ...`。保存后重新打开核对事件、条件、动作、顺序、启用状态和执行账号。 + +### 3.1 主任务镜像 + +| ID | 触发 | 条件 | 动作 | 默认方式 | +|---|---|---|---|---| +| Y01 | 新增正式关联主任务 | 主任务可可靠识别;需求为待处理/已确认 | 需求与主任务进入分析中或获批快轨 | 原生/桥接 | +| Y02 | 主任务到分析完成 | 正式关联唯一需求 | 需求到分析完成 | 原生 | +| Y03 | 主任务到设计中 | 正式关联唯一需求 | 需求到设计中 | 原生 | +| Y04 | 主任务到设计完成 | 正式关联唯一需求 | 需求到设计完成 | 原生 | +| Y05 | 主任务到待开发 | 正式关联唯一需求 | 需求到待开发;负责人按项目配置 | 原生/桥接 | +| Y06 | 主任务到开发中 | 正式关联唯一需求 | 需求到开发中 | 原生 | +| Y07 | 主任务到开发完成 | 正式关联唯一需求 | 需求到开发完成 | 原生 | +| Y08 | 主任务到待测试 | 正式关联唯一需求 | 需求到待测试 | 原生 | +| Y09 | 主任务到测试中 | 正式关联唯一需求 | 需求到测试中 | 原生 | +| Y10 | 主任务到测试完成 | Testhub门禁通过 | 需求到测试完成 | 桥接优先 | +| Y11 | 主任务到发布中 | 发版任务和迭代范围有效 | 需求到发布中 | 原生/桥接 | +| Y12 | 主任务到发布完成 | 生产发布证据有效 | 需求到发布完成 | 桥接 | +| Y13 | 主任务到发布失败 | 生产发布失败证据有效 | 需求到发布失败并记录原因 | 桥接 | +| Y14 | 主任务到已关闭 | 产品验收证据有效 | 需求到已关闭 | 默认人工 | +| Y15 | 需求状态变化 | 存在唯一主任务且无循环风险 | 主任务同名镜像 | 可选原生 | +| Y16 | 需求快轨到待开发 | 存在唯一主任务 | 主任务到待开发 | 原生/桥接 | + +每条双向镜像规则必须有防循环条件。平台无法区分“由本规则写入”的事件时,只保留一个权威方向。 + +### 3.2 开发任务汇总 + +| ID | 触发 | 条件 | 动作 | 默认方式 | +|---|---|---|---|---| +| Y20 | 新增第一条开发任务或正式关联开发分支 | 需求=待开发;开发任务数量≥1;任务已指派 | 需求和主任务到开发中;对应开发任务处理中 | 原生/桥接 | +| Y21 | 开发任务完成 | 非取消开发任务数量≥1且全部完成;全部相关MR已合并 | 需求和主任务到开发完成 | 桥接优先 | +| Y22 | 开发完成 | 同需求无未取消测试任务;幂等键未处理 | 创建并关联唯一测试任务,进入待测试 | 桥接 | + +现有的“任务添加分支且标签=开发,待处理→处理中”和“全部MR已合并且标签=开发,处理中→已完成”可作为Y20/Y21的任务侧子规则保留。需求侧“全部MR已合并”只能看到正式关联资产,必须执行多仓库负向测试。 + +### 3.3 测试、发布和迭代 + +| ID | 触发 | 条件 | 动作 | 默认方式 | +|---|---|---|---|---| +| Y30 | 创建测试任务 | 正式关联需求和主任务 | 需求/主任务到待测试 | 桥接 | +| Y31 | 首条用例执行或测试负责人开工 | 测试任务=待处理/待测试 | 测试任务、需求、主任务到测试中 | 原生/人工 | +| Y32 | 测试验收完成 | 本需求用例包全部通过且缺陷闭环 | 三个对象完成测试阶段 | 桥接/人工 | +| Y33 | 发版任务提交 | 发版任务正式关联迭代与本批需求 | 发版任务和本批需求到发布中 | 原生/桥接 | +| Y34 | 生产发布成功 | 流水线执行、环境、范围、签名和幂等均有效 | 到发布完成 | 桥接 | +| Y35 | 生产发布失败 | 同上;失败证据有效 | 到发布失败并记录原因 | 桥接 | +| Y40 | 需求到待开发 | 未关联迭代 | 通知产品负责人,不自动写入未知迭代 | 通知/人工 | + +test部署只能允许测试开始,不能触发Y34。发布完成后必须停留,等待产品/业务验收;禁止自动进入已关闭。 + +## 4. AI与Webhook桥接 + +| ID | 能力 | 最小门禁 | +|---|---|---| +| A01 | 发布原型/附件并回写需求描述 | 外部存储授权;链接可审计 | +| A02 | 生成并保留更新内容历史 | 不覆盖历史段;**OneOS 默认调用 `$oneos-autoprd` 第 10 章「功能变更记录」(自上次定稿以来);旧段挪到「更新内容·历史」** | +| A02B | 生成产品可读需求说明 | 不写实现机密;保留来源;**OneOS 默认调用 `$oneos-autoprd` 全文/交付口径写入「需求说明」** | +| A03 | 建需求和唯一主任务并快轨待开发 | 幂等查询;正式关系可见;**建单写描述前先完成 A02+A02B(AutoPRD)**;进入待开发时按 [auto-stage-task.md](auto-stage-task.md) 建/复用与需求同名任务,负责人何斐,时间沿用需求 | +| A03B | 需求进入分析中/设计中自动建同名阶段任务 | 标签分析/设计;负责人=需求创建人;正式关联;时间沿用需求;与 A03 同一套查重 | +| A04 | 拆分多条开发任务 | 负责人、范围和父子关系明确 | +| A05 | 按MR证据关闭开发任务 | 所有仓库、目标分支和关联关系完整 | +| A06 | 全部开发完成后建唯一测试任务 | 幂等键=`项目ID+需求ID+create_test_task` | +| A07 | 按Testhub结果完成测试 | 用例与缺陷口径满足TC/DF控制 | +| A08 | 按迭代创建发版任务和更新说明 | 正式关联迭代和本批需求 | +| A09 | 发布成败回写 | 验证环境、签名、时间戳、执行ID和范围 | +| A10 | 产品验收后关闭 | 明确验收人和证据;不得由发布成功替代 | + +所有桥接必须具备:最小权限服务账号、签名、时间戳、防重放、幂等、失败重试、审计日志和人工回退。不得把未部署的AI能力写成已自动化。 + +## 5. 迁移就绪门禁 + +只有全部满足时才停用旧R/TK进场规则: + +1. 需求状态已包含`待测试`、`发布中`、`发布失败`,并配置必要流转边。 +2. 主任务能被可靠识别;若只能靠标题前缀,已明确由桥接处理,不使用无过滤原生镜像。 +3. 主任务和开发/测试/发版任务的工作流已选定并验证。 +4. Y/A控制逐项指定`native_rule`、`integration_bridge`、`manual`或`not_supported`。 +5. A03、A06、A08、A09具备幂等键、负责人和回退方案。 +6. Codeup前后端仓库、真实`dev/develop`、正式关联和多仓库MR门禁已验证。 +7. Testhub按需求分包;未执行、阻塞、失败用例和仅“已修复”缺陷会阻塞测试完成。 +8. test、生产发布、发布变更和产品验收证据已分离。 +9. 已保存旧规则名称、条件、顺序、启用状态和执行账号证据。 +10. `L01`、`L02`已停用,发布完成不会瞬时关闭。 + +未通过门禁时:保留当前可用规则,只停用会绕过验收或造成误触发的规则,并输出迁移计划。 + +## 6. 分阶段实施 + +- P0:补齐工作流、唯一主任务、快轨待开发、负责人和旧规则备份。 +- P1:开发任务拆分、分支/MR双关联、多仓库完成门禁、唯一测试任务。 +- P2:迭代、Testhub按需求分包、用例和缺陷复测闭环。 +- P3:发版任务、生产流水线成败回写、发布失败重试、产品验收关闭。 + +每阶段先在隔离测试需求验证,再启用下一阶段。新规则不补触发历史事件;历史数据修正必须单独记录。 + +## 7. 验收与负向测试 + +| ID | 操作 | 期望 | 负向检查 | +|---|---|---|---| +| V01 | 创建需求和唯一主任务 | 正式关联且不重复 | 重放不得建第二条主任务 | +| V02 | 未建开发任务 | 保持待开发 | 主任务本身不得被当作开发任务 | +| V03 | 建两条开发任务 | 进入开发中 | 无正式关系或无负责人不触发 | +| V04 | 只完成一条开发任务/MR | 保持开发中 | 不得提前完成 | +| V05 | 所有开发任务和MR完成 | 开发完成并只建一条测试任务 | 重复回调不重复创建 | +| V06 | 同迭代其他需求未测完 | 本需求仍可独立完成测试 | 不按整个计划全绿判断 | +| V07 | 用例失败且缺陷仅已修复 | 保持测试中 | 未复测不得完成 | +| V08 | 复测通过/失败 | 用例通过+缺陷关闭 / 用例失败+缺陷重开 | 双方状态必须一致 | +| V09 | test部署成功 | 允许测试开始 | 不得发布完成 | +| V10 | 生产发布成功 | 发布完成并停留 | 不得自动已关闭 | +| V11 | 生产发布失败后重试 | 发布失败→发布中 | 原因和执行ID可审计 | +| V12 | 产品验收 | 已关闭 | 无验收证据不关闭 | + +## 8. 已知风险和回滚 + +- “全部关联任务完成”若无法按任务身份过滤,会把主任务或测试任务纳入,默认交桥接。 +- 仅标题前缀不能替代正式关系,也不能保证原生规则可过滤。 +- 需求和主任务双向镜像可能循环,只保留一个权威方向或增加来源防重。 +- 分支/MR只关联需求而未关联开发任务,会导致任务状态不更新。 +- 发布完成自动关闭会绕过产品验收;`L01`、`L02`默认停用。 + +回滚顺序:先禁用本次新增Y/A入口,恢复旧规则启用状态和顺序,再恢复状态流转边;保留已创建任务、关系和执行日志,删除任何资产需单独授权。 diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/pipeline-release.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/pipeline-release.md new file mode 100644 index 0000000..4290aca --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/pipeline-release.md @@ -0,0 +1,66 @@ +# 流水线、发布与最终验收 + +## 目录 + +1. 环境证据边界 +2. R11发布完成 +3. CI02回调POC +4. CI03证据分离 +5. R12最终验收 +6. L01–L02历史规则 +7. 连锁触发验证 + +## 1. 环境证据边界 + +分别记录以下事件,未经批准不得互相替代: + +- test环境部署成功。 +- 生产环境流水线发布成功。 +- 云效关联发布变更达到完成状态。 +- 产品/业务验收完成。 + +流水线成功不等于测试用例通过,test成功也不默认等于生产发布完成。 + +## 2. R11发布完成 + +优先使用“关联发布变更达到项目真实完成状态”作为R11证据。规则要求当前状态=`测试完成`,目标环境和发布证据满足组织口径。 + +如果组织明确把test部署定义为“完成发布”,必须记录该口径的批准证据;否则test回调只记录测试部署成功,不推进发布完成。 + +## 3. CI02回调POC + +1. 复制现有test流水线并使用明显POC名称。 +2. 保留原test与prod流水线不变。 +3. 只在副本Docker部署成功路径末尾添加回调。 +4. 普通变量传工作项ID、环境、流水线执行ID。 +5. 密钥变量保存认证材料,不写入仓库、命令输出或文档。 +6. 请求携带时间戳、签名和执行ID幂等键。 +7. 服务端校验需求当前状态,只允许预期流转执行一次。 +8. 测试成功、重复回调、错误签名、超时、部署失败和安全重试。 +9. POC通过后走推广评审,不直接修改prod。 + +## 4. CI03证据分离 + +CI03要求项目配置分别保存test、prod、发布变更和验收证据。任何桥接都必须记录实现方式、启用状态、负责人、证据和人工回退。 + +回调失败保留失败日志并可安全重试,不得伪造成功。同一执行ID重试不得多次推进需求。 + +## 5. R12最终验收 + +发布成功不等于业务验收。默认由产品/业务负责人确认后人工进入`已完成`或`已关闭`。只有可靠、可审计的验收任务才允许条件化自动关闭。 + +## 6. L01–L02历史规则 + +- `L01`:`发布完成 + 全部关联任务完成/关闭 → 项目终态`。默认停用或收紧为独立验收任务,避免早期阶段任务误满足。 +- `L02`:监听`测试完成 → 发布完成`状态路径并立即进入项目终态。默认停用,防止发布完成成为瞬时状态。 + +逐项目记录两条规则是否存在、是否启用、真实条件、顺序和证据;不存在也要有盘点证据。 + +## 7. 连锁触发验证 + +- 不按规则名称推断行为,逐条读取触发、条件、动作、顺序和执行账号。 +- 每个目标状态都检查后继规则和重复规则。 +- R11后至少观察两个时点:刚进入发布完成时,以及等待5–30秒后。 +- 未经验收自动进入终态时,记录实际后继规则并判定`误触发`。 +- 跨两级以上流转必须有独立负向测试,不能只验证最终状态。 + diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/report-template.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/report-template.md new file mode 100644 index 0000000..3bc1210 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/report-template.md @@ -0,0 +1,97 @@ +# 云效自动化执行报告模板 + +```markdown +# 云效产品需求全生命周期自动化执行报告 + +## 1. 基本信息 + +- 组织: +- 项目: +- 工作项类型: +- 流程模型:stage_tasks / oneos_delivery +- 迁移阶段(仅oneos_delivery):P0 / P1 / P2 / P3 +- 执行模式:audit / plan / apply / test / document +- 执行时间: +- 执行账号:仅记录显示名,不记录凭据 + +## 2. 观察到的当前配置 + +| 项目 | 生命周期 | 标签 | 仓库/集成分支 | test流水线 | 发布证据 | +|---|---|---|---|---|---| + +## 3. 规则变更 + +| 项目 | 规则 | 变更前 | 变更后 | 启用状态 | 复开核验 | +|---|---|---|---|---|---| + +### 3.0 项目级规则实例 + +| 项目 | 规则ID | 云效规则显示名/人工门禁 | 精确触发事件 | 源状态 | 必要条件 | 动作/目标状态 | 顺序 | 启用 | 观察证据 | +|---|---|---|---|---|---|---|---:|---|---| + +每个项目必须分别覆盖`R01`–`R12`和`TK01`–`TK08`。不得把一个项目的规则ID、显示名、顺序或证据复制为另一个项目的观察结果。 + +### 3.1 完整性台账 + +| 控制ID | 对象/流转 | 实现方式 | 启用状态 | 观察证据 | 负责人 | 回退方案 | 结论 | +|---|---|---|---|---|---|---|---| + +每个项目必须分别覆盖全部31项:`R01`–`R12`、`TK01`–`TK08`、`TC01`–`TC03`、`DF01`–`DF03`、`CI01`–`CI03`、`L01`–`L02`。不得把“规则编辑器不支持”写成“已完成”;应记录为`not_supported`并给出人工门禁或集成桥接方案。 + +`oneos_delivery`模型改用48项台账:`Y01`–`Y16`、`Y20`–`Y22`、`Y30`–`Y35`、`Y40`、`A01`、`A02`、`A02B`、`A03`–`A10`、`TC01`–`TC03`、`DF01`–`DF03`、`CI01`–`CI03`、`L01`–`L02`。 + +### 3.1a OneOS迁移就绪度 + +| 项目 | 目标状态缺口 | 主任务身份方式 | 原生规则能否过滤 | 任务工作流方案 | 桥接幂等/负责人 | 旧规则处置 | 结论 | +|---|---|---|---|---|---|---|---| + +旧R/TK规则只有在目标状态、任务身份、任务工作流、桥接和回滚证据全部就绪后才能停用。`L01`、`L02`不受此等待条件影响,应保持停用以保护产品验收。 + +### 3.2 规则顺序与连锁触发 + +| 前置规则 | 目标状态 | 后继规则 | 是否自动二次流转 | 是否绕过验收 | 处理结论 | +|---|---|---|---|---|---| + +## 4. 验证结果 + +| 用例 | 触发事件 | 期望状态 | 实际状态 | 结果分类 | 执行日志/证据 | +|---|---|---|---|---|---| + +## 5. 研发资产覆盖 + +| 仓库 | dev/develop | 分支关联 | 提交关联 | MR关联 | 全部合并判断 | +|---|---|---|---|---|---| + +## 6. 流水线与发布 + +| 流水线 | 环境 | 是否副本 | Docker部署 | 回调 | 是否影响prod | +|---|---|---|---|---|---| + +## 7. 测试计划与缺陷闭环 + +| 测试计划 | 用例总数 | 已通过 | 未执行/待测试 | 阻塞 | 未通过 | 结论 | +|---|---:|---:|---:|---:|---:|---| + +| 缺陷 | 关联用例/需求 | 修复状态 | 复测结果 | 最终状态 | 证据 | +|---|---|---|---|---|---| + +| 失败用例 | 关联缺陷 | 缺陷已修复后用例状态 | 复测通过后用例状态 | 复测失败后用例状态 | 证据 | +|---|---|---|---|---|---| + +## 8. 人工节点与剩余风险 + +- 真实开发开始时间: +- 业务验收: +- 发布完成是否被后继规则立即关闭: +- 全部关联任务范围: +- 多仓库漏关联风险: +- 其他: +- 范围外通用自动化(通知、指派、SLA、字段同步等): + +## 9. 回滚 + +- 禁用新增规则: +- 恢复旧规则: +- 停用回调POC: +- 测试资产清理(需授权): +``` diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/role-trigger-prompts.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/role-trigger-prompts.md new file mode 100644 index 0000000..71815ff --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/role-trigger-prompts.md @@ -0,0 +1,499 @@ +# 云效全流程角色与触发节点沟通话术 + +> 本文是异常处理和精细交接使用的详细版。日常操作优先读取`simple-role-commands.md`,使用“记录需求、开始分析、开始开发、提交测试、测试完成、发布成功、验收通过”等短口令。 + +## 目录 + +1. 使用规则 +2. 通用话术骨架 +3. 项目管理员与流程负责人 +4. 产品负责人 +5. 需求分析人员 +6. 设计人员 +7. 交付负责人 +8. 技术负责人 +9. 开发人员 +10. 代码评审人员 +11. 测试负责人和测试人员 +12. 缺陷修复人员 +13. 运维与发布负责人 +14. 业务验收人员 +15. 集成桥接负责人 +16. 异常和催办话术 +17. 节点交接清单 + +## 1. 使用规则 + +这套话术用于让不同角色把真实业务事件准确告诉Codex,再由Codex审计、规划、执行获批修改、验证或形成报告。默认使用`yunxiao-requirement-lifecycle`。 + +每次沟通至少提供: + +- 精确项目名和需求编号。 +- 当前角色、当前状态和刚刚发生的真实事件。 +- 关联任务、仓库、分支、MR、测试计划、缺陷或流水线执行ID;没有就明确写“无”。 +- 希望Codex采用的权限:`audit`、`plan`、`apply`、`test`或`document`。 +- 期望停在哪个状态,不要只说“继续流转”。 + +权限含义: + +- `audit`:只核查和报告,不修改云效。 +- `plan`:只输出修改方案和影响,不执行。 +- `apply`:只修改话术中明确列出的项目、工作项或规则。 +- `test`:使用隔离测试资产触发并收集证据。 +- `document`:只形成说明、记录或报告。 + +必须遵守: + +- 标题、需求编号、分支名或标签不能代替云效正式关联。 +- 首次提交不代表真实开始开发;人工“开始处理”是效能主口径,分支关联只是自动兜底。 +- `已修复`只表示交给测试复测,不是缺陷关闭。 +- test部署成功不等于测试通过,更不等于生产发布完成。 +- 发布成功后停留`发布完成`,必须有产品或业务验收才能进入`已关闭`。 +- 未明确授权时,不改生产流水线、不直接推送保护分支、不删除规则或测试资产。 +- 项目仍为`stage_tasks`时使用R/TK规则;只有迁移门禁通过后才使用完整`oneos_delivery`链路。 + +## 2. 通用话术骨架 + +### 2.1 请求执行 + +```text +使用 yunxiao-requirement-lifecycle。 +权限:【audit/plan/apply/test/document】 +项目:【精确项目名】 +流程模型:【未知/stage_tasks/oneos_delivery】 +我的角色:【角色】 +需求:【需求编号+标题】 +当前状态:【状态】 +刚发生的事件:【真实事件】 +相关资产:【任务/MR/分支/测试计划/缺陷/流水线执行ID;没有写无】 +期望结果:【目标状态或需要生成的资产】 +请先核查当前状态和正式关联,再执行授权范围内动作;输出实际动作、触发结果、证据、未完成项和下一责任人。不要用手工改状态冒充自动化通过。 +``` + +### 2.2 只核查为什么没有流转 + +```text +使用 yunxiao-requirement-lifecycle,权限=audit。 +项目:【项目名】,需求:【编号】,当前状态:【状态】。 +我已执行:【事件】,相关对象:【对象编号或链接】;等待了【秒数】仍未流转。 +请按事件、当前状态、标签位置、正式关联、规则启用、执行账号、执行日志、后继规则和异步窗口逐项诊断。不要直接替我改状态。 +``` + +### 2.3 请求修改规则 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +只允许修改项目【项目名】中的规则【精确规则名/控制ID】,目标是【目标】。 +修改前备份触发、条件、动作、顺序、启用状态和执行账号;一次只改一条,保存后重新打开核对,并用隔离需求做正向和负向测试。禁止修改生产流水线及无关规则。 +``` + +## 3. 项目管理员与流程负责人 + +### ADM01 项目首次审计或接管 + +触发:新项目接入、规则无人维护或准备复制到另一个项目。 + +```text +使用 yunxiao-requirement-lifecycle,权限=audit。 +请审计项目【项目名】,先判断是stage_tasks、oneos_delivery还是迁移中。盘点工作流状态、标签、工作项类型、全部自动化规则、执行账号、前后端仓库、dev/develop、Testhub、缺陷状态、test/prod流水线、发布证据和验收门禁。 +输出当前—目标差距、规则控制ID映射、风险、P0-P3实施顺序和不能自动化的人工门禁。不要修改线上配置。 +``` + +完成证据:项目画像、规则清单、模型判断、缺口和迁移阶段齐全。 + +### ADM02 迁移到OneOS终态模型 + +触发:准备从阶段任务切换到交付主任务模型。 + +```text +使用 yunxiao-requirement-lifecycle,权限=plan。 +项目【项目名】准备迁移到oneos_delivery。请检查待测试、发布中、发布失败状态,交付主任务识别方式,开发/测试/发版任务工作流,A03/A06/A08/A09幂等与负责人,多仓库MR门禁、Testhub分包、生产证据和L01/L02状态。 +只有十项迁移门禁全部通过才给出停用旧R/TK规则清单;否则保留现网主链,只列安全可做项和回滚方案。 +``` + +完成证据:迁移门禁逐项结论,而不是笼统“可以迁移”。 + +### ADM03 规则变更后回归 + +触发:新建、修改、启停任何生命周期规则。 + +```text +使用 yunxiao-requirement-lifecycle,权限=test。 +项目【项目名】刚修改规则【规则名/控制ID】。请创建或复用隔离测试需求,验证事件【事件】、源状态【状态】、目标状态【状态】,并增加错误状态、无正式关联、部分MR、重复回调或后继连锁等负向场景。 +结果只能记录为通过、异步通过、未触发、误触发或阻塞,并附执行日志和回滚入口。 +``` + +### ADM04 定期治理 + +触发:季度检查、状态/标签重命名、新增仓库或人员离职。 + +```text +使用 yunxiao-requirement-lifecycle,权限=audit。 +请对项目【项目名】执行运行治理检查:规则执行账号和权限、失败日志、状态/标签重命名影响、新增仓库Webhook、多仓库MR覆盖、超过7天无活动分支、回调签名/幂等/重试、L01/L02以及生产和验收证据分离。 +输出需要立即修复、计划修复和仅观察三类清单。 +``` + +## 4. 产品负责人 + +### PO01 新建并确认需求 + +触发:需求范围、负责人、优先级和验收标准已明确。 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】需要建立需求【标题】。范围:【范围】;不包含:【排除项】;负责人:【姓名】;优先级:【级别】;目标迭代:【迭代】;验收标准:【标准】。 +请先查重,再创建或完善产品类需求。若项目为oneos_delivery,校验唯一【交付】主任务;若仍为stage_tasks,不要擅自创建主任务。完成后告诉我需求编号、正式关系、当前状态和下一责任人。 +``` + +### PO02 批准进入分析或快轨待开发 + +触发:需求确认后决定正常分析,或低风险小改走快轨。 + +正常路径: + +```text +项目【项目名】需求【编号】已确认,批准进入分析。使用 yunxiao-requirement-lifecycle,权限=apply。请核查范围、负责人和验收标准齐全,再按当前流程模型触发分析入口;不要跳过缺失信息。 +``` + +快轨路径: + +```text +项目【项目名】需求【编号】批准快轨到待开发,批准人【姓名】,原因【原因】,验收标准【标准】。使用 yunxiao-requirement-lifecycle,权限=apply。请确认快轨边存在、唯一交付主任务已同步且没有循环风险;缺少任一条件就停止并报告。 +``` + +### PO03 需求范围变更 + +触发:开发或测试中修改范围。 + +```text +使用 yunxiao-requirement-lifecycle,权限=plan。 +项目【项目名】需求【编号】当前【状态】,拟变更:【新增/删除内容】;原因:【原因】;受影响资产:【开发任务/MR/用例/缺陷/发布范围】。 +请先做影响分析,列出需要重开或新增的任务、MR、用例和验收项,以及是否应回退需求状态。未经我确认不要直接改状态或删除已有证据。 +``` + +### PO04 产品验收通过并关闭 + +触发:生产发布完成,业务验收通过。 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】需求【编号】当前为发布完成。验收人【姓名】,验收时间【时间】,验收环境【生产】,验收结果【通过】,证据【链接/附件/记录】。 +请核查生产发布证据与需求范围一致,并确认不存在未关闭验收项;满足后执行已关闭并回报审计证据。不得用流水线成功替代本次验收。 +``` + +### PO05 验收不通过 + +```text +使用 yunxiao-requirement-lifecycle,权限=plan。 +项目【项目名】需求【编号】生产验收不通过。问题:【问题】;证据:【证据】;影响:【影响】。 +请创建或关联可追踪缺陷/改进任务,建议应回到的真实状态和责任人;未经批准不要直接关闭需求或覆盖原发布记录。 +``` + +## 5. 需求分析人员 + +### BA01 开始分析 + +```text +项目【项目名】需求【编号】由我开始分析,实际开始时间【时间】。使用 yunxiao-requirement-lifecycle,权限=apply。请核查当前状态和我的关联任务,将分析任务改为处理中并确认需求进入分析中;如果项目为oneos_delivery,则按主任务权威方向同步,避免双向循环。 +``` + +### BA02 分析完成 + +```text +项目【项目名】需求【编号】分析已完成。产物:【需求说明/流程/字段/边界链接】;未决项:【无或列表】;验收口径:【口径】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请先检查分析任务非零且全部完成、产物可访问、未决项已处理,再推进分析完成;回报触发的是原生规则、桥接还是人工门禁。 +``` + +## 6. 设计人员 + +### UX01 开始设计 + +```text +项目【项目名】需求【编号】开始设计,设计负责人【姓名】,原型地址【地址】,实际开始时间【时间】。使用 yunxiao-requirement-lifecycle,权限=apply。请核查正式关联设计任务和当前状态,再更新设计任务为处理中并确认需求进入设计中。 +``` + +### UX02 设计完成并交接开发 + +```text +项目【项目名】需求【编号】设计完成。原型版本【版本】;设计稿【链接】;交互说明【链接】;响应式范围【范围】;已确认人【姓名】;遗留项【无或列表】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请核查设计任务和交付物后完成设计阶段,并检查是否只创建一组正确的开发任务。不要因为标题含“开发”就视为已正式关联。 +``` + +完成证据:设计任务已完成、需求到设计完成;后继开发任务创建成功后才到待开发。 + +## 7. 交付负责人 + +### DL01 创建或核查唯一交付主任务 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】需求【编号】需要建立【交付】主任务,负责人【姓名】。请先按正式关系和交付标签查重;不存在才创建,存在一条则复用,存在多条则停止并列出冲突。主任务必须正式关联需求,不能只靠标题前缀。 +``` + +### DL02 阶段同步异常 + +```text +使用 yunxiao-requirement-lifecycle,权限=audit。 +项目【项目名】需求【编号】状态【需求状态】,交付主任务【任务编号】状态【任务状态】,两者不一致。 +请判断权威方向、检查Y01-Y16、来源防重和执行日志,说明应补偿哪个对象。不要同时双向写入造成循环。 +``` + +## 8. 技术负责人 + +### TL01 拆分开发任务 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】需求【编号】已到待开发。请按以下范围拆分开发任务:前端【范围/负责人/仓库】,后端【范围/负责人/仓库】,其他【范围】。每条任务必须正式关联需求和交付主任务、标签=开发、状态=待处理,并记录目标dev/develop分支。查重后创建,禁止重复任务。 +``` + +### TL02 仅前端或仅后端需求 + +```text +项目【项目名】需求【编号】只涉及【前端/后端】,不涉及【另一端】。使用 yunxiao-requirement-lifecycle,权限=apply。请只创建实际需要的开发任务,并把不涉及的仓库明确记为不适用;不要用缺少另一端MR阻塞,也不要把未盘点仓库默认为已完成。 +``` + +### TL03 开发完成门禁核查 + +```text +使用 yunxiao-requirement-lifecycle,权限=audit。 +项目【项目名】需求【编号】准备判定开发完成。相关仓库:【列表】;开发任务:【列表】;MR:【列表及目标分支】。 +请确认所有非取消开发任务完成、所有相关MR正式双关联并合并到真实dev/develop;任一仓库未完成都保持开发中。输出缺口,不要手工改成开发完成。 +``` + +## 9. 开发人员 + +### DEV01 真实开始开发并创建分支 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +我开始处理项目【项目名】需求【编号】的开发任务【任务编号】,真实开始时间【时间】,仓库【仓库名】,基线【dev/develop】,拟建分支【feature/编号或fix/编号】。 +请先确认基线真实存在且任务为待处理,再从基线创建或指导创建分支,并确保分支同时正式关联需求和开发任务。完成后核查需求=开发中、任务=处理中;不要用首次提交时间覆盖真实开始时间。 +``` + +### DEV02 提交代码并创建MR + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】需求【编号】,开发任务【任务编号】,仓库【仓库】,分支【分支】,目标分支【dev/develop】。改动摘要:【摘要】;本地验证:【命令和结果】。 +请先拉取并检查冲突,再按仓库规范提交、推送并创建MR;MR必须正式关联需求和开发任务。不要直接推保护分支,不要因为提交成功就把开发标为完成。 +``` + +### DEV03 多仓库部分完成 + +```text +项目【项目名】需求【编号】目前仅仓库【仓库A】的MR【编号】已合并,仓库【仓库B】仍在开发。使用 yunxiao-requirement-lifecycle,权限=audit。请确认需求和交付主任务仍保持开发中,并检查是否存在“部分MR导致提前完成”的误触发规则。 +``` + +## 10. 代码评审人员 + +### REV01 评审要求修改 + +```text +项目【项目名】MR【编号】评审不通过,需要修改:【问题列表】。使用 yunxiao-requirement-lifecycle,权限=document。请把评审结论关联到需求【编号】和开发任务【编号】,确认MR保持未合并、开发任务保持处理中、需求保持开发中。 +``` + +### REV02 MR合并后核查 + +```text +使用 yunxiao-requirement-lifecycle,权限=audit。 +项目【项目名】MR【编号】已合并到【dev/develop】,关联需求【编号】、开发任务【编号】。请等待异步窗口后核查任务状态;再汇总同需求所有仓库和MR。只有全部相关任务和MR完成才允许需求进入开发完成,并确认测试任务只创建一条。 +``` + +## 11. 测试负责人和测试人员 + +### QA01 建立测试任务和用例范围 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】需求【编号】开发已完成,目标迭代【迭代】,测试负责人【姓名】。请查重后创建或复用唯一【测试】任务,并将测试计划按需求编号建立独立用例包。范围:【功能/接口/权限/兼容/回归】;不适用:【列表】。正式关联需求、主任务和测试计划后进入待测试。 +``` + +### QA02 test部署成功,开始测试 + +```text +项目【项目名】需求【编号】的test部署成功,流水线【名称】,执行ID【ID】,环境【test】,部署时间【时间】。使用 yunxiao-requirement-lifecycle,权限=apply。请校验执行ID、范围和重复回调,再允许测试任务和需求进入测试中。明确记录:本事件不能推进发布完成。 +``` + +### QA03 用例失败并提交缺陷 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】需求【编号】,测试计划【计划】,用例【用例ID】执行失败。环境【环境】;前置条件【条件】;步骤【步骤】;实际结果【实际】;期望结果【期望】;证据【截图/日志】;严重程度【级别】。 +请查重后创建或关联缺陷,并把缺陷与原用例、需求正式关联。保持用例未通过、测试任务和需求测试中;不要把创建缺陷当作测试完成。 +``` + +### QA04 缺陷复测通过 + +```text +项目【项目名】缺陷【编号】已在环境【环境】复测,原用例【用例ID】重新执行通过,证据【证据】,复测人【姓名】,时间【时间】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请将原用例更新为通过,并把缺陷进入项目真实关闭态;随后重新核查本需求是否仍有未执行、阻塞、失败用例或未关闭缺陷。 +``` + +### QA05 缺陷复测失败 + +```text +项目【项目名】缺陷【编号】复测失败,原用例【用例ID】仍未通过。实际结果【结果】;证据【证据】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请重开缺陷或转回项目真实处理中状态,保持原用例未通过、需求测试中,并通知原修复负责人。不要保留“已修复/已关闭”。 +``` + +### QA06 测试验收完成 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】需求【编号】申请完成测试。测试计划【计划】;用例包【范围】;执行统计【通过/失败/阻塞/未执行】;缺陷清单【编号+状态】;测试负责人【姓名】。 +请按TC01-TC03和DF01-DF03核查。只有全部范围用例已执行并通过、失败用例已复测、缺陷由测试关闭时,才完成测试任务并推进需求测试完成;否则列出阻塞项。 +``` + +## 12. 缺陷修复人员 + +### BUG01 接收缺陷并开始修复 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +我接收项目【项目名】缺陷【编号】,关联需求【编号】、原用例【ID】,开始修复时间【时间】,仓库【仓库】,拟用分支【fix/缺陷编号】。 +请核查缺陷、需求和用例的正式关联,再把缺陷转入真实处理中状态;分支和MR同时关联缺陷、需求及对应开发任务。 +``` + +### BUG02 修复完成交给测试 + +```text +项目【项目名】缺陷【编号】已修复。分支【分支】;MR【编号】;合并目标【dev/develop】;验证结果【结果】;部署环境【test/尚未部署】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请核查代码和部署证据后把缺陷改为已修复并交给测试复测。不要关闭缺陷,不要把原失败用例改为通过。 +``` + +## 13. 运维与发布负责人 + +### OPS01 test部署回报 + +```text +使用 yunxiao-requirement-lifecycle,权限=audit。 +项目【项目名】test流水线【名称】执行【成功/失败】,执行ID【ID】,提交/MR范围【范围】,时间【时间】。 +请核查本次执行关联的需求和幂等记录。成功只允许测试开始;失败保持原状态并记录原因。不要触发生产发布完成。 +``` + +### OPS02 创建迭代发版任务 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】迭代【迭代名/ID】准备发版,计划窗口【时间】,负责人【姓名】,本批需求【编号列表】,生产流水线【名称】。 +请查重后创建或复用唯一【发版】任务,正式关联迭代和本批需求,并生成发布范围与回滚项。需求未测试完成或范围不一致时停止,不进入发布中。 +``` + +### OPS03 开始生产发布 + +```text +项目【项目名】发版任务【编号】批准开始生产发布。审批人【姓名】;窗口【时间】;生产流水线【名称】;本批需求【列表】;回滚方案【链接】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请核查全部需求测试完成、范围一致和审批证据,再进入发布中。只操作明确的生产发布,不修改流水线定义。 +``` + +### OPS04 生产发布成功 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】生产流水线【名称】执行成功,执行ID【ID】,环境【prod】,完成时间【时间】,发布范围【需求/MR/版本】,证据【链接】。 +请验证签名、时间戳、执行ID幂等和范围,推进发版任务及本批需求到发布完成并停留,通知产品验收。禁止自动进入已关闭。 +``` + +### OPS05 生产发布失败或重试 + +失败: + +```text +项目【项目名】生产流水线【名称】执行失败,执行ID【ID】,失败阶段【阶段】,原因【原因】,影响需求【列表】,回滚结果【结果】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请记录失败证据并推进到发布失败;不得伪造成功或关闭需求。 +``` + +重试: + +```text +项目【项目名】发版任务【编号】批准从发布失败重试,批准人【姓名】,新执行ID【ID】,已修正原因【说明】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请核查发布失败→发布中流转边、幂等键和范围后开始重试,保留原失败记录。 +``` + +## 14. 业务验收人员 + +### ACC01 验收通过 + +```text +我是业务验收人【姓名】。项目【项目名】需求【编号】已在生产环境按验收项【列表】验证通过,时间【时间】,证据【链接/附件】。 +使用 yunxiao-requirement-lifecycle,权限=apply。请核查身份、发布完成状态和证据范围后关闭需求,并保留验收记录。 +``` + +### ACC02 验收不通过 + +```text +我是业务验收人【姓名】。项目【项目名】需求【编号】生产验收不通过,失败项【列表】,实际结果【结果】,证据【证据】。 +使用 yunxiao-requirement-lifecycle,权限=plan。请建立可追踪问题清单,分析应回到测试、开发还是重新发布,并指定下一责任人;不要关闭需求。 +``` + +## 15. 集成桥接负责人 + +### INT01 回调未生效或重复 + +```text +使用 yunxiao-requirement-lifecycle,权限=audit。 +桥接控制【A03/A06/A08/A09/其他】,项目【项目名】,需求【编号】,事件ID【ID】,时间戳【时间】,回调结果【未生效/重复/失败】,日志摘要【摘要】。 +请核查签名、时间窗、防重放、幂等键、当前状态、权限、重试和审计日志。不要绕过校验直接补写状态;给出安全重试或人工回退方案。 +``` + +### INT02 幂等补偿 + +```text +使用 yunxiao-requirement-lifecycle,权限=apply。 +项目【项目名】控制【控制ID】需要补偿,原事件ID【ID】,期望对象【任务/状态】,当前对象【实际】。已确认原操作未成功且不存在重复对象。 +请再次查询幂等键和正式关系,只补偿缺失动作;完成后输出新旧事件关联、对象ID和重复检查证据。 +``` + +## 16. 异常和催办话术 + +### 状态长时间不动 + +```text +项目【项目名】需求/任务【编号】在【状态】停留【时长】,最后真实事件【事件】,负责人【姓名】,关联资产【列表】。使用 yunxiao-requirement-lifecycle,权限=audit。请判断是业务未完成、自动化未触发还是异步失败,并给出下一责任人和最小恢复动作;不要直接跳状态。 +``` + +### 状态被提前推进 + +```text +项目【项目名】需求【编号】从【源状态】意外进入【目标状态】,时间【时间】。使用 yunxiao-requirement-lifecycle,权限=audit。请检查后继规则、全部关联项口径、部分MR、未复测缺陷、test/prod混用和L01/L02,定位真实触发规则并给出回滚建议。未经确认不要回退状态。 +``` + +### 任务重复创建 + +```text +项目【项目名】需求【编号】出现重复【交付/开发/测试】任务:【任务列表】。使用 yunxiao-requirement-lifecycle,权限=audit。请检查幂等键、正式关系和重复回调,确定保留项及合并/取消方案。未经授权不要删除任务。 +``` + +## 17. 节点交接清单 + +每次角色交接都让Codex确认以下内容: + +1. 项目、需求、任务和流程模型准确。 +2. 源状态与目标状态明确。 +3. 事件真实发生,且正式关联可见。 +4. 当前责任人和下一责任人明确。 +5. 交付物、代码、用例、缺陷或发布证据可访问。 +6. 正向条件全部满足,负向阻塞项为零。 +7. 自动化结果与人工准备动作分开记录。 +8. 异步等待后重新核查状态和执行日志。 +9. 没有误触发后继规则或重复创建。 +10. 回滚入口、人工回退和未完成事项已记录。 + +Codex的节点回报统一使用: + +```text +项目: +流程模型: +工作项: +源状态 → 实际状态: +本次真实事件: +执行动作: +触发方式:原生规则/桥接/人工门禁/未触发 +验证结果:通过/异步通过/未触发/误触发/阻塞 +证据: +未完成项: +下一责任角色: +允许的下一动作: +回滚入口: +``` diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/simple-role-commands.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/simple-role-commands.md new file mode 100644 index 0000000..b94c7b6 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/simple-role-commands.md @@ -0,0 +1,417 @@ +# 云效日常口语化操作口令 + +## 1. 先说一次项目 + +每次新会话先输入: + +```text +项目:统一运营管理平台PC端 +``` + +之后直接说口令即可。项目没有明确、同一编号可能属于多个项目或用户切换项目时,Codex必须先问清项目,不能猜。 + +口令可以自然表达,不要求标点完全一致。例如: + +- `记录需求:增加车辆批量导出` +- `帮我记录一个需求,增加车辆批量导出` + +两句话含义相同。 + +## 2. 产品和需求 + +### 记录需求 + +产品输入: + +```text +记录需求:增加车辆批量导出 +``` + +Codex执行:查重后在当前项目创建产品类需求,初始状态为`待处理`,返回需求编号。只有标题也可以先记录;云效必填字段缺失时只追问缺少的字段。 + +**带模块/原型的记录(OneOS):** 若口令含模块名或原型(如「记录需求:保险采购」),创建前**先调用 `$oneos-autoprd`**,把「需求说明」写入描述;若用户同时定稿,一并写入「更新内容」。 + +**统一运营管理平台 · 快路径(强制):** 项目为「统一运营管理平台 / PC 端」时,先读 [oneos-pc-fast-path.md](oneos-pc-fast-path.md) 与 [oneos-pc-runtime-ids.json](../assets/oneos-pc-runtime-ids.json)。用已验证 `POST /workitem/workitem` 建单(创建时带 `document`),不要探测 `/workitem/create`。推进至「开发中」时状态连跳:`待处理 → 设计完成 → 待开发 → 开发中`。 + +**优先级 / 推进至 Plan 单选门禁(强制):** 用户要求 Plan 模式单选,或口令含「优先级」「推进至」需点选时: + +1. 先用 `AskQuestion`(可用时)或 Plan 确认拿到 A(紧急/高/中/低)与 B(分析中/设计中/设计完成/开发中)。 +2. **禁止**未选时默认「中 + 分析中」并继续建单。 +3. 「批准 / Implement 计划」本身不等于已选 A+B;计划里仍是空选项时必须先问清。 +4. 标签须用户从云效标签 catalog(`oneos-pc-tag-catalog.md`)点选;禁止按业务条线说明/`lines.ts` 自动推断。点选后 API 打标,失败则停并请用户回复标签名,禁止假装成功。 + +**完整可复制提示词(推荐):** 统一运营管理平台建单优先用 [oneos-pc-record-requirement-prompt.md](oneos-pc-record-requirement-prompt.md)——含 `$yunxiao-requirement-lifecycle` + `$oneos-autoprd`、A/B/C 三门禁与 30 项标签枚举。复制该文件「用户口令」整段发给 Agent;确认后再执行快路径。 + +简短版(填需求名与模块后复制): + +```text +使用 $yunxiao-requirement-lifecycle + $oneos-autoprd +记录需求到云效 · 统一运营管理平台 + +【需求名】(填写) +【描述来源】$oneos-autoprd,原型/模块:(填写) + +请先 Plan 让我点选 A 优先级、B 推进至、C 标签(30 项 catalog,见 oneos-pc-record-requirement-prompt.md;禁止 lines.ts 推断)。 +我确认后再执行快路径建单。 +``` + +确认回复示例:`优先级:高;推进至:设计完成;标签:运维管理条线` + +### 确认需求 + +产品输入: + +```text +确认需求:ONEOSP-123 +``` + +Codex执行:检查范围、负责人、优先级和验收标准,齐全后把需求改为`已确认`。 + +`确认需求`默认不创建分析任务。确认和真实开始分析是两个事件,防止开始时间失真。由分析人员下一步输入`开始分析`。 + +### 需求定稿(原型 + AutoPRD) + +产品输入: + +```text +保险采购需求定稿 +``` + +或: + +```text +需求定稿:ONEOSP-123;模块=保险采购 +``` + +Codex执行: + +1. 调用 `$oneos-autoprd` 定稿流程:更新原型 `.spec/requirements-prd.md` 第 10 章「功能变更记录」与 `autoprd-baseline.json`(只记功能/逻辑,不记样式/UI/表结构)。 +2. 若已有云效需求编号:刷新描述中的「需求说明」与「更新内容」;旧更新内容 →「更新内容·历史」。 +3. 若口令要求进待开发/发对象存储:继续走下方「快速进入开发」或 OneOS 快轨口令,不得跳过 AutoPRD。 + +### 快速进入开发 + +产品输入: + +```text +快速开发:ONEOSP-123;原因=小改动;负责人=张三;范围=只改后端 +``` + +Codex执行:检查项目是否允许快轨、验收标准是否明确,再跳过不需要的分析/设计阶段并建立必要开发任务。条件不齐时不跳状态,只提示缺少什么。 + +**OneOS 模块已定型进待开发(推荐):** + +```text +「保险采购」需求已经确定:先 AutoPRD 写需求说明与更新内容;发布对象存储并写入描述; +推进到待开发;自动建与需求同名的【交付】任务并正式关联;负责人何斐。 +``` + +执行前必须先跑 `$oneos-autoprd`(含定稿变更记录若用户刚定稿)。进入待开发时**必须**执行 [auto-stage-task.md](auto-stage-task.md)(同名任务、标签交付、负责人何斐、时间沿用需求);已有唯一交付任务则复用并改负责人为何斐,禁止建第二条。 + +### 需求变更 + +产品输入: + +```text +变更需求:ONEOSP-123;增加导出时间筛选 +``` + +Codex执行:先分析受影响的任务、MR、用例、缺陷和发布范围,给出应回到哪个阶段;产品确认后再修改,不直接覆盖历史说明。涉及原型功能变更时,同步 `$oneos-autoprd` 更新 PRD;用户再说「定稿」时写入第 10 章。 + +## 3. 分析和设计 + +### 开始分析 + +分析人员输入: + +```text +开始分析:ONEOSP-123;负责人=李四 +``` + +Codex执行: + +1. 将需求推进到`分析中`(或确认已在分析中)。 +2. **按 [auto-stage-task.md](auto-stage-task.md)**:查重后创建/复用与需求**同名**任务;标签=`分析`;正式关联需求;负责人默认=需求**创建人**(口令写了负责人则用口令);计划开始/创建时间沿用需求。 +3. 任务进入`处理中`(或项目约定的开工态)。 +4. `stage_tasks`:需求标签含`分析`。`oneos_delivery`:不因此再建第二条【交付】主任务。 + +### 分析完成 + +分析人员输入: + +```text +分析完成:ONEOSP-123;说明=https://... +``` + +Codex执行:检查分析产物和未决项;完成对应任务或主任务阶段,核查需求进入`分析完成`。 + +### 开始设计 + +设计人员输入: + +```text +开始设计:ONEOSP-123;负责人=王五 +``` + +Codex执行: + +1. 将需求推进到`设计中`。 +2. **按 auto-stage-task**:同名任务;标签=`设计`;正式关联;负责人默认=需求**创建人**(口令可覆盖);时间沿用需求。 +3. 任务开工态同上。 +4. `oneos_delivery`:不同时新建交付主任务(交付主任务在待开发时建/复用)。 + +### 设计完成 + +设计人员输入: + +```text +设计完成:ONEOSP-123;原型=https://... +``` + +Codex执行:检查原型或设计说明,完成设计任务/主任务阶段,核查需求进入`设计完成`。 + +如果项目已经部署并验证自动建开发任务的桥接,而且负责人、仓库和范围明确,Codex继续查重并创建开发任务;否则停在`设计完成`,提示技术负责人输入`安排开发`。不得创建无负责人或重复任务。 + +**进入待开发时(含快轨):** 必须再跑 auto-stage-task:同名任务、标签`交付`(或`开发`)、负责人**何斐**、正式关联、时间沿用需求。 + +## 4. 开发 + +### 安排开发 + +技术负责人输入: + +```text +安排开发:ONEOSP-123;前端=张三/ln-one-os-web;后端=李四/ln-cloud +``` + +只改一端时可以说: + +```text +安排开发:ONEOSP-123;只改后端=李四/ln-cloud +``` + +Codex执行:按实际范围创建一条或多条`【开发】`任务,正式关联需求;`oneos_delivery`还要关联交付主任务。任务初始为`待处理`,需求进入`待开发`。明确“不涉及”的仓库不会成为完成门禁。 + +### 开始开发 + +开发人员输入: + +```text +开始开发:ONEOSP-123;任务=TASK-456;仓库=ln-cloud +``` + +Codex执行:记录真实开始时间,确认真实基线`dev`或`develop`,创建`feature/ONEOSP-123`并同时正式关联需求和开发任务。任务进入`处理中`,需求进入`开发中`。 + +首次提交只作兜底,不覆盖这次真实开始时间。 + +### 提交代码 + +开发人员输入: + +```text +提交代码:ONEOSP-123;任务=TASK-456;仓库=ln-cloud +``` + +Codex执行:检查改动和测试,先拉取并处理冲突,再提交、推送并创建合并到真实`dev/develop`的MR;MR同时关联需求和开发任务。提交成功不等于开发完成。 + +### 开发完成 + +开发人员或技术负责人输入: + +```text +开发完成:ONEOSP-123 +``` + +Codex执行:汇总该需求所有非取消开发任务、前后端仓库和正式关联MR。只有全部任务完成、全部相关MR已合并,才确认需求进入`开发完成`;少一个仓库也不提前完成。 + +条件满足后,如果项目已配置并验证唯一测试任务桥接,则查重创建测试任务;否则提示测试负责人输入`提交测试`。 + +## 5. 测试和缺陷 + +### 提交测试 + +测试负责人或开发负责人输入: + +```text +提交测试:ONEOSP-123;test流水线=oneos-web-test +``` + +Codex执行:确认开发完成,触发或核查明确的test流水线。部署成功后,查重创建并正式关联唯一测试任务和需求用例包,进入`待测试/测试中`的项目真实状态。 + +test成功只允许开始测试,不能变成发布完成。 + +### 开始测试 + +测试人员输入: + +```text +开始测试:ONEOSP-123;负责人=赵六 +``` + +Codex执行:核查test部署和测试任务,把测试任务改为`测试中/处理中`,需求进入`测试中`。 + +### 发现Bug + +测试人员输入: + +```text +发现Bug:ONEOSP-123;用例=CASE-12;现象=车辆来源显示0;期望=显示中文来源 +``` + +Codex执行:查重后创建缺陷,正式关联需求、原失败用例和必要开发任务;用例保持未通过,需求保持`测试中`。 + +### Bug修复完成 + +开发人员输入: + +```text +Bug修复完成:BUG-123;MR=456;已部署test +``` + +Codex执行:核查代码、MR和test部署后,把缺陷改为`已修复`并交给测试复测。不会关闭缺陷,也不会把原用例直接改成通过。 + +### 复测通过 + +测试人员输入: + +```text +复测通过:BUG-123;用例=CASE-12 +``` + +Codex执行:重新记录原用例为通过,把缺陷改为项目真实关闭态,再检查该需求是否还有失败、阻塞、未执行用例或未关闭缺陷。 + +### 复测失败 + +测试人员输入: + +```text +复测失败:BUG-123;用例=CASE-12;现象=仍显示0 +``` + +Codex执行:原用例保持未通过,缺陷重开或回到处理中,并通知修复负责人;需求保持`测试中`。 + +### 测试完成 + +测试负责人输入: + +```text +测试完成:ONEOSP-123 +``` + +Codex执行:检查本需求用例包,不按整个迭代一刀切。只有没有未执行、阻塞、失败用例,且所有缺陷都经过测试复测关闭,才完成测试任务并推进需求到`测试完成`。 + +## 6. 发布和验收 + +### 准备发布 + +运维或项目负责人输入: + +```text +准备发布:迭代V1.3;需求=ONEOSP-123,ONEOSP-124;窗口=今晚22:00 +``` + +Codex执行:检查本批需求均测试完成,查重创建或复用唯一`【发版】`任务,正式关联迭代和需求,生成发布范围及回滚清单。 + +### 开始发布 + +运维输入: + +```text +开始发布:发版任务=TASK-900;prod流水线=oneos-prod;审批人=王经理 +``` + +Codex执行:核查审批、本批范围、生产流水线和回滚方案后进入`发布中`。这句话授权执行明确的本次生产发布,但不授权修改生产流水线定义。 + +### 发布成功 + +运维输入: + +```text +发布成功:执行ID=PIPE-789;发版任务=TASK-900 +``` + +Codex执行:到生产流水线核查环境、执行ID、范围和成功证据,确认后推进本批需求到`发布完成`并停留,等待验收。只说“成功”但没有生产证据时不改状态。 + +### 发布失败 + +运维输入: + +```text +发布失败:执行ID=PIPE-789;原因=镜像拉取失败 +``` + +Codex执行:核查失败证据,记录原因并进入项目真实`发布失败`状态;项目尚无该状态时记录阻塞并给出人工处置,不创建近义重复状态。 + +### 验收通过 + +产品或业务输入: + +```text +验收通过:ONEOSP-123;验收人=王经理;证据=https://... +``` + +Codex执行:核查生产发布完成和验收证据后,把需求改为`已关闭`。发布成功不能代替本句验收。 + +### 验收不通过 + +产品或业务输入: + +```text +验收不通过:ONEOSP-123;问题=导出字段缺失 +``` + +Codex执行:创建或关联可追踪问题,判断应回到开发、测试还是重新发布,先给出处理建议;不关闭需求,不覆盖原发布记录。 + +## 7. 随时可用的查询口令 + +```text +查状态:ONEOSP-123 +下一步:ONEOSP-123 +为什么没流转:ONEOSP-123 +查重复任务:ONEOSP-123 +查关联代码:ONEOSP-123 +查测试闭环:ONEOSP-123 +给我回滚方案:刚才那条规则 +``` + +这些口令默认只查询或出方案,不修改云效。 + +## 8. Codex的简短回报格式 + +处理成功: + +```text +已处理:ONEOSP-123 +状态:已确认 → 分析中 +任务:已创建并关联分析任务 TASK-456 +下一步:分析人员输入“分析完成:ONEOSP-123;说明=链接” +``` + +条件不足: + +```text +暂未处理:ONEOSP-123 +缺少:开发负责人 +请补充:安排开发:ONEOSP-123;只改后端=负责人/仓库 +``` + +自动化未触发: + +```text +未触发:ONEOSP-123 仍为待开发 +原因:分支只写了需求编号,没有在云效正式关联开发任务 +下一步:我可以补正式关联,然后再核查一次 +``` + +## 9. Codex执行口令的规则 + +1. `记录、确认、开始、完成、提交、修复、复测、准备发布、发布、验收`属于当前项目和明确工作项的执行授权。 +2. `查、看看、为什么、下一步、给方案`只允许读取或规划。 +3. 写操作前必须确认当前项目;项目不清楚只问项目名。 +4. 其他必要字段缺失时只追问最少信息,不发送长表单。 +5. 先查重、再创建;任务幂等键使用项目ID、需求ID和阶段类型。 +6. 项目使用`stage_tasks`还是`oneos_delivery`由Codex识别,同事不需要记规则编号。 +7. 当前平台或桥接不支持的动作要明确说“需要人工一步”,不能伪装成已自动化。 +8. 自动规则执行后等待5–30秒,再回看状态、正式关联和执行日志。 +9. 不用手工改最终状态冒充自动化通过。 +10. 不修改生产流水线定义,不删除规则、分支、MR、任务或测试资产,除非用户单独明确授权。 diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/task-lifecycle.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/task-lifecycle.md new file mode 100644 index 0000000..7432685 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/task-lifecycle.md @@ -0,0 +1,87 @@ +# 阶段任务生命周期与自动创建 + +## 目录 + +1. 目标链路 +2. TK01–TK08规则 +3. 关系与代码资产 +4. 原生规则和桥接边界 +5. 验证与回滚 + +## 1. 目标链路 + +```text +需求设计中 + 设计任务处理中 +→ 设计任务已完成 +→ 需求设计完成 +→ 创建并关联开发任务 +→ 需求待开发 + 开发任务待处理 +→ 正式关联需求分支 +→ 需求开发中 + 开发任务处理中 +→ 全部相关MR合并到真实dev/develop +→ 需求开发完成 + 开发任务已完成 +→ test流水线部署成功 +→ 创建并关联测试任务 +→ 需求测试中 + 测试任务处理中 +→ 用例全部通过且缺陷复测关闭 +→ 需求测试完成 + 测试任务已完成 +``` + +分支只能合并到集成分支,不能“合并到测试环境”。test环境由流水线部署;部署成功用于允许测试开始,不用于证明生产发布。 + +## 2. TK01–TK08规则 + +| ID | 触发 | 必要条件 | 动作 | 推荐实现 | +|---|---|---|---|---| +| TK01 | 需求进入设计中 | 已正式关联设计任务;任务标签=`设计` | 设计任务保持待处理,负责人真实开工后人工改为处理中 | 默认+人工 | +| TK02 | 设计任务变为已完成 | 需求当前状态=设计中;本阶段设计任务非零且全部完成 | 需求改为设计完成 | 原生规则 | +| TK03 | 需求变为设计完成 | 不存在同需求、同阶段、未取消的开发任务 | 创建并正式关联开发任务,标签=`开发`,状态=待处理 | API/Webhook桥接 | +| TK04 | 开发任务创建并关联成功 | 需求当前状态=设计完成;开发任务关系可见 | 需求改为待开发 | 原生规则 | +| TK05 | 正式关联需求分支/代码资产 | 需求=待开发;开发任务=待处理;资产同时关联需求和开发任务 | 需求改为开发中;开发任务改为处理中 | 原生规则 | +| TK06 | 关联合并请求状态变化 | 需求=开发中;开发任务=处理中;全部相关前后端MR已合并到真实集成分支 | 需求改为开发完成;开发任务改为已完成 | 原生规则 | +| TK07 | test流水线部署成功 | 需求=开发完成;执行ID未处理;不存在同需求、同阶段、未取消的测试任务 | 创建并关联测试任务,关联测试计划/用例,任务改为处理中,需求改为测试中 | API/Webhook桥接 | +| TK08 | 测试验收完成 | 用例全部通过;失败用例已复测;范围内缺陷均由测试关闭 | 测试任务改为已完成;需求改为测试完成 | 桥接/人工门禁 | + +## 3. 关系与代码资产 + +- 设计、开发、测试任务必须使用云效正式父子项或关联项关系,不依赖标题关键字。 +- 开发分支和MR必须同时关联产品需求与对应开发任务;只关联需求不能可靠更新开发任务。 +- 一个需求涉及多个仓库时,全部相关前端、后端MR都纳入TK06门禁。 +- 首次提交可能晚于真实开工。任务`处理中`的权威时间是负责人执行“开始处理”;分支关联仅作自动兜底。 + +## 4. 原生规则和桥接边界 + +优先使用云效原生规则处理状态变化、父子项、关联项和Codeup对象。若当前规则编辑器没有“创建工作项”动作,TK03和TK07必须标记为`integration_bridge`或`not_supported`,不得声称已经自动创建。 + +桥接创建任务时使用幂等键: + +```text +项目ID + 需求ID + 阶段类型 +``` + +重复回调先查询是否存在未取消的同阶段任务;存在则复用并校验正式关系,不重复创建。流水线回调还要校验签名、时间戳、执行ID、防重放和失败重试。 + +## 5. 验证与回滚 + +- 正向验证每个TK控制,并检查需求和任务两个对象的状态。 +- 负向验证零任务、错误标签、错误当前状态、未正式关联分支、只合并部分MR、流水线失败和重复回调。 +- 新规则不会补触发历史事件。历史任务仅在核对MR/流水线证据后人工修正,并标记为测试准备或数据修复。 +- 回滚时先禁用新增规则或桥接入口,不删除已有任务和关系;恢复前一状态需要单独业务确认。 + +## 6. 进阶段自动建同名任务(与 OneOS / 口令共用) + +完整规则见 [auto-stage-task.md](auto-stage-task.md)。摘要: + +| 需求状态 | 同名任务标签 | 负责人 | +|---|---|---| +| 分析中 | `分析` | 需求创建人 | +| 设计中 | `设计` | 需求创建人 | +| 待开发 | `交付` 或 `开发` | 何斐 | + +必须正式关联需求;计划开始/创建时间沿用需求;同阶段幂等查重。 + +当创建或修复**分析 / 设计 / 交付(开发)**任务时: + +1. **任务标签(右侧基础字段)**:分析 → `分析`;设计 → `设计`;待开发主任务 → `交付`(或 `开发`)。Title 同名或前缀 alone 不够。 +2. **正式关联需求**:每条阶段任务必须链接到**产品需求**(父子或关联项)。只关联其他任务不算完成。 +3. 创建/修复后先核对标签与可见需求关系,再报告成功。 \ No newline at end of file diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/references/test-defect-closure.md b/.cursor/skills/yunxiao-requirement-lifecycle/references/test-defect-closure.md new file mode 100644 index 0000000..dcf7b3c --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/references/test-defect-closure.md @@ -0,0 +1,62 @@ +# 测试用例与缺陷复测闭环 + +## 目录 + +1. 测试完成门槛 +2. TC01–TC03用例控制 +3. DF01–DF03缺陷控制 +4. 实现方式 +5. 验证场景 + +## 1. 测试完成门槛 + +R10只有同时满足以下条件才能完成: + +1. 范围内用例均已执行并有明确结果。 +2. 不存在`未执行/待测试`、`阻塞`或`未通过`用例;获批暂缓/不适用项记录批准人与原因。 +3. 每个未通过用例均正式创建或关联缺陷。 +4. 修复后重新执行原用例并通过。 +5. 范围内缺陷均由测试复测并进入项目真实关闭态。 +6. 测试计划或测试验收任务由测试负责人确认。 + +流水线成功只能证明部署/自动检查成功,不能替代用例结果和缺陷复测。 + +## 2. TC01–TC03用例控制 + +| ID | 控制 | 完成口径 | +|---|---|---| +| TC01 | 用例执行 | 每条范围内用例有真实执行结果;未执行、待测试、阻塞、未通过均阻塞测试完成 | +| TC02 | 失败用例关联缺陷 | 每条未通过用例正式创建或关联缺陷;仅写编号或链接不算关联 | +| TC03 | 用例复测更新 | 修复后重新执行原用例;通过才改为已通过,失败保持未通过 | + +## 3. DF01–DF03缺陷控制 + +| ID | 控制 | 完成口径 | +|---|---|---| +| DF01 | 已修复交接 | `已修复`只表示开发交给测试复测,不是终态,不解除测试门禁 | +| DF02 | 复测通过 | 缺陷进入项目真实关闭态,原失败用例同时更新为已通过 | +| DF03 | 复测失败 | 缺陷进入项目真实重开/处理中状态,原用例保持未通过 | + +不得关闭缺陷却不更新原用例,也不得在缺陷仅为`已修复`时把用例标为通过。 + +## 4. 实现方式 + +先检查当前规则编辑器是否真的暴露Testhub执行用例和缺陷聚合条件: + +- 支持时:使用原生聚合规则,并保存条件和执行日志证据。 +- 不支持时:使用测试验收任务/人工门禁。 +- 需要全自动时:单独评审Testhub OpenAPI/Webhook桥接,要求签名、幂等、失败重试和审计记录。 + +不能把`not_supported`写成“已自动化”;必须指定负责人和回退方案。 + +## 5. 验证场景 + +| 场景 | 操作 | 期望 | 负向检查 | +|---|---|---|---| +| 用例未执行 | 保留一条未执行用例 | 保持测试中 | 不得完成测试 | +| 用例失败 | 记录未通过并正式关联缺陷 | 保持测试中 | 仅建缺陷不得完成 | +| 缺陷已修复 | 开发改为已修复 | 等待测试复测 | 不得关闭或通过用例 | +| 复测通过 | 重跑原用例并通过 | 用例通过、缺陷关闭 | 两者状态必须一致 | +| 复测失败 | 重跑仍失败 | 用例未通过、缺陷重开 | 不得保持已修复/关闭 | +| 全部闭环 | 全部用例通过且缺陷关闭 | 允许R10推进 | 检查是否误触发后继规则 | + diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/scripts/oneos_profile_tool.py b/.cursor/skills/yunxiao-requirement-lifecycle/scripts/oneos_profile_tool.py new file mode 100644 index 0000000..c1849b6 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/scripts/oneos_profile_tool.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 +"""Validate and render a OneOS delivery-model Yunxiao project profile.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import Any + + +TARGET_LIFECYCLE = [ + "待处理", "已确认", "分析中", "分析完成", "设计中", "设计完成", + "待开发", "开发中", "开发完成", "待测试", "测试中", "测试完成", + "发布中", "发布完成", "发布失败", "已关闭", +] +REQUIRED_TASK_LABELS = {"交付", "开发", "测试", "发版"} +REQUIRED_CONTROL_IDS = { + *(f"Y{index:02d}" for index in range(1, 17)), + "Y20", "Y21", "Y22", + *(f"Y{index:02d}" for index in range(30, 36)), + "Y40", + "A01", "A02", "A02B", *(f"A{index:02d}" for index in range(3, 11)), + *(f"TC{index:02d}" for index in range(1, 4)), + *(f"DF{index:02d}" for index in range(1, 4)), + *(f"CI{index:02d}" for index in range(1, 4)), + "L01", "L02", +} +VALID_MODES = { + "manual", "native_rule", "integration_bridge", "disabled_legacy", "not_supported" +} +VALID_PHASES = {"P0", "P1", "P2", "P3"} +VALID_TASK_WORKFLOW_MODES = {"A", "B"} +VALID_TASK_IDENTITY_MODES = {"work_item_type", "task_label", "title_prefix_process_control"} +VALID_LEGACY_DISPOSITIONS = {"keep_until_ready", "disable", "replace", "already_disabled"} +SENSITIVE_FRAGMENTS = ( + "password", "passwd", "secret", "token", "cookie", "credential", + "private_key", "otp", "密码", "密钥", +) + + +def is_text(value: Any) -> bool: + return isinstance(value, str) and bool(value.strip()) + + +def require_text(container: dict[str, Any], key: str, label: str, errors: list[str]) -> None: + if not is_text(container.get(key)): + errors.append(f"{label}.{key} 不能为空") + + +def walk_sensitive_keys(value: Any, prefix: str = "") -> list[str]: + found: list[str] = [] + if isinstance(value, dict): + for key, nested in value.items(): + current = f"{prefix}.{key}" if prefix else str(key) + if any(fragment in str(key).lower() for fragment in SENSITIVE_FRAGMENTS): + found.append(current) + found.extend(walk_sensitive_keys(nested, current)) + elif isinstance(value, list): + for index, nested in enumerate(value): + found.extend(walk_sensitive_keys(nested, f"{prefix}[{index}]")) + return found + + +def validate_controls(controls: Any, label: str, errors: list[str]) -> None: + if not isinstance(controls, list): + errors.append(f"{label} 必须是数组") + return + seen: set[str] = set() + for index, control in enumerate(controls): + item_label = f"{label}[{index}]" + if not isinstance(control, dict): + errors.append(f"{item_label} 必须是对象") + continue + control_id = control.get("id") + if not is_text(control_id): + errors.append(f"{item_label}.id 不能为空") + continue + if control_id in seen: + errors.append(f"{label} 控制项重复:{control_id}") + seen.add(control_id) + if control_id not in REQUIRED_CONTROL_IDS: + errors.append(f"{label} 未知控制项:{control_id}") + mode = control.get("mode") + if mode not in VALID_MODES: + errors.append(f"{item_label}.mode 不合法") + if not isinstance(control.get("enabled"), bool): + errors.append(f"{item_label}.enabled 必须是布尔值") + if mode in {"disabled_legacy", "not_supported"} and control.get("enabled") is not False: + errors.append(f"{item_label} 的 {mode} 模式要求 enabled=false") + if control_id in {"L01", "L02"}: + if mode != "disabled_legacy" or control.get("enabled") is not False: + errors.append(f"{item_label} 必须停用,防止发布完成绕过验收") + for key in ("owner", "fallback", "evidence"): + require_text(control, key, item_label, errors) + missing = sorted(REQUIRED_CONTROL_IDS - seen) + if missing: + errors.append(f"{label} 缺少控制项:" + "、".join(missing)) + + +def validate_project(project: Any, index: int, errors: list[str]) -> None: + label = f"projects[{index}]" + if not isinstance(project, dict): + errors.append(f"{label} 必须是对象") + return + require_text(project, "name", label, errors) + if project.get("migration_phase") not in VALID_PHASES: + errors.append(f"{label}.migration_phase 必须是 P0/P1/P2/P3") + statuses = project.get("requirement_statuses_observed") + if not isinstance(statuses, list) or not statuses or not all(is_text(v) for v in statuses): + errors.append(f"{label}.requirement_statuses_observed 必须是非空文本数组") + if project.get("task_workflow_mode") not in VALID_TASK_WORKFLOW_MODES: + errors.append(f"{label}.task_workflow_mode 必须是 A 或 B") + if project.get("task_identity_mode") not in VALID_TASK_IDENTITY_MODES: + errors.append(f"{label}.task_identity_mode 不合法") + if not isinstance(project.get("native_rule_can_filter_related_task_identity"), bool): + errors.append(f"{label}.native_rule_can_filter_related_task_identity 必须是布尔值") + for key in ( + "main_task_prefix", "development_task_prefix", "test_task_prefix", "release_task_prefix" + ): + require_text(project, key, label, errors) + + repositories = project.get("repositories") + if not isinstance(repositories, list) or not repositories: + errors.append(f"{label}.repositories 至少包含一个仓库") + else: + for repo_index, repo in enumerate(repositories): + repo_label = f"{label}.repositories[{repo_index}]" + if not isinstance(repo, dict): + errors.append(f"{repo_label} 必须是对象") + continue + for key in ("name", "role", "integration_branch", "evidence"): + require_text(repo, key, repo_label, errors) + if repo.get("integration_branch") not in {"dev", "develop"}: + errors.append(f"{repo_label}.integration_branch 只能是实际存在的 dev/develop") + for key in ("codeup_integrated", "required_for_mr_gate"): + if not isinstance(repo.get(key), bool): + errors.append(f"{repo_label}.{key} 必须是布尔值") + + test_plan = project.get("test_plan") + if not isinstance(test_plan, dict): + errors.append(f"{label}.test_plan 必须是对象") + else: + require_text(test_plan, "name", f"{label}.test_plan", errors) + require_text(test_plan, "evidence", f"{label}.test_plan", errors) + for key, required in ( + ("iteration_formally_related", True), + ("cases_partitioned_by_requirement", True), + ("fixed_defect_is_terminal", False), + ("tester_retest_required", True), + ): + if test_plan.get(key) is not required: + errors.append(f"{label}.test_plan.{key} 必须为 {str(required).lower()}") + + release = project.get("release") + if not isinstance(release, dict): + errors.append(f"{label}.release 必须是对象") + else: + require_text(release, "evidence", f"{label}.release", errors) + for key in ( + "release_task_formally_related_to_iteration", + "production_evidence_separate_from_test", + "acceptance_required_after_release", + "production_pipeline_unchanged", + ): + if release.get(key) is not True: + errors.append(f"{label}.release.{key} 必须为 true") + + legacy_rules = project.get("legacy_rules") + if not isinstance(legacy_rules, list): + errors.append(f"{label}.legacy_rules 必须是数组") + else: + for legacy_index, rule in enumerate(legacy_rules): + item_label = f"{label}.legacy_rules[{legacy_index}]" + if not isinstance(rule, dict): + errors.append(f"{item_label} 必须是对象") + continue + for key in ("name", "reason", "evidence"): + require_text(rule, key, item_label, errors) + if rule.get("disposition") not in VALID_LEGACY_DISPOSITIONS: + errors.append(f"{item_label}.disposition 不合法") + if not isinstance(rule.get("enabled"), bool): + errors.append(f"{item_label}.enabled 必须是布尔值") + + validate_controls(project.get("controls"), f"{label}.controls", errors) + + +def validate(data: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if data.get("schema_version") != 1: + errors.append("schema_version 必须为 1") + if data.get("flow_model") != "oneos_delivery": + errors.append("flow_model 必须为 oneos_delivery") + for key in ("organization", "work_item_type"): + require_text(data, key, "profile", errors) + if data.get("target_lifecycle") != TARGET_LIFECYCLE: + errors.append("target_lifecycle 必须与OneOS终态16状态顺序一致") + labels = data.get("task_labels") + if not isinstance(labels, list) or not REQUIRED_TASK_LABELS.issubset(set(labels)): + errors.append("task_labels 必须包含:交付、开发、测试、发版") + policies = data.get("policies") + if not isinstance(policies, dict): + errors.append("policies 必须是对象") + else: + true_keys = ( + "require_formal_relations", "require_current_state_condition", + "require_nonzero_development_tasks", + "require_signed_timestamped_replay_protected_callbacks", + ) + false_keys = ("allow_automatic_final_close_without_acceptance", "test_success_is_production_release") + for key in true_keys: + if policies.get(key) is not True: + errors.append(f"policies.{key} 必须为 true") + for key in false_keys: + if policies.get(key) is not False: + errors.append(f"policies.{key} 必须为 false") + parts = policies.get("bridge_idempotency_key_components") + if not isinstance(parts, list) or not parts or not all(is_text(v) for v in parts): + errors.append("policies.bridge_idempotency_key_components 不能为空") + projects = data.get("projects") + if not isinstance(projects, list) or not projects: + errors.append("projects 至少包含一个项目") + else: + for index, project in enumerate(projects): + validate_project(project, index, errors) + sensitive = walk_sensitive_keys(data) + if sensitive: + errors.append("配置中禁止出现敏感字段:" + "、".join(sensitive)) + if "请填写" in json.dumps(data, ensure_ascii=False): + errors.append("仍存在“请填写”占位符") + return errors + + +def render(data: dict[str, Any]) -> str: + lines = [ + "# OneOS云效终态交付模型执行计划", "", + f"- 组织:{data['organization']}", + f"- 工作项类型:{data['work_item_type']}", + f"- 项目数:{len(data['projects'])}", + "- 完整性口径:每项目48项Y/A/测试/缺陷/集成/旧规则控制。", + "- 安全边界:不修改生产流水线;发布完成后保留产品验收门禁。", "", + ] + for project in data["projects"]: + missing_statuses = [s for s in TARGET_LIFECYCLE if s not in project["requirement_statuses_observed"]] + lines.extend([ + f"## 项目:{project['name']}", "", + f"- 迁移阶段:{project['migration_phase']}", + f"- 任务工作流方案:{project['task_workflow_mode']}", + f"- 任务身份方式:{project['task_identity_mode']}", + f"- 原生规则可过滤关联任务身份:{'是' if project['native_rule_can_filter_related_task_identity'] else '否'}", + f"- 缺少目标状态:{'、'.join(missing_statuses) if missing_statuses else '无'}", "", + "### 旧规则处置", "", + "| 规则 | 处置 | 启用 | 原因 | 证据 |", "|---|---|---|---|---|", + ]) + for rule in project["legacy_rules"]: + lines.append( + f"| {rule['name']} | {rule['disposition']} | {'是' if rule['enabled'] else '否'} | " + f"{rule['reason']} | {rule['evidence']} |" + ) + lines.extend([ + "", "### 48项控制台账", "", + "| ID | 方式 | 启用 | 负责人 | 回退方案 | 证据 |", "|---|---|---|---|---|---|", + ]) + for control in sorted(project["controls"], key=lambda item: item["id"]): + lines.append( + f"| {control['id']} | {control['mode']} | {'是' if control['enabled'] else '否'} | " + f"{control['owner']} | {control['fallback']} | {control['evidence']} |" + ) + lines.extend([ + "", "### 迁移门禁", "", + "- [ ] 目标状态与流转边已补齐。", + "- [ ] 主任务可可靠识别或已明确交桥接。", + "- [ ] 多仓库需求与开发任务均正式关联分支/MR。", + "- [ ] Testhub按需求分包且缺陷复测闭环。", + "- [ ] test、生产发布和产品验收证据分离。", + "- [ ] L01、L02已停用,旧规则已有备份和回滚。", "", + ]) + return "\n".join(lines) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + subparsers = parser.add_subparsers(dest="command", required=True) + validate_parser = subparsers.add_parser("validate") + validate_parser.add_argument("profile", type=Path) + render_parser = subparsers.add_parser("render") + render_parser.add_argument("profile", type=Path) + render_parser.add_argument("--output", required=True, type=Path) + args = parser.parse_args() + try: + with args.profile.open("r", encoding="utf-8-sig") as handle: + data = json.load(handle) + if not isinstance(data, dict): + raise ValueError("配置根节点必须是JSON对象") + errors = validate(data) + except (OSError, json.JSONDecodeError, ValueError) as error: + print(f"配置读取失败:{error}", file=sys.stderr) + return 2 + if errors: + for error in errors: + print(f"[错误] {error}", file=sys.stderr) + return 1 + if args.command == "validate": + print(f"[通过] OneOS配置有效:{len(data['projects'])}个项目,每项目48项控制。") + return 0 + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(render(data), encoding="utf-8") + print(f"[完成] 已生成执行计划:{args.output}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.cursor/skills/yunxiao-requirement-lifecycle/scripts/profile_tool.py b/.cursor/skills/yunxiao-requirement-lifecycle/scripts/profile_tool.py new file mode 100644 index 0000000..5738bf3 --- /dev/null +++ b/.cursor/skills/yunxiao-requirement-lifecycle/scripts/profile_tool.py @@ -0,0 +1,536 @@ +#!/usr/bin/env python3 +"""Validate a project-scoped Yunxiao lifecycle profile and render an execution plan.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import Any + + +REQUIRED_LABELS = {"分析", "设计", "开发", "测试"} +REQUIRED_RULE_IDS = {f"R{index:02d}" for index in range(1, 13)} +REQUIRED_TASK_RULE_IDS = {f"TK{index:02d}" for index in range(1, 9)} +REQUIRED_CONTROL_IDS = { + *REQUIRED_RULE_IDS, + *REQUIRED_TASK_RULE_IDS, + *(f"TC{index:02d}" for index in range(1, 4)), + *(f"DF{index:02d}" for index in range(1, 4)), + *(f"CI{index:02d}" for index in range(1, 4)), + "L01", + "L02", +} +VALID_MODES = { + "manual", + "native_rule", + "integration_bridge", + "disabled_legacy", + "not_supported", +} +VALID_RELEASE_EVIDENCE_MODES = { + "native_release_change", + "pipeline_callback", + "manual_release_confirmation", +} +SENSITIVE_FRAGMENTS = ( + "password", + "passwd", + "secret", + "token", + "cookie", + "credential", + "private_key", + "otp", + "密码", + "密钥", +) + + +def load_profile(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8-sig") as handle: + data = json.load(handle) + if not isinstance(data, dict): + raise ValueError("配置根节点必须是 JSON 对象") + return data + + +def is_text(value: Any) -> bool: + return isinstance(value, str) and bool(value.strip()) + + +def require_text(container: dict[str, Any], key: str, label: str, errors: list[str]) -> None: + if not is_text(container.get(key)): + errors.append(f"{label}.{key} 不能为空") + + +def walk_sensitive_keys(value: Any, prefix: str = "") -> list[str]: + found: list[str] = [] + if isinstance(value, dict): + for key, nested in value.items(): + current = f"{prefix}.{key}" if prefix else str(key) + lower = str(key).lower() + if any(fragment in lower for fragment in SENSITIVE_FRAGMENTS): + found.append(current) + found.extend(walk_sensitive_keys(nested, current)) + elif isinstance(value, list): + for index, nested in enumerate(value): + found.extend(walk_sensitive_keys(nested, f"{prefix}[{index}]")) + return found + + +def validate_control_list( + controls: Any, label: str, errors: list[str] +) -> None: + if not isinstance(controls, list): + errors.append(f"{label} 必须是数组") + return + seen: set[str] = set() + for index, control in enumerate(controls): + item_label = f"{label}[{index}]" + if not isinstance(control, dict): + errors.append(f"{item_label} 必须是对象") + continue + control_id = control.get("id") + if not is_text(control_id): + errors.append(f"{item_label}.id 不能为空") + continue + if control_id in seen: + errors.append(f"{label} 控制项重复:{control_id}") + seen.add(control_id) + if control_id not in REQUIRED_CONTROL_IDS: + errors.append(f"{label} 未知控制项:{control_id}") + mode = control.get("mode") + if mode not in VALID_MODES: + errors.append(f"{item_label}.mode 不合法") + if not isinstance(control.get("enabled"), bool): + errors.append(f"{item_label}.enabled 必须是布尔值") + for key in ("evidence", "owner", "fallback"): + require_text(control, key, item_label, errors) + if mode in {"disabled_legacy", "not_supported"} and control.get("enabled") is not False: + errors.append(f"{item_label} 的 {mode} 模式要求 enabled=false") + if control_id in {"L01", "L02"} and mode not in { + "disabled_legacy", + "native_rule", + "manual", + }: + errors.append(f"{item_label} 必须明确为停用旧规则、获批原生规则或人工处置") + missing = sorted(REQUIRED_CONTROL_IDS - seen) + if missing: + errors.append(f"{label} 缺少控制项:" + "、".join(missing)) + + +def validate_rule_instances( + rules: Any, lifecycle: list[str], label: str, errors: list[str] +) -> None: + if not isinstance(rules, list): + errors.append(f"{label} 必须是数组") + return + seen: set[str] = set() + orders: set[int] = set() + expected = { + f"R{index + 1:02d}": (lifecycle[index], lifecycle[index + 1]) + for index in range(12) + } + for index, rule in enumerate(rules): + item_label = f"{label}[{index}]" + if not isinstance(rule, dict): + errors.append(f"{item_label} 必须是对象") + continue + rule_id = rule.get("id") + if not is_text(rule_id): + errors.append(f"{item_label}.id 不能为空") + continue + if rule_id in seen: + errors.append(f"{label} 规则实例重复:{rule_id}") + seen.add(rule_id) + if rule_id not in REQUIRED_RULE_IDS: + errors.append(f"{label} 未知规则实例:{rule_id}") + mode = rule.get("mode") + if mode not in {"manual", "native_rule", "integration_bridge", "not_supported"}: + errors.append(f"{item_label}.mode 不合法") + for key in ("actual_rule_name", "trigger", "source_status", "target_status", "action", "evidence"): + require_text(rule, key, item_label, errors) + conditions = rule.get("conditions") + if not isinstance(conditions, list) or not conditions or not all(is_text(v) for v in conditions): + errors.append(f"{item_label}.conditions 必须是非空文本数组") + order = rule.get("order") + if not isinstance(order, int) or order < 1: + errors.append(f"{item_label}.order 必须是正整数") + elif order in orders: + errors.append(f"{label} 规则顺序重复:{order}") + else: + orders.add(order) + if not isinstance(rule.get("enabled"), bool): + errors.append(f"{item_label}.enabled 必须是布尔值") + if rule.get("has_current_state_condition") is not True: + errors.append(f"{item_label}.has_current_state_condition 必须为 true") + if rule_id in expected: + expected_source, expected_target = expected[rule_id] + if rule.get("source_status") != expected_source: + errors.append(f"{item_label}.source_status 应为 {expected_source}") + if rule.get("target_status") != expected_target: + errors.append(f"{item_label}.target_status 应为 {expected_target}") + missing = sorted(REQUIRED_RULE_IDS - seen) + if missing: + errors.append(f"{label} 缺少规则实例:" + "、".join(missing)) + + +def validate_task_rule_instances(rules: Any, label: str, errors: list[str]) -> None: + if not isinstance(rules, list): + errors.append(f"{label} 必须是数组") + return + seen: set[str] = set() + orders: set[int] = set() + for index, rule in enumerate(rules): + item_label = f"{label}[{index}]" + if not isinstance(rule, dict): + errors.append(f"{item_label} 必须是对象") + continue + rule_id = rule.get("id") + if not is_text(rule_id): + errors.append(f"{item_label}.id 不能为空") + continue + if rule_id in seen: + errors.append(f"{label} 规则实例重复:{rule_id}") + seen.add(rule_id) + if rule_id not in REQUIRED_TASK_RULE_IDS: + errors.append(f"{label} 未知任务规则实例:{rule_id}") + if rule.get("mode") not in {"manual", "native_rule", "integration_bridge", "not_supported"}: + errors.append(f"{item_label}.mode 不合法") + for key in ("actual_rule_name", "trigger", "scope", "source_state", "evidence"): + require_text(rule, key, item_label, errors) + for key in ("conditions", "actions"): + values = rule.get(key) + if not isinstance(values, list) or not values or not all(is_text(value) for value in values): + errors.append(f"{item_label}.{key} 必须是非空文本数组") + order = rule.get("order") + if not isinstance(order, int) or order < 1: + errors.append(f"{item_label}.order 必须是正整数") + elif order in orders: + errors.append(f"{label} 规则顺序重复:{order}") + else: + orders.add(order) + if not isinstance(rule.get("enabled"), bool): + errors.append(f"{item_label}.enabled 必须是布尔值") + missing = sorted(REQUIRED_TASK_RULE_IDS - seen) + if missing: + errors.append(f"{label} 缺少任务规则实例:" + "、".join(missing)) + + +def validate_project( + project: Any, index: int, lifecycle: list[str], errors: list[str] +) -> None: + label = f"projects[{index}]" + if not isinstance(project, dict): + errors.append(f"{label} 必须是对象") + return + for key in ("name", "work_item_type", "release_status", "final_status"): + require_text(project, key, label, errors) + if project.get("release_status") != lifecycle[11]: + errors.append(f"{label}.release_status 必须与 lifecycle[11] 一致") + if project.get("final_status") != lifecycle[12]: + errors.append(f"{label}.final_status 必须与 lifecycle[12] 一致") + + labels = project.get("labels_observed") + if not isinstance(labels, list) or not REQUIRED_LABELS.issubset(set(labels)): + errors.append(f"{label}.labels_observed 必须包含:分析、设计、开发、测试") + + repositories = project.get("repositories") + if not isinstance(repositories, list) or not repositories: + errors.append(f"{label}.repositories 至少包含一个仓库") + else: + for repo_index, repo in enumerate(repositories): + repo_label = f"{label}.repositories[{repo_index}]" + if not isinstance(repo, dict): + errors.append(f"{repo_label} 必须是对象") + continue + for key in ("name", "role", "integration_branch", "evidence"): + require_text(repo, key, repo_label, errors) + if repo.get("integration_branch") not in {"dev", "develop"}: + errors.append(f"{repo_label}.integration_branch 只能是实际存在的 dev/develop") + for key in ("codeup_integrated", "required_for_mr_gate"): + if not isinstance(repo.get(key), bool): + errors.append(f"{repo_label}.{key} 必须是布尔值") + + pipeline = project.get("test_pipeline") + if not isinstance(pipeline, dict): + errors.append(f"{label}.test_pipeline 必须是对象") + else: + for key in ("name", "environment", "evidence"): + require_text(pipeline, key, f"{label}.test_pipeline", errors) + for key in ( + "is_clone_for_callback_poc", + "original_pipeline_unchanged", + "docker_success_callback_enabled", + ): + if not isinstance(pipeline.get(key), bool): + errors.append(f"{label}.test_pipeline.{key} 必须是布尔值") + security = pipeline.get("callback_security") + if not isinstance(security, dict): + errors.append(f"{label}.test_pipeline.callback_security 必须是对象") + else: + security_keys = ( + "signed", + "timestamp_checked", + "replay_protected", + "idempotent_by_execution_id", + "failure_retry_tested", + ) + for key in security_keys: + if not isinstance(security.get(key), bool): + errors.append(f"{label}.test_pipeline.callback_security.{key} 必须是布尔值") + if pipeline.get("docker_success_callback_enabled") is True: + if pipeline.get("is_clone_for_callback_poc") is not True: + errors.append(f"{label} 启用回调时必须使用test流水线副本") + if pipeline.get("original_pipeline_unchanged") is not True: + errors.append(f"{label} 启用回调时必须保持原流水线不变") + if any(security.get(key) is not True for key in security_keys): + errors.append(f"{label} 启用回调时必须完成全部安全与重试验证") + + release = project.get("release_evidence") + if not isinstance(release, dict): + errors.append(f"{label}.release_evidence 必须是对象") + else: + if release.get("mode") not in VALID_RELEASE_EVIDENCE_MODES: + errors.append(f"{label}.release_evidence.mode 不合法") + for key in ("target_environment", "evidence"): + require_text(release, key, f"{label}.release_evidence", errors) + if release.get("test_success_is_production_release") is not False: + errors.append(f"{label}.release_evidence.test_success_is_production_release 必须为 false") + + test_plan = project.get("test_plan") + if not isinstance(test_plan, dict): + errors.append(f"{label}.test_plan 必须是对象") + else: + for key in ("name", "evidence"): + require_text(test_plan, key, f"{label}.test_plan", errors) + if not isinstance(test_plan.get("requirement_formally_related"), bool): + errors.append(f"{label}.test_plan.requirement_formally_related 必须是布尔值") + states = test_plan.get("case_states_observed") + if not isinstance(states, list) or not states or not all(is_text(v) for v in states): + errors.append(f"{label}.test_plan.case_states_observed 必须是非空文本数组") + + defect = project.get("defect_workflow") + if not isinstance(defect, dict): + errors.append(f"{label}.defect_workflow 必须是对象") + else: + for key in ("fixed_status", "closed_status", "reopen_status", "evidence"): + require_text(defect, key, f"{label}.defect_workflow", errors) + if defect.get("fixed_is_terminal") is not False: + errors.append(f"{label}.defect_workflow.fixed_is_terminal 必须为 false") + if defect.get("tester_retest_required") is not True: + errors.append(f"{label}.defect_workflow.tester_retest_required 必须为 true") + + validate_rule_instances(project.get("rule_instances"), lifecycle, f"{label}.rule_instances", errors) + validate_task_rule_instances( + project.get("task_rule_instances"), f"{label}.task_rule_instances", errors + ) + validate_control_list(project.get("control_coverage"), f"{label}.control_coverage", errors) + if not isinstance(project.get("adjunct_automations"), list): + errors.append(f"{label}.adjunct_automations 必须是数组,可为空") + + +def validate(data: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if data.get("schema_version") != 3: + errors.append("schema_version 必须为 3;旧版台账不包含阶段任务闭环") + for key in ("organization", "work_item_type"): + require_text(data, key, "profile", errors) + + lifecycle = data.get("lifecycle") + if not isinstance(lifecycle, list) or len(lifecycle) != 13 or not all(is_text(v) for v in lifecycle): + errors.append("lifecycle 必须包含 13 个非空有序状态") + lifecycle = [str(index) for index in range(13)] + elif len(set(lifecycle)) != len(lifecycle): + errors.append("lifecycle 状态名称不能重复") + + labels = data.get("requirement_labels") + if not isinstance(labels, list) or not REQUIRED_LABELS.issubset(set(labels)): + errors.append("requirement_labels 必须包含:分析、设计、开发、测试") + + policy = data.get("automation_policy") + if not isinstance(policy, dict): + errors.append("automation_policy 必须是对象") + else: + for key in ("require_current_state_condition", "inspect_rule_order_and_cascades"): + if policy.get(key) is not True: + errors.append(f"automation_policy.{key} 必须为 true") + for key in ("allow_automatic_final_close_without_acceptance", "zero_related_items_are_complete"): + if policy.get(key) is not False: + errors.append(f"automation_policy.{key} 必须为 false") + events = policy.get("development_start_event_candidates") + if not isinstance(events, list) or not events or not all(is_text(v) for v in events): + errors.append("automation_policy.development_start_event_candidates 不能为空") + if policy.get("require_code_assets_linked_to_requirement_and_development_task") is not True: + errors.append( + "automation_policy.require_code_assets_linked_to_requirement_and_development_task 必须为 true" + ) + idempotency = policy.get("stage_task_creation_idempotency_key_components") + if not isinstance(idempotency, list) or not idempotency or not all(is_text(v) for v in idempotency): + errors.append("automation_policy.stage_task_creation_idempotency_key_components 不能为空") + + test_policy = data.get("test_closure_policy") + if not isinstance(test_policy, dict): + errors.append("test_closure_policy 必须是对象") + else: + if test_policy.get("defect_fixed_is_terminal") is not False: + errors.append("test_closure_policy.defect_fixed_is_terminal 必须为 false") + if test_policy.get("require_tester_retest") is not True: + errors.append("test_closure_policy.require_tester_retest 必须为 true") + for key in ("accepted_case_states", "rejected_case_states"): + values = test_policy.get(key) + if not isinstance(values, list) or not values or not all(is_text(v) for v in values): + errors.append(f"test_closure_policy.{key} 不能为空") + + projects = data.get("projects") + if not isinstance(projects, list) or not projects: + errors.append("projects 至少包含一个项目") + else: + names: set[str] = set() + for index, project in enumerate(projects): + validate_project(project, index, lifecycle, errors) + if isinstance(project, dict) and is_text(project.get("name")): + name = project["name"] + if name in names: + errors.append(f"项目名称重复:{name}") + names.add(name) + + sensitive = walk_sensitive_keys(data) + if sensitive: + errors.append("配置中禁止出现敏感字段:" + "、".join(sensitive)) + if "请填写" in json.dumps(data, ensure_ascii=False): + errors.append("仍存在“请填写”占位符") + return errors + + +def render(data: dict[str, Any]) -> str: + lines = [ + "# 云效需求生命周期自动化执行计划", + "", + f"- 组织:{data['organization']}", + f"- 工作项类型:{data['work_item_type']}", + f"- 项目数:{len(data['projects'])}", + "- 完整性口径:每项目12个需求规则实例、8个任务规则实例、31个闭环控制项。", + "- 安全边界:不修改现有生产流水线;不记录任何凭据或密钥。", + "", + ] + for project in data["projects"]: + lines.extend([ + f"## 项目:{project['name']}", + "", + f"- 发布状态:{project['release_status']}", + f"- 最终状态:{project['final_status']}", + f"- test流水线:{project['test_pipeline']['name']}", + f"- 发布证据方式:{project['release_evidence']['mode']}", + "", + "### 仓库覆盖", + "", + "| 仓库 | 角色 | 集成分支 | Codeup已集成 | 纳入全部MR门禁 | 证据 |", + "|---|---|---|---|---|---|", + ]) + for repo in project["repositories"]: + lines.append( + f"| {repo['name']} | {repo['role']} | {repo['integration_branch']} | " + f"{'是' if repo['codeup_integrated'] else '否'} | " + f"{'是' if repo['required_for_mr_gate'] else '否'} | {repo['evidence']} |" + ) + lines.extend([ + "", + "### R01–R12实际规则实例", + "", + "| ID | 实现 | 实际规则/门禁 | 触发 | 流转 | 条件 | 顺序 | 启用 | 证据 |", + "|---|---|---|---|---|---|---:|---|---|", + ]) + for rule in sorted(project["rule_instances"], key=lambda item: item["order"]): + conditions = ";".join(rule["conditions"]) + lines.append( + f"| {rule['id']} | {rule['mode']} | {rule['actual_rule_name']} | {rule['trigger']} | " + f"{rule['source_status']} → {rule['target_status']} | {conditions} | {rule['order']} | " + f"{'是' if rule['enabled'] else '否'} | {rule['evidence']} |" + ) + lines.extend([ + "", + "### TK01–TK08任务规则实例", + "", + "| ID | 实现 | 实际规则/门禁 | 触发 | 作用域 | 源状态 | 条件 | 动作 | 顺序 | 启用 | 证据 |", + "|---|---|---|---|---|---|---|---|---:|---|---|", + ]) + for rule in sorted(project["task_rule_instances"], key=lambda item: item["order"]): + conditions = ";".join(rule["conditions"]) + actions = ";".join(rule["actions"]) + lines.append( + f"| {rule['id']} | {rule['mode']} | {rule['actual_rule_name']} | {rule['trigger']} | " + f"{rule['scope']} | {rule['source_state']} | {conditions} | {actions} | {rule['order']} | " + f"{'是' if rule['enabled'] else '否'} | {rule['evidence']} |" + ) + lines.extend([ + "", + "### 31项完整性台账", + "", + "| 控制ID | 实现方式 | 启用 | 观察证据 | 负责人 | 回退方案 |", + "|---|---|---|---|---|---|", + ]) + for control in sorted(project["control_coverage"], key=lambda item: item["id"]): + lines.append( + f"| {control['id']} | {control['mode']} | {'是' if control['enabled'] else '否'} | " + f"{control['evidence']} | {control['owner']} | {control['fallback']} |" + ) + adjunct = project["adjunct_automations"] + lines.extend([ + "", + "### 范围外通用自动化盘点", + "", + "- " + (";".join(map(str, adjunct)) if adjunct else "未纳入;如需通知、自动指派、SLA、字段同步等,应单独授权和设计。"), + "", + ]) + lines.extend([ + "## 执行检查", + "", + "- [ ] 两个或多个项目的规则实例、控制台账和证据已分别填写。", + "- [ ] 已逐条读取真实触发配置、规则顺序、启用状态和执行账号。", + "- [ ] 已检查后继规则、重复规则和跨阶段连锁流转。", + "- [ ] 已验证失败用例、缺陷已修复、复测通过/失败时双方状态一致。", + "- [ ] 已逐仓库确认真实dev/develop分支、正式关联和全部MR门禁。", + "- [ ] 已区分test部署、生产发布、发布变更和业务验收。", + "- [ ] 已执行正向、负向、异步、多仓库、失败重试和旧规则连锁测试。", + "", + ]) + return "\n".join(lines) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + subparsers = parser.add_subparsers(dest="command", required=True) + validate_parser = subparsers.add_parser("validate", help="验证项目配置") + validate_parser.add_argument("profile", type=Path) + render_parser = subparsers.add_parser("render", help="生成 Markdown 执行计划") + render_parser.add_argument("profile", type=Path) + render_parser.add_argument("--output", required=True, type=Path) + args = parser.parse_args() + + try: + data = load_profile(args.profile) + errors = validate(data) + except (OSError, json.JSONDecodeError, ValueError) as error: + print(f"配置读取失败:{error}", file=sys.stderr) + return 2 + if errors: + for error in errors: + print(f"[错误] {error}", file=sys.stderr) + return 1 + if args.command == "validate": + print( + f"[通过] 配置有效:{len(data['projects'])} 个项目;" + "每项目12个需求规则实例、8个任务规则实例、31个闭环控制项。" + ) + return 0 + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(render(data), encoding="utf-8") + print(f"[完成] 已生成执行计划:{args.output}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/AGENTS.md b/AGENTS.md index 084b079..80969ba 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,6 +25,7 @@ | 项目资料和文档 | `src/resources/` | `rules/resource-management-guide.md` | | 画布 | `src/prototypes//canvas.excalidraw`、`canvas-assets/` | 原型画布和画布素材 | | 业务逻辑规格 | `src/prototypes//.spec/.md` | `rules/business-logic-documentation-guide.md` | +| AutoPRD + 标注目录 | `.spec/requirements-prd.md` + `annotation-source.json`;定稿写第 10 章功能变更 | skill `oneos-autoprd`;规则 `.cursor/rules/oneos-autoprd-sync.mdc` | | 对象存储发布 URL | `.axhub/make/axhub.config.json` → `cloudPublishing.s3` | `rules/cloud-publish-url-guide.md` | | 原型标注布局 | `src/common/prototype-annotation-host.tsx` | `rules/prototype-annotation-layout-guide.md` | diff --git a/docs/superpowers/plans/2026-07-20-h5-vehicle-assets.md b/docs/superpowers/plans/2026-07-20-h5-vehicle-assets.md new file mode 100644 index 0000000..be0f1ba --- /dev/null +++ b/docs/superpowers/plans/2026-07-20-h5-vehicle-assets.md @@ -0,0 +1,65 @@ +# 车辆资产 H5 Implementation Plan + +> **For agentic workers:** Execute task-by-task. Checkboxes track progress. Prefer inline execution in this repo (React prototype, no separate unit-test harness). + +**Goal:** 新建独立 H5 原型 `oneos-h5-vehicle-assets`:只读查车 + KPI/异常筛选 + 详情多 Tab,口径对齐 PC `vehicle-management`。 + +**Architecture:** PhoneShell 手机壳 + 列表/详情双视图;数据与 KPI 规则复用 PC `vehicles.json` 与 `utils/vehicle.ts`(及各记录 utils);UI 不复用 `vm-page` 宽表 DOM。 + +**Tech Stack:** React + TypeScript、本地 JSON 种子、Axhub annotation、`nav:sync` 注册导航。 + +--- + +## File map + +| Path | Responsibility | +|------|----------------| +| `src/prototypes/oneos-h5-vehicle-assets/index.tsx` | 入口:列表/详情状态、筛选、分页 | +| `styles/index.css` | H5 样式(ONE-OS 绿) | +| `components/PhoneShell.tsx` | 手机预览壳 | +| `components/VehicleList.tsx` | 搜索、KPI、筛选抽屉、卡片 | +| `components/VehicleCard.tsx` | 单车卡片 | +| `components/FilterSheet.tsx` | 证照/保险状态抽屉 | +| `components/VehicleDetail.tsx` | 详情顶栏 + Tab | +| `components/detailTabs.tsx` | 11 个只读 Tab 内容 | +| `utils/filter.ts` | 列表过滤封装 | +| `.spec/requirements-prd.md` | AutoPRD | +| `annotation-source.json` | 标注目录 | +| `nav-menu.json` + `nav:sync` | 导航注册 | + +--- + +### Task 1: Scaffold + list shell + +- [ ] Create prototype files with PhoneShell, list layout, import vehicles +- [ ] Wire KPI via `matchKpi` / `countKpi` / `isLicenseExpired` +- [ ] Search + license/insurance filter + cards + page size 20 + +### Task 2: Detail multi-tab readonly + +- [ ] Detail view with 11 tabs matching PC `DETAIL_TABS` +- [ ] Basic/model/license as field grids; record tabs via PC utils filtered by plate +- [ ] No edit/action buttons + +### Task 3: Nav + PRD + annotation + +- [ ] Add nav item under 车辆资产 folder as「车辆资产(H5)」 +- [ ] Full AutoPRD + annotation-source PRD node +- [ ] Run `npm run nav:sync -- --prototype oneos-h5-vehicle-assets` + +### Task 4: Smoke check + +- [ ] Open `/prototypes/oneos-h5-vehicle-assets` and verify list → detail → tabs + +--- + +## Spec coverage + +| Spec item | Task | +|-----------|------| +| Independent H5 prototype | 1 | +| Search + KPI + anomaly filter + cards | 1 | +| Detail 11 tabs readonly | 2 | +| No edit including admin | 1–2 | +| Nav + PRD | 3 | +| KPI/anomaly口径 = PC | 1 (shared utils) | diff --git a/docs/superpowers/specs/2026-07-20-h5-vehicle-assets-design.md b/docs/superpowers/specs/2026-07-20-h5-vehicle-assets-design.md new file mode 100644 index 0000000..68508e5 --- /dev/null +++ b/docs/superpowers/specs/2026-07-20-h5-vehicle-assets-design.md @@ -0,0 +1,189 @@ +# 车辆资产 H5 适配 · 设计规格 + +| 项 | 说明 | +|---|---| +| 日期 | 2026-07-20 | +| 状态 | 已确认(2026-07-20) | +| 原型 ID | `oneos-h5-vehicle-assets` | +| 关联 PC | `vehicle-management`(车辆资产) | +| 参考 H5 | `oneos-h5-h2-order`(PhoneShell、交互密度) | + +--- + +## 1. 背景与目标 + +App 除推送外全部接入 H5。PC「车辆资产」为宽表中后台,不适合同一页响应式塞进手机。采用**独立 H5 原型**,口径与 PC 对齐,交互按手机重做。 + +### 成功标准 + +1. App / 手机浏览器打开 H5 地址,不进入 PC 宽表页。 +2. KPI 分类与证照/保险异常口径与 PC 一致。 +3. 详情多 Tab 可浏览,**全程无编辑入口**(含 Admin)。 +4. 按车牌搜索可定位车辆。 + +--- + +## 2. 已确认产品决策 + +| 决策 | 结论 | +|---|---| +| 落地方式 | 独立原型 `oneos-h5-vehicle-assets`(方案 1) | +| 角色 | 全角色同一套界面 | +| 编辑 | **手机端不做编辑**(含 Admin;改数仅 PC) | +| 异常 | 只看:可筛、详情标红/提示;不催办、不跳转业务处理页 | +| 列表 | 搜索 + 横滑 KPI + 筛选抽屉 + 车辆卡片(对齐 PC 信息量) | +| 详情 | 多 Tab 对齐 PC,只读 | + +--- + +## 3. 范围 + +### 3.1 第一期做 + +| 模块 | 内容 | +|---|---| +| 列表 | 顶栏标题「车辆资产」+ 车牌搜索;KPI 横滑;证照/保险等筛选;卡片列表;**触底异步加载更多**(无翻页、不展示总辆数) | +| 详情 | 返回 + 车牌(透明顶栏贴一体背景);浮起白卡摘要(营运状态、标签、里程、证照/保险异常);横向 Tab + 只读内容;**无**底部悬浮 Tab | +| 数据 | 复用 PC `vehicles.json` 及关联记录 JSON;KPI/异常规则与 PC 同源工具函数(可抽共享或复制后标注同源) | +| 壳层 | PhoneShell 桌面预览;真机/WebView 全屏内容区 | +| 导航 | 注册到原型导航「运维管理」下,与 PC 车辆资产并列区分(如「车辆资产(H5)」) | + +### 3.2 第一期不做 + +- 任意字段编辑、运维负责人修改、运营城市修改 +- 批量导入 / 导出真实能力(不做入口,或仅隐藏) +- 催办、抄送、跳转证照管理/保险采购等业务页(不做;外链 Toast 占位也不做,避免误导) +- 在租车型占比弹窗(PC 尾部 KPI;H5 第一期可不做,避免干扰主路径) +- PC 页响应式改造 + +--- + +## 4. 信息架构 + +```text +列表 List +├── 顶栏:标题 + 搜索(车牌) +├── KPI 横滑条(点选切换分类,计数与 PC 一致) +├── 筛选入口 → 底部抽屉(证照状态、保险状态;可扩展运营状态等) +└── 车辆卡片列表 → 进入详情 + +详情 Detail +├── 顶栏:返回 + 车牌号(透明,贴一体渐变底;无底栏) +├── 摘要卡:车牌 / 品牌型号 / 营运状态 / 来源·城市·项目标签 / 里程 / 证照·保险异常 +└── Tab(横滑):与 PC DETAIL_TABS 一致 + 基本信息 / 型号参数 / 证照信息 / 保险记录 / 租赁记录 / + 事故记录 / 故障记录 / 违章记录 / 异动记录 / 调拨记录 / 年审记录 +``` + +--- + +## 5. KPI 与异常口径(与 PC 一致) + +| KPI | 口径 | +|---|---| +| 所有营运车辆 | 非「非运营车辆」且非「退出运营」 | +| 租赁车辆 | `operateStatus === '租赁'` | +| 物流车辆 | `operateStatus === '物流'` | +| 库存车辆 | `operateStatus` 为「可运营」或「待运营」 | +| 非运营车辆 | `vehicleLedgerType === '非运营车辆'` | +| 退出运营车辆 | `operateStatus === '退出运营'` | + +| 异常筛选 | 口径 | +|---|---| +| 证照异常 | `inspectExpire` 早于今天 | +| 保险异常 | `insuranceStatus === '异常'` | + +筛选与 KPI **叠加**:先 KPI,再筛选条件。 + +--- + +## 6. 列表卡片字段(建议) + +Boutique 横卡布局:左侧信息、右侧状态区(无车辆照片)。至少展示: + +- 车牌(主标题) +- 品牌·型号 +- 营运状态标签(语义色) +- 证照/保险异常角标(有异常时显示) +- 车辆来源 / 运营城市 / 项目或「车辆在库」标签 +- 里程(有数据时,右侧次要数字) + +点击整卡进入详情。 + +### 6.1 详情摘要卡(与列表同语义) + +浮起白卡(同列表卡片圆角/阴影),**不加**车辆实景大图: + +- 车牌(大标题)+ 品牌·型号 +- 营运状态(圆点 + 与列表一致的文案) +- 车辆来源 / 运营城市 / 项目或备车等上下文标签 +- 里程(有数据时) +- 证照/保险异常角标(有异常时) + +Tab 选中态对齐 Chip(主色字 + 浅底),内容区为白卡 Section / 记录列表;字段 label 12 / value 15。 + +--- + +## 7. 视觉与设计基底 + +| 项 | 约定 | +|---|---| +| 交互壳 | 对齐 `oneos-h5-h2-order` PhoneShell 结构(桌面预览);真机全屏内容区 | +| 主题 | **SaaS Boutique App 壳**:对齐参考图 A(顶栏左标题 + 铃铛/加号、KPI 图标卡、筛选项行、实景横卡、底部五 Tab) | +| 色彩 | Primary `#007AFF`;页面灰 `#F2F3F5`;卡片白底;语义绿/橙/红 | +| 风格 | 一体式浅蓝渐变底(列表与详情共用;顶栏透明不分色带);KPI 图标卡 + Chip;列表/详情浮起白卡(无车辆照片) | +| 字体 | **苹方 PingFang SC**(回退 Hiragino Sans GB / 微软雅黑 / system) | +| 字号阶梯 | 标题 22 / 详情车牌 26 / KPI 数 20 / 列表车牌 17 / 正文 15 / 车型与 Chip 12 / 标签与底栏 11 | +| 字重 | 标题/车牌 800(系统会映射为 Semibold/Medium);区块标题 700;正文 400–500;标签 600 | +| 壳层约定 | **列表**底部为悬浮胶囊 Tab(毛玻璃);「工作台/任务/消息/我的」Toast 占位;本页「车辆」选中。**详情**不挂底栏,仅顶栏返回 + 车牌 | +| 触控 | ≥ 44×44px(Chip/图标区可视觉更紧,点击热区保留);`safe-area-inset` | +| 动效 | ≤ 240ms;尊重 `prefers-reduced-motion` | + +--- + +## 8. 技术约定(实现阶段) + +| 项 | 约定 | +|---|---| +| 目录 | `src/prototypes/oneos-h5-vehicle-assets/` | +| 入口 | `index.tsx`;样式独立 `styles/index.css` | +| 标注 | `annotation-source.json` + `.spec/requirements-prd.md`(实现时按 oneos-autoprd 同步) | +| 与 PC 关系 | 逻辑口径同源;UI 组件不直接复用 `vm-page` 宽表 DOM | +| App 打开 | 固定 H5 URL(发布后 `{baseUrl}/oneos-h5-vehicle-assets/index.html` 形态以云发布规则为准) | + +--- + +## 9. 用户故事(摘要) + +1. **作为**任意登录用户,**我想**在手机上按车牌找到车,**以便**外勤快速核对车辆信息。 +2. **作为**运维/业务,**我想**用 KPI 与证照/保险异常筛选缩小范围,**以便**发现需跟进车辆(跟进在 PC 或其他模块完成)。 +3. **作为**任意用户,**我想**在详情里切换各业务 Tab 只读浏览,**以便**不回 PC 也能看全档案摘要与记录。 + +--- + +## 10. 验收清单 + +- [ ] 独立原型可在导航打开,桌面 PhoneShell / 窄屏可用 +- [ ] KPI 六类计数与同数据下 PC 一致 +- [ ] 证照异常、保险异常筛选结果与 PC 规则一致 +- [ ] 搜索车牌可过滤列表 +- [ ] 列表无总辆数、无翻页;触底异步加载更多 +- [ ] 列表进详情:一体渐变底连续,无白顶栏色带割裂;摘要为浮起白卡(状态/标签/里程/异常与列表同语义) +- [ ] 详情无底部悬浮 Tab;顶栏仅返回 + 车牌 +- [ ] 详情 11 个 Tab 均可进入且只读,无编辑按钮 +- [ ] 无导入/导出/修改运维负责人等 PC 写操作入口 + +--- + +## 11. 修订记录 + +| 版本 | 日期 | 说明 | +|---|---|---| +| v0.7 | 2026-07-21 | 列表改为触底异步加载;移除翻页与总辆数展示 | +| v0.6 | 2026-07-21 | 对齐参考图 A 结构:App 顶栏/底栏、KPI 图标卡、筛选项行、实景横卡;写操作仍 Toast 占位 | +| v0.5 | 2026-07-21 | 视觉切换至 SaaS Boutique(Electric Blue + KPI 摘要卡 + 横卡列表);产品范围不变 | +| v0.5 | 2026-07-21 | H5 按参考图标注尺寸落地(16/8/12/44 栅格) | +| v0.4 | 2026-07-20 | 手机交互增强:粘性工具栏、safe-area、系统返回、空态引导 | +| v0.3 | 2026-07-20 | 视觉切换至 `src/themes/apple`(Action Blue + utility card) | +| v0.2 | 2026-07-20 | 视觉基底补充:Premium SaaS Mobile 质感(品牌绿不变) | +| v0.1 | 2026-07-20 | 需求访谈确认后首版设计规格 | diff --git a/docs/superpowers/specs/2026-07-21-h5-mileage-task-card-design.md b/docs/superpowers/specs/2026-07-21-h5-mileage-task-card-design.md new file mode 100644 index 0000000..b30f560 --- /dev/null +++ b/docs/superpowers/specs/2026-07-21-h5-mileage-task-card-design.md @@ -0,0 +1,16 @@ +# H5 车辆卡片 · 里程任务区设计 + +| 项 | 说明 | +|---|---| +| 日期 | 2026-07-21 | +| 状态 | 已确认 | +| 原型 | `oneos-h5-vehicle-assets` | +| 规格全文 | `src/prototypes/oneos-h5-vehicle-assets/.spec/mileage-task.md` | + +## 结论摘要 + +- 列表每张车卡在标签行下展示「里程任务」区块(全宽)。 +- 有任务:标题 + 完成百分比 + 进度条 +「剩余 xx km · 预计 xx 天内完成」。 +- 无任务:标题 +「暂无里程任务」。 +- 预计天数 = 剩余里程 ÷ 近 7 个活跃日(日里程 > 0)平均日里程,向上取整;无活跃日则「暂无法预计」。 +- 业务来源:采购合同关联批次车辆里程要求,经任务工单下发(原型用本地演示种子,未接真实 API)。 diff --git a/docs/superpowers/specs/2026-07-21-vehicle-assets-ui-measure.md b/docs/superpowers/specs/2026-07-21-vehicle-assets-ui-measure.md new file mode 100644 index 0000000..9e87e42 --- /dev/null +++ b/docs/superpowers/specs/2026-07-21-vehicle-assets-ui-measure.md @@ -0,0 +1,110 @@ +# 车辆资产 UI · 边距与尺寸标注(估算) + +> 来源:参考截图标注 +> 基准宽:**390pt**(iPhone 常见逻辑宽) +> 方法:按屏内容像素测量后换算,并按 **8pt 栅格**圆整 +> 标注图:`docs/superpowers/specs/2026-07-21-vehicle-assets-ui-measure.png` + +## 1. 页面骨架 + +| 元素 | 尺寸 / 边距 | 备注 | +|------|-------------|------| +| 内容区宽度 | **390** | 逻辑宽 | +| 左右页边距 | **16** | 搜索、KPI、列表、Chip 统一 | +| 主内容可用宽 | **358** | 390 − 16×2 | +| 区块纵向间距 | **12–16** | 搜索→KPI、Chip→列表 | + +## 2. 顶栏 + +| 元素 | 尺寸 | 备注 | +|------|------|------| +| 标题行高 | **40** | 「车辆资产」+ 右侧操作 | +| 标题字号 | **≈22–24 / Semibold** | SF Pro Display 观感 | +| 通知铃 / 加号 | **36×36** | 触控建议扩到 44 | +| 右侧图标间距 | **8** | | + +## 3. 搜索栏 + +| 元素 | 尺寸 | 备注 | +|------|------|------| +| 高度 | **44** | 满足触控下限 | +| 宽度 | **358** | 满宽 − 页边距 | +| 圆角 | **12** | | +| 左右内边距 | **12** | 左放大镜 / 右扫码 | +| 占位字号 | **15–17** | | + +## 4. KPI 横滑卡片 + +| 元素 | 尺寸 | 备注 | +|------|------|------| +| 卡片宽 × 高 | **90 × 100** | 约露出 4 张 | +| 卡片间距 | **8** | | +| 圆角 | **12–16** | | +| 主数字 | **≈20–22 / Bold** | tabular-nums | +| 分类标题 | **12** | | +| 底部分页点 | **Ø6 / 间距 6** | | + +## 5. 筛选 Chip 行 + +| 元素 | 尺寸 | 备注 | +|------|------|------| +| Chip 高 | **32** | 若偏难点可提到 36 | +| Chip 宽 | **≈78–90** | 随文案 | +| Chip 间距 | **8** | | +| 圆角 | **8** | | +| 字号 | **13–14** | 选中态浅蓝底 | + +## 6. 车辆列表卡片 + +| 元素 | 尺寸 | 备注 | +|------|------|------| +| 卡片宽 × 高 | **358 × 118** | | +| 卡片圆角 | **16** | | +| 卡片垂直间距 | **12** | | +| 卡片内边距 | **12** | 四边一致 | +| 车辆缩略图 | **100 × 74** | 圆角 ≈8 | +| 图文间距 | **12** | 缩略图 ↔ 文案 | +| 车牌字号 | **≈17–18 / Semibold** | | +| 车型副文 | **13–14** | 次要灰 | +| Tag 高 | **≈22** | 圆角 6 | +| Tag 间距 | **6–8** | | +| 右侧状态区 | **≈72 宽** | 状态点 + 里程 + 异常标 | +| 右箭头 | **12–16** | | + +## 7. 底栏 + +| 元素 | 尺寸 | 备注 | +|------|------|------| +| 底栏总高 | **≈83** | 含 Home Indicator ≈34 | +| 可点区域高 | **≈49** | | +| Tab 等分 | **5** | | +| 图标 | **24×24** | | +| 文案 | **10** | | +| 中心「工作台」 | **⌀56** | 略浮起 | +| 消息红点 | **≈16–18** | | + +## 8. 推荐实现 Token(可直接写 CSS) + +```css +--page-pad: 16px; +--gap-sm: 8px; +--gap-md: 12px; +--gap-lg: 16px; +--radius-chip: 8px; +--radius-control: 12px; +--radius-card: 16px; +--search-h: 44px; +--chip-h: 32px; +--kpi-w: 90px; +--kpi-h: 100px; +--thumb-w: 100px; +--thumb-h: 74px; +--card-pad: 12px; +--touch-min: 44px; +``` + +## 9. 说明 + +- 数值为**视觉估算**,不是设计工具导出的精确标注。 +- 若要以设计稿为准,请用 Figma / Sketch 再量一次关键项(页边距、卡片高、缩略图)。 +- 手机实现时:**触控热区 ≥ 44×44**,即使视觉图标只有 24/36。 diff --git a/docs/superpowers/specs/2026-07-21-vehicle-assets-ui-measure.png b/docs/superpowers/specs/2026-07-21-vehicle-assets-ui-measure.png new file mode 100644 index 0000000000000000000000000000000000000000..41b8451d89e3d89477e0c574fb876043745819ee GIT binary patch literal 467875 zcmb@tbyQT{`v(ewbSlyvf=WquhqNe2gEUBYGf0PYNtb|hNewATcX!9oL-)+w=Z)_6VeutT5Sr-1x&9R1u);o3yNqjD?k@wgmlzJMz?(=Cm6>NS4QH zu{ge|4lyPHtwaD4@{=RQ&y6=oPf#)bdA~%Ce24$f0%S5x&P{zN1`uC1Ue{jT!AvYFx;zzbA<>9Sv#n&W>@)<@;>xa82G zf}zuBzp`MN<8r@mnd!0m6c-maI8KKCY>URobMRF${I-we<+BJWl@+(Sl_mG1e+SEq zYBZ?O9U3igzgOUyJ;wu!=3k`^PJ+7}FV5`5lz4jB%vFBU)FcQdWRP?7(pWlTUWH5J zi;9>JZbxynTOH0;FlT$7%hOQ7Gu9rjYlqkcPdib>Z`Xr55U_>}hxYv(M5{{&?7HQ& zi)PIS>gTcQHRk|&$Y@Qxr~<9H0gvEW^bWf8i!F`2hn6+Z6_k^ zY;8GJ>MnB(NI#yPV9?R{-%cQ-D6B90U!Z;^sI{8N zhuy5@5Y!^z4@Q1hyuopeYyOb4J{qQAU#JgrrpsvaJ2;MErgtq;c8#Ii1B06uVvg5tN!{o@9G8FDb0@h2Bdj}C9B#F0_+Q9TH^6dy&lM2&a|U75 zLIv33m~>@Ysl{3BtDm{jzV(=_P|ol@?61=xnvQ|V-YO~1rVC#7KFk5WHjR5!zy)@X zn~!lH(E6El_q%DkG=?Dj7V3Y!0Ff*Dsi-3)_Uh3GZ>UmgRs zp~eJKt_G01$JsD?Tz0(%+oSoK6&m1f3K)0YG?g=MSp?312*U~v*RVN;!H1O~*iDBT zwcB(&gM18q@HFr<7c}1Cb~u}!u4`GTJ?k%*z(|~y|L;D@4I4o8MpL|C@)>@+w$S7> zwL6kAbieOlH<&a4ZMzYpGdo#s{#c~U-q^yw=6e{<5VN=YvtdE4wVpJ|p_SyIQtWJ4IHgyV{r`M(BV=(#pf016I8D%5P^-v*RxMcoq&3 zM|e&DjNjY39^p4io?J<@%sxx)cx=unShNs7yXLo@2bNANz33IjW;DN{xcBAWOqrIE z*A9ou)OQiDbGJEzp;}w|_}dGi)6lqLR@-(pLkv_XT!l9}Vc| zEoN}sBnYf`Xdw^mpSYrZkuh$ZAc#%6JCM_j9Dg?_C%+IGGjJKv42}b0639j!4y|Xp z#X7AAKp?I0Z5@q{WjT;~!nKV2E;hn0D%-BVs1tSEZVvgJG($Z)9#mFm1kC!^>7>yh zu#f*t`kul$u+%?aeSAsf^vBp`ci7Ix=I|1As8$iN5JW{NMa*{Z-W7Z{dWV4Dbcazo zkAHt#uo8_!C1iFu>wQDm`sM0?cgZ4i<*xYx7iG-rZbxfWU`s=t2$K#7eE4?a7*6-P zKn%e-usiF_1}4q8fIZ3#Yz`g`%w>q5_#OP= zs*D)b`0o6Lrl#i1jAjO(9sH(aiK*45vU$+u3EhwK9MSoSR3m?0m)!wRxJ^|XU1XPW z*vRSfDE}HOxtQ;w>cdQmJn?`C=3s3!u3PvdhW-f`PAc3!de&vqZzTta+BR|Rc^q!uZs$zYHP zICk>R>F+#V0wVBj|GS6`<^Oai*p^{!AKbZbzH~d5Y-#H)qcc zfJ{vjOG|yAqQ^i%{qp6D+*xHmW{P<-ybfJhxBBzOFKeHTG}fVDsk|r-_yi5yvHPSY zmO{kyeO`-8hP=;u7XDr+dh@uH=#xWmKFmF4N*}g4wVAo=0KDBV&RKIA>wq?Gv4e0$ zZ$iSElv3dE8-613Xa8cA{!By%?3m`I@_d%LPB&)<%>6Ch501yy9pc=#YT~d*S&}Rx zVC&7#U-mP^A~s3HM|`jW#ITwdM!lTszzkg>l%n>(JyS@0$*zmaOdz#8hLB14qr~2P z3c#i;(TAD3F?hR6EV$BzvTq#hDO=Rzm)-%>czp$gF$Z`rFf_DRp2lx;@D1qGo|II0 zK8r&KjJ?lEHs*++&cN#YF9Q9X`Vt`>hPPnXSo3h)I98nFpRHKPI&^?7?qs{+$dat6 zrKwpsWv!`swth9Y);cCOHCv&lh9L;M{3!Nd`?a(bU*z=kDM{v%<-g+{6*~N;&3X^y z=dH02><*FRUmr2(TA=NErltiCZ!7ehzW^l?7$^uBsH+gS-@QLhw}!@_seR>mVCPk} z-}nAa0VmBm|L;M*c;S={nX5D)iS64wBdw@dW%hq2r7CvkbF+K_1u7y1MTY-r5cg?r z<)L&aj@U^PlvVq*M~S1&dsJ`@u^#00?&0D5_!b&N@GMGpWh_50Z)3@UghTh201*}y z=&|NRoXr6I*w29fhKmh8^>{otJ3Bo)OCZ$__nx>t+lth0^1ez3V8!-;kHDW*kQsXV zackl~g|Y>F3_6&8<+9t_xMXkoFs&xlf3#4Q<gNH8wnySBe_mv<;x9+eCMCtn*I8$G@J*vCIL zGqbbhI&~=oweH8@(o#hctMuIjrXdVE0AS;$m$5~*`wDwOM`I@n2)yKWGgwuAt?9TrUNm1>sZcFP!e)7&Xw2=4!QZs_a8=bs~Z!T3<=2?yX{n(z(m z{kz^E200wic6{HM=*3k8mqJqrq7p@Bib7PlzOG|A)l>f1)o%#fQ3)YM|`*QTv?t zQXr~LdIsl=Kv58{DV|u->pkP{P~DWPNu??PAbmAom{Juk1cOV3sjSnlFQ>+so+-$x z17J3M#bd3ax%sXcVmR$)knkssTP;$qV8Qkp;0Y>)lkx9ve@&^rN@50$W?p~jvNx++ zATi2>0eH*MV`U~JRQ;qL;Fx!oIc)EVcIHiEsfFE#Yt7$e``CXnH-EazD|Eld0Ole4 zAM;h%?zmBGXu&RPQR;aZnjtB1F`8Yk>5COh7YC425WW~^{JUkhIlmUD|4!Z5sLUw#8!FObN^P`-6LAVRF4bpcns zjHIVC8%n95L?@0@$>58o6bvNba^4PyRP<~>d{4%jdJBfH9YJ(6I3(35I;fsCu`27?j z2ER?rHaykk2fMI+IxDGoYJ#|$i|UOHqx7}2ivUVYv&$NOnDiR#qy~1c=^?}nd>;i- z!@t2@2!>yMF?Iv}Z$`;Nf7dqrT_2$6%F6lzoY!|do>^6>M0(~2N?JWZCDX)pz&544JcRQfwFJiYiZJFbj z_eT&DYw2Gi^I-r~Mf~X|HS#94Y)lW5o!1E!!ROIa*GMscAkQQ@rELMqxBe?nuv=W#7L&s2gWB` zg6jdWiPOJV1ElwrlWJhs^*<4w%Xq%F6fy%V+=T5MT+BA}tGEm#mniN<-c^f7=U2KaVDayY(onLnd5TW(rue>{I9)lXvN z%WJ=E@8!c7tW8u%^bLAmzX$uQGYe3Lqu=N>0hGe3Yy&&pdouz-o}QG6JN8; zvGzg-YCN!SbG#20@WnI7oR)!Hpo}4SJr5PRnyv#t3Vi){P>c{3{T`n1(O*)14HOOI zb)!}4x0@&N^3-EXQ67~o-p8h~Fi0TwDD3efWGIDAt$2zQ)#;?!lE!VC5I{i74^R*b z#^bBvOzpwUB(Bka0D1M4XJ5fS8G1szFt(utbDcT2p<3+}UL0w4qM=&P zYyeWFn8$39hHkhJ$kr4U$r1IuXJJ%$Hg++}_`S%QG_>-t$l8RLmNLRz=S18XnQ=@R z{JXXsk;jn9Fkb?KpJ8N2D1{i4rkJl-dRHFsP=#<)^FNZ9$jg2Tu}(k1&Qj~4`^&+TlnUW;0{`UNdi7`(+}TnD4Cua~6;1={hnBT5z|q zrlzd=G)vb8r~mJj59&me5v8R9e9O6yho$3F`>!HDzwl(h5hav}XM+L3P2&P8ajLQZl2Y!z8saFNAqb!|BEWf(L& z?*Ig|#&v)4{@z=ih)KcOT~3l9&S%^$Ypdx!3sJNjuqtaEpl|dal#wD4eB6E z#S7IYX*}5RgSC5cM$=TF3*FeoiN(r~X>L0E55XeD_AO1^-jVXcsasC?b{9~h>@B>D zKb+qnzr9Fl5w;9DUm1pdpxGA}TrQ*5p0OdLV?z=GNg0KmVKKtOmGm)Nl%p>kM#h%B z7^7Ps)FLLsAJV{HM4&p^2B>P|KD}>6PRFHMflu~DEw6tAzB#^`F%_OPk*pzRCMvv4T(EWT4#iDVE~=6k-T;Vn>r9}}DS>exygCcO`044z#l`vg z`SI~M#lrOT^ix0-l_`?c)U+LEa~5og$nYg@=D2Y}dSIvRS3mvcE1P!u_~<^(j2nnda4K9`S-2 zRz+g#u}^QB)}}VU$XHnS1~AhcVxuBMwQOu`EbT3t0shpZ*7BN~j2kR5 zQTgFM>VS5`_^hPpXL)&fY3adSm65FM)5=FPE*&GIwdv`k+eIaC4mvZk+XUZBk8z4Ht! zu2WXstj7(8Dw83THD$J3a8XpYEA5zK#~z;;2V9SLeYMp zhqqFEI)kxrw5+G{!p!D5ydX@&OtlcNFkP7gHpW=*lP>Y2)~sD76#EGpLDHl!B3a42 zL2fh%_Si*Xb%j?U2n2>9@<{2x*$6*Y(5C*lGSl_93swNPmBFU#)e#hDrcmeTs+w z&h@2Ain+>E98!Twq%o;F5!G?lQp|b-PQG^F!#88n5)G_{C$@r=csGZ0RbVi^; zSa-XlMTpr!Rs!5n<6F64e5VaphSqmQ~frM6=h%gM)>y<(y zUVBSqk}$9hfWBKm2rD;i1A`Foy!RMuqMDlOVPm!BtV4gt!f&rHD+?440JSUtw5is7 zD23es3=oCgT?>ucywAZ&)NC+)d4VsMmcowMSfdmtyC~S8&n@ZQ7uf?sTGbMzQ)ZY{nIt zzuqm?BMrzZxspM>fcN6G`3HEqndxa-wO0ETux8mOfanut?0B^|H#h$^WKa#}fPJAn zC9)oX0oE(Op(r3M|k0&JYQ^ zev|LrIlvKsT~XiA@FhJ|%nwMb1i@B3kq!c4;u`0|YwB9N7|Q-jt$I4gWUUal^9}>A z8?lbX-2O+%ieA*vUdWFcDGpFGg)1G=2k^a~yGFTvNSI5YyqBR&sC(>G1sjXZdl)y= zzHeb6>c}tT*UOWb}+qlaA1g#6ii#(zj1 zPxDBQ;ql;gB^Tn_GwOj5a<$=oq@*Ly6KPG_y4YZeJ4KCkh}0X}w0J5)&AgY!sE|;w zE5qi;m!VSl0jPi(BV0JoWBY(a90tgz_RCF}&tKwDi;#vq0Q#se>}n2(ybUjEG7L&a zZ&tFqvU*chG&LKcja$YsZMjd$amQ}6h54+L zK4#6Wj*%rbCGM__WDcbSBj`#^T+x zUS{Y43nC#@lKVNEM~*!it1h9IM1y`Rk_zH~k%u^U8|+zy?Gu#lhsSYmJ9O z@%588f#=totD&w2pu3&azqX9wVzmNEdmm-kxjg?b^N1S(z^^Q*u?kxzYcIHp+peaBl$nRiUWT zD4F<|jFrJJeOF^|Q3XKfe^67p{9ou!hRjNw)vMp%A1#V~(szARHbOrc!>}Wq-?0S6 zCn=V1S^u?ledR=fcWRt>N8|S}Av$hM6h{YGtNAROAMF7_-pWd%LIU7YKpZzboV3um zj!S+lR(*kL3=}*-)S3bIGL;Gvmsc%o@rROuMCKe2{LanIjb;0G!10xv=(#W2_%`ef*2--?L;k8hy+WbT6Cd7uEEh(RczA?Dm>1EG=G&FKosHh&gH%@^p0+`Z(!9>u_nZl;WXE~bbb+K!=+Ddj8G&rb; zbMTRZd;FJvQR=vmU`3)H_ggZA>m#9)_GwtPw$PF;q;Lk1wDT(5x;bkdyczZe#tuenWsPi1CtPT{gQVb=x2Rlpnn5u0mq5r^F=q zOp#0EXYq;+b*RZwJw=e;_S@G$pduRNNfUj4!Im*%zfjARG2%?g59A<* z7=g_=X}prMvMHWpK!wt-F_EQ@m!OgU#hcsY2KbK0&5-Op({O)PD_6(K0>KD=R7n=1 z>c#z)_=I8;VfG>RI7BO2$+0rLrG)MbXP}OK=zNaF)ktK_l$FMtVT&2-o&{Fm0b8uM zfdYE?)!|&t&xRzS!BlK+P*EdPy9WCx>kA zXIl#3>-cxM;uTxD5EgaOL9l9aT=u0vTPZ$1L9UGJ)Jt73u7HFNiy!E8T(FO3!4Az_ zCk1l%j`3NvTzp)?g)`%`4X^gXLUW)Y16b!2^JsG3gVS#O95KK99}0=&2lDS&sPwNd&05 z00eo47Y3+%Y(vgAHb8;2YLKteQl5k~oo^sfHj>0jcEx&@on;oEWeSSgwI-wc_!f z)v=`VJ^c+t9&wc5Nwn&LghyqCv+>nW{IrM6${GeydKh5YlGq`Zs0!a*8_*)`o7(DE zyd;EwsyLTX)}6KJx%--D$wqK=HaTqp$o9Q~LG-JdaXN9Oc-3NF6_@23%96IY7HR%n z4v7FNB&OHgjSDUBE9a~0y4nz)l$a|J0Z`Y<@PJ(Bg|7&;d_t!V#mLxfG7-)jEsDWO-$*r?38`2j)*1G$dcbm5|nt;c}q_ zAN|#=5tuztAqoD8E!`Ti80s@mXf1O?$L}n+>_%M0RCDYIrg!VGw=dxrX9~u|#^t7g zF;BG50A-V{M5DC69+=+}$lnGTNcN%GTW9{jUjO1aWNN3$99Nyi26v$y5VPDbs;EbZ z-(PI18lQUYgSKwwBAt;a95@tV&}%DVpY^?f2{4;=_4M>yT)0PDi@zs_)w$yl4@f2Q z&kkLPauTENJz%ij`(aT#M;V<*KG@$adcL8b}L{v z8~S+dR?oMJcY7Dx;U7j{4s+sg;i_`qTc) zr8kV4PZx6rel7d^Y#ukFw_TH$YfRbR9vam%DPfGw!8Y)_;^=M{%XRCqv_Qot_t_oZ z>`pD0J_*@eIP&2N7)s(xORgP>erb&>6G#`fI^RH}4^22V$Aut7eUDvYVdL!5Me7?Y z4=hoV8xIe5V?>(|m$v6?WiVCRS(7p8$jFGDon3NrGO2E|V&UsIZ~9_s*5>E;eqP?$ zHXM%%C%Ww7_#g8&jn^JHv}xX5>qTwd8~NCoO|y77;TGrr=F2E+Kgv$HAro9X9!?KY zJr;$tKz}6d*y{!-xRv6JVaq2OwISdeLHG^YDSO_#LHMjgSTw$z-4`iEg{wXsSp&iN zLgA+(1&1X!s}!&S=AV^ou6H}<-^fZ*je0e+J+F1>hz|kZETVDsOVe=!awB{u-ytmT zkkvTcnlRAgH-QPWAoCx0?W^bhTIyuCGMX8crf#gC-Lev1U+PAglbO6djsEIFnnEYQ zj5j6Wrzt_=HJ8o(jSJumX{1qzMI$@b2A9E0!>ztOfqwz;!q z40X|61Lo`Fq*jmSwPEUmQ? z-A(^F0!kJ1BPb6^fF)P6o*8SG^JzM1N<{41D~WZSPB9ZwmFVa+4IxTAd`gke7rU;q zva%{F(cGcQsrnz>mYs-aMy0+66la?-#Q(+@`R)xl?p<<#AkI@WufFbqUnzXxD?9h~ z;j7TcrAwX_J&5B?ND|ptF+;{)C|8PKyvW|V>15e48;fdK`GHuKYc7u{vfgkc?ZDpZ z3oBBK&2m=U3?ccJLTsY5u|HK%oH=>_cwu)qhh0P_wjOXhoMy+-`k9AcIK;iQvSamI zU-XiFHzeNoY6fVvlAe{+sq3u`SZ6q53;lq@MWq>8z%KuTR~&y< zF+z|l#uxVjjV(RYcw#GithJp`HX!*%{4mDRE-HAj`<3~X!>Wr&TM|925y9%%;s0!p8Ii4YPTfZ` zqSH7^0xFbwGz^Eil}>y18d=Fv*QYC5_bfTb=K&M1{ny)rI~Tz*c*gw3yCNQSGY(f?q zU~(cUyo$N#kQe@XsMj38Eam>e6SYXyAea+QeXQ(@fXfin8u|6i%%0cRI~KB7v_DRU z_ceosvI*pf>WuuDuAv;bl;ME_s3(0}1cx~VmV@cM1A;CI(Dosj0$v0ZQ9tD$8hM_g zLhVZB{JHtYXxmZqg^~wn5vE8j?Ys0!8?YR$dkVOz#s9+r!54j3%W&!<|P!Ng+xj4 zRX6ZH35~m9Rtf`q(D<_yxEy|Q}R!mj3fmt74X6kQkkWAa)2_?6edTxiYUqzJsNI;j1L3oW6OTst{hahkleE{) zi*8O%>)4TVVZl?)~Wfxs-p#=yM1>Z zcRWkvWtM^S1NO%RcPV_!wn*@0K+FIaCcz$FL9kkA^$A06UEZ1s%AYrgJ3ze?tt1nA zoH0@EHBmO%ZFdQFYi|*Mn!+DG@cj?%OPja15fD9Ar%~4!VLz{vE^hes7E}0nNPma8 z5jf|u#YTC1CWS-?Of}FtH<*$LSI37?wO;sQm*|0Womih14!L_4WmJs4L3plz9}rz^ za$StE1iH`wI}-5nGKJh;%W#_~zbE~0!o$mJygmtVEC3KwlpEB#9e!H=BM|dkmeA!k zC_!AyEiRq7(_zsrr#|P^3!KyQh6X+jsoR<0($b>E#ZrI;!#h#fs@ioCed2iF2YY73 zn9G=`$4N8f!>ctz#_u6dXV7AD+ydAW5K>>lR<-X}B;yxry*7+-5;@vQMd7#8%gyr| zFa(@LcmwrufFR*JZ`t{eL@sDY)xI}hhTGgYk1-;YX;kBr48HI%J)`%$v7SS7k9C^; zg#hU-#PPS)VliEHE~bRxJf{L9JwU3WSM82~MlH8Lg!11g1#7{t@fegPHC6O4Bhl{I zS$G5IuvU4%@z&|R7xZF`#p_|h-0lzhceZzhRux%g=bi224WkW4F*&TfZq>9hCFfzjgm^-Zq8`HS1EWIqB#s2sIk6s5cx_P1 zOn%1%fh;0qqe%tvpY%C?eTFmRem*Wnu!NWMGwCctSU^>x7GL+-c+HbL3$jF2r);(7 z{e1WFU1=gV3y_a;!-CHdgZ0APUCiALElGjLg)r(R@s;ulpBbWO=_UBe_D_W87u9{t za@218Z_#&ie7D8rN9_!pM+I98Xo{4sRD0CdSlFTm>pV2bP z5(mKtliyH~lPhC?GyCZvBghOTWsOSVFkY|VQ>kXvCQei|UvACy+2W!n^uKomp|htu@;b+GS?n z?VD-VNIb(*ZC{&>So*ndld^RbrxX_XAs^YO8U>H+n17)wI%T)q~eYf@DXt_b6@UyrSW(x*7O1D;D4p*`REz5 zh1-4o{jJIc(8!WxewXx7HQs!|$<#HWhB#-;09TmpZtNlb1?Uk^R-^qcw5)WHUEiwo z%VBX&;75fH{|pjVT5u(suGv}ZVg~}OCa(FhOpxXXbWDlUy(*h#1a)sG9EK40nlrMH?>t^#cQ*{#PU!%D~33m!DbKb5|S z&)3{mRlF`Bnz&YR>aQ4iMenn8wi-o7g;i3I)kKoC>76$BbS#Lz)Dodh^|I-GM@)vF z&x6H-h(Pf!6&tI*rS5*q&aiQ%(}7LqR-^0Gw92#Su#c}a=U(Wf$i~m`tUO7UV9d}o z)8S$>(PpHK?s-!d8cXTctFva*VR7X$CVm0f8>~9Dj9IEd<s_ zt3JrH9c&nohL*G))ke=5F1bS6T3PX1Hqc^B;~k1VP!2gw){TkjX==Kye?{*#c{|(| zx=B!~fi3>k01qeSdmSigjMd|A@g&ymRC}KrB1RHnnWO)% zeg`lQnd~iDu~jfT!Q{mCPbhtkJUCj8YF7XnC*oNx*Qh03%>MLAE50LfYh(;Mm*cqL zONqKVdvyqZaez?0C#F5Gn<_#rv$3;4_7eU5&U_$VOPviId&E7v1Dsq#y=8BRZs9x0=-XGE8cFr zOz+zt6+N|(ezfr7-$8$w>*v4|VF#{b0iw>O@_`)r_0erA z!`LHPi?k(;Jc&>Eix9yMxl)6({hRYME5&7sT!fhc`uR7h4pX3yfr=_6>zI(^E90Yx z>Gx_)cHLilK(mJz{0?$>cnH|ulApg4>*JuU3dl(Lae4F5nH^c?c-jB1z?C{dw!t(p z3N_XTv>)*t{#edo8fMkUyD?BZ&`csUIvPmJ68WK@Kk(JYf?FS`U(GLaszA#0&9v&- zZ=x?1w)<-0xMir3R+5}}hP zf+Zt&AH&)P?1M|$S*MVmL=IxnMmz2Laov3GE&7+#$L!Gp9HB#9C75!5MOiflxRvjxo3a10qGzIR&~3; z0qAb=%sIZ!SEZVHj@lpViZJ8vc^U}XbD#6Le=;ywnV#MzC+&PoIq#1&eL;Rqc(Vo) zM|0o~mMNG3yh=~br*zc|k@Vrp{#}2FCQS+wrQ z^myDU5)scfm=Ur6MnJ1$`aebZ?q>*DQ_~NzCxCZ5WO8y zze~o`VYA|gBIY*KN=tRK7f+Y9r0LSkBT7wdv0Ft;u^t;~F+^0lbFE*_AVKI7L;4+y zS=ck(usUG<+|))MPZu}N5d+fjrLkAJhAfr7um7NK@6R>nY*S>|DDmK?NQY#;isb5x zmw6(Bk_{L(OZF{x7ey*O+F05`%LeK(^826;%`)XFfOPE(OlOjWzez~jY66L-D{t2L26s>zPnD(BKu~1DII!ZMXu;jeK-h;g^z4K zSt02MMGQ(3rv*c6E z8MqGUr&JhKYM=|Kq9xmXEDu6dWz{ke;O!lx`UkS3p&*uT%wwPLR&-OOYN>BHeRRyU zoCu7XFnYHh|9bqM{d2Ek5w~DPfPshJ+?AvDb#wXsuRZfg#7P`Nifp81TqBRkz{-=V zPBm%VA9)~e+*OCiPNCRNNGC0UViTFUQ_bK;WgfNOjToF>}=tO+YCDgo!{ z-ZIQHvQ(^AOsu;VfP&X_RI3!1r%=qDKi}N7w74(;Y7wd^>#oyVPI-w~Mf*alsR?L^ z4?shWU<@GK*uGjKvs&fPpHeOwEJPLppVTFGI-N30ODS>t2b740R{95@ zx?xBeuw9`P0kED{oILh_z zbrw!oH>p^}a5b`Umi>N_F^eZJuWmuri`Tz|ed&iL;ss13X!Un8_k$k=A>25JGLOO& zkEAWoKWCnY|NKkxLqs@|Gr{k2^5I%GIE+btb*u3fd?+H*Zo4SfP(`dc1H;s5z zx?O{#qoalX7?c5?BNXU%vKY%Y(9!}<#X>Lxksg?ncQ()Zfy2C_fa&^NO$6vnQ72M5 zZJ)>7u>5qS=F!tpD?r}(Mu#R9uC$@?KB1cR5Fd2g4On=aXHAudFJ8P5hpnS1lVzzE zt4oKJzQ%CK=UY|nt45@|B`}(Ekqz-Y%uHwbT`y=Ez*+!P@iN|+EC#Zmp-9L294~62 zT(v%nPVMWEdjQgbpK=dh%acjaZ%e!4y5ls+4$b$pvF`_7LhKG4QS)EFEZL)6eeYc% z!d8g|`U1$KbyMgZeLoG5hEB}Ro-g#}uo{3b0W|giWE%|RN%j~_* zF3;Mly8-bL*>O7DLPmuRN6*P03J<`^J>WR!&Aci8m-eR@nY4ogJjzW}x!7n1@emyq z6!?; z=wrz=4g53=@X6J;q#%oUMZib|L|a2_~)7WddLruWY0ODmhg-8F;^i2b;5#Jd?|iIZ|hmW z>iuOkF<0^ax1bGq+`95M9ck?BYm{bmyNl@)Nz5+;Xh|Q2QLQpKdhiRv&&8|0(vp~} z)nidb&UK)k0CaMkJdi>~R9LQ4+@X0U{)`^7qpM8ra_L zR;(rI8)8gz&C|1@1jREDVo0y#Jn%011#vGfyzmr31=cRO` z3gJT4@K>_rB0MPO1stop3&<4fm+GI{KwJYx{lq(#mP(4mIdCI4J;!k$hr!w7P5+Ib z%gLZ0lXeQw#sah^yYgjJl$8O7dVanM&_*glMA!25( z8BBuCXdJ)qY(w{E!=xsJDw^H#{S)#uz~Ob+8$->sVHP^F$*a$u$}C;Xi#!U?WYSa^ z1DNBaJCQ#RQ7H9au5af1r^u zQk}rK$K#sF7vVqMJQ>ieMxG=&Bz`YWmrdBVXWh@3Lb7*@$-HaJ!|9xUKM&iV-(TsY zLuI^K3EVkoi_A7f|G2B_eq%F%Dyd!7BWm$W#}y+RDo}Q9WZ*=+{;0<9m8eU=nr=zG zTKlp8&9;gsOZy(h2~U}IoY)zdC!*{JK^5V>c&?h zEzw4OyAyIr;jN3|t<7O}Mrrm+KldthG2p+*d&+b%o@fkk254+OC~KfJ@vGF)tLx0s zne}qw=NSf{QU0fkXF9MLQPpD{p<<1Ea9VgtxkPDJs;4AtzDiE zsNz7&kq#jB+u%IM3&R)Ojsw~%9OkR(vO=W-FCb&UaWhEAO&c^#&~B4GuuGTni}0ap zl%LT(2UU^vs^=Dvj-vsm4zP}iu#N%v0=(Wk;21hjdiAmTkF1}c=yu)kQxm!w57(>q zK%q~p)E)apdM9l+tNw2FRyZGtgq85SYxaM!M?UF}vz<*cS4i<1-u~#AE2iEQ_WQg2 zM?Zz%w8|wp1`-o(Kg0asR3_%d5>C8Fk99bHGlMV0o3}SzM(>a({WU{;^Y;X#Xha1N zb*vc7rTLPZ7TV;ri<`xeca<=aoISa8VVWo^Z}F@!*~Fsmv~0D=zv}{#KQyYz7hX> zw8DA8Je5tqNrLEUiaCyS@qH7XV0c3mPc54Ye8r)zpXfzS8@sJ&b-%LIiwk#FoalW5 zvg|3Ggm>l`L#Om>vni21U93Z^j$11DqPG}aSTRT0*j@BI)Yw+yx-e-vOl(vDJh16C z0Ij0AMMb@wDzchW`82k1cO% zh!18iv|`2b;tr!BkL=1|#4gNqNcgp-?KSR4xykqms>Xu9`p`fE)0wA8#t3js6-e85 zx2GFGd(tP38G5t5Q{xDY+LA)`ndIGFIoW~nTH-k4SUO@g9q)H)O2vs5ot)Bn7$a?i ztB7@J@%FvV_l%`B?{`BtwBsxU-_1pCpt0PI%jIrjD_I@1#|W~sEJ-kNO$E~W5vsVQV2R{BOdJ1jw=)O7!rIE_$}gs14{T9i1|5>NbQ7%^Q1E81Vn2S#2|8<<^Mrv z1|E~cX^|3>iANnHjntu_Nk#?V5w{l?Si~*Ye4v>zsCx>pUrU9gWPzyQk&UJZGnG)@ z-h=QH>DP{sp#ET_DK79Rxn*~b6lX~*8rc2{u) zERA0X)b`#?DTz8G}n7)w1 z^}J)E>kpvAVG~F)?iS88Fo<+oapGBZCGjP={cXcY9kf?5{xt4gV-NRwO~6T2yZtu* z=d05*u)R9i;n!8V!UQh)THxIfXA*3jQ>FhP?(_aLPmCV>naVQ=CM6 zIEccA7j9SuB6MKTLmX4wv}r0WwE#`Yp~E8}gR~Aaap74^EiGB3DMX<4(M-n~GpDO+ z+6Z=gFsh8Dx$9R$pbXenYurrW2G{SS6*&+Z3;Z%tuHaao^HYXt`G)D-LjEC_PGY%n zSCH{^O9<3%Kg95ri8sZl-_FFwU-ZwC@&L`pT09P|n;Z>1&&1OF{z8WVnkg zaI;L`?sw2R^}IhT8@AJp1bsLhpV*}0tYYb(Q;zUhXKPOkMmBN(d$WJ-oG=xZw5rwl zFo3OhajgILSd^?!8YaS%2^=4#(zK`9Xx=PuRuG!Q`@2iDx`}0dj?IgE9Xlr`S?oGS zlB`2A1M3W12~Sa8@!6P$}z#nQP3qY3j#Nu-aid!5RjT$EhP*Y zsi?kyE(3UGQ-II(xFu`I2>3;MnSfjoFS5lR@to(Y^o{o)TJQ4^8zq_I(y+xsHd%Mz zHvB;Clhv8c!kHnnFEJ0}rxT;0iAvqYo-nHs#NwLS-f$iYxS|AFk;N z7DLUcfnGugipGic{9E09J|6xhUu9Uhy53X3_YS)u6|))gUip}ft{!!Le|~JwJm$-( zlgAlh0B$FK)%x0@)Ae1t2(Q>$@#WOyE+*}WhnSeew8Ce|KcdkBHy8hiC7{8e)61g@ zYqc9{P@Fpr`0DXi?EW+L38sgc*+F{1JBQOG1>k#%z~u^1eP-GC(R>F2?98m^gjd8I ztHr<8Je_z-QZSyLTRCi25U^`tqar;(hM%PV__6MF-`;|_;;kUNg(J^A7;lhr)VNme zv3ho%`;{Jt)q2YFZ zw1AeTCb!iR+pj3B@ZU-$GvM)1hut?xQK0%+N)x#X%^h<>a&i2jrFI-cC!hE7PoTl$ z;MrkI2T)}LxI}YlDZHZ^I;9)|uhR(-q=V*)S!8e`_w(}7k`e|Gs#TD`1#FWDWB!Lp zHXwY9Aj=AsHtoFra+eO|#NY(Z!0p~8L&{?Csu#1#}tn2}#Dp&ZnlMaElKCK&)O!S^-)KPW4C z)$~Krf{*NrIjZm9ceATviZQ23JRhldx&cf`jM%k{E%v3u@yuoUuf(L}{dghPcESx} z2VVJE-jLx;(|00VM}kGJ(SV$ImOo-AL#$uCXn^Xg~K+?58n#zisq@pI2XfHKL zDgpn!fajw&-AF@yz13pPZ>ET`bk2DI<4{!{OJ&^$s|f;ji)o`WNd(?AVvmb=R|+XVjIurN3~=@U zYv~+(xk*k$@TBA45@m72lxtmUe&x>_44yq$s$E`lZL$V!b{uI0BdA|y7Q-VM)3qFT(U@_6}^a2w1AOki| zj)H02D69=z={yY>SV!@ge5v^k`%&h9`dL*~OnSZd&n4FH4Gmy?91Zcp4<2-964U(tp@0fwE!x=eExz)t=&z!0g?3*uvl9d_UIOw12eU7N%RQ z?(wkcsJ&EYc&6cW(+7v*F@c)6pk9gdbd_B0ol<|fc`feLw3FVj?(OX3DOu(;DtN8G z6`?Ro|556u8*1L+Bu#@bi1VkE%j?u+?~`O1UIOv6`)qLMB0r}vp$YZ419x1gGF;f* zK9j}FW3=GLFBvW9QpCx3EPsV-+j8-Mc0RiT??>kg#enVNFPf2T5zNnbQkzo zRd&&K(8n7kXi421xf^vw#Kn&ARopkZKsT9-}pu zTm*A$^po(4|B*b{Zm^Mz*a78%)@Mr7;$>RcutK>MrcFQ-Gu742H20IYV@YL}WyOpB zsT9*c@bR~85e>6>^nPWtTU*7xNZIS|Rw=1H&301Z$)ANer*fyNt$oCc1UKgel$hG@*%nbjCOb_6 z;d6CWtjY^nczvb6Fw&RVQlX2qLL&ASB>4JG7yfNH_C{0B|F~+$L1pcCd&xKIC-Err z?vg2&V5IE>M1U5s9L>!Eu7W(VP1idBXVPqQsos8fagZM!h0lph;6hE@iL@p#{$WAZ zJXhQSC9^f+%%j3vOAZ){D?nKXos9pC*9%JAG640y>Jx?mBsaE$IrgdH+v#mAzD*PJlEUMA4d zf;k?efUno31O8W=L=xK*%Cc!l+CE>*_pi?huLKIDh(~{?JASe_Ga)Cd1m&uo{TE&S zu<2j+=0+29k%%-*yIz;|w-Qs(&FFSWnUwV`XZTL+ z&jQ6tsIE)Mr%SADonr(Z&zU309gaiyXF9L@%0W%|)FYg*cd~e12kMw#Ey4>6Ey>Aj z2+-p`DcrC4oundCO4$89_kMHNJ%tD|P%2>)B~jY@!b$=$@G|#fwIvZX?*DS!r1KsN#kxJHLZv$1Aao@MRVPJzL#M(l^XliNS{Z57+QUnCz5 zu|^2Za+#Z8^!w`R&j$hK5EA3km1s+bLN+Zyf;`LX5wLWCi6cdZqINs!Cw&AyrhAG86eBg{ zaEOK%1bgjFn*DJ(9Ej?e(Sg2jU%SwK3YGJse1*6b9*Z!sg`d#ig4aE^Ba!guCIc<7 zPj_4=tgCm^RS>vmosf#om2UyLf`D6K@6|Qn+_aUV&k>c^1h)AJruXgq#6aF_D&Xib zS9J>^Ia0t%YZmiOcRn|B)tvXHrgfaiYkaZy^;#0$h;SB8$L_Frx={7yr1N{`Qw<$$ zNs4W=?t4DZAMSN(13(D%da+g5>dkc0t{?d{ui2~jI91UVz6wV$_{KMKAru`O(knO< z!Bc`{C;W~rEkq*+&W8p9Rn0%ta{`-}AW2pN-N*v(M5~E-%aD-Qx^Z96MF6WZA|QW* zT%y+a>n;XCbbMbyN5@*tFQ895YQuB`eixw1r;tfe(9{go^A$8Qx9=VRS_v3gzJFh+G635(#?I#} zU@3XJKL@-5o=gtJPnmMOHJRxu`8%YrLWg3CZLg2V^1P>PNFLq8_E(J9hjmy{u%%Ml zda(W@&Wkn11Oq^Bq^hEF*iuod#YV%Wln#LS~3_jSwc&hDBO1gg*Z zrnO5M-1e~5N<-vdTyIDU)@L^spSa**yO;OCYktZq{BnE*RWGfl)ZlQ;#sO+kn}1dIEm6_ z4@cB?w20)*XB*SSyq=2Mxz%}$D`JtqV(Z?AE7HMRvZX$5nZtp6Tpf*PUR$0dR^evd zD@%#ArMY)-$=nn``jWR|8&881#BCkjg@lM@#VlbgmI2E-Eyx(<#spzP22T*bVBIt|1x0tET;B z#0m#AHlyERnD+aB_(9JHY-now`q5(0;AGfNWi z(5%_dEf293wmh#+ExmyTX_Qgh{~E_ge9pWS{Kzi@QH1G3M&KxmmnyLM{$065<^L{z zDYf6^MKizKGaKOiM01kiATz5@|$b1Jr{FLX_%T3kEx=xbH%IHSoLwJxM`f4--P2>W*2flsOonMYWH#&v9(1phg~{tZ&te53C8*Co%*|KZjJFlV3Ak-GCZri(;7mUB zhWP1F5W8!wv_ww*_?bR9A~4M+YN`3QV$~8%7-#ff`pdx!ijn4*V}Wryl~tS^?VS3x zZjW%uN_I$nqS~9iULeTXXn}7pI6vLF_1~F)x^{g-1oP9eT6oMu znNt3D(eN06q!G}8yX_UwiA`fdeLsg?zSL4s{ZMRs)sJOmkFRrerEW7sS;m_GUHF~L z#lnI(tx}ShfJW>}d64XTv=Wu0W_b=m7I+p7V0jZIt9Ft7Y)=L%>OnsiPBedxSz;&d$uv%z-x=bRv~y z`EM3S7EiOYcgOnb%F~s=%4fGwl{=Em06355V5>Xns5-AYI3+y`UsqTPT|#IO-oRd& zYI%3>-E`M38a-atE)M5oCDJdA8q z7HM-+-^!ZpoNn$&_}Or62(Uv%&)Tl#l8ld!(}5w3g=VMq*sj>>i3@>E$bSr!P`2K< zr?bxcAbjfyN6)LBAwUV!ZF9N&3`jTtRd@|x!1%mQI-fst5Fn4B#Y+fjvAO7K_FU`m zWNwnu@+WMx0dt*trB-(pU$0O*T~A6Ixk)V`f4rV|ytQ5vH#RnKppS?Mz$LCJs8Udd za}RraLrE8Xo8#~rfJdP?=aLQDor9cH3hObd)l}SYs7f4&EXv z5fOcductcb_k{!NK9}4ES0uX(q^z;iC$?S+nmkLf9tg_>Nb$xHr5thQvI)n&=^o-#| zm+u=Dj{HT8cC4@Vq(5{=W_b()jy3i6;$al;LCLz_kf`W zUF@~7^Y!#!^H`Uy`#7D=|J?Q28~jV;d7~I)?FYyULspG1rvT-^e^Z5vc_~3r7Lqqu zlSAgd9XX%vewLaa&27@{2Ys8~aKT3uT=jt()?U&s ziPy^_;gq`fiyki2!1SpUs)ppy9+5WmMMNmo#z66!iMbdHdBkEu2V2g7yp?8jsz5pz zxt8$9B%;pn)iJ-l5M<=;b&iWX&g*_Z=&Sli6rMED4TyQYxsaB9p;M*I>78XD^I-=9 zdvU_Y+iVuiT9MzMR_F(`zdb=Ri9`Q^bKB40E0@E7im5KymKz~-U-m#Nn9Efdj18Ui z_ntF0RDidW`8s3-zKjvZ_R)^|JM)v`s{sByUGb2~cXT5(X~Rq_g5Wg<_Cn9AA@AJe_mmJv#eD%}xkh;86*hFk7d<~JA6~?;pQ$Q;unH*a&V!?ji$IPX z&Hn?ER;$w~zyt$Lez0sNk0bD%f-wo+7)CV!f0rm1<3Bfu))v zDTQt)Kt9MQ1J&ooxc6nP!-uuc(`?||vFKq2US1V_eH8iRPk_mH3Xq)->1AGop`vI* z7-Ojg1xh90OxCmMh6XS@4`8(Y6bt+1!#ArjFxRB}d$a9{%y$7mpeeQoqyZX(hrGMK zD@oYV(UKnF!AEU(6WkLS|;<)trrc~p*8Yz^- zUCVJ-s5OT}8H!Y?LZuQ*tmoNN!6r1jjUX7h7WDG(DJ3eydi@C(fKCJZd*}%fHt;j} zu)KqM4W8JG&ZT)W3*Yk#P_(k&V;lt;d{k0aryXHAD=3z5&hS3%BCFsZ3~3N53uC$% z)Oat`&#CX7Ib@X6l@CS<6nIF}c%B>VRADxlY2T3`lYBo2EL`+O^PV-Kn`Y&i^9=ZR zO6aY(%+0AC!9r(g|C_IY;tw8U;P%h}eq6pha!IfZ$O;jdY9;R)YYjauj3bvX})&J{@4ytSLwA9y)n7Bp1$~T8bch9T< zOhS2k{WC!&oZ}yiGR@DYiu$cr|`-knVHGVs<*>a|ckQ~gC zaRjLYp(l<51i5ko>0@~x1Tc^tSqK+KNJ` zMBs?SV`wXywovBv-oP?8!}AP9TWg72ug_|cDMCS_hAvK#q)uM7n=@t`M`h5KpwYVb z5jU71!_tZwX)L~We9|7E<-7q4Cnew%jJ5bcFAM0jd4UMnWV1hj&0XyB5AS)Of(^>% z2rbW^r-G+AqaInHn{LFkdTtVT1D(E;Co+hqBDCAqZ1f5oJ>5 zpMx)`MuGRt6qT$)x7SdJn6S?B)vo-W0}^4JZo)Qs@jc;nzV6!gvC$HcKS>!3u)`}A9BVw za}77o<5+XI#7ss9F{Hg8#`#V$Ru!;3pJq3@A%TuTL3#E3$uz?pmppN}I4x1>Cc>ha zLegj^A7>x)Kv)y;>k;zn{jE+Q`26j5Nd&ae#DXk|9!vzG@Ayxix(AC>XIz_6huhguXHt8 z*;QOqug(dRa4o&jh1^mLW8?qqAW~&afSPmDh6`?!PY1AJLBU(?fcL-}%|tmntYO`7 za?^{J5szk5^5{BaP?&H|wlB23?g0-&gd9X3m71Db6fWytFN&ZJc%Om4e(JE!Jni6q z-v!Z@0D<8pnt)m|E{|&CdYq|WvofG}Q`S2*?||oL&6h^|iD1$t>VdTM`vV6Wo;8f! zA-leXazhyV66kYD2`p^-$Hykaj_H*Zmswd(0Ff1|p|V=4d6@5fy$xp^`(u(|4nxf5 zerNepzqB++gjB-0*u+@Vy7UJiFiveK9Dt+`Tn@{W(xcYXhULOY{A|zTa()VVW_sUk zmys#xSahztx0kEoC*oG;$8MO;=ixFHvmEF>#Y2nz2S1^KEciqQ&%lHQ>7poD!g<6# z&YGJ=$vh@NtNhXV0KX-oYGY?Y%8R?3BSr8Hf*48|z(+Kwa8-;0X5r*D6gP~&UADc0 ztKo_=lNu(Y=`}+%WJ)r@3n~=x3v{X%^b{9Tgc!%@K=88}HZS7$*w3$9BjzJ}T*706 zD4OQ1Ahb+f^v=BZ0OmrwH@Y2OXw@UqA-=(mHi&a%aU9)X1*6ZwGqrK#|0!D$46NF$ z)}sjg%_%&XU$^NkNY=?SnIe!zMDlY)lBL8NvCo69KF6ZG2r{+^q6x_}_!tCDDyqni zKtMsK7suFf!xFj=K>e-{#>rr96`TfW=YiyNADWmoY1PWtl-4hR>T$*XydAJm&j4Jz zi-~S*LcEb(#6?ko4gFUrUD?5x0CY?7jSR7V=IFe88JN6`Y2qD;W`6t@14U`?kpD7D zwCOfQawnc2uJ0GUJNM=s$k7_Q=%GZoNoZ!6xP9kk{db{OtGLt#kB)Ye-%etKi3bZE zg;Cj6cg-l29qxBXXu}L_WymJOxL3hF8err~LHQv9DkV@x!tww%$~=zSehg&LRTJAaG28w$a&j1xIK0R z?j!V#b#-;RZilmJe%>m~zN3+k2 zGJ~*}jUPo~y3UMk_`2)FZbp$LU3G=oC2T|n7& zqx&EkOiul8C6?(pr&*Ng)IS)j01ZJ-Jn{%)H5L~Z;6KrEd3~E0HYK1uP}rGSGtr&$ zJ|mtFwZo0CH2jZf@K<6hLN8OX07=o31B}HIcV@Rc8%Ct6y87E)qq@CcP6=x1R~(_I zYBC4`gVE`*A=1)P63Hzv)z2$EN?|s%362&e156)PvRE4;m@fi`bX~|QL$xuM$~(`Q z+!rWAf+ksZ`?=o%wGIY#nhl5mLq2{2i!o4pqk{m#lVavM|3^sg!Z#s7o(~pjN~@LG z{|Rr>4KrPJ`Fz%QIxVT_X3X02bXlYec$%N>t1-XL3ug!$_$2>!?;RO=3z`n#{QbK< z#%Ykbm6m29=|>)?icF{y+KWKSxXMq z{L-K+oG!)~eegdsF;h73iqOZdAbebAynaI6(Xp{S_jtmVL5Sf<8=Yesof*Z(4N{~# z_7ui(8dH)Dk0tIXnSy>OBtdL@Z8_9&zeRS5ZMKd$u?gNXh;k zZ-PTpudil(N=eVC3bR1-Pl$O%#24pIb5$6_qVo>+ay%u5wCzC@{YF^hi52w3@vgXY zSVW!b!T7wJDM#s=#YuIqODhiEfbF2ShqNCc3dcGlM!FK&HH;vWZ*@qvmxRE;So5_T z#S7#R-CHkJbq~D{SRt$-2{JHdiFpEs1Y{yU6^!3NJ9e=XFHZ#OKixX5w8zCcSrZjp5HoC_INa!NbUzRFVSrKYi(A${HoX>D&a%s*i&ca1*Zu?W$Q-ojfu-xeqy(t~r6_y4 z0`HjsC|*pL_fm0eKvoDcZ7wRkbt2h^a6cfLw^Uqjd>C{xy_2F2rwsGpDeohjnldgE zNPn}HmcG6p=WA~%yuut&2i&P``Ddc>Yrn4b$HvaK>$7D*-+2JKCfp!E z1NS4TYiQ_T-!E67GWsTp$_y2%4<0ec$WnY7QdD=yw7HD)bk@}7T%j}KXKCv3yzc>> z-_g1Sogj ziZk`2^ZJZ3rg2vEV|`Q1`g9>~JPO`Pg&94w`)X4p4!Y}x(foG*y?fWv!A~4EeXFHT z2Z4;kNjx0oFW+iyhJek$=g~i@df8-t7)FTd zs+<>uPKhdFK>cF?dV+|ge>|{ruY1G1l{EJA&v|Q#SH7Jq9E zSqC6dUH^IYC6O$Rg9|*tXuIPLZ}3(?{zecC^Odg4<0;ML`Cve><5a$p|FRDqyjdSk z8tGUM)b&w6WN=?ps3}YRiYZ4AiP; zaBl{*!Ic#qW#w9@(<1=c90R#NK#2?z$mSjSiK4y1tsNv1@um5jFBo9eD&NdJE=FaD zUY<}t%Sk?2h5F*fD_EG~NJPuSK7b};@*1|rdUh5!=n~iiK0EPgfie7kR|XSkCPZ2cKYZ}?VCz4ke@iJb zTEeSy8U5#yEIs=8!!KD>i=G%AL^!iKaA+xaF~505ysjv zDn0Ex&smKr+0R5Ng8-5De(i8RRsfCRNu9M;;h*gL%H90BV!Ba*FL<2`<>4Iedgu;;Bbs3rH zGDkUdVfdR=(1LTm?7KrO-7?v}{Iil=<)-kzv?czT281iS>L`5#JDMRQwD`N?uq+93 zG8LYAHhKc&BIQK@oPE+DM-z7E4kH7~s=B%W5mGIv=r0fr$=hb8-MwtuT3TqKP%RiK6r*}PnIB0@nh7y?viLJ*Z;v@fxg7e1Va7pfQF9(F zgR(^VsVTf9Jd-9K!X|4oo|cm&Q@@k)gkyVN-ciNuiAB;4UdkD8q!GaRHG>ZU28C$!`WcKh z*k#L#i(|;)&@#%7!Z2xVf(JHWEi?8XEV2IjSTpz#?F)zRt5Hi^9FZTdN73$brFlOC zX41b228UIgajHRz3m+&`?QXY6yCcblWUwl)yzn04spl$ArkZ*f1+3$I=<^(?7U|SM zG~N!%{=>MdFo}Z*4;E>3n=H7-OM3C%SMk{^UQegj6S_woDBPU*LIP zu#*KVl6+c<;`_hgAwGb4rj%RBZ!VC!O-Cbs3FSTtqhCHr{HHEPJCowX&>vH>~QjZAm zSVrS8Z-p0riMNJ#ko5gH%Nj%&#k$tfr4C2?+P!AcV>!Jd)nWqCNwno6tE}KqaHN?- z(TwOF^-0jr0_xxsmr=h#y(6T;&%$LMf)DisX+Nl1w%<)quWORoRQlxMI~aEJhp83H zi_(507!dn(sY27sLgH4aL>+}mP{0N?tS$3hI-M@qaTP;M6T?^)njOyUifZBkr}xb6 z9HFhKw1n1NVkrC__Wj#a_dm5)ZY~Jh_+IgEI{89{z#RP5Ib>k#)LJHrjMRCafKn!l z?*Y*8zDtwqdEF)g!5<4nfO7L#+JPqt?NYho1S#t35+Vm;w_2IoS8fQe~Y_r?cRi;&Jin4bon4K4CNbos9On9C-uS zMe8v57Fgj846$quEB(eVqVo|khjfB(+ARH35}-M}Bbj4S-u8$bxd{#9W# z1sk`=po5|Fd;7A)VF*vxtRH&GYN9H=OTNv}m3`4AQ@v2^ZrX1<@GrkPYbo zEyv9)JDo8L&7#KB3zsvkhM}vtA>WeAjHptO?{z8E#f>+*7e!#tIl?pwN0~tq=9ee* z6Uzm_OGO$f{)`D*L;mm)31jYsU{?2S?|}~#6?=!^4B7~JuEU?8O{`+U;(yqQ@7$`wS?fz$_R2aJ`qShi-(Aeg^x1 zDC?gSs_}~~Q3|XUJpnNT04ni#oh-@G%)vQ?atYkN5jf26@9j^J38h-9*Rbr`Xu{ogSJ$PD%EC=`u3AHQRWpF9KUcvFq&MQQ_Fg|PR2!m)_5nMjqI)5g z0vu=h7|%Fk^7X(UETPJ+ik2gqpGXUthJ!O~%MYFTkLBntR!0o>kQAGj`*X97x=F^5 ze!_L5yU8y%zs}#jUMI;88QBfSkb?1W@TxK^m#?Xia*<{PM6)XOyiAFpIF8!qNUDMK z07XZ`wYF$oZeFVabD9Ekknbi72Atw)S*w1|Q8W-{i~1JI0q3qnF{WkosNw?&Bc#pi zv~M)G1eXq(_cH6@N5w2ceA_OsU|Zp8K6hR1!WSzsK?6hgn3L#;@DBCNwCI@Ui_uUG zEFA@^B_v%ZR@+Ci`a;~GLVq&^rsAfE*Q*pS$lm?0;rmU%=9gtROkJXaH^Q0+pIr4T zHX8bb{AR;FGUKv&yM%XG(>r3e2NSvA`LkTmT2C@FrjPMkxke;_ z?f5TY-JT0l`%PXtkC!4H*dH|zb&yL6i`u`sVTDCBBH-T%ps)uB;hQTKymw)*+8`Rf z-r5W%ejOSh&QFENxZCUqk^CAoxnBZq&gru1>gppk&ky_A#6TUh-5vJD7rw z`ZSD-cf#Ut++C~dY*9$(jB;70W1Z8vFW-*&cV4-LMNrCUEz?qvyAYp+8<@zy2JHjf zY4iYa5T&tyUpeyR9C9W?X^H+q7OM!Lg1~WMMrQXxdGK)y*GB#w=68o8h_PH_%~%Pe z!LL-O|4I27qakrhErhbz&t_Hpq;bFh=h`!JLi(q=MC zre-3@UDRS4LVXNgqMEwf1JSd+@(>N!uMs4uCb;2342oI5zqUD9L(;RI24a<*=h|}@ zlGzwhIv5MtArJ&*x)iDS9EgqagLKJ*NX`|Al40D1Q~N9)CpW`E`%)szUM( z19tN+!@^48qp(T#u@6q6R>@1PMR3?&V9q#ih?$-czgNfHgd22a2yg%8Mu=O=ZLWX$ zpl}>U!@H1X4=>aymv_J6up>Nv`?W=xjpwV2Fe_5heg%lNap6NgQnvY1;~4$=@dFid zWB+f`H?}yexXN5dwIQQTagK5mObN^6>D$(6>4_?aq`+VHIa!;e&PC71`uhKku)tbj zO`2Dj7}<^3p)YdaC%YqUFPr~RhWabbXN0QIfE&OpaptM{R>CaWzhq;lI@s*hXS5Win->*ku4OGoVg` z)-G2;0z>#A;LsQNBVzPLo4p6V5wr$<8;%s|QOnKgwYs?atJ*5|=jI?1{KAA_5-K3< z(vK{)x!SX&{;H^uqliDaK6mkQvyOD@Ed*XjR77e9=CDffQC1?G3_xv8r~XoDk|Ttj z@S1D+j$qVwoiJ!;=ly5>jOUYZI9$(S#7XP)3kIJRqV2g8*E)~D!h*kWVhRD%N0;>j zHC4o^AGog$&QCaBAcSL-hJH?)kKfgYcU4k;rwricD%yz~a8<`}`*xg$l{e%MUQ=+J z5d_Uc~y6k%r3W+ZXSFb$0@^U}b z2n$MJ>OO9$tU%hV7kcw=$8?G9GMr7it>Ne_c zsMLEoRLzU)MtKUl&m&lMwIB6*^o7cN^}>P-N=td{_hd#fc2XEu%MCz~cO_}~-XfPST)^uEiVXuWWxrsV1W$5Ez>3=)de%EL z?b4y$;e7TF!wrOQuzQGu$Ad|&WN!(3_LsDnMD$n){T$2Ov5(tb2pc~sM5ie`KBRp( z`urB*D=#Cw2)pWm^A**u#o9v5UampEk0|$LHDOgcR=ovLHE;_#HU!9c`&!RhXtBy9 zDB|Tv?ZUs2BSM_3BN$>RTNiLyaM1!(4)5HIXaTd(m(U+3 zqbx6mjNO>kmg%&mvSPLHgoo55-mnT5EhH_H4r5Sri)1q&VH!I#u)?cJG->*#m6-jL zw(E5BasG;;W3*Ul{4oGUE%kNzxaA+d=gZ>TSzAK?L6+1@Y5`T$v8 zhrz@4p?X!_eO+9SR!K2;(K)05k3K5iY%L1tC*HuV5TuOs5O86A$If%m-zeGE*7!|AYIzIP_C%q^buFAXRfS<_bN5ezD-u1%Os*=}!grfK*U3m6&dj-3grSr-3 zcPc?tkN){A#qVVNy9BXave2O>DqP!+ultvk*~hSz(nV8QHbt!y$&ext$w z%8f(=9Dum^(+Z+Kcxav%YS!f{Ar2&Wy?-db*iq9W7p~&YvH|mvf=-e#H(sO(ODe#Z zfPpyTy>qFwYZebry&XX#=*#_8hyK%ZasTPSaV{`bZ$4D}>LVX`)SLF53OdG)YoY&)Dmwh+_(Tp3=& zX4hv{q#^WFrMm;X(lvDW(F2Xvt7$_ttd21IqGp}Y^cZA^ zUawR~X;`;XV`d&n!{dGrw|DtnNq>Xv?$C2n^qn#|)pyhkm0_rw$3-=3!TSt=d?Q!SC zNFDQOAsb~QB0U$}OyF);L|~|iqV=?+=hhB&G~d-Ak$c1sbICqKX0geynSS!TI(x^B zp1D}M(s$U-r9M%VRnUQk_xZL;Gs4V<^ZLB3iMx2!r&0nB-MF2*C9MXXnaHQk?IRNs zzVvm|w-cAU7M;#jELs%S?oNBU1duz7W~LfCcM5Jz`OIsQ182+=e>K6On`OFym_JQm zj99M5^wI@Ma`LWxcw2Z2XGy#PV=8QLs2^kYhH}ZZ_RWl5u@q-B$=^qS52CO&kP} zmf?e$pg_S-I7$KYV+?WDHng74s1!0TE&k7GZ#J6lSM{C#RrqE(cSmj0 zRTHfuX!%-Q7gX2H;wg{Dp|3PJqm+!WF*I_>VinDMD8O)s$7&+;{W}X@Xl~L7^_5x+ zS}{VJ>-Y5}j;cT7$8r0S>oOCEUt1=mZ8xh%BC!RDPmqJyJ!T~CAy&W? zR9WR?H62h&1F>cg6Ji`>1fom<3sH}_lhOv#)pGTdUZxIZE>YMkklJL*g#(qJB^#cY z9?JBa0b9;;>TgKNI6w{Q-D}Wp{aeTH#D$h5y@n($z)Z$G^h>jaqOh`jI?FDDx*&FI z-+|@~gNAd%mbkl6MK!XYme}=VvR40X^R#jG4up=@HPpe9k;AYf^|P6k8(B<3ak6R9 zX5KGs%J2TsToY{yykD2wuMJr(c_YpHjz~}8XU_C_(<+2>u|vayS)cGN zls{wbK%^#@K;Egzq%Qbg=Zz^sS`|!0=c5UEIk3V($iNNK3h&7Pg@he{my`j!s-%??MBH`w%<6Kpf{Mmi@Hvg7J71D&XyKj+abjW&H ztpSTDsvKAT}KF81_g?BaR4^z0U z(6gf}+09oa7leoifAkU9Rde!B|C?~&Are0lb7m5o6jg)|?gf%OK2UL^K^zpAJ_djK zI99iWmeVF?$}qZw1uuNKpu}^k(mbntT!i}W%<8sqf`)uh9 zM8SZF;~d8Pdy()sZ7CMVh;-GJJpw| zv9nH)v>UC()O@y^K>C}wO};HuR2wZOt%zLOj9wjnzRGgvzJmKM@JmK~j%Dt3J5A>h z33)17Mj=UMe|_!8%A&YTAGh6LkTYQY>Y<%4e@NKr%Q8vZjzjhh8=})*j zL9VB_hfH?lgIRVtT*_QkN^oy{kNvKc84Z0%Ma)`9vohy_>InTLo;W`MtJvEDi=*ZM7R)X5oXex(zSb zO68L?FQ=hyE?FqSy?)s@Q=#@e#{Ge%S7F1-;2LlVyl@a zlb3XK5G8h}++=QZ(V(-;hzhm;I@}uNjo5OiRbKWW?!HTSZS;NL#j!F)j$+o3OfLB^ z&kdYxneWGmmd;RCM`*)uzhpL zcJCcYXAt;#_r~9zg?hEO)i;eDO%{+*qdt3h|Eq4OQ#A`gd%p0$AD@;-XA9Ezd1scc z3vYuIhh|AsWY}d{(L#+_)5zgKSY$mrW@z8tn)^DCrM@2YghL_gOjm!y8B12`|0>io zAR+B=V&|Fm0cpvBFjvyHi*TkFb?2&%$(T^QzsG>Fm>urzC_5law8D#a-{=@yOfgPW z6*~FKMx!vb$x(QtGx`PJ`ox6K@K=>fc)y8>iH)@{j}&8P z9~V|Q4y6w+ah)-^G)`c`b-&SXh?0T$M~YuU%LZ$=7AZzE2h}MQu(vQ;732jl{-sc% zhJw18L>i>X#Xe($*XYbYj4kZToTg@^$lq?^+8L0&Pl>UQ;A)dbB-|ST-_Uz_Q?hV?MybkCizubV;G8MP%RZoc{SSO&Pz*o>1K1Q#dG0%w- z_{N*4Fw40K7#_+3z?J}R1f^g3vAcgEjvmR!Uf}ytw?-^pp(S8~vQwTck?JRZ4x@OQ zLW(kzj>ix6gOb76H^Obx;|naT4(|MU@bzCqrwDmUo|BP){ho;x>OZeEXJhYL5KH1_ z7sk})kx8A=-`h!~cDj&6LXHHYh!iTdB}J{!&L&PqjpS1F3?*MBE`q*25irKz!lQ~8 zvi^TC)ocfDZ5_f5!iZLWLsxBkg5RJ1;6B5Q>OcON{H(M)Dp2U9h46K&JZ8;JMBsxF z9(PQ(c$_`iS7V9Qn<&orCl2hbwd&H)BpuRfP+~ZgNKSccOSvUMmepLd59z&Au%LJf zLSzA^yMlw`TIb*(!GtoJ*Vd0Y-=f&~QlK+gloq#@BT<3Uk1q8e7(cOZYb&ytwk;Ve z@fqBR>4`It$&(>Z&Jjv#9?AJ3fYM{$HfOD|POBBWi9HNw}f>GKfsByZfW8 z4Avh|RbdYT^c4Vi2DI%~g4THslyM_!4+{J*tIo=)A{(pAt4yC4gb+j4MgPB078U%V z0@$Hh^iyM9XUy%*s^|{Ol74(o_-A176B4Hnto@yPwT|i-_1bTTU)vE4FDbzF<(9qd z5;n3F^1+mb^K}Q3!Xls@Zr7vg!pU6%cvWV*D=o$*LS37vyChqsA~$tGVP>#$lhvv^VC^5#@sD07 zgiDTVQZ<2ZUI~*KT|BsWUB4-8AEdRubVvTt1DDkfXO|gB^LvueN}?hf+2qhzxw9z^ zAAv_`0efLpb+tT={sxWFw@YLyaj{}kd}&5pdwT_%v=LR>2zFq68Y3A7_~v1XLkPnz z?K4L8`Gl5(=#CB4P>lAduAy|$_&Lo~;p>e!=n~-vn zh@ExPPcX1o4gKV+rWM#HP|N?i@C6?>(yOTwDqjs6C=MFcFMLehQrFQT%1TY|-JEp5 zbksA*Pp_E^4jC1eC2mQ;nYeR&MIAEyDA{XzvO1tD2##!I ze(NH65tb+KQr%%nB~bw?MAMLSR{W*QpQc9b|5xfETC}#GjX{8)C@=Se4SNM^eFgrA z@DYp&7~OlY?H@s0!?33(CKjQStPPSM{6>PiAx;!gPHv%pDR?Sp52v>78fxp<&z8pYXZ`6 z;bP!XKdQug2k~QcXaOfK66#@bEKciG@Q~mR$vGi%zqXDJr9DVr`ZjAA?_rC4(7B?1 zI9B8mvMk#2vH|H_QR{cCcsfL)!I~^Gy-~wf{@3z?)ss=eV6=;F5_X16R`I_p3<@8H z%6kVLN#t*r^jgfDB)^qjf?jfTym+iT#xgs8!Uo!j`ymG11Q%hsr9s$!E1s!*A%6HN z`_5LFBnKd;PWa8T6pILrRR`gl5Y7_%%P?h zj*-yJ-H&9mc(W|@1C$xm2;y*5d079pZ+V&HIcjah`HmnKe}mO?4elJrSDJK7rE^Um z`tl(Lic1{UHE+ZRABWuL9}vDgJL`$&jLUE;pRZn)U9V@pfx8hMH!<9(l@yp8I&0|c zLQU72R-G$4GY{D1BE{84wV<&_^E42%87=b!5IZ@nLVwWu`Qo*i$lT9WJ`~QSS66d# zsjh@l(O(op351BRBR%!wp-5+6ecT*+hsW&0ZswnVczF21(U!_y&Vp zQpfHc&$ZGYbFd2&lnst^?M(&xe$%UF7d{NO-dL-xOqGrXdpjaEzrAc7=i>;pTz7w! zwacN2@)G?;LVf?pOLBcx3z2la1zEr{mh48n1e0(#O8~A2n!0qY_vH&o>3)etZ#+Ge zlQLe)qy8zurGhu=Qc8~*e9lpFsdx%qFVuM&%>4LX?aALhpT6jr=B6%=kg~MSA_xPs^vWMQHeg9 zqm{8}jkm6=0dAqLhaH>B85=T-(iqrl6x%Gxm!K6vS^>FEvqE%_)uI4QHls^Cl4ZF# zuI#YhV!%g`{|P6G`)eBpi^%-T7()V+Zk1m@uI>=)6&{ChKEIJ6l+*u0#hir+sEkbxEmu32p4ruNspS9uLW;gYtcg%XY zm(@=E^PTBG#pSuV4=o}>Gl3Xs`ZH#`nW!EL=-M92|K{Zn6Ozye9YaibuK8E?srgba>kFlLw&r26K ze}u{n^-a$#6DcvpO-}Ly4)Yvbd)C^FnhUupD-=};@mQH0Wfw|= z%i#|@fS}=oDO&xCd@>6BS@?a-yxW~f#8LfGH>6`N&&C{;>dWFNrkYMXQo(uU;bC4L za|0e5_hZW%@#f^lZNHu?20UkhJ!{JCmgQwt#i7tu(CG+^+P)ht=Omxbj0ba%5N~ir z&GJygJxRd~Ws=uU#AT0dQ&ODC?|;MR4x=PmYmu1U*#EH4R_C97oVw1b=s`0$u^(G? z8ElCiF-!!U-|RBC&%c1i;B<*595jHkx z+dCIn1A~%I#kTz}4MsnV{V*?qS4LPx-ZxWwb|fQ(rdF?M6l^ks6T3~vq(l(P4TIoo zt~rm&*e#r1lTrTaM@n5fm?ye354aN;LMJKiJz`|Nk+f-yQT=WUm7A7@3#2Hle;z{B}4w^NS}nc~k-=7G;QogzR z=7zR73b4rmb0Ljb$Vlrj%~J&P0sRopkO3is5NJL6<(6)$RoRdwv`FhwfeN>|G5Qc= z;-BfOmsx4;XI(YR7@0nljh_kY4T-kvwok7hu9}xdg2fDMS+2S-aDB(&FJcc|0^a`0 z{#zCq2f=1fxi8~@WmV`d6f|1Bc$;n`XDA~&D_}0Wu1s1SRb|W4AATs`Z|^dtFW45r z?^H=HalyYhqTSx@8Nxe|i6%GA?DTr^ZqrUIom3GYV20@QG=!&YFlU7rN8kd)dv?_l zRf@`QXa!_63ie_lupx6h=x+==_+zF_(yU@kJ*VZpm~kA);J>&-**(2ISgeZM`uLB{ z_|UXnBVk)1avE0|2JER&B&vB=Qk#;kbxXyw-CO9L95L^McfZz@p10uqEsOLcl>;l( zhcQ!w)4=KDOo=3JfsadKTg{`H9bB3IL03W@oi}>LFR2n~ez6I|XA`U8$3mWgnPfIW z3(CSgHJ9<7GMDYsW5+@Xiy?m~#^)o@G|3X6=Ap?vuUgQ3UIhfrle z|KR^B!A1)&8tJI-)sRO7=83nqFFVnV~B&!h5Gsqw;-1#XsP3q-nlNvWSbzJ zh#Mn%-)nrgp}iRrTih(os56_W%(s+}ye9j+b-5PXzuQ*WYOu?)nNJ&wPPvWwK@us3 zkzTaBb{H>-;SHbUkG;Y*ZQ0#a_w(v<(7n@|FQoH_y6b`0(&|;{bnup7x{p~5T8+oY zqVV8K$#)XbkMPOYO0?kxZHOgNk6q|sL8z8s&Woe3!go-nJwy<+-)+Cpz=wyFbZ#gE zo;y3Z(-|2F`Q(L9@$yEOwlI)djQ9~vy2|D~ms5m-)-G@g$cw1J-4pJ+Ll^_JAN{x9 zK8tTrR+%J5Rj*rhf3CvLKyxXes~PWfmqkm?E)7_6^a43{P% zJ)(x%;~{uLOYKGU*ESX;4I>uW2)Z}nU1Eqvg%pjf;s}L3s2an3v&CCZwtc}fU9{~- z-D)=N{QKF?#8gp1^s`v)CzMu*7H9-WPMbcfHEFXx>RWBGrH1dO2O7u^D+y&hJr9gY zdpdT2mpWrI7caGP{WEW35I-zU_`tbx7SU|ghi;__*s`hwLKBgdcIw+1jm%hDN0iyw z_rE-JNW)8OY!B4y*gv74WzM{;&RqnAPgBi(99#bMU*P%~M`nEqw(u>wZ5>R{>vG`@ z_V!)5rvRE3590vnT%PIz!VWKgv2UJp9H60~ufAiqaPm)z&e8+si%u^>M(_ zFvJPUAu)-*_mU?L*-*BHzK@^S@c+!=fTf2-{E`<=LwTb}UnJd6fy-d&`&eP+nSJEB zfDNn77UP6&Ohq$fLumdF3{rbv#zXk%+&M&+kD#m1tE))|A`*7?rc4D{<&wNwYJHfL z2oBm!youzsS+b>|3U@}?Y{3X*(IS#DcFs9dUNIi>*(8@dCtX01!7yK%8t#G_Da1tv(gN|n3kWwv_Y0J$jjH#PkQrb{y-D2z;D3J{&ws>`q35N^$nF? zDp#Fi;-)sMv-}69cYhtz?d;Tv5b1h1i?jJmJ=!y}S1*b?QU6qB#@Uz7z@ttNbIHWO zA#5RZHTWX~$r1g3Sy}Qq4fqqh1&g}iJ;_uhYZzrtgc*Qsd4Q9^%5T<73!&|b6`NjE z*fkQGhjJS3R=`$cL!YtDr}`HQY!*6gz;8hmKg=%Sp*823c8sXP3r369K*YeOK&Qa$ z69Yb;`bk|$Hf%QH27Ku`PcVEhS@jiq)Rd0mPacX8^n!B14jVM)RJSFwqPI@#r3+@s z_YT}8HABW@y^G{%?2I$WOcP(N>z~ww1p*shasJqcO7yB0gm2#I4?dZpxe4CQkwbGg zKeL|UGA@Kfrt4N#75-&1-Zq8X94o}x6smkg>+`Gyqo(1nY6%L6`FmRmw~N19MjCz~ z-`14e^RI29)(wfaq#?#=H5m|b6xH_(`6x{xKn;QNY}x3f4*I_6SB!%4hbc<8*lZ?+ zv97)Q?gY7L(ladZQtv^*U4o64^WZO;1r|fgzTJ&bxB*<&pp=V=MHMJM;;B9AU%Ro1S== zL|15f*g_M9KG>Gb4TI}b!@zNM8>bJlkgX?y?w-EKsjO6x#7bp4i|P%(43lH0>TeLF zK`zFV_pwJ-`(%+^>Mn-N>l{)0E%)eHaL?r;{5rqpyg{u@R?a&m?S-i@I9`IrFOTRl zN9~9G5;)QE^tm+<$7v(|T|uPV-oZRY_!(@+D;jcl5k4VPJ7DXBiu~Q1Vn^8ia z+g>A;BFT7ZF)>}D29-9{t><67g6g4YW;}{*;jk%155uLdU`OL@OzpOLwYLchpi|Lw zvc2lw>kgnVR_lxh%%e^U-hjHw(OIhxU*NmJjDdKU~me{V>8e?W!=E^gHh^_r%I$o zQJv=g=8)P_fE zI(I?^#hGN@eDuF7dnk#{)D{xp^^e<89+8#m2>7#r?$SSEs@*+yev4tnbmU<3PqU(} zFeTLTIno2_GFJkxFI(R$am9>G=}S#uZS6UHQ?!D(`%ct4%_GdRw=>7-`3{}aR{Edl{qkcuYZv_C^^Xr; zkCbJ65Y81DiQ1o2gB_Ws1Y?1Mtn+2%?LlXL>476V{{7A#P533h<3sFE(jg;ejJBO# zjTT!UGQt|;eiX%gE%a~FTFes48feP7SvU7Ddme6 z-9EhEMPWBL!v&_HcZ4^sO{R?dfVs_$v}|Ts-pO_OAN0hZrfN_xh*C9(f9*NpGYb!s zUN>|~Ga;3Gjnwm>ej~P=pZU3cO)^K;pxr7u(>-Kc`;m3l0l`})eCd{C@MMMrbjb?KdA`e3e71PpNp}sFL~|~}K9z}k2W1s2SM(fZEb!j|`H_Kv>997kz}uZ@x9;UO z>wFs4!&Tp_yD{1~b^--^>bw!Qatjx&YEve|VPvv_m@g4%wzeGc8D5{dh4+`t6tye7 z@N%f@vwrvIehahY?5l^G$5TYy?>%Z%>1>ch0RkL^kcMbxO`Nu2B8eP|8EK*hDcYV< zmXXQdYUn3kh8pwI)fwqH{Q?{Bj(CED)~@dPOA;{&OWg0F1#^S~*aHPx}iq5u<>{R8{xnwUNJhv(GQz2Ms9V2$$% z^xDuqW_uk&EX7#Q{%JLClC;N04`52QluO;WpgZ&h02n~ypO|QfEw(s{iY-OarIQw+ z*b3+jM)VaqXlr0gp^LJ7MhGjS+i{^yQwDyvAI5m-3qe72B!Cr1PK)SIQIy`{W6RLu z)JoDBsJs}{Y|8fG9hgg_ODoMSx7AhTq^Ct3&!QU1DUZV}l&{JnmViHrpt-dAWR^*r zs;CcKG%0ClF%cz6YE-57jHJ?{ha*s^p(v!{8+{HFRi`8I_zJhqGUN(O;8R5r*27?{ z#9~m%2W$-PB0m>BeS`iG?LI&{x?_R6qGVRx`;~6FQe`4r!Q{dFxsWAth@~pM@}F`E z&lo?4Nu{+vh9eKJ1yvuL|><@|YCe(&3`Dcj!y1V{Cb3fZ4xX(Vb#8 zJvnQGIS@Hvkp1OaW}aVS&gek&LcZ%MXs5omltV4er7FJ80!901px)A^KDv~HSk;sv zTRCcg7Tro-G&HJ@`5@voMDM{`sd^H&=rmd zp}0TLSW6`*+Uh}4SeHA<5O&vkUX;t3LLDldY+LV5eL4kxxonRbZBiX1T-PU%ZM4-F z9Fv>Lg4=BP1N7ulY#fD7D@%f|)8ayytg;-q8Dfj;&IHkt-~_}^ZS>+9>olEdV}lM7DS^VhS3VY5Yf#YTy8^45r4Rt1JP6YhC+PtX1rAa0B zI5+L#ZUpJV2~gNbtu&?2K#JP{dE|{zgNL0-F463Z_~UOh;r5rhj?|U-L47)aK$tFvXTvUVIuAvk)n_dithnlHWv7OflL?ZqEd0+1ZvWKmCD?$xc z!CordlLMRYe)beridqz{{og3*Pt9alAU96FdP`SiESy1)QFcDw@*42C8!lybSgBa$=Dr?N>h<(~Q+56KIN%uya+KwVWk)#ezN+v!B-t@BZA->fR_`c&W3_hF%RsS@n z61LQ9qQLhK?7Ry6ZRMCQ@ppeM$Pf@o3g}ZrsXATycar?zn6+%BE62fK-g>__u7oaq zd?c}9jd)bgzMVY-y@LN3uGNf`)&;&a{j9~bqmA(j)2#6x{3J<3o<ADO}=ytkN&72wNyL#|5$<^C;KA)Z^9R&g`o9Y^dv=PqEQ^wo{(XF1F zZ~nvd0LHU{(dy$3mG@tIHh|o#rUyI#iwewbK6j%B)&;K5&OuK#V&TuN%~ZUJI$9S~ zKnDa!)^xvljR0{WQXudH2_q=QmL`Ye;RBY}jiVopSJhRdZsOva0fRY**cusIfBsY# zcOs0Ec8}0w91DK^H}s#&3b|hwus{JweQqa*+S*zNN5=$7ikxx~g$(laXk|5Ff;4-6 z8&ul{j*+m*Z)0wXEBsMMvo&Yt1Huz|htgq_j;83t1q?q zNq3LjhEGyI7{6vWw|fj;o4lXuxF7xg94)>ulSK^Tu}E|NE%1~^hMjK#G(G^r9AXnNg ztXu!vXXthqZ#GYeAfxJbN55>>JQUilkT2f-kqF zMFd6qXmJtz+4bedOd*KUYqCNaV(X7a%dn(XSO%brKTHYGpL90faZsk0`b?be(AMNd55)a_Ulya<^ z|9IQ_03de&YT~E84uG!)Qf=Z(o?^+Wo{Kp)O^o$rD8z!d01L;_ULm%4c{wXqx*s4x znsjf@diG_dei9G%Xz?i!*_)pu{dE-tPSydyXKO#`Gsc^_cwCEN}GH=`GO-Ulpy2jX;QS>eY*gjsfrTtD_PM0Q52akkbs_8!u ze2y311rx;(x9}FAMUsE-f^hOv4O$BE7PDpwhvX@1gdfSGu|J7_91XaB(x0d%o-fBx z(D0FCz0J!cwYWa|bUf8eCFI#tfb#|~Z8c?YGf4x>Ty3SOyl*Gz2>d20j%AZ-3e>TSCGWgPEGOVAAN+iSQ1^JFCY{b!VJG|k@p_jZD zq9Q{SWJ;<{}I_+ly*HzF=vQ|HwL?7l1llF`jIb%aa zWvm?r9hDYmbF(Yr!)V7oW|79AhtE)Uxbxk{Erqt_2EICw!pCT+nu?nf=Y_deT!av1=EsI3!0b8t*+sNQN zQ^@J)x6|Ce{rJoRhr`cSy3-9`Ty;em* z^6^`;yrmw^2j)-f+}X%`qw&N9uRL$JHAt6h-}STO!cofpAS0UxJeMKv?@6F<~i=7`d2S zh^II@2UDrWh8HlpQmx@hsICR&YD%lCv&|cc$>Jh?wu=pGN`FPpHB_|HaO7t=WqiD&Pu4_;g#;y=^vu@ys7rhuQ00-Hq!7#v zeXgOW{5tT(cbkkVDp@g(AZBoGJ+%Nbvm@$A%gMOv9y1HDh5wIA< zkw-%cCKqY^GqToYpbC@PVikSha@!xSscRo)9R~*aKySlGFz7$$Mu8w7Aqv1iXIOF2 zo@Dl9MzW7J9+=886SVEZIPD%B%IJ&9WDFpqDc&n0X3F$1@vu`kd?zN<^V_oE+w2 zooLcx9{Tit38lE6zhTOr{)#OJV0*{{7$YNMIugFu99y&3PR51NT5D`uvWyB^VER;A*xR8vB)f0P0~ zp;4da)`pv$@$~Q;8G$3UKa#@6+b0z<-aO@s{NcZjKsE7l+kdUj#ECg{*KVXU8v+pp zBL8LDee-FT`(7cnv|6+|R_g5_UUc3Uy%@&qr{86rIlZBZ8x%lI)iZt^giS>g8UcOCRK5Tn)ax<-qNh-rVgr=3zP3U!6LtctM~ne3qy~UPny_;H-V|X5GAGRG zksIbm2ouG>`44~e?++20;NKpGADwRFS!_VIo0#8&mwX@4YXBI^IMt_*fnZV*i$6fp z8%?*<$F;lKt^fyB;2dG@n{)Bt*WzUPG60sHByjH|hl@?^c>F8|Pjb8chIX2xXQbuc za~qD_5AiO&R6KJGP~5Q*BY_ub;Hqtf6+40I_*YdFs@#cLmQIpA8PAtQZ&-Nd1aw76 zXZG*Q35gw010I!~5Ilo7UVqWQQ;&8G!Y+wl(>fy-508%fe_tc1QuRHc;2hsO1oJ4w z8+>Tu5^Wq%d0%p0w}773Zj6jC!KXYOeiZig(qvwS`zPtY-P@WaeaW)nBbW2VNes|O z3fvvsssD0wYG6`LyUqFQaQ$qo{_-wEMwsJu4f-51iq07fr@LSqkUx+jX(q>>be3H6nYj zIkuva@TdDx$_uKbj*K$}x1WrBeh2XU;zh|+asb%Nn+g*Y*#`){dZ&WmB9TgFY?PEH zfQ|ouSP1~3`MMj&!VjR#0I)5e+rI3asV8Cm((J6hfkBGAHEgK8tt}|pGI3A5Xt-2P z17NRT18fZ7&vj*Da?Eli&sGcq^moYpiiN^PYS@}SRG2Zkj5G*PEC{h+ku*GEEYy@<9Oa$>yqQ?ld@;^4SExQ1#vE`zADI54FVs8EpNAW$+y=HG+W>evrG{|b_FK{On?+$r=pkvNeTeb(AslX0JeFPi~b0Y4WU zPO`ShH?uO5?cOA)?jqK3Q=F{fsj&YTcH*M`xzSf_A9)zc#+A5{~0h$&SC{zXsaOT1)Cfq{5EQ~>_2JZhRyvM&LvpPQQvQ;@W zz2;GPJ;H##-~AnjlJXSsr}Vygx(VK27piJr|EJZ&fytAO#9X;b_H!AFVR^-fu|!Nr13c^%lr~Qc5n@R`w^uXG4jq~j4!XQx^E^0NQTs~E!SF@fH{No zbSXKo3&%$8w{gylii%|@@O_SDsPlQd`&mqDpX2&`VvUT9e9{h-RpUNhxjxGegSiv99JasQ`WWt^!)`|RXa%?7_bYbki+x|!Uh3+q=eZ6p2 zp&X)sN!X7@=|4U`1~ld?8B-hnA8tD8coD#7U2FUs(Ol5|zBYrUOO-hjD7()$MzL)I zV=l`<@-W@O(dK#r11+_EQ13zGe|nWL=n`mdC1W*g+=H2wY%Q(0aEkrEbqw@{i+>@_ z-1XfSzsoM4r6{md)hpe0Pm+@AX7HDf{D$Sm{HaVKF~=*H3Zty0&~EObd>pnHFSawq zm*<$BCG%~1&sA=LPn>w;c%(&vMlD)Fc3Y7Y#DCXo?dOJ&OLDnT2u*c(-Bf2`iex3+bF~Ut~oQeJP919Rc{-NB!P5Waja`-Um<)XYH3i zZ{tNvqKB77hFoI@)&2bsiMAf$Zg`#sS%TPr0p$C8&^P0G(yay9=sa|Qu`DAkEm$E{ za8z}-m1;?#e)q4_X?aTMVLbwb<;UM)HIXwI1`^=s&-(Zz=s;xg@8#TD-_dDlF^%N_OjZ_vK(^9k z9eluG;(M5Ft!oa$lfS0%MJ2a(?@D5mV?Vn?ic8iMYxNZ_;vdse z4@LRIz$^I9z-&XDFD>ix!avg~(4!APX>Ep1z{{||zaJTo?PpD)Z0j`Mga{d@u&K_A z+y%QSe=ti7WeHo0BQRnF)>V@4hJSYO?Bg4kBlDfp>+;aMEkKn@+zWW@$5YDXwwx+>5vYjHZh|2_DIrzh=F}71 z$aA5O00Xa;74)G#I*0{Z{veo>x)iq8XcUDSV^!TAx zRL*pgrXw4OdujJF*8`9bUmJ*ckeSX!4y53=eev!nDY;wBDAu&GBUeiu`=`H-pOycK z+pa}5wng~IEgtoqh|uN@*6>f%ASD&10u{8rv4m{D>|V;BHO{WW@?q<9=)0_b)We>4 z6-GrPNJ3xTEWRZ43QPynA*#P<^XLGp!WdBJS|B)X#kV+#uW=W@qf|O$HSUC}vL!ux z6?c)?ym&SHa;!FjPNXbq__@)sQClz?VK(jd)ZmZlXzQtWT?Fr^01qo^1oDfEZ<^KxAKgzA z&hG%GfWs2vh93h4Vd`c5NcbT}B_Pap3}tM!!F&k7S^^54yJ-oueAhn&0G=42Cj~!3 z|H5W8XgNyg}GvPG>Qe%6yR6H8h6BpvV; zm`~=d!~=PUoM7oWtliwmP)E<3o&a795)w7?gWA681O8%UG$O7 z8z&~s=8M9UG+JVVv)}=U=Fo_Ke^WZ$t4hU5B@$o%MvE|J#*JmNd}IbcWuE=$E_gZz zjw;T9XTZh58(D>N{_}-%FcB23M^og9eIH>bSaxTAl#MjzVOh*Q{g)F{n$h}{4ZA4o zw`c2Jh_V*esM5mXfg7lSSr^VMSawggFof`#cJ2W-CTMdR9!;@QdB_}tO!gw&^2;F7 zvvK-8-sMAw?E*Gwt6jwsE?t-vVs*DXEpCT3DCr17mCz>iF-}hW{WHeu$(|kcy zdVzCO%p%r(lD98J#zDuCN~j~JVA+WH&?fp5dTbIk3=*PT{#{>5pI$GIiLgqCb_%!} zUN}YJxPhBjuWWPb1%~DKp5TW_8liM}ZmtjWc=xuvBg7tK$uK@=7W26WV8eA`OccB+ zID2{WL4ehx4&f_T@Mfrw9zf-K4uPG!fPXVOnz23iH9MrJ0 zqq{9dyO_XVu^m#vdgfw52X6lvQKq?+njDXQE*ZaUIM*HUI_RSQkzCMVg%9RSQr2Z&aMvxI4rlEU{Psq^1v<13B$Nqk){x4EUms8 z1q>>l{qIeS7O)LHcGYneb~E_`EC>K<4%~VczTQ@dYW0*CM)-@iix7>5;T1FBmY zxB%{@9UB?|9VAa&nzvS9`Qn^`5$XvA-`nDu-`@D8IAb)i+Pk$iyFHZEITTo9-4Nm9 zD~XNZ8X^+NeN+=su5>Wd* z+Wx@bOH51z3>ja}7QaUo1^#QCYHDG0rT(*U9d7(AA~KDxr3`K~PC^rLX|-J8{LH^bL*}2VRc}fFZQ`>7D>)3jSI*K@_)dVCQ*M#%$NSaC`;dt7l)^Wo(mU8> zTYduS5^li|tc&n}&gcv$IUoU&{`)f#tt!}mzN=RAabkRltwP)8^#bI86@@M zXHs@`NZ4zz{L5D%fESZmbLIuSPB0KlP`=cA9w+ z4^_3Q)Nz?|OC3i$$A5VPztKi8%>3cuA#7bRgGfu{=X!4X0Ro|4D@F`=R>f2 zD2Ug?)zk`K-6U4kWq}wm8JXk|t-m53au-yxmx+5JN5bffDR#M*8v=H=`Yka?l>N>> zf;CbOhJcj8)9;6a=qMaLU-Jv1;NLetDBy=-`w+hg;w>@_fG`hxEFc8aERA1JRQ@}8 zG0LVMJ&_1_S+Eo6fjQ*CuF3~)PXKfeI0klaEi8m1{K%CJg9_(og4RuknVDBiOk=~u zE>qg(Q0<(ThAZXT^O(cn9cczljlr>+W?@lnG_!q14^v9kaZu!qZK>qZygTL~`Q&e( z480x}_6eVSc4p!2#>5b~Wek38S^J1*JllqibNaOtWjj}{msny4hw&Jzz7hRT6eeu= z=&#jH_qR{17WxShUS$cqPq`s&izMS)3ZmYyjAgCX;x|xmi*R&0rhuOeu~fqGk(1mmg)?^Ow8=UQ zdLf%Ga16E@bU)*9%;U6`mrl7&rHKubLLkSi*D^&>F_B<^(7Dz^}Azsb6?dtx~A8tRG#-IR15;EY|@+BI=(R4+-e#iOp+^! z$z%b|1Cc%HTUUEenCpXj>_Z@NNold7Y7No+I-g(9PDcoW$cA92(7`EPUgduU)bKWc zTwa6rS{>IokLbMT)nt4di-!;gQa(aff2bjx7rG>ym0tg{l`Yr@Hcd$+Q(7NPVPxOO zSmBy1q7kQj*1kgj!4L5x`uZg3P=4RyP$rqNR*=zH`wdA4ZHMBgK8pAq zIZ(t9i2EdJ%1Mnj9YOm zK_^JS#r)HT^9QTvvq#*Y8+mu&lFoZ&Lba?UKw>QfC z{EKqf>k&XJ_kL3N4l;j@WC>{K=p&*r{b^RA z`y-qzFL1K$LF7?%g`Ho(?Vxd_)~AQ`=}&PGL?sQ17ymZ#fbH?$C%xO3i}FcKiOTQQXJm+Fw(3uowAj21O3O)*5RrDC+)1c23xwR+G{u@u>+ zB|L!9+O5qn8&|y;GCGP1w1WH#mi0<6rxk*eOrt{i4w(nF6a}_Zl}8N6Ajtx{K66eE z8&(bb#@F*tJV0jczZ<&>b^%xl^O4Eb($eIJJSYjz=`c>nhQtCiJ>fl~N=f*wS&%#< z$#rF+I`#JAaD+LE__ON@`0s<7 z&jC*zNdapzuJ#f4=EST_I6697;K8p<;bB2|Z ze@aY~I-brx{slY7=NLCTQPYn*`ab};K}Wu!vSbv6vQA73ap0eib&OR4@dX{P??fx! zK*O3yL@(X%`l<)|FWuj30F?$4VT@$-U1l2CYo-4%=JjhwJp)fesK@Is z;TCGNL#hU}huR5Hxolj1`Q^_%^R(kQZ@j)1A++sd)8s{`QbV|*y|5f%BriP0!5nfd z?S=a%U%Lq*yzS%DltrgH1Y+2<7nV=Dc9Zr$AIdFx4|?%_!_W=Ww1)cz@BMZ6zuuJF z`ugBcJ|P~o41-YsrYQ)5s+DiK^=5?7?Af!AJ@(kn&dx$Uzi82-2@@tf^~|%|wr;0q znFh?#Tv>$RYHDNya`Dn6dP1R4*uH%`OHQz4$&#U=A$BAftHDu208J<)3=WNmqL|BN z3nRlE$8FoTO;Hq46c0M+AXlqhalNc8rl-HC;--e^B?JlzV7)4ppnS7sS;Zp6HL#FQ z_KrA?Q&qjKEvv_P{*Mvy2%7`y61;G;x_(UI+gtubV=arsMNGu`WbCcdKTR=ffiB*( zfB(DRJ@Ld7k|b^1xbc)zPC*E@wY5PdAXBIHJE*fAHE1$dA;$p|rzA=J0|Pcjx7>2e zDW{xr?z!jcx=vk!Ah|uYsf`vm$IkFvDUU9>XwHk@dQw-rkfL6mm{IMlTE z2S$R0I)DDhw-lM%p&DFvBDf*Yq8yrLaRhh(grU-rAQ8#71g-Cox!uc-MCez57C3@r z4p}-BKIVu3c?3~FV+zoB@how{$(Z6&{iO$hW*>S|p`;pGS$+OKFz{C~?fsK~z1e#f zM=cia_@A0;?`pAl$4?}%s_xPQvKx2r9z*^|>4o5z`#my~lKeAEyjwG)p0gN>JfTrK z)tOTUyvjZBvlohooRB%;!=*>Av-5q9ZHtGTpe*}P`O)j`kv_N0tV7ww5?t&@2z7ks z7h=!U(j7m7@I}M4Ms~`}J`5hR-P7#CAR!n$^C5T!hTFMw=SV(3ciz0EOPAhp#~sf; z`)p@d*9j+_Flo}H8*aE^$x%m(BDlR!;ohxnWjUZ`fY?GEOph!lfubnSKmWX?>joi9 zmMnpKqm_cBjFz)RLuJiuYj5WWGEFO!$yk>4>Z`BHvaDpYbLPyk9mEMxUYwd5-DnHn zJ7t;}f1qZ~n~z*)j)QCmnx-j=q-o%OC`k&>L#AZkR)P3}-k$8>5O^?zURCSW^${K_ zJ|~Y|bIzL<9pcg)jj5%eRWR1+mBxBp7W-q{rXUE19kIBxrxy-+rPzl#G&C|Yk}nKr zvspM+iZVPjNC;tyf$7s`B8<21*ug4;BZL+%SR{#j(a?s6hr4>ZR8=E{?tkWK5fwn6 z64avFos|!0N2eVz>9!|#7)0PE~8GX}5XB>`o=3axKJP_#oEgtfk=$MoI3+?$r@%tPq z9=uFG{v!3U8=QPU!=)XI-*(|c3- zo4=bm^^?{u-%4JA)*j^Dd#v^t^4}=L2}5D!e`_s#L%92{h& zr4T}$GxrlZy16Z{v`wEQOxqX8IWHM+tdu9t5U20!w%KQYv7?*Y`fB_1IdDaDVW-=Maac;QbhPKYm`Sa%=ee}_9 zz4g}AsZ(h|0iEUXCA?57m9kld=TJUhQq|Jr3B4HO=bwKbAvA5;G)Y4Ff<-H7fkDLc ztqrJ}1IppZ>wYjzOBDG+K@~+YlgTirPQyTkp>=h&m&=gzkA-*k^%rxwtYMhDz%{qE zKE3`7BysD|DES!;bCBLxmd44Z=>WONxuM+ztwP@{?$@>|u7532f>^t6V-1tp)MPIwZ8QLZt z@vc%m@*lA#<0gr3qp+9T{=<2W0;HwoZ;r~%ZcZU4`4v@l;o*m!K!NGvcwWdIdx3HP z)tQArwnb6M9DQ!_uB)U&PvRlAjyB==?9%h~`>$3Op2F^-@-ZkNIF8HZa+z#aRunR{ zRlV<;w)3v2JQI`>D~^nmN~Mo|>|^`xyKlK%{@w3>_p_^i@%L|vGZ{NPXySuwsEZ%U#4TVDC*kg~KI(6!l zDO1|p+h^q?B7dZ&K(oM(|f-#|yIxNPI?tzMCO+M^&2RTyfme1$z@==neWXWNIqQ{6?TUbyi;a~FLlWtPY?(aie_91;$IunFy{v{wHPWh5C#4Glzb*v9%{|l#V`6 zfAM~HAiUxrFn?CuQY#g%xwP%0KOpTr-UMvN>FVtr9v;>-pvR&pcW~SXKX8$fk!;(_ z<#OxSzs)q^%-HX#@dZJcIB{Y&qior->8Yoldfec-+c4U>#n;llgZ4OG2__dmwoudA3pHF{RM$Db;~pzNm73FqaSSCxG|e; zTeN5q?U@*cP3!YAkI0(kYVPDHic%;*K|fkK(9PUz7-02h89g};7YZN%HB``{g)w97 zLm-P^N_I1_MU%Btqa@PlZ*PcKPC2ngV;o|A1@=tw%LAV0RaF-R5YuVl46yf?%jH}~ z(RH=0t?i66&iLGCKmWb|{_a_4oz>adna}47g~EB~op;VT=NN|3+1Ux|MsN~_+(FC- zndP%1ge#T637{paDFfsWge{a2(4zg&XFs&?KY#w5%ujSA-gHFBHi#_B03Z_^90JN+ zDkD)5+uBjtM7oW5UOXUnpAt(5=O=kdIw zZM8Uj^=A=sM0%y|6F+vxYA7Szp0){HC2(?(am$^F>WbLvS;&$Mw;|VSH;8sUhrA4Q!5C7I4+6MB=WK}u* zoz@fAtFx+8*i>#zy6KBojVEzRtd13BYZZK zyXc~ezVn@Lm&@vRzWv>=U4F%5k3Dw%^?zEiV#VO#;Hp)tSFK+2)1Uq9l#@^X`q#g9 z_@cugSINeWKm6g9f*^HtbR2u^v4&w-HW38gbdc%Tl~e3v$e18hbpxopP|REt-RqIB zhBf9ma5!Rm5zoO;*C8y5aX1*m;wSH%w0?GUCgBYbys`SaP~84(!fiew486{LS`Cp5 zG%ajhbK<<~aW**Hu4dl!8VCCEXDpAKOtPcpSU{62%S%o%_9!yg6)2HHBh&O7hC%PzZ&2J4DNRU-oIc3^gC(IBt3 zJu7EdV-KaSObEhokdLaSc{+&EFKep8XDl5_COT;m;y!oy&#!;u%^^Oo3rIw~z}XHm zFtT`JI2tik%n3yvb67wE;w*x7p0l`&z-uELdX#~shjd)=sW}sQ#Fvo->;qQm3#0{% zpiUtN9tBWG#B-;jy8|+oc(_zP?DXU(z^t|J2_G|g8@9=2 z<4*b?hYS=z(wC=C}@AX zNnib#Qy3n(^JUK^IY5`yfBC1iNRutkwrHYm&=e)!?S(1@JPo_6XxCr|F) zzJ0j8z1=j;Y15|t->iWOP-V7EWRIG92+;BE|`#}Zvzu3S=; zj4TL(ZrB+`{@BMp_TKltm(~9Sw*W;hmdYkg003S`gf}TW72E$P0ox9w8OBh0*g_a$ z^U<*fpsAmm`_tqQVms!V5VEGzON*n??p(ifH5qw(ZmF zeDFPs&Nv!%bI2MgE3)DkRdxib5ks**nrkS;x_h5oJh^TKvJ7KA;7Q!xTU_~|p_Pp{ z!2L+;n5e(@v|}0U>)+3|4C}nZZN?q>9}5`G)~`i*DaK+Y>1(`Hp$E`lRWA2l^On1X zsgJAeIOS5waS)4NXR-J_v`wXl>c{RMuVNphr58v`FTe;%P^i}?l`KOD;bqHCggoDb zbau8I8syETwM5bT-uJ#KQ>O5OIAzL|lTSLmSOnj_VzDU8@<%`V(fgO*+tbrCY0{+q z_uuaw?>K7y{Q21oUshc=z>=yt2n&*o5oFtK@8~+~UGF;O)YHHA^?&>6Pk-9o(K&PG z%u>0W%VlL*`RP?xz2k@_AO7$MG{e?)jpx9BM-e$)uciIr%x>Z3YrsE0Y}(%q2DwNv zHxR0h_6qQG0Kw$&sTHBIT9Bxy-DDTYNCnS0U2V_-R5Kn@pjtx~|3WQ$G>4rnNs4Vz zHGpFkiYmu(ot@n*r-kDyF3N`ybr8ar;QwEDEHv&&paw&IZ36he!~jo57k)MzUm3Ap^%BJL$vc z&70GW>=7hzs57JFhoJUYH6I$K{p<-UR?mnjFpyMpPgc7tQ`x}X z|KlRqJsOg?y?0dkzn=h}a9q=v`)l|T^|G8u$SRjbJs#>;%G!4q{DN07y z!AszV>u)3+*dSR{F(IT>2K68d_7g=>E$bGT_OUEWmSvkJ<1jj}xZ(;PlVYLFyd26U zHJg<$y66KO6as;AM69$1FZuFz#RFCQk80KZT{UDiAH5xY&kbO!3kegb(afh#teN~o zO+0Tn4>8(ffv%=o?5gE*ftRbYhsQZ zv~TaxhfO`?0MyxzpxSQ%P0B&SU_(Q^B-lKvnlW9gnL93|{q^!g)4!T(NpdQb(lqFa zS^vf@q(5%4gh&PyOBg7AW+v3Q6YyvIR0ofParru+C?bFCb%_?sfWKeFs*M~)eNxzYN{>C ztqfknz9Ct5la>e2juY6+ZU2$cO|5#74Ty=MR#0(RrA{}r2a9IFC0O8jQZ5^^Ea>3B zYxA^}MU@0rGIf}=V%gDi zk!879)MP=-X8A&1rR9N$VTe${1QGzlb3rX3s8U$bsdj>9MN4rK4h9uKT19D}zxNf0qY zrU4>N4`+6CVzY}w4iX4Z8qT2R4vaDcLB%fYV2**J36k2h#8M6hymu%9s4ww=q$#iFUICeK3w zO`6G0VOzN(1GtSH2T2mrG%A7H>bm9D(#=2uV`ex|6xp(%-Y83_t7&j- zA=8M#epL)pH%+rpKppM!@USk+qNW*y=VV#PmvqCh1)i8J&v5KA1+*MZf#08?rFs2Q z!X8b2(nFXekE!+DUkWQ3_K1~tW7aG7yBPccS7=cmM&Ur;M(daIl0-BObaM2(OB|Hc z;!Usun3z$nR14CucFjgekPt?qr5TQCfPhW}f@MMG9Ua*Y(lNpUGI-rKEg9?;sEm!O)#$6eZ=(Ui~_BVctTy$RO*7TS}^u}OjS!*HBSY-c1!}sJ%VlaG z+0B#B{^~js1j2IX$+9p!Ji_yWra=Ksj>Awe*K}wwJXb2&ZJBJL$nu0!j~Jl+ASghv ztg1o!Ap!oGrllxyJ`dG?4a0_%L!c_qRn@R8Ff=NPtg4`CpjmY-+eWg?%d*6#)ZVVJ zrH~}B4h#=N*G|RLc9SWE@V9_@;kob9w=)@s_pUE>6699hTCB9TDS(Zr< zu}!Rr4|vSX(7S-HHKgih=qO^lq`8tF;_8COL8I#W1oN#YF)*z)J4KXz z=1da|*j}Z($d~QnXaw1CY2Y|5SO)>~Zn$}+oi_p64|QFsp!tYNdiT0H@Eu2xK!a!6 zQ1k~naA@@(x0RP7*M|Q(Ag$Xpj~+stT6PpAs~a}q1fa>_vgbAjR=|69ICB^itm{zW zm*+*x1fos{hv^CgQt``z2n;-i;vEy=v@Bc-^5EsYpoOegdy7E^<8}NrZQ%$9)HW z3X9I(hmb+zm zLL}%94!!AZBnK)1W6ad|#5Ry3Wv~qufH;8%48>;RMnSMSV6OU&Iak>4+bV^DF5q2Xw{gj4ijY4 zGMlmKS?IQo^F|B~jqRYm0S-}w`%btAhZ5e3t6?+l4vpT@-~hW$^l!XPgV zFs9Jlh-3GDJ-cjcyB%u{%6QQXd+zSzLzBUXu!n@`S2{y_#U;^@TAXo)HWc=&1y(?f zJs8W2&NxF>QO&3Ouem7@#OSgY-oP%fnbg6_0pZ&jCoj6ykj%b}Q`f)06R1jielegP zC*A~voq(EiZS&vm?>Ww85A9tL`9zUbeaDpW2N;g9fZMdKJwYL{8^M#w(%%wR+n~J{ z>MqWhCbOyw0oUEuqJ`!jEnui}_O9&_iR}~M@09#E>+N5?z3H8t;PD>#+(d6-h}3pA z0sgs-#-D2!1a>AjNWZXkN&fCaAK@H3{-4r!5_zSyXmRwG#K@;hvlRo(S^9A!pM4zi zxN-Y4O|xALV>E{*x=mv&Mtde((9mk^VbXugqoh(w2BY%)PY2B*soe!(%sWnE*y_UQ zC)PrJOmhrq_+p$}?`XX6niH-3Xf*vT3G-}d0%>S=g1#!bpWO)Y0kMs}_gx8ooat_c zI47Rn#63s;b;iNc?PAol$B_Ts2rbuHHG#Dm&|m9|q>Xl)1|n4yqvuyzt}5TX%tyjg zv}&Q^6aWF2pV31@2s)*Eum5@)@5?kqeUD(8TMmJh+-0Pzt~t4(9?~EG7DkYUui(GG z{lnnzo&4`Ej<34!Q_p1lFsEU4McCzSFMyPZ0=>p)_rd7x5u=&^Sikh+cSrvHq-$5c{y082?rRz+jz(Rg0&Xu&z*v}v_YMg7 zSftT2w`$y$zl)l^*Se0I!~SU;5}UA7q`zgH@AJBaE%u0~*!AF4POAswfrZ+|02!oP z;*}HELs)6q_#~UTJx&;9C9zq=XBz7x%NY=mBxnyQW>}k$DvZeB3gN=2yzY6<$=(17 z;QzI?I~ylo94@-c>j(FtX#8>by#!d3W0T>tf&=Zfhtg_X+vP~n7vtZlf5nk!CgUTFBaBA-3IUq#(E$rBDgyC1hCn|SKZ zDjbZOz9>VapG3k2PI$WTlp;s*?}tdI&VZzofBcIEF*`fm=J2s}f`W_F z(5Uqoi!`%sm4MXf&PmQ}Gn(tQZ5WY^ct#-CwVgEQ59^^;Hf*vjKdfGLTBioy#Kd!|!4UGqx=AU}Uqn9oeo_(qNBoXwRUbLPY)zJ7)HA9?p^Ee-~)#;&|s|f!Qcrj8WsJSi>k ztABqsI#!xBsSAy6>}0{(KcC2LT=%ZPaLM~Qa2Gqj^kN;Y@F-Wzp}RDF4}nEB`3GjU zM-|T=E?Zyw^X9*<8hP`(&`LDlF?amx1+H^1khz& zO+zw?4LBS$G!uXS;l?#N*wghBDOmfH{w7SXdT)!!f20zdR-K7c($-g-ryn<>_Zaoe^}EwLqU+DalE(hxL0J0V?X+W;LCeU0+%9a@ zQpS~nq})Hpj_EmY7JZK9d>KmC5g56%U0E^_#eOXebk)@&_* z?uL!azcA0AQt+}HH-GM=iO27kdt_bV|J}R&2j@-y(f!+xo}0bo_=!Jzc*l>I@A$W~ zrcC(ai>Dpb&J%3d&V8##cK&d|2Y$8w?1dfoygb4>@!xXg@*V$v)|4OJzkSKv?4`ad z$;%j!V#B()z)wESUVU2F!u=38VqD6dFiuIHj|F+tV{eQkT9N$(_EMOiNU4{OBL;C= zhq_$_bjwa{KjEz&6_*;B7s zGBXh=ms2HwO~vi7)!e(-675bC?Qw`_t;<7eo);kYsKYSnP^V*N>bMev%UMK!r?x(lh|4RPc+AIFY89juT zcb6&U;wBK;I04ZoKV4*p4LxR>mT_(3*17H+Mv3ggyg6+?n=0lW6%I7^qq{)+u#tW=w7;S7{bUZ{?+3xXUMF6gTY^N-c4;|GLxc$g>nM5cI3YLUU0`&~bK-Eo|DnOu3nbg0wo0EfZi53jsn+ShO2a?N8qPMzP*YIV{nxznOw;NJy{ zJ3svExBmEv*}s3PuZjx&!VP^CdZ)+9*IaRL!Khfwv&hGqzagz}0_lo$jr4wMZvC4O zI2uxT#@XOg-WxY=UBNWL{u=1h#$eI6F~T;KfpqJwB@zHyYr0a+mHs8H`lNS-26VD5 zf`|jvT;VS*SY5rVrU&-7@?20n6GO#oS_ckxKP#sh{OdqrxQOfid*j+iwxI^->J=NF zHKI*TKfY7R&TPzSjrbdG&p!Llesh0W58?jfW*9kV_U;;bBL65+*Q?UM$BwX|JD%sU zAOu2wJScp2PVTBV>zfAjWlp6vhF(Y+WUNg#Lr^S*v^ z3y()=dyH;-e&p95nJw`6&7Im`U(OSZC$@__^5*0YVcUo?u}vfx6O6B0G5EoE^xU@w z8hB%Q{Y-2ZcjnD09m2L@xCIj&zT%DE)7{Qoav2ikFw8$r7KxK!8nvm{m9|r!&$>IR zt}xNKzM)b-+OvBHiaWb)MhSL+0K?xT4F|Om0=r=QNg_{(Ma#2RuFx9mlqrCg4q@#b z8r;R&(47oyhyQQ&N$G5$iYxt0s#u+_2<#(fNM;qTgN!A+&PYt$75`r#^I>KL)O_^b zh&8JI^^-VuT9e76ca~fJ*FSplC~?0A=I>z}9&>T+8IutH)w|b^yKp6dGqAyQT{jG) zRMH6{q9_UiXb@?bKy`<+r~!th>xu!X%_cReU$8H>$Q#b%Q7T~zZvS;T>_|quIn7EM zBV!mJHag1Rk@Ww|U;YvvnCA&0%a$$s{`bGn(mpUHeB51<0bw`@(-H=JDid&~0Ksiz@x9rGARcHE*#u%n{r!b634@`f2~O?wC2b{U_IC0`l(y z_W6ZP#rt28&OD^!`V|A`9M*{tI{Co1Tc01iblJpPR}P&#za1g8Wynws`>aDde)V|Y z))6Dz&&daPw+xS~8+XCY_M$LqqWb>6?vb%fjCa`TyPTe#btvL^+d@@qstJKcT_OcmFZ0?MHzv z-U^u3DgMX1re6Gqx9|J>yyTYOJZtJdU%&Au5A9euJ@d6=dJ#fjJbBV3*KN4#rPo=^ z9zy8(jim`~{F84M4a-^ib}8J+7f%V^629^e*sEat;;w(dUI7dtGD{@WyHu`2r17P* z;6JslRt2A25jGIiwdE)LisStxQbMbw|AhWq$WQ!TkpCGoW~eF?vs(tQoPT{X2Y?Vd zpzU^q(E4v5Y+06CDu5nAmPHY4T{biL!?pkoL_lkl#vSanD6m_)7JspREsE3FJ8l{N#O(Q=U)z>PwL4 z;CqKQaYP$c{*MOIj1~723rA-v_sG6SIX5uBM~)jiQ|#)!>AaF=#pYbof1J^zs>rp2 zncwQpr?J4NxOJ}D9U4(~glE-+Evcc#b-nXaQkPvregcz8UL2+<<$19FI}R$BbyZa{ zCW0Vnm+kM~-k|cSAzF*;&9jL`i%h-MTACeBFP5@#;QC%p{+nCz5xw-S=YDnnrWG%o z|9gZ`-Yi}A#8pFDp;K!6-a9_iCP3^QgXi9L9s6+jUp{~kdiLFa@UWl%m8ky$yebE_ zPh7;P$gVaVI#7k)Yu-ZR9{Mh(YHu;w7nB{{jnWCx~Q3<=D~UQ7PN4?ORN3r?E8SmGrcJNLfzq^|3y z?sFt@h;Hf2H$L}M{n{)D? zUSHl<9`>63^w=*=kUM|;;tlNUBimlMZo#(@LTAo6=ARz>ue#*l+l5|MQS$vYJkMtp ziABg!M^PjghH`;@FY8Y={5G$kbr9 zK|9q!<8JXAOvd_v)v9o6Ad-oRBiP|L#ZXlM>4d3N)Qu6)bU;yHYsC+dgh^}b4Ao@$ zjpO`+164@{_5G@yTl%*gr=*R29WC`FPv0iq=cCFN_`3{K(AKgAaq~&D^0m+9zpF-u zj>JWx$h(<#2p<}Tk z7r@C7~9EUY=+R21pQQ%ij)vAJi<@G@(03R^a z$w9UZw?BHOs8b<3LxFmi$=+LGmfFvkk44tP1awpnd$_dFcT06LVMaNypHLS+tZAk!^M=8sptx~?`~w1| z{jif5n63X2Dhz0i4j`Gh&a7n*ZhqGN`sRb@&OYIRikUwWdX&zc>JUO`M|rSEg5dZ#@Gr4XmDfy| zA8cf&4whwk2$*jy9yRT+KcNQuchddZoCsf6S?57R+Wy8Bo>kr=4(})5=s|_{@68}f zVQhoF7>DpHO9Ls@0|_dblzpbZm=Y2d)M&Ru-1N$2)v}Nz2|Q19-8N0raUh3}ZQC42 zvnJ6SP1ELi%yC%PO+w&<;~+uc4Z|@_QxIb54()->z%>xG+-*KUiSZZ+8^6jMoRZ~?{KW)yDzkBWO?WKXZPL7|v@U9I{d~DuXcWr#) zxG9V34oZ&!XPRKtXM}n4|67n?%k*q!fuDGu1qVU{W*ak2I5|Eo86?E;WskHVhbJ7Wh|}_D6mVdmfAi0%BRBY6`Q2b^gn{lJ@H_ zJs=)*9O59WS^@;kr>z3Z(APXB9&#f4CQAf#08XWdhNoKydt6gf0!<4XZJA0?r9?tQ zq`I{v#}DyZsKE6){ftIaI~vI4WYYwkBSM-`cpZcwSr%1QM~Js=Tb6l*u&&d$=O9_; zbsdbIJP$rk9ESx#&~?bW7ui2#UB;;Gr|VT!HBD2JB-^%mo^QFa&uKM3mwuG-1A8<1 z55TH{Hzp{Z&kwA#vGZd8I$hT%=XzFbU!z<43;k>3?)uEZ=luJNzgoWe85a1LW}s@e zNSgCJ%`IX2Y8aqNs}Oz{A4!q`Db+!rhIHM)7>gn=2t?N_*0V++EE2`xjuD}*$?0defuX((D`wx*IAMU$XQ4 zj#kXP`wMI!_3=MxPuv7o&iSf%@bT(Xf9Cf>S-dMzZA(JhyLUYE~@Zpb)uODPL^GO~k4h(~tHId^Go`>Y)h5_P-EQ{2k59_+kaYPh> zy4yD3E>R?=37-uEZ29%f7H;YbK@e0`O(-Xr{^_Ddsaoq=s{k0cf_@~_a8;v6!D9!b zU)HtnyRjlSbH-7IVHgQXf1f|!%@WwgXMT3fmjg+ES6WNeP00Eejb2 z7!f#*a2&R6u%el!$s7jRFqQ>e0yAYbw_<*aS3^Qiz*JUubiYk;SGqDK+v>tl*Qy$) zXbF1*&`$`ZCxZq?&c#xQ1dZ35Kc9tLdEkFCv%xRythqn*^ z*Ev9>M|R4meOg&|kvC}ZcmLY{#T&Q@GprqNkNouGUH|!j_mlegpQMW99f0yfzu{&b z+UD!I_=m45r+r44ztmXscqu(-W>V)#p1yaN{5`FK7ONe#-;9YfTdu2fN=mef*3E5 zLzp(pZv_u5X2QzX)x-pP8*BWQ(rZ23zOFTQcY$Ztr1CvdRX{|9fO;QersM~zUoM+e z1IjaeMP+x6g*XWU&vC#IFsFe8U?zD_`QOkyo6rtAW3U+#oI=J9`!xihxj}yiuzmaX zQQgm!^S;lKY5%=LYuk}>LrmW>zgQMygbB|R79U_)wyx`fAeg4fco#_m9~slMJlda( z9~(l5fv=QnPvVsQzxvGzaZXQx-r`(l(mzF{{WyXi*XyC75gE~9|NMjcE1SF36>AHg z`m8_hSAL&E#eZd}3#n%ZQjYyV=A@5vz;eaK;dLFREc=c~^`>Kbfcf1=aUmS6^vFagj zlI@pVEiO1kn7uGOJ*O}zFQUcTbpQbX07*naRQoU#iRzx}6o(vy_&J9es~>g`QH>@) z_`WJuu$pCL+&TLjw%SJKX+bT51bMwVk+h+ydZ)5E2{_zaWIqw{HMLg)HxdFSf2O@> zj()an3j#0%CIAve!Sm`D1T2ai)#qbM@-4$~cpeoBNjX><3&?~7e^s8kkVcMAForr~ z#u1J;PR;|YY1(+^P?`o$%5)rOU|`^~%P#xXuYOgP{wbs_`6JP4IS^5508!~udj{qj|H`bn`svCk5kQe02L2M-Ssb7vCri5V_wtOR4eZ#`?e3c*_E zquoDz4q<{Ed*DlpyMMSc|G%FW4n2vRJa@{^xa8^8U5A9Fh<JY$#PJ>)Ncgq6 z^XTnuzqW&YW?nDj1S%RD$6}b1&X8&X+%OJziD;>wN_$Lwl{}SCicvsOR8s)3+O^lh z+vk-n%~+;N!)vy*uKesV@Iw!_8WE=vs&geR%==Y>7S(a&b6Pi$jvS_Ah zl}c`kb7srWWHPR&b0QDt+fB)MV)PG!1Eg{EI)E`s^Vf1$HfA)0kQ@Zi{QJCZ~! z3&NkDeB#OF4?Zw$%G5b?=FFZwJCn%_4Gpbdzkc1iH@0lqcI>gooN>mf`FuH-Q&?FH zR#!V66BP=o zEW2fe9S0SQ#au3{s%kDL>n38M3e>QN$|~YHa9p6(m658dnT%|k7Q{q2HZKs{f=LS; zGA(3Vjv(Md0ZfS)V_62nho)<78L3z-3xb3(F->!yudim~+O{Q0oThOBgx7UllE5ul6h#P6LSRfWO|V~Tnk5Jvc5KVC6h+W9NGvLfMAHmH zh-EoE52u4>A_E1cWr4y|H%-EG1S7*VGqPaOIC{fE97d*%)RMsqf@7PK!VeD?a_u?W zv@s#3VQ~Txc|15==<3LM{+oh;N+lzg0oVVLpMOd`_;{4-CWKf2c2nDDu0|LYulu*` zh5v>*-m$HLFCN}=Lnr_3cOKX+dQJX=RNU1r8HYM79ZUp9QvH9uo|^h+4h?hVuo;n3>IJ8koI! z_A_z>fuk=7>$=7Bob5Yq0r0Z>1@!iDmL}q3?$dbJvpfW8& zH^1sDFvB#?ZZQrM?AM6rjcm`~x#SX26j?DddW#!nZdJgxZNo4g zdF0`1uDRxxTW-l_v&@~jGH5{a>S_p>_uq~keeLb-f%Ir$QsRsIhd@Nap^tpzBX{2M z7sD{TP$>^&MbY1PxBqtRf1Avt;d?cWO4Vrs3YwGnN4Y2$)*{u83Cq_{lqYk<)8;2d}#TD5540ZN1grN_ndIjvVHg6r?-i71R&0xcRld-+ZzoN?7x;}6$&L$6se0Mu`x{Ji6@>atLlRfKDhdo zwU{7L#HEt`^i$8g`pQ~aLglh!+bEln%4IE+QJC3J5L{~_!wdJ__aF-#G7OV>6%7p) z*wfS0p$2_$VDB;+u~brR+b$JVf1~f z1g=6I5T8Z!mWvZ_G$H@Y(`Zz+kyNiyujHO zdTrgC_uqd%@CrQIv19Os7hZblrPtnmd!tRWn(KX=%yLm$bhwlG2-e*~s@#gc~wEZDX%H%ofYfo90S$^$lt9=qx8kwdEw{x=> zT3cR)*wHC-+rRv$&M)8C`Q;nizx*el%H_QN{JpS$);)uIpi;B>){8nd@N0j&iESa_ zkY(yqe>My=|M*S(LC1rCz`zzuEAb0XHFQHa45u{WcJkT^r+Wqrs~bbt{IhuC$MDQU z=`2ib!x}t;vCB+LjN00z<;$0s)H3jzJXTfnnP*nM^2#gEJ^R8_Pd)eQtLq4Xl`oY_ zvMk?n%PqshkZp5lXz2Af-`KHZ$JVV|H*DDS7QjYUFRsy}*Jvb5OJMg3h4D6Tf3jYj zhKb=gPS1OM~>T`2!s>-0PN?GiXzK)`6OKpN=pY}mN*;fEhyy!aib zoN}^AqqPmA+PJf`^Ncgjux4+JeAi4i$?!#@KRf zP9Pj2_uPB`f&~kvOqur7Q!8vtUVM4g`t|Ei$EVMqf1sq0wQJve`st@d3LuIN4-X6t zuUfr^=fz?X)Or_OaGtJ1RBoxH7Yc=Z9w_ec@Nj#3uD`zzqy$hT7>1EiRdwp*Nsfc^ zBZUVad}#6FBRGy11aWY1X&o8A-rwJES)g4&7z?6kfcLRoE^8}St~~tk#hPlYe)W~3k3P~c z>{nl1YntXk2OY$ci?I|XYu2n;uwX&OLJ8^tPaeV;iK6)Cn{W2@^>G~0O~bZrLVca6 zbYTt;=WWXw87T}64yvW{&_Ld@4Oih|+q!8?nKDHZ(fYTx_Vx9(wYBy4_lu(V>e|7etS8UT1f6A|6MJiZacye@77m8%0dAHHjvDFzLfv< zW$Ht})orKaV?Tg?&cCAY>o1n?`8mJO0tX?>wD?m#o&VJr&~tYS?Y-Gg{Mamy zFG*6Vq-QcB<)Xk=ZwQAMk#f$w>295(B znuY<031~9`V*$r;Z@lrwx^=G~z2w-T!I9Tqd+o5p4kLtY*sx*c%9TAmJrgEOAOx08 z*Y#Ilef7Wt4-`P^2PQ+)G@ciklG8A3Ng^v(t~_Y|A#8yaMJkocJkKk#uy*a*7hhb} zmTTX!qi^cescY7(fm|iBKqE4ANa-U>Jv}|Tj$WlOXU?1lAAE4?)Tv9C9wQ3qo_ijd zK7BgVC`|eFW~Q>o7Qfo;CB# zn)~^0a+F2I)E+BFYJv0hydGPWYiSRwN<}A(X!0pTA#b9`v(Hr?LBM`5_1`<|TpSP@p=`2PS<9cT1 z7{&idlV0CG8 zGqZ2*1m}zwHN`15ed^+cfVXX?ncg3Qv#m{=$Mukq5RlXB+4-tF69Lw3@hGvs!@Gg= zbY^41!AkU$(7$bsVRJP#(tja);pTc9J&n$Lhp_N4So|ZpNy8Ywh)V5Vl4OXzR?H%i zkdgm$juF0_;;_Qgyu7$@OX@(l7M$m-bHS01s34GUQCgZ7m*YZINb7egtEfPtcUSaL zF6}^`l&;Xnh62}f+GTwV20lz{2@+*Jp+um{>3DO~IGlT>#gWXxKsHZKR=e8ReZ9dd zT?X5YadL;Zzrk?;UZYf{bMH@Y06?JPL4>^PqK&SoU`K*g(jTZu1V`!rZ~}3+S&YUv z{>gU4O$#GjXac!KoAH-e0bH{lSQ5^l$+X{qCC?Zz^GNljt+#h}A!~ut0JTrFKLjP5 zBKjY|DJ8_m=f7;*b8C+^)3ur=q{9uZLX6)h2|7HWiX@VCU)zrMT5SHI-V(_#wqT-n z3f4P-z4-6`+u)Ryh$d)v3mZJa@014ogu=dsr*U%r(;zLb9)!qGnZJf{ z88zE?RSAt{fF|9w=z62Fq6XsxW~PVP;=C2OViqGAI52^u{dBK*e0i~isPQvZTrS9U zY0{?WE><008z|vCQ>c|P1-ya5&1`rlJP6!$N!BgW9GU{!GFa0yC>v@DFgLjqmF;^2uNXM#Yvh$v2P=5z{Llqo5Q?al^OP8D`_H+0-5}3=MPB zOzsDWuqa)r9axwMO)Vo}lyxcjq@yNbe-gLTij^HNgL(kd5|6}fjLfQ8lNM9A<>ESp z-B$tipW7?Eb=d{;xN{`-#nz$!a#x!DGD4sid|aK+Nv3>Am$0 zIFJOn1O)zbz$oF%h*eY|2rK)Ft4vJ+nD^L?0O(pB`qUg?Ku%o_SlqL-|NaplfH4Y? zZ^Av)W#)H3QHZtCPfx!(3?0ALO_pcKU3YZfN;C`2}XUcZbu! zNW_{(+74EFNjpG6QKW7(AC6YUZVQ*!VpG-WK(6;_&LAATdjqD)ZKg9*rzddt9I(sc z%|cHhVi%Ij2p%0`lvPpy$6-CS z?HiTnWba<=Tg#BSBa=v}mL?U^>Bi@q`|WYuno6nBW8>QQvw1!-^Mh`rJvxw))U7;A z^8=Q5x_+z;k5AS8J)DT;9ZzR|_G3=OaDY}(3#Pw04OkZ;P!eY zQmd)%zUoEaX-_yR^nSmvI9<9dtzB-&%4D=K2{p;--I_Cb04L)?lp0+@3J1(y=P*(z zxoc|u2BxZ$sT9`M_<@thZ{1bF#Ps4tSAlP4mRu>uW@tho_zsoWijgb`InsHxeG8mOusZra|4hfK-fRKyu3U= zXT-eCG-ghfUu27(ZJHixU{VW}4VgCJet7i4fT;N;@GAPy?Q!Btz~ks-Z;t`>17m3V z+u9%slgQIKhh@?N)6svZ!=0FfbUup-6Q|F!sSFMrWi!NG z9#2Y&wyyg<$9h5UMXz58OD9gJmhWyJ7n4a9+$0qYN=D=PZKB9odU zXqE=LHk5OewYxH4;JLb=rZX)*w)q&VUHC)HN{E}#YA@3~v=m)<~(1}ZaRf!(@c0dI)-7RT2i z43+cK)8O@NZ2p~a3`C3#V0O5~?oX~r(8K8xORav&nKy{ew{LWz>V>vk$0mbYWs_yNZK0^elBr6rKmzNhmKR+Mh=$I&SXW%Rn34Sw2+yfvD zR!&|fMm#|3?GKQE#hY+Zyystv$_!zYf+^+Ftp0l*0AcyIPW6j7o&pY{v6*970K`!M z*MfVv57-G{N<>O&W1`yXZU>7wueUg6)tmu3wL!ZfbvOwgp6_Th7o@t!!i-QFhEvUWLe^IiDs2moY(6uiy>uC62s zYY`p@dw@z%*W24$Sy`#e>8l9nv*sGF?|?WiWHo_#XfZrwE*_=LoKez#mAwj}hY6z$ z(-@<^n*%u5NdR4Munaki1dg01QkqSBq^*B9q>}@i@W+AMUhi)REHSUeNNi0FbGY$= z(FUTe4&ordIT<#;dB!oBux=!z259FNGMK~5#(F}WoE~rW3KLe}HoJU*ulV{WW4gpU z?*cbs_=f!K=z0+4^Dg%@;Fv5D=&>)-7EvDghFj?^=a0miiT)(LgS1NFL>$=~&8h8> z^+jobh=rzv`j8SYFQShYhfBW$-p%U&5VPTZkJsYo0jE&QepaBzz}E|Zz#Bjb@^9G!N`ZG?SYj+oOAr{^1uYyP-PLMe+%*-8UC_>ZsoJHI*UV=c{ zT3CF27=&R?!0Qhe!Sb%`B-cu`wVv`vaYGGlfisN0a4#A8+r&{2Kn8(JKYvsWA}(<8wABo#deR~i&6EWGbf7BHbnDcC`DJ^WiDGEGRv!7h^GW1-NI{}qLR1K>sP=Lh50U~M|jdh}Uk zS;W3tqfReC1c`?PkM2Ou!A=fG97HG0DyLG|l@Y#0!!Lq$HuK>P+Kb}>tgen4cY-}a zbmJ(0i0s9B<97fRDE(jqiokiDz-3n!YLnew#D7up&X>Ltvhs*GzJNB1+mRnIPsPkw z_V5Sv)%d+S{S%;c^aX^026^kp^VL;G6I$HKG@=+(d_v;QwwnuY0DVCSi-%(?$fcRZ zZs+I;P~-R|FCaN_)z!4ha2^0WQm)sU-J-0SD`yPDxff{1x9$h;PYuJyL#@-o(qEH( zFmuHgbam@~PQwM#P{)~W`fuQOkM_>a|Glxma{vwn{BDlj-Q9o*7H2-aA~ypzko_*$ zuG`_c3z7w>Tmckqc(UjMLZHS8dIjxW`7L+iW+}*ORZayMDrVB;Y0h`w9WSM;?7flv z(k@zC`Bg9AvZ66USvn4m$~nJ;2`dMYTN!kZy7Q#~%Z^4@E`%(FQy=3K)fC%9@f@tv zYgP>Q{Zyxy9$Ntw3c&!gDVUKUk~UhA)ig{V{hx!X6t#Ige#rAE9oRaU-RTSg9}fym zBSAhD5@LC)A6Tm6`AakB&==0rXAyktIEvd(xrc)_E^?BcJV z(Ttc1`rH&`L#bT$Kp1d;lmaSCRwXM75unNg?mr6#p404u2akg3 z-dzCsbpVk26s!L*4FpHh2EM;NH##D? zovSJphL_1sND6^k03=}E3`;t@K0_-tRFYP}`!P%)8&5cP+Q&B(8EJbB9P zjFZ3(M}6S%;MnpVspi?@bl&V@f7rLd3$gX2GQh0g>-$=3-2HOD5K@KDFU8!a+F%ML zv)$hCg$+V*20;*0Y>tyD1>D3#&fGWM)V##5+)A}YfxK%vB)ag+)@ zw~l%eiJRhz^e^h*N~BY1ptE{MPt>A19QqbT=I?T;3k?&`JKvXaSJ2q@Va7dTg2mv@ zpbgfsnb|x?E-;kX-mX5i=Wc4v0Xz& z_~yU1Q?u3M@6K9EHy}_?X7e@r`ST-U(6O5^hFJ6iAWMZ}b!X=*fD$^pvC(bXA1aqZ zol)|!VxfYF6sLIZM1n`R(fby{WYD6T+CU{MDhLR^z@brHiH}qe=E(gS{Qe~3@^O9r zx2lS^z%tB4$nUn?W=r?!WoR>J+1Q8zZUFFR&GpG<`4uzDBA~qg&#j(OF4{_Lj zxBYTPcA6KPY-QbT7DKuc~pA%Wh&H;Cwp)RptBpd#3gM!^44FyUubK zAcICeDrdGiZP;IZn<)pq%^V_J1e=rwka4Zax`lFkG zj{s^Eon%=Z9h}jf2K|WVyKL|@&x(A{bAay$7{Y;AmDIw+{lAve-V%DDh8NCWq_XVy zC$eSdvfGJoJreP3cr5J1;2AG<1Ci-Vq1A&5dKwtaxQn{s59MUcfZO z&u=Y8s@rCu?;S-jsxHNT($-J!XK)9HH}(Ri2NADo?QyZ#Zwq?1#5~7G%s-)hrA_Y% z=p#Y$%h%`hc^M4$LYEQ}d{W|gl;}8X18a*SDY&BbyoW*(pWJZ=!0;sj*meNC4CVd-SWoY{ z9R#iEMNig@r~;qA8KnSZ>IHDM^H_HsO_eWZW>s$52>R@XC3NZQ-15YafD2^!HAx6y z7CAj@AOdYTncwmwiTQ!mr0kE&Vfi(wCj}FdndqRs1O0P+?EPH;*v4G>tuh+mM2N`9 z-rn9b85}YIb|S=Sl`(~Ol5k2=1>2EvGEpt^8JFX|?nsdnP?P;HJH7v8^KB!PExTqKv`b-6AF z*Xy!FrXu+40YvtyRSu;EOfi;ILceQpz+a@+`<}ZeuolTO+{Ee)cMVK@**#8mwMe~A2GG?WEg8?y@(<> z03z2>vKo<}L7hKNtg81N5ou8b;hc-Z`h3t8E}6(eGvXXY+M5SALieW#L~#w^Z~=4= z*ny=1Ixyu6)Fi+|G5&ik$#VK{<$>?cXEz@XM0d7#q`Lr1XZkEa*Oo&irNN@QTN8Q` zoZITX1Au^lbILW0?L2NtvKhbSbqH6VqNoI?*fc4eE~<}q0Jqzxzx*E9CW+FvlRVS6 z?YkNj7w1&SW|BUFun6e)<)HWFC%`=un@K+kxFC96ZN@FE#YHN>fy9>|DzBxGDIFN7L0%%Jr zc6|15)7GpvGT)p5+BAdODxEbM2 zbPF4R4V}f~90*vMCkR~fmW)KAi>BhSA92BSe8G2KupDn;KWON21ZgjDvXk?08oCfR zIfAf5v->}K%4S~CjcARzuT>6UgR7xYhs?daH`%Ji&wrSXe+7DV6PtpR+5?Mx=*TBErM}3B}2&N%-f8BSTSdfe(3; zdMO){!b%Uu2agnxN`R&y{4+eO#R>059qS?LHC0~r7)}ge+j$7Md`W+y_fqvY7~xct zbUW{zW&6CWz2-km;)T>>o!1kigm+2ut;T ze%sjvNlckdq``6WFUGIm!N7bB!SPwp?vGKRNn@qvbNAp$!0nLEVNY^808ej%5y~<& z`uRwy@Ca7^ij+d3yFE4p-#r;G(9&I4UAUoTCFYP1-_UE80fFX&mP}VqfR~o{qb?6D zJnNc`s|mI7Dl>gY+m&9J!}o4I!O9>W>EkULf-DGPks;*0Z&SIrKt7d%1`6o&+GT40 zpsk^XmQNqHO)utT#!;R{GVeO`%nWOTLw@W6I4`Y)^y~!&V?Y{fixX^?o;+p1X+@oH z{DXdM;TC8d1_Ibf;rII#0MI?fp;Bw=3b?-j!O^Ba6q>_E*`(2Wy8CGZ65Q0L2LrwS zSv>dMV0GHa!honf_8<>v0$Bp%V8D5QE!*QUh{Qu1%?`NLSfmkefItRgILo~chg%Pg2g(i{r6BS^S0AHcQE`PPLbS6{iy?Oa>nmn``<#55J z)Q(MK0}>uWQ&JKd^%%Q?2Q!0(3l-$SW6$d!@Qiu(=wU%rZdrJ+<4(m?LeUzshn&I+ zYZ)1Xa;Q+iz;|jZYqez+_|Hgl?&*4_*fj0}rbov5p^D**LYR2)pp@n=7#pq-+YwP4 z7niz92AfPLElx31Ca9sTTQ5{t6|7~^j$?@sE*qbsf7kCp8$I8S05E5TotJQKFHmfY zGLSMT-iX3MX*jGk{jl!ZK~kErOtnZ5mQIrbMLZ$o{D830FJn~?xZaTg4h8oMN5at% zZ@3wOG$Uq6k`cjIR7h`IE=3x$SSy>!rQ; z11xpWpcFQk?-*I+zFS5>{o>LbUw-LX_EQ(Egb(yeu-D~z=hLP?z}N)Z6fwi-qalyx zK;;dX9>QaRm3x)Q5P&UhO-c<(RFrvrU$XTDUoycn11Zd6F17Q$oJWY4udjFb335MLX)= z<>a7*@oK32j>}yoD3(~dHY)aww_l>;h=YGQRrP^G*?p~v?m3_*$c99zcJA-yCh%gj zFm+e}*2gZUb)SHp=bkS_l3OD3pX$Ns9C*IG$0_S#w^d2hGRU26;HFh5@z$ z^xFP-HG?~C2G^5N#XGi8pi%-;mr)jZ_mI6bqfjFiO^dFuSR*3JSWtnMTVA{fLtY8G ztXgdVbL#sE=Nd{%*kYRmMs?iRAt;!A6h$7rjk&tIN(X14ryMw1FO|eGUD3A)W})bEpIbt z=hhR=fEQ9;!fBCXQHXr-#O(z322yi&cgVJ3%f@q3Z?6`+#ey9HRg1uEg8^{hyUFBw zGS}p4?+EFov1tu#0Py1fHVL^6S+vdJ)VIeB^-x8tZ9Gv7jxz?Go>(?XqiWq7&QVw3 z{fVvqW#yt5mw}kqUs4_*@nUqY0M4^X`)+%yUUX$yOZ6FOOFOQ@zEbUYChE#3>l@KO zGTvgMjbxPbLFc2^8kQJt_9a^j(b&R7 zL%;Yv{#~3~<&IdKU>70dj{nY?J{owy4=tkGKK3P!b7A+OAo z^s1iMf((nyPOgk&|SRkX{o^_N5ir>(4vnmve)IV~efAAG6g7Cp-t z72#G6seW8tTv$6I4j!L=Wk~fHFn|kDPpX%xT8}9SsgfDiYgJBWU=~eQ%h%=9Q_W_C zJj=t@%K4R1r@S{x+bDXV6My6YseU`hACk5wYFH*#J*imYN_grm`kk1HVzx;EeF1~j zx>6IuPtN{#zrQOXr~e;KeE0;9jiGCOGdXxu9s~JYNGYG1&+VG7=q4%WN+ zH_a@cHX=j{%`&iqH*o{{mUHkd5uQc!IHden)#IbrS2(aOISp8Z;5NeI^bjmu#nFJos}`MNm;i2_==*yM5{q$*TJX@@VQiFJLd$jE~wi zDsLVLc#FQzB?R6Bx=rKI$A5|$)i1;3@u=^i@^ERCfzz2hHEp{DC3f}6(wz_CoAO|>|`WrCL;S4RM6DwFAQb5}?FHEW&BB6MR|F6vJ=nB-b?fgM9|2@|Mg% z@GsHjBwSpj=KH!ZZC>tzkq)qL)nwHoyZvm5g?3QeIzGf8G{ESzc$8V-krz>xK*N>f z!|x|LvY=%xY%U!)EARSRyeNk!FZFL&>1{0<6aLdE(n+ts(ogb}VX(Oj)o< z;p1E_x+X?S1k~}p^mtJp2Z|(|qyyQm<8GbpH` z*dxxtL)!6trVy2zfuDLhig#CAV2LZp_ov%(q;e4Uj8>Egd|1|1yT(xWBq!LCqhWhg z(%H0qI^nD+bP#4lrh!nG#XH;g-DC*pHw~=`o|8M0@Fm-Q1ritD#~9R*28e|D88Q(T zeDS0R5snt2tSX3K=|~~iGIk@6kS3)GQA|z_^Z%lrt8$2#8Xz}3!4ro32Byi99dM!f z5(`nY(xmdTL!24RWfmA`L4#9FNk^h`qEQlYkD;Wib+?=anaW^-+_r%bIbruQSTe5H zJEVkv6%iKN#rVjLSsi@VCemcKYn&xeex?pB1_duDo#F;3N^c2d;T&t)zL=vuwpfW? zix5Sbe&vt&)Vl}In1*Xp3H6nOqCyuZ1h3@K%xZQ}4!2cC;FH|$7s$i5`NxJF6ZOy{ zV^n9SnTL`A^->uUZkks|-BOTU8TjD5!K*3v#+SM3WHz2|#KYrdaS;GPr?(-b!tx?h z_FFD-ePo5sCk=d9QUNgjPN$Ob*Va7bKGUUzbZU>U(Ra6*>E4_1#>K8fQ2btE$31%E zu>FN);8e109c=PaltRH%FTzLv^cE3|XfLgmjGB6Ov&335jg8})A9rB}Uy>hEx|l(> zK*n`B>-yTc?3*FMueZW(OirXzMOFc-Mht{duwpBEDZ0!@D38V(8hJ>@Fy{k)OC^=# zX^Sr_@F?C0w#!yjp}bXIALs6@V>FBh!d=n|db2bLgKqb(SwDZ-wh*pWHdq>et4jf%KHm z4J!>ZO%qHh?W}P48!WMwh_})?o}&HXvVbdcy+qX$>yk{zsNZe}qb@X6hFZljc|p{J zu|4{Z;BIHoQ|7$&0V@RNwpO7Y_*zxv)5&QA;5(vcZRY&-L*4R zGgG^RvLadOXh+jbq)BY9jnMRk>f`bAv@-n==2EywY5Z74t`#J5G0U->;;(&s7?*BF zwDrX6RtxZF8T%GbBe?-ZelHTc=O{#?U#6uUE>p#*;o0d+^Ec89T_bam(;b0V%gsqM zfq*EqE;63bD-fdJ7+Sz6N13hJ%l44`6XJp3 z!lRTc;9D{3A8TOL(gJ}%ddM51yrS-??a&$a9XMMMTyx*pSeu_p@Yh7dK7-XV%f>+V z;@==LnBVBc0+uTWhpG4;(Xal|pdW%s+pSbG2uEO*4u#OmgxG9Bt5&MY4GvY}koRkLV1lzM$qS#K;t!|q1u zWIGBnO;yjC_SopCMN|!+g!D@=yj$E+Lm!0>R|_ZCJ(Pf{CBR^H3#%fBzFW1*uS$GSdTj~JF1G; zXwma`D~=nMfpk#_y$7}OWB+b~ACQYQ$&=+>q~06Jx#StFI<%DpHPf8nYEOSjN7GId zH;~_bjnJKLzF$E3kZMQq1`hrEh7QwGGX8~3#Uz;()W&dKCtNIb@by9y5_0&?tjDQ< z>!(sAjU@2_bN{F({i>aF`#Y z!Tk{`z7og4K9Jw(+>fU?%1Eodjgg7#fpdPB_#?k$@;+86hmug{sc=a%+*u~#v_Ap$ zsL~+dxY(vVVTE{AaSb8envhu$R4P?te48Bc8q3Mr%=%r%NF>pcC7jcGXimasE_u0* zTxdB%1(%7k>o+Y<3w|Lbq_k2N?>9}$urJ1T*GshhgZXTNcHpxDG2Fh05R?uvpcVd%YNh80Ox^h0dQH?{2aL|%=3 z-Us=0Kif9{JP_hSI+LA9{uTZxBdfEspekF@8TrfC_WVsm%OJwpo9b%9C`36_{%5*{ z@o%yvMmHq29=Lsp@1L^*o+?eh5(hzi59yR`#j3P^WTO@`jM+^x4z=IAwVzcA(~1Fk z7jW&{A_RVLagR?Q9|v@_LW9a1?eFAbs=$w|xNh z`9}fmq+7nKDQFp_!su=Y@h5IUccl3#6tP>ru;c;}8O77viw))Lp)^96_V2E4K7R?x z#T~C|4J<7K%87W5hthW3)gR0Ax#ghGhV(s^^__&gFN^gPTG;=tDm=Zfy)`KgW;G+K z)UNvpTD9w(*&M(2_-RD1r|z5y$9XsZ>G&+)+Z1Itz<@?I#>y1j*Sd6={=RMED3xPB~RwK~QNl6>L_i z-D}iMJZ|Z)7_h{GMF@g8O*xI}E7Nw}Wez6d%K($QuTNM~rq?C+>7H+}{%~bTiTI|1 zO_Pk37AObPAijdkq!0ekq5A?mW1GcVIq{S9iZ%a-N-{y`YpN)jr?ZgWcmWj2QXK?W zcRc0u%Vnr5<$J64`+)I9JdpsV)cDa|{N7J_sx-kr1brCWgQzCE(62W;(-N zaBu&p*=O_;w_lYF>2G|6`;~wt8{uN(S`H^}%SNjtosE0us=*F5>m(G(z7?Ed>66I zz}e#QR~|pnDDDMY&6_6H*M&rK0i!jNEu)_C*?;$Sg8QqIVE#Jc*W0hr8JStne8doI zI0VvgNs;<=Eq@TI^}to&V8BFTc$vO~NQ-=)=vWGRZle$TH9XV*d zudx(rjsvb3THr$b5aeYrWxm0F0#V&;JSx&}X|cSx_tKpWOYcrB^(-Y~132^gR4raX z)kfr}V;U5=|Gd77#14q0nYF=&QE$Wi?llWe;MP1u&HLji|GF*GAbvqLA7tNOJzYTy zT#W&=;-+MS2)a+9JFSNNd_FjDEjj!dyG(06)kM2wyiz2B|d z924_)%hZuKdyq-r3GExOnik?Sf_1%B*P)mB!JYY9gJd~;}L@=ubi@5)yT@0eS5Hrv1F$udtB>p^& zGX#;oxat{(e`4u~p9GYrrj+b=-;8}Q_zQfrUK@W!Bba6NQ<%s8*Q?0{k)tEii~@J+ zp86SKHp{-1=TWrOxK3{HvF~tV)bc6z`h-}>GBJd$^&_s_+x$Zm zlGa_jLiuTP?GV1g!98cD?#`K2EVY6f)oBIt6nm#U!(*06SKYi5M5$WU{N}3raS6l^ zvD8Ud1v%{0e&*vsk{cRIC0W+rcVA+cS*4n8f}#w;KT}4kB?h3#Ulonx+8$~%7N_YW5+NeByN4{2CsH=nJr3nT`HsGRS`>M1cS6- zlu%Zg4DJ+3*)wh)o?X$|FA=Ey34n|n@15_l?`;u4Jb z&-uqU7Xj@H47r``HBPZ=@=pggE4_H7JqZHZXms2H=3f|_G&^NCW2%+McK`T)m9{1PmW=Q*kV#K9?#{jEU43O|d?s@Yw!*0~}_n76Qqk29X)i6cyQayOFcPY^7_} z{g5=#A(OvKdFnF_lcXH5eNifMc!uQ!_yCD(3-+kK>?z;O#HkLyj@&l-o6)CM#lgA% zZ6*qBQxPh!ugTIp4rjj$Yz{JFA(jrLU5$8Gf~ywJIF5!mZqpte4dY*CSVidOV@!{9 z37@S^9SPSSy(KB_lc&~nBS82|U-ywDryyO*8XH)e)FmbLd2$elvN4&~_7{gt=sgOg z=E~vl(*%()d*$8Gw>Rl)dXxqpS8Io=9UkR{@^?Pdj;}d56e_geh}t6f?()Qig%~^& zXLm7s;&zV8!j)IbCWxP%ekcj-X{jSCQuOs{@#LsD|AHakLO7dy-+oo>jK`YE&_pH9 zTO-wyepxXwuaRl=K51q=XP<#RlHTH49F2G@$&d6}o@Thy$-hheLKk8uX_|BBFdB%s z*M+cO@7YZ9gq z-W!H(8&oZ=Ze@{i>5#TYHhD-fN^f55%h3<9!Q9FL?d}&6CBuY0;e{Cvq$0aqQdn4P zfeE@|TfGg3ko1TBJ7SpvbT5AihH;3yWHhINO2n%&)4h8QS7&EL@R26PFRu4n0`iJk zrtWgKc$$_4#{gia)X-Z5WeQj?I=67pYS}$7Si?yIsdx=Qh!~?FTds zlwUDO^KNQl*av#Kn^b!^1>}x-^|n%Sxa3egbp(vzHHWp~F@RkJ8|b)(xHy|xG+Mob zgV~{cW@41y86G_4D*Bw+9>ltD1RaXM_1arx5G0Pg~gs=ECM4OZCg11j@(G zkBgC%))$;C+%h$4izjjU+gaP>!#PD$Mkhs|-nfD}=~Qgjc^C0XC}JV`ZGxpd&!a0Q zl?~WhL%B(0?$=X|jOcmMNi5|Z|Gn_&xdblx7XM%6X(>1K#5Vt26v21vb^~Ju>zGXP z)V$Ie`&oOF+e~}e4#1=vuL#JUr^N_g6v{1Z{pK=t(_~o^?~kc7sv-1;FKF;>}=h2jkEXm3f#OiETYPnPuQuD&FGa-x8snxYj)-Z}kzH-fH?{Rt?% z@?V-a`5dc{6Gj=j6F@aexK;%ZmY&6*_dbnk-~|V+k00fJJwre5JB{CsC%!pr?;Q<~ z&TY))KHg6}a(YlTd`gq~zK%F*^!8+alT{7p4*Bth$vltK&0M5+bd5pPdw;FqApTb+ zUB!~-q&-A7YHChm+CJ0LmaL7n0?>n0QvN_w80i zF^U2ho#Up`)0NHTK50#=$DIn|xXYA|CK1vb zo;hEHqXkuzX*!F&N3;E}JV>siO!=UpT;1I2eN}^ymor-@bs-;3zdaZy6{JF&!qG8> z8ow*%(k|X;$d!va`?q~{xlhm4`Hn2Q;z1+vT+cbfmBtxP#~^r{Nr?84k}PaprEyWD z*S2v@$1kb9o@CG97%tum;rT!IGH3HHePvbXFv={9EHS-dlIOc5m1zQ#v7p(i)eq-V zBmSmzFSLCaEUIiXN0lO<(0^?QE@%D zTTd&yH*rr}Wnn993XK>xgtDphIq)^A_A}&kU+0f3MN?Jf4CC3QUiRteZ*wB}2HoO# zCrIi(I-QvQleEn1$}D~l*9flF4ta}vjoLS-&_*St+0Z}rRs_*3p?3og>EwD8KN3Y3!+25CvrZ4j(A7FQrQ$I;D&P5@Ixf?x@^F6lVn_>an$VGDPT>xbV$6UXlHFrYJ}y z4sSLl-VMT^wrKJ6YN8am(N;+pR-bF9KJ8-^e_%?Z0YjRd&n|URcVED9fO4m|{_>#? zBM=voGwFLHU({vfml}(&G;2n$b@0`4MfvnZC^`!ghMylH*{&3@@o1ELY=J5`?`I|K z51@}MpM@N2J4P=#u#+Oal{DICf=n~u9(LJa_p9X#SvPwxS(+^lZli&6W6JmKG>51* zGB2@?YVFkzf?)!G>>``Vw~NofS*glAv)f)7M%Unel*#wF$G;Uns1gT_6iZX;T`-uf z0etq$O2>J?LWAjTfIhUvT96UX_}sP1G?QHsAN4`h&Y7Ad!(SQ8<=1d-SLeZ_8Y%7v z&dda%BrMyXi8_yR&BS(vutTdDv}?Hi|E&|gG~K(57w<&pI(s^DgOOq1sTqjVYvfh2 zYv!Z3>BA3l`TbkTvw__V*CMgNP(N4DN%O3yrP;YtyIN-8;2B?LYvWly--WW@zOa0^ z!uCiVzfjN2VMdrG^Qh23^R#*gSL|j-FG7GTd5}EIYXm4EvXM2@=vO>K)nV~1dJh-R z?iNoT2y*+Q>zu_LU+Y0KE-rHL2LyMqoWQo5CCa@nA{Tj0z6-pBW&0pv)y zI+&g_)c_=c7Q1cwKKwQADw>(Qcu0on^+`PWj{| zA9BHI7V~2t=tmGU2YNhuB>6w;xm-W{ODeUqy`MO`diAy2ZZ+=n&i&+D1Dta#Mqzw2 z_`j|{6efT9SGlUgD&eV`RgWNZC6AueTPf}<{cV_;1)@1b78pvk+(H@-nte1lbt@sV*g;FA-9PY$1uqg_Z zF~&5h#tXYa9mPo(rck;Y^PM}9t8a7q`u@nz(20=baH$8p)ZSgLR3k!nB*&r|?`Zwhp z^uyqx*>Cu1`H)txU<|?n=lNC;6v@2$R7*3ecE1CXOtp}}fqSWEGG)TWc4w@F`MvaQ zGEQEn6OCMI^Pbx@;Z&w%AU0rRACcY;zbX5T28U7kSY)N`Z)xdmT%}~(E0p}N=?dlG z;$LQcV?hd%c~QevzujJzkR1|+PKK*RUJWd`A4(%)+2EmDH3|ia<_bqv^c1NWxe48$ zr2U0?Eu3u#uO>b_CW7na*hOz4ZAhn}e7cO+3Dt8Q0q$h!U;dj0&f-|%IdaKxh*uQy zqRdH#K9q<^r4^Jp(vTwbE(^B*_ha>I&IhQi8YfM>{yU4gc%*0`lU9 zs>!40Iluc4GA%ns^k5o?xiBbFC*27MovVk;C4qrJEBIMB;utuy7`t>+7PV@_1JyRR zLF98)Z|Jw!a(|o+=h(0jrWF*mnDKOn&RsIK&sZ(5&d$5;-vnDd(p@)Bd6GOeCb+Puhxo+_DD^mkk zTzM$AWST=B3D&x?G*nPG0IpAPbjegmWd6nkQ34}HsjWDF^OU>h-^QZd$lZ`D+>C?* z$jbvGnLU^c<|j;eHTXZc*%3_|1nHzT9mJ>~w@=(CjF`GE+iweaQ3b4jP`ngK`=ybM z2^7os>`%Tpj!~!kUsLX(Ur!?RT_%j)4a4b=6*Vs6N_2_Eb$3 zqN##Q+DSaS8nUPm{#mFhSu41vTiGdGcm9Oo522UwUuz1b9dQdYY~2&jEf-a1G{Q{-Lyl zE3pyokXI4TSuftqY@w+PKV1JNM8iBI?dl=C%r*(Q3WEI0Du49A@!i0M z9IC2&gH&k&s)JOmC~~tqL~}uQsfEBbwhwSCNwrOt(PSZ^GhOpnG8ja{sQgO;Tf=jy z{!W&W?=po1*G2gx)LCqDkS^OSksh@W1SYt)2Uu+I99Cg0MDZmd?WrMhACDP*^lMv5 z-K%P$LcfgeI9*yf+`FI(esYfMqFihm!?E^F^9|!xo?Yei-2UT<#;pbmaYbj|i-5P9 z>@YZ$bqU*5YrhpFp_#uh8}rkSJ|Y2ZSL^KSPu9)iPtL1C=HCb&^Qv_+vx8LM|B(K) z9wH0JU?}^U^Q&S$wW!iWSo*X|&FLw4O0zwEwvi?fyamh|_ViK|&KbmSkt%ovyY@eh z&N3>irVGO~2uOFAG)Q;1bcb{|NOvRBEiEN2A>G}gAn?*1(%t=h!}rhSg2g#=W@hhw z@0sh)f#=rbXZ&lZK@R-n_WNjs#jAwowmA_V$jP0c&OcTM|D)#8ALG!S@g(bMrn^=D z^afVg!i1VOXA8>lFIOCY1w0KQ>LO-}K)yig9(BBHW{A9S_1P&zKpTCXlUn=99V>O) z)ZR9f@9?myZQ^;iIeDk`7Y5vr89{7aGPI+8%FC_GZ82(wgFxa$)MV-rDY3;My8h}ni!^nO0|f&Oq+T#1k>I=_#?S~dZ!gDmD|ib1{V{o zWJncB%)9P6@$(J$8lQ=Bqz!<6;Wr;ubKdoza zw7>E4{tPW2rdE`7&}S2EyKD&*xEABw1#3P}QVkE)GOgg?ZsIJcuppSEuEj;1X@9_= z2Q)3jD^|>-U=Ht=T9)j6-)j5K{ zuI&<{f_bt0CWzsG*Lg)_ju&cS0v_nd`OAVZscK|bvFH&}E&>L>CYCo_Qno~YR1|+c zCA!@YCPB0t3TDmr(eqQ7^!qP}+y~!xzcUB}u6i@UaOV3J*|qS)MjO{5mc@5U7mrJd2^6k|dZ!bxlO;P++ z_xY>14>V{)qF>-N=iT3BYW?D|<-`jgo%z~I>|5OvH<}}rb-THH`i%c9Nu$xet{1Uq zX(e58Lc*xmia2L~5chYvRbef+ysnRr{Fr4u)>0g~2j(qi^7c);qCKfs41{%>3toKv z6|1$>1TRjH7xSBU8=OVWwe9;r#;RxBdVk#A!T0kV!XkkbS2kN3*c_N|<7s{WLsY2U za7X6XEdI++@IM~bt$6_^R_(tBU7&D7#cgDu<$v!LNhN8brZGFLHQu&sSkjNyU$vu4 zJfl)wtEnaY8MNf(ezMzM>R9i(O{end9OT?yG&5$dB6# zA!qgcA^X3gwTe(AWwOg+P&#=yi#oAU*7mt%T0Ts8tEfk%2Yv+_MVWOSv|369N)zV4 z0>~P%J3qh88eQ=xHlc^E!uXLcu`i^A5sFl8P~czGF5gJ~a}R5gIA@0FKebGWtf)t8 z5~U0wa?HGTGlxI@AGcNHZZ!w_7DeFl6qOTAeS z!?b@;L~UnT9w^pU9bX?`g!SVWYK~XmlYF}Baun_GVv^WHymi#);Pso^y%Dxkz8N0# zDza`~ndnOIvk7W(F))r3W|7uB>E`mIO8R#z``IUsc(W3g{U2Flw1Z-bb4#wEk>5_? zziYgX&&6iZ7eh8DG~UK`Nt%C?G2tKH-gyZ7#pNs zT3gAV{NCF!A-tW~5|h8=Q8|wAlH4)zY_U_Yb7*QzAn!_kx`XyvdBpHGF4MEwTZLLt znr8{IQnas&S-bF_*V!VUUy1^U6$kSn;rYUFir7zHiqrl#3)Wso^QL+j0r<*@=Sv9_ z&;Pk#ch=n25b}Ge;Y;i-_qUzsbH-wPd^s55N7?wrAJcXcWJ$w?76X4qWk!0WJ70^@ za`g!B_Va5k?SFG|h9x_WpH4_;L}{sH4G`Zu$3JHY;n{z1Jaz~*u*lvc{{B;|GeG^# zl=ZQpyz({XKwN><23BI+SVYgEymIY&IjvI#O&(GV@#7aH4-`bw_c33*YBVA24afEq zFWdsE-hU5t9^PNjIit;pGhMn^Z^AYsHmM)=EY8=~#*vKFi(Rj37X6UOXo7!7w}$$x zptSkT8i3La5ftRvqZ|3N=9ruBhO2WqVOA9r+$duqEf<0%eOxeQuqLTu zyxU+Gfe6}SN%C-sdaBf%7be@MvHAC8-NfE=fzacA)d-ciabiW_C+;$qO>>Z0PF!G~ z>$S{?%!Bkk*-coHvcJ_hTKd3wynTsV?sUFmtkcXzRIKw$fB9*tF-vHT`YwCWlUPKM z1?O+SrcX`Mi%ZN;J(3C%uj-&Gn6&w|rem|(j8Til#YG%xLNYS4neiXEl$$wLjA^Yz z)^_+`{tLg?(uA!FX5&$eYVV)swlx zCFsnd!LY!_KTp}KYdvk@xlB)Yqh7^&U&QL=f*MBjVfdM*J1TpyrJclX5ar*qm5c_w zS3T=n)1t|WUqZJf6N=}EYivku7{97ZhmBGizFl}H9awRkS;*+98z~>xPX6>O$MY$~ zhuy)(*ZQJ_W#GH6sb`(K1xD9`p^Q7Hy;v0f-TyDfk zEAnpqH>TFNd;^Th)L2_N`cb z^v4DMn^Kbbz_H6Ph<{&^zBXDeM>i~``vvrIa~GTUeqB_vh^Y5P$g#)p+uc3i_t7Vz9_0irTQ!yaB_u64k zIe#U$k;7%W8kSAKTc|d_vW;>? z#Nx1RJ#6$J)x56X$6q0;U*AagHtf)FN2@z3y~8Jdj+6fWQ!o^5nJfu)kF#)o0jaB? z8u$Cjrgs^mvKpqBd7@+VUue}bi|N~A&o$4gQMSGMS^hs~n;|Q>rPU~Gy^Y0h_0;>q zqlrDVxO*S5+@ai$>^>vCS95RdUu5;4Gkn%SjvSQOcd{TM85>sZhvohcKg@8V@`&|p z*1Dtg*_McH2;iyQr#>tJ=46Y@7C<<87+U}LPW%CKv9BR|-F_Y9W10y6LK)uWez5(1 zx$e0--N-s5h~W!bTE;-*@cZX|Iq0Kp??OdOv{6pf62~4*S>B{Wa?VtCJo5iW+JjU?Dy|JLcaf0X<*E+ zfh`-|9wI|%uPU_Sg_yALV#o&zXqQp)kNFbiFF1Cp(>EZv{}bRVd`J~$M*Yh8gQ9Y5 z?E~Ma8jRX)jnPW*1sB`wCcbb6EAE>*l&cy&_O*qPjUNR^A(}#09I79h)))n=>KHox z`#t!{PUqx0KOTo_&hUpIh|E$R)(&g3UQbR!LX{wKHZ7&3^qEsvA21Pi!h zth;>5v?=#@!=g?EL%X67pRB#53z)noFUNdw2vUJ(X8X%#!1Hqh)AFgP$XIrc{$AM2 zm)Wigb=TV*lcH=|T?D2bJF<(|Hn>vlYh*|>Gm}AnA6x!Nrvkq1M?dGLSdK|mZ?viH z%o3Upm4Ve-Z{Au<#uSd^;!|oeeyv-mw9OJysvdU9F||pCTR{Ck{)WI@DU2!6r>1Y; zjbzyA&BV{T#eq7jDN7>rBr@Da~ZZYHP zFu`FKW3;?ux>JC36Z%5eMQQ0apYk-9L?i{$Dq@iqQ?1(9uEXIF^%cb}u zLn8j$HbdZ^0%ea1kFsd6K@rXaW&=mO7b=$%Re|#=HisLP6b(h#ppf1yR&N%XhrJ)l z*!)AXu7sHy&cew>ZEpM*!g;g?M{oEv%PAp3skyf|Da>O>Mro@Vo$0@fW~4DYvcZ*o z+LVHChy#=lCHafgxaRb;Q|{k(rH6SGi~4&fS@bArS46{UC8P-mOBgQlKh}9FgFFn&-_t8SM6g5 zg8BZNBoU75=;HNzdDQv)1L|1#UZ?wEhE}jD^3;+g%~H9Zc<(W|i@<|jX_^hy^N0zN zn2mbc6d)u=5YU*+cIYh$4C;dI;*R&Q;(VjF}|saP;6Vl!Jw^LuCKe6*O3r{6^nFN$y*~ z!{SfcmDr*)aZ)py>CNkWACeIe5auaOZ3cgkj?d<8@#&>$X<7b=W+x*gG#wlCR(cvB#buxRI9Y;ys`vBHMA~ZO%$Pq}y0>PHmbD5|WsA zH!(*Um}NNrV(sC+B1U^`EM|!^(j_g3;SGIm@PUOocWn1EgL*-+Nt4k8Q7CEQWXfZu zt|$22hvYBcrPP>lkYn9zJuZo{Ghe3ssPGmBlCyyui4pWtw2?x>ji09KD?T>EW`H)4 z%w)zO85%K)HNIV*mQ4mmFcFPz5$_8LJ%coQa(p6#nMe)3heA~<>QKDZMY5S*OQl}P z@h3QYnapppT3U`vARcFx)Xta`Mphp>D9INm-yTS-S-h-}4C5lhhpSp!ThHfF$AwrK z^s;`+SaIB5+Z9!>y=y}FGZQ?=Nex*QcO}^khDDQGQ-TVpusf=?)!gkx;!j~-xFYbi zv~H=XQNfW;t6@tS#gO30M2#_~2zOX%`?`B?R!bjEWuCH7&Z9*CeGD$3g7{~%)5=F? zyaik9HyE82fFgO&*5z|)C36R+mHqlYlu!ZBgC2!>$DrD-O(hYQM4D+u)}Zxg@kR%e z55I1L|8vf6iq0uCJ~TG0ZTVMM>xgx?t4d-h`N+gO{h=pMXQI0RQeI;vJ;}vA!$ay2 zW}&0`4r7NeJSy3dlQ}z5y_b!okI5{eIjI1;u)$Fobn$lYPcR z)kIvC;5fi9w&k%+Aoq!H{L}gw<+uF8U;MhYB=8G{W@%wf6utUwA=1TcWidj|hoO#V za0`SXYprgA<^cd8Y)WuMD10L~=OJ^-nThI$A+4sV1&+xf74-IstKsm+o6BFkZ*<3)W5g(7%&jyghjjtp=z$UIL z4DJJ6%I8P z0JmI*qOf`ct{W&_$h_A9*cO`9JwOblexZ_i>=oOTp)J%gQd01t&O$%2GJTh=4&(h< zT5yEJ_K5s~j549esF*7a87Af--Vd=YoihZ*ToEZn+@B`=R~H>rSKIMgkjslW7)<8j zmkUaAgZXyjK{i-bv3IgKGDl{J=`3-mo=AF9d=|QRR6M;UO5@#U%S_dGLxO0cB>a1G za|Z1mCWuttHa2CQodJLijc?>l*`JY2^Sm4MvI_v@?g!`Cp@{ zhGSf2PwID>BE{JUk}k?BlJw&RZCyryTp=rA}P`9jGw9=vCOB3xzxh@^DJ*k1$dx$X0xQGtA`3 zGjL|Y97$q>tjfbggzzvgEBX?p5_nfxpT-}5--Yu2DFO0y;|$KtPpgPN=eUhyO4k~& za(Z57*;g+BvOmCF0oXU|Sl!~%5~0c^xJBp=?nQ@ExO;fKefyRnx-TQlf+|`gb{rAAT0Yyj8%H|h+zorG9i#09wV5Df9*d(Fq7Hz$W+zjH^ziuFEF)-GG|~aH_Ya z<3hexm-(ywD+1?Vx-kIlHFF|_Ql0~Vr*sTnm0|nMqJ2%H<0F6(hyWWM=kDhlaH8HG z0A1w4+Ddu?H57uqE`DQZ;6c!yLpTeq=DoD=whcq zkjKpPYw(hH`zk@cbB103xDVOH@1{>5Q>_2xvJ8+DX)O9X3^ypBVH6lZ+|bmrbj&dI zAR%Mb(cg3vFMdNF;y1SAnuYd@L~CwEz6Ga~Q=s?1yHE;LboATnYxbR;q7-E;xDf3@ z92J3m8kx42M={HZRv2v-_}%^Y(}lZuQ)txPjfTQxxMtD&M$_$ufp^)riNwXorDID{Sc@BMDB z*Y$hI=~s`M8vRE*725WI%aUeZMj5W+6}%CERe%YgME>yi3)@hCfB!2I z0?eQefF!QcZw9nL765u>|MZ(K%m<1q8mfFQZ%scDH}PkZCDkU95D8bm7(W`NAKX_@ ziU5MI%oZ+XevFK+7kHJMdDCp>er8N*a2+%VnseC&MKwmLcKGWssv|E{0>Wt&cPVP*yZCjl(inqUYxV!S?pN*34t zxZtS}xZAE%ZE%0=9OO3&n7iq$!hocM{0e9X5iCKzcnSc1I86J(0lU%f;cEZ4)$Hu7 zmEJ7aMFB|sFFBeu!R|s~847Uw@Ve;m3{O zCC7LJ?w6jO^&(x)H^DbJ3AX(!UL*tDMb zHu&DF33+2^aB^_aZJ4EAcD6@CNf^!{RxzBfFI)LT9dl8|b9d)S72Qh-{ulSC-@@m2 z`=`QR6mz9XpfeEjpApo8E$@JncF^T@^QR2R26~ND{0wU%x&LYRn)l%`Q_vH(;auia z^^YHiuLf<`x6SD2=;VG!wSa_hm1&)O4?L^AriwoN#%!#rimM`?xCG2*GJS!GN!e*G zEv>ixdq+n?H}mG)O8P%ddM(ophrBDD0}k~8pXg+N|9*(KYjSN3a`2<(A%I7?d+f?Y zl9jxT^}nz+4_GKAC~>^XoE}WredG!1_Fx2^ud_m30b7fk5bIOH^=;Z}h_#Hz827Ew zPnTgH6IcWdN2Cm8n*u%z3HmpG;3U3;6A4g;<6t2Pn+WjGUZM5Eht0@> z$$kB~H1Oq;ljO?;#B*OkBhQOJQ^?0vQnG)s(H<<{c3_<#3O1wEDR>mnSoZ?)M>(74 zI9Y*}|N1`-%&SjeY)YnW8?s(kemCU#G<6E>Iy+DfOD(_m;ys}ps5>pbL_r+?FsULb zV*K=LYG}?emWM>IAsZyIN360)jDR%;tVGUVV-olrf~Osq7>jO0gx2l{|JeexAb<+D zA2%%x#gn#=v9=3=KbjzXGY{xD&ESFjZ&$p8{cZ+q765E7oz<{yE%0%0%SCD02u$5q ze-3~Sd-Y8K^p)U;h{M)2ZSy@CfS0#(DC^ ztH3TQlhgX;&=^9*@4B7AZcf!7PG;G3i1lt~;U_miT?*|~y|c6LN$2(c#fzujLYkd* zXKLGdjq%HMTJ4I3)q-uv)YyBDS(>Es^A=eP9UOTirBx-rT(_ae?$eZ`wmQ>i*e%vS zx$-0vO8P^H<(A6Uwr(pG%0^24)-}&3!Ny00w5W4QnK{)HG{4NqtVQ|HC}b82eb$^D z%QjLS7Cw@i6!={qa9fNdf~5p-2KV3vPqlOmJtn2`2_w{ujn@h!5xGt#ahTq}&jawi z*8u=v8o%7zL40EXTiyX_bd9^d~g|C-gjJGFwl|IE9B)utJC1c%>4J?vfpVZXlYsJ`Rrr@yj%hyXN3AkL&Ik< z@xf)YCa=>~`W?TkJ$CSnHQg`1faH}rrORxkhsT)z@83Uanb^qTExyZp*d@wfC}knB zM~y;n#Ui+17ZekZD7k?og9OdeWqc#!i-b~N&W~!Imx}L7m1j*W- zXZ^g$Ib3gY2DgTegXV<2m_Ih`fq2#hJ|HGvXh;GyECOG=hMwL%xZ?wYAZTEHNEQJR z4n%#D=3jtUn)4c=zIm;J_jv&cgH+*FzW{WTc4f*p=&C8!V_ExWYF{RYy=K-?M(b8C zGKW&qh06|I<_MzE*A)ytCgjLk)K878<8xb^fR+}z&{>b#cbOHCu1<(Tgo+y^zn}qI znHi8Cfm~G2okK@UYoY0RR^OQv0Ciyz(NIn7HA)m)HzJ5{0MG^?G;@3p%7J^3%)F>M zD(QcK*38Vrq) zxSeQtom?O0T`fMw&E$1oKc^#ltzoOUm?#t;a#2gRE>=}8uDkKD1U!x5*O2)|C>!p` zTUyrgzC4b1dcD-@7hb5`t_eEt_vyLq`kycCg6Y&B@m8{IF<(3^0-apM@aqS6 zTsi?0(#MCNHi^}sIVXNwaQpyAMB3c<0p3K!>UW0nHvvi>Ki~+2g*f!Wp z`L`1#0k7O^C)0WrWMY6?Hp%st`njS)IZuRC?Sy{NaVdUy3tZY;zMK%PsW^B|_>-t* zjX*MReSHmvQJml}a3a&LF?#;RR0HZC(iWo0)6l=`X%di7S$GmMoP0R4Br$y|sX`)J zXto6G)%D5;tLZ89_A%lzwY*4|;bp~+|DIHg$vpeS5PC*4s`ui*e(Z4Am(BC{9<{{R zNBHL5)5~c+aUhsK42BvMMfx2WZNe|-2|>)jaRiK4bm39cl9Su+I4E6Zfk{v|Sa-LF zbI!n^Lfo68WH`AD|0WvP`;lY|p@iE6GA7sSVGsW(>JRiS^KJ~ifyiz(z z1sVW=5)^m80DX%L+=4zhI1mp5UB~I&x$c+7Exy-42@qkV!~B4{&9-m^zP$mU$$?(g zU$3|&WXf%@@E6)V?6tIJz=mhHf+3r%pUK}>8wS`+ zbRtoHxn4ZHt%qbP>0?{YBC&IC?pN5Be)Wd;D7O5O)^6VV(`9|B(`(=D#%EXLq7<>) z=zIl}j?C{aYS_qcP|MtFZ3`#@FJL7gqLan`JjfOH6FDEi25CYzhowA#JO@55t+|d* z?_Ys^z5#^2SI#3exg;|l3XT#9Uj-(T`-EPt_>C>vMv(V2PPCp3)=@Y$68}b6zOSn{ zCPDz0-j{_!RIo?68_u!C>Ix+ZC$JcI361o~X#N6Xz^f6B*Ps`jQkEtt)yp;LVuQt^ zDwVR^K)`Et+Xb^4@|r^ehWROw4Zy|>MqHEe8u($@gAmwO4Gwx>O0p!KdM9bilX>3V z`Xi4!WH9hvBB{`^{>#XPDW<7)hY{i*;&UOXGqWwRax*A!YJ*mX(HvwyO!=pEDDqJD z_){$r9ccl^VZf=s>znZ7xh%Qy-7$w=+EjQ;Md83^?61>~=T)G#7d$*N{!!z|u`ZZz`E2Bi8w!{kO()zvp|i!U1qr5}JDK0@x9P znP;G<7e%;h^ASWYh=h-Zv2ns1%_he+;3`mJ3(C~;*CdtLxx;0z;0wnPmy+Pc2sJ*d z0YS{5cBO98sKsoJ@hPA3a6IW`1sGDmZ4lYZ4->gthwW#ZjY}7h9?fSHS6gOcU~l&<*T~r^Qd@mvqI>Gtn6f7g z5l7Gp8axfZVmf5^yYw;_K+mlAQLFK`p2SangF-~B2Xkxh<71@}op~-VY?>77IL5wl zM#5#1P6>+`csHAUT`rK$H#0Ls9(eT zT^-iOY+E%oE@;mI$<7M|0PCzBU=(T$@uc3{qFzE@W~(e(gi?dK^z=sgjX}xv zs)~T%N(P*!fI$4GP&$T<`Vd5mj_qUxB+B=lS5sh}I)SReTZn9yL4WS!0AdcdI>S_p zt~wtVS}y&%Dow7WfY&zGKP<}%37VRM5}wI|rIN)e$%S*E)wsWCvO07Y9D z&7#h2JsZa2^)P_b)3Xv-9?}K2ANBMIDOL6Tffp1=GgXAn!-1e*d%iWCo}O->rEP3n zLoe^+a}NNC#4&?131mtP&wvXLTt8fYvk&K^lbo04Twaa~qB}--0TJc3$5X7{eK+_% z+Cmq{E~$P%h!L6`Lx{IKzgF%6);nCmB2%>lBOQAjr$ndps3IE8f6RB3+Eu8)_rq73 z$RaE))A+H-Sa-t1S*Nb&_;xTVam8za9w+zKHM)%#V7v?(Y$n0-J_8mX=KE=&&~+b- z#(mAGRYmr0j)M5&7yRo(4`&R2Wp8$1$$<_GJ>LcXa?8|0Q z_PFy0A!B%vw@Zqqx?&8i1;?s6jL2@Eg^43V-U}`20udA6wdgnYF)$MXap(v07+AG^ z6!y~6gFx16@i^4>=VfGM1cYvv<Le>2cN`O zjSh68p&6D(Cx=Q+Ez7TO$b}x(qmrJ)*);O%lblpj1!@N$)9e>9Ae`4I$)58jzuE35I~8 zB#+eFL9I!I{Qe6m-bTDbYEoCgW8m$InZ!FSwKJdjG*H~Jmi1Ctlk`#ZIIqT*OucH< z1(IJ1E23=~0EXRL%;XCj()c|vwp;@h%NuN3;Du8h1bG4LXZ2@4d1NU)R5Bpj@GqZTF{w5_Zd6HYvlNUAc@I zJK@tDZTu*y%GVG(wVw#IjTu{Fv#;hi?DgatcdsTViaYd*rmrAd@Na!JH8tQbprK)| zmu5;(RtE}A5CPFi_+AsLG|Q@wjds8+2w38r3Ds0rJM{!ZmuuPq9UT-4BtqVw0s@{v zF2)`=c-aAV&|5GMM?+e8HR3rgVCOUpKT~0*D)b^E8}ars!2KuJ6GG!hR4^))PMO_c z*u=xkq~iXWBv_zpaPkKN>N`6`vo?7}gi_>EkGDHY-Q9Frr@4<~Ya}hFC+%-Z>&)&e zUNt`8@P>lN+;ULyBVP374tOp>CA~|tBz61E4pj6tu={WVPo39X9VC7Hz@P#FmEdo) zv+U!Bq5bXl~Gom0a z0&@5TdeB|}-sJalrj0k}r!W};>!r4wEHVxf51tfX5{@754(rWF<87;3xVg0&OgG6^ z;zcuB(9TNa%xaKuFKiLcl^JxEO)=Sv>Q1o{*E0NwGMDpaHx#fk3?8 z%qdl8DAE;&L!f#Bi~ld78&Cnk4qp!vk|sx$ZvxlgSAsy_45RECM1iZ*HPLxJSA$QV zGcL0Y9hSzoqzySf^{}qFAJ@5D;!U`zeU~4&dcIvjH&hx~rW@m^BQi0200M{V)~lyi zA|=@8PlUYB)}yfL%$QA{AFfBHd`~-XUmZ+9n~Z{zCR4FwdjA!f%)9DsPPyw=U!^A# zzUe7}d5#_&)N74c)RVEq8s05!prsXg_*U@#E&-1v$?vvo=_louZgxuTBsLnAVS7u{ zzkQc6}F4(y#dwj zCjy$hhX)5Q7wO%xq=MRM|6U(N4)qKa?qEL-1TBG4iwiJcIhEWFXRC59fje{o`2S=Y zLm6et?QQ{J=x=B##y8rRh|xXcD+tlN}VA_ z!tJ!B-7oS~NdyA!dq5N%8yg!=AYbbaf@m^+hUS(^aGL+Q0bZpIsC2Js%WE4ARHXsW z*KG(OdZ@BqcKUhud zgi%Wi1XC%lA~snFZ})+y20G_z*xjh8gbd-zqJ9a=iM;0k5A7R@wlW2 z9{a+kJ!FQFWOS(mzob6q(+zH}u^OGt)SXu31>H2KE4N-{{qXZVT&Pi5tE;OM33yP< z6A9R#DhB1V3rT5HQxi~JA}@8M%ws9x86=WC4A(#opO`<&7JG5win?ZEw|sZLh0^-0 zze1%q`f>TqfqX%(|1I+Oua@U|WI@Yxc>!I($4QMKOi5&`A-d?mVqq)EpsAn$dV?dj zwjJWlOO^*5HNlrNG%|`$ohjF3Oc^y9x(CuM@Fw8{%Ll|A&!c%Ytr>8v7R0Vaj}=8) zY_XBMV4{}XNz0SBK{(pxx-9})KmZNzA zx>EJW#)I2#h%ihxTg0&snT!^W^gT#{fT05BZ(*Oqs^$wd+Iu%FXc=1ph0Pec zcruDe&r>Arh=@eIPrHo+UwSiyJrf4au<4XZxb6M}v(XJ4VW8s00Vl_ojDV#U=x+O6 z&**>(vAR~*0rbnWxNN6V88lu%ooHIkP*D*gj=QW=V7cCSv*J+hzbulfUCc|O(yj*) zMN+C@_-);81U%5Jr+*;o_DNCIf&vLR*Hb&pu*HIyP^?}KrZZe7bP}B3y4Xmwa_RKz zK?;zRV@C}D%?`C6D6e240o8k0lS?m6HU>{Mpm=)!)piv`4p^%iOAbPJ)ECgrKNC6y zqGAr1kYIj+JaMNRJmEJ158qdlt=p5h3Q3H|3B!R@SENUmceHvrWgHa2586rf2`8Wa zLX+B&sN+$uBo{GJJ0Z)L_gKO01fAbIhsu@dF zCqkZEyv_6UF?;Q*5zvSZ*zbnli9Kf;O!feHSdYkTwPE*MMlD^Q@2?q+66U*|Lk$(d z;;yd1=S2@7>T3Z}`qfeZI5G=^iX7-N=ewNi0QZm9HL^YtbKLFM2 zOY9gpo`C+cfet=bos?v9Py_avIDK^JHH?KGFqa*cy#anguN}=n!{v8a)zmkcoWqv{ z!^opJ|4|LZyH+Mi1_!9BahZ)soytcjm5~TF)fMFK8AfW0cMudH<6I=uNc6H?UF%$y z7`XhqJ)AvlUTe`%5hy7k!UzHfI>L8-#?s&C@j9;tL&LthvMAE_^8+B&7D!b@fSv%_ z787}*i4=XKL%0n~cy?}A{CAW5%<{+6F=;7Z zFDpGK47$I=afuVSqi|Rv%fP8Ew^ajWV7Cx%GY90GXFx%gi@5=^2COQR0>SFvzdtR< z(lnGqSP;r3euk3YB5Q?}+H%*p)q>z7fdugf^7$RELm@eCT4 z!oVN;!k*^g@qJ39fwGhm|HO_|5_1an7dq}nW5V+0C@eY`nOLIlbq0uQ6!yv6D@Ue4AKgT2ll zio$LeAj=xGd7l5Bp9Wm@g7xNLg_GpE~4NV2v|Xd7SY37X_lr9dtd+pfSu-W9Pur%4*Lh{ zEs6K|I~cSnqaeA&Ds7WCPe3ifX`e%y=b=53$w8fY&d;yXl}AOM;5Q4*kkw@U5W82I zqnk4l+9tArnT;(RwJsK-QA>kt?1GepYx(P+j@+_7-zo7zU6AL~xxB=fF0I%*gL&k$ zud?Rpzwd~?G8)dI^Q{L1g%aq*F0TzdFy#v>4nRDr-5!1)c#h&8{9U0&Hh~POwWm-d z=Mt2x(1f95Qqvv*1Cf}o2?rV|g5*3nIac0uQY4|oaHhkuhPosOq>fOC7=$EI)JaKa zR;3m}S@ca?lzGwHhYx?ydY|ov-66Hk0t|%ZaqTp;Scrve2DEgH>QC>UgL%|1fRYzx z9xrfHX`B=H`y)7ZQRXsa#;%vft<(~gySx-nuBf9 zKI?wY$jYOf?HZ3$A78^`5$*f35CE2Ex)JgdTk6!S7bQ695&-&C5a7Rw3UoFgXCBVg ztN|If0B8R31cHrha&BgF4%!MicXPX0&PbF@&ctZuJpzYh^Fd)wM#$x=x3Qt86!x8E z1%zZQ(blmQStU^p0UOb^y6?G1YGwSdK@8w@-&3u;c(tq!fkmt~XsrV#>R@}QsHp)? z+FCRD+wZ6l+zp4F;otSav_t>-3|dL31Z*Z?F9i9A^vJ`nuS_6Y1?M9))SxXD`p}>{ zP)=G)hH0YON>KV#L%qACdX7F3q;PX@@%?ri%Wx=~j?Ud?O%q3S?h?~R!4SJK-S}8G_{tktfy7qCcAyK{UN9dS{{4~@8ehKZ*awy9Bf8YPUAK4t&GE`%5uA93=6|^5 zeCUHFSl-`=iRCA`QPt9DGZ2}(94YaW4f)Rd;~zx|DsB3S>okY_e%2E7?H6KJ#_~uG zus-GNVjfi2Hzk?`4ML=`CZbiOx5tb&n+NcvrC}LcbI0i2gy*2VMVNIH)W#_@jm%Z) z=1p;~l;=_ml5&VXL5E=-WGqi%xlCVZX0{<9*3xMx87kEJnyiGXdv z9!bULPIZyfHT|*nRzYY&-#@^Z`S5e2sA{rlpzZG1E|$dXDs`xPWZY|#I)Q6&`@2M*3J(r1KOEM)o(bfoP)c;lXW23j_s`aDI5fPZpcT3`{K65!u4lezY&9_Rt)yf0HD_mNr=ADu+d3 z+lc*pL*0)h=FK~(WkMFnln`7XFGGYyHSp5AY}`tp%Af+BtHQu&_|@5Y76T=pa_S2>O=pji(*|BdS(pJI7{g9h@M(`#+y# zDxZO{Pt+T#<18gDtcX8Dp}6w3h*YNv!v^a`LpdlFiFoy~Lc&gnOHZc&A1Pmq zU0P3D7+)C)*(f|6qe8y-p?5IsA>i!lTyI_WZhVs($%Za;@B@RiZ}HDrU4MB=I+M?D zmye&$vgU&#k2vN+5Uu^z6<`}l{NV%NM;C53yT2uYI{Vls6|!2+DQ)xOo5nz({7G0x zEwjIs2HTrmxX>o6@=;M0Ne$T!yR6V<9P>z*rX@dTBPmu{h8*vx90wvA|9N6lHziNigaxC6{w zfXK9h@}}9#T>ts!pS~f^Y9{aqVg(}(k?V)z>e|~<_us6&kp4Qu(aX^wZZO1s3XoFg zl_x|ZLhgg~c^}F2B9w@v<@U~M>WsD3hB?FNDR?TtpgU>*gcmtIQr*zSY%4W!bsf3y z$Ikx#67lX2nme5g>%m{uY;s+1T-;+uo#h>x*)OH^*0M6t5kFkMP*7foI|lafg)6>E zfK^a_?0(!reg4`A{fPm&ECJ~&%6D<^j&+#-;PVv;LU3{$axnB?NH*Ujg;GA%$*wt1 zm(Cu1u#$o~g{r0QLT^(tXZX^ii#0+=Hk2A=>ZkMj#%(Dh4E{`nqAFY zTocX6BdjC8+x7Q%&wnPz`wnTXuNwqzrcY4b&A+`&f$bOV_f#(mOs|jcw=#?WsA?00 z-n3%{59Jhq;WaH*L%U$-`1$VHc%c-N9 zG_|3k>DO*KltZ`zwol(z5+xV(_x}9$;>XcR2(7XD=hg6geC7Ew%U9~Y{?AzY!f~Gq zkCFoLjM0Gdavk$!km|0 zlFy9kbn76*ou4(47k!jRMZ4~m+$bg4Od>2g_)f1N1tlO=l;yKUP}kwR?tY0I*&n>K zISWo(*;CQ@oOi-V1i1U0PkvC?Z$ubb6OnD%1bKX?9L9VEde8meQb3qCw5gtLFmWk4 zWDTTo%wuMrkoViid7i{(KPCPA+f*uoZ z(yK9>8jpLyOO($yM)aOvUl; zzoVjpu*DvIPY;^T>Jm?Crv->|i`)X<;&Q$W9VTLZS1MBUYY{k6bVN_jd+vGrO^e;u zV9)qPevP}FntLM_p-lDhJ*&YGg_hYO5SY-Z@GM4qzBeLLU}0ge>isZyKJAz>-*-xl1xj?sQ0>iuTWQiXhor*5(G6^n&2M_ z6duJASZk|wy2FJ56XV(@rPocv8@Q}TH!1i(DKq{~l@n|_(-sJB@@mBfPB?C-UpW`P zO)jJSF>(+uj`29|UJ`-4S+iJ%Bgy+Bl=q{`-lT+fKhcO|{4t6=*cQFEO_u3H;ytTJ zcZ5%jEIad%h0fkd`KhXWB;|i^ZNw)nHsBW07?H=Pd{`@qh_Cn#it?p~FX7QNunG|m zr3r}Y%lB7hQ_{BR@Rc*aum`P#)y0aRFSO8U)|<6+UPY|HPQILlo%AwC5ojy9Cw)cW z-|GM7aMJwqR$V9|&?5!w2b~1Lze+hr+_qPQE5*JCL?{ub1a9pQ4Ot%w6BbKE8>CE7By>wstEU z*p=6)Kvb)tUTEi>_b^ov*-_Zg?pTF%A!w-|wY#il+R-LZ&VDbs$5y&pnO)H~8hgcf z^UWbi$F3Xh!?`!y-S!1^ zXaA{~7KPYwA9l29%+1;cmEo7m9}p#YT(|6wb*25n7wNx5ExozRq7Ha{DD@9!dSq^T zI@nH?E^MxR(|cgy4IIx8;USHy=P;_3M!K8YRr^bI5;uqc!y6c1mWEua}qt;+5p*WwsiHT}~n;RnvVrNIz`ce{Igv|)ML-_S)8Vmp7j zwCzhvIhZlaFZjf%ZR~_ZUW#y%9s1xTGY04!LELLyf<*F~Tn|aoCfuHpH)F)R_+JU! zsn7iu$Bz3&vmJ!{<6C;l6vjaE#C;P7Ov67S|@u0je3>TM)3AOsy^0Jh9 zgyktz!_-ms6w7Ew6)7?yI~Lq!ubbr$H)uJ7RL~IZRAQ>+*?hzTkz%z2n%|6|2XMKU z_MsUx;p+^?JI>JDXx`?UY>WAi6`jc{_DvAKNJ%0~zkUs$Igo5e`uaJ(;orZ1mk_Gc z)7sYkH-U_eXx{E~{MUqNpT{q=^82Xdw?6&#P5FkG`M#78HIRspQMIUv3A^&jl7BPS z%N4?+lPzVlYJ|zw-0O1U1ou}tc-w-GSf^xvrY3{pndLfef~)P(Osw%cBxj}VgtOID zSc~#6T2>`b%X@Ois{(;VE3rXd&u0$#d}Kr#I^_yao|lx`X)e9888{Pn@+bD6tG8KA zKh4Hw7G9EQ4pNjD>TMT54Y^or5@iE=kSX-e>oyMPvJ4!3^xk{H61CB%sJ1~Vbz1x^ z)}G<$aQo?mtX}K+CXW9pmP4$YUl4afLnPC&-y{W+(pe0HN--G&A`MaL0;~IhW7lhCtL`z5e?1I`!Q{%(2GcPghOYdh)(+D_v>!Y zQ&_IstEi*%J3bWoI9*wmLnF9#<rW}3-IKhN z|2emm1knF-o+%9VbR-Vbx?wZMqC;u+|Edb_Uv-7*3r0F zBAgoyMFqL~-frmP<+YvJ8jKh2bJX2Q`!?o$5mCyk_|2$gCq5Yrw?M7cEL?f(c~3h9 zMzr!zC2oGxwZ*tTF+&n8jD^&vC{zwwtQ21eE(oN4(w$`EvW@iwM%+*LEMb1Z(pACi zNAcBh;C=ORfYbP_cVH*)mjN9!Rn+Wtt5)BC#O{=`691Sy7$2z+1Tf0)<*&Njytl){ zlU5-KX3D9yx%P&F19o9{M`4ZwuF9nml&V&k^b9iO#Dp6TbH2+H4_O^4@=jl^y4`b1XE`lnODikc<87fF`Yn*!N_beyyA!$21CqFQ~v$363Pk<#<% zw$_E{D+GEkeY}5BuDbQ8c2W^K{m)YQ5-*xBCTjCck$;%p=k%{KK5vgpBXap!H8o+H zSolH64a`Wa5{mc+-Dp{3LtOz~96f+G=}s|p(`iYKC21YMeQ+=pJS`K~DK5fZA}1A< zb!awrhe>czvP`>=UE}?cXr??gbJM>IpV*}JaWx6D_02Iiw?a@r0BDJPfc~PrZr47% zD6>n<^_?P)WDJOxy+5 z@tsStbU78Z?^GN#9*e9Oy~1QAMO^ZZCn^`VJ~^$LYBNv()f`X!&p6_ax>W!V*O_&c zD(#joL(YT~tE{P>39m^6p3gVQMpht*eCp!A7}iYDj00?&TuhV#e$9HD z4Y_NDf30Ryo(1w)Mr9rSmWm1{D&W3eaUx1vR#9QU+-MggR|Z!Ur*& zv>CxQnoB35xK)kJeK42(=f^?xu7JH%IJlG(GeV1l==F(= zzW(H2Fcs01_yeVu^8t9|m&>4BDY=U^M#8VoPZZ~}+_(=0xlqZ&Kn#A8@J=)UPXWhf zlddx-F!NjqP&B!K0PEgf#`^w=NuoRaVbT(=ltGNE@| zKY?*R81Ktggd)Qc4kw=?(-#JCJ;OKkO6|o4wLQq|S}1*J?D$+Ap2}40_&L4v`QHgp z;tT=UYA}gf%4xn5kU*t?LNCx*g+M2}fTw^?&BBne&sV=+fY|~VL;V1fCh#PMU?w1d z0q+f`#{tx^o0ud#TY%$z9Rb6Qi$&F<5uRnmZ z1e_yarsf4^`rtnYI$bhEq=3*QlA4u_jENTc;7n+id%Etw#U#DV#W>>Nd`;3+UibU z%?&nTbv0;=&&RIy6QPL7d!Q)*{YDU#0s;O3xM~^o zx%cfyOmhP8Q$ppzK5V{p0e)!qs9BtfX_O}(ta@!p$u>}l)YR22KnYZQSuj@8*@**akK8VX z8B<8p6POH~04$XE)8l=brlg1)p`0v-$tUy~boQu2-oq@&tKXD6qenm=FIwPZMl9iz zqc+HcVU$TY1N3Y!P@yjPa;W(2$AJK0!*4eM{LBf+8Nii*pGfn&u!Yl0W4OMF+Z@F8 zfRZ8{nPIYLKZg4hZ+rps3azOKwIJshxlqsOJj+X9$yebRBL_#Utgep9S1*!CuRQx2 zTfz&h&2jsuK2&B~z8vU_0U*a8@e81emWdb26`lC^vf8(@6Co`m;`5@x)wWvxB1ad(1~?=-H1ZbVF-?~Hrb_G5L6aD{&sDs2gS3peM1VpJJ$L&2B90L3a z94=yym;IRit$=r$k+}o=Whz(j-E^@GboYQYMP*hKwH{kI2G z;P-t6CYxYimK<(ETn3i_)=u@|l-F#LyZu)w=m^UNs9z5fC1j%>*uGt=nwIkFMI;^v z3jj_D)l3c}fO&#p5;TB;$vA-Dbyc8v5pZ8{S!q5Z281aH*UwWy&o?DeR4NR3P^CU# zEIc!{PTUUE&z?<#$SsLNwSqQG4vFp;%f6p>2fArojmX?ny>9{}68OjFjkY6zA?N~e z8mI@Z!L+ibvJzkd#bH>f41h;Dn)^Z?=?)lRD9Qk2NN-<&u%c}42s{h6{y5(EU$WW1 zW;MCw$sOb$WLR*-p37GhFnQkt3JdV_zxEYQc$m{{qaJ{MP)Hpg^W%I06hHyt7rfav zfDm)CtwI{yaIr}Ach#6aS04jV>JwlQpg1DQH|WGxZSL{;0o+$jNJt300tD2Xz~l-9 zc5ppTj+!}$EA4gMhV{~^3`6d2CYwOJ8E|`g+nBx^iC$E`zU3)cY7n-kE;OgMapO_X;FI_I#wx2e_!wb;g4vPL)uh z2zcYK0HXumg?NSvtuRgCgZdXbYw=o?O~|iDqCeCa7@J~)hIA>?Cvb-@z@{P{ILbozbjpbq#|0v1``yB^>{H8suvnp7^=fw)iuo)U~%3|pM^ znbKs?k=+@dpPMFfiDmCE9^pDZt|l$~r3L_s`**bd_cvw|Vk+2L1c+I!49b~yVEOhz z`cV7z>(`nZIb`}$qR{_hxEz|t0DBLZ?^no{HnK`CF14jBm3FI;b)dsgaRd}8cz<9Q z*&02AEyg)^yx#Gus{w@Y#9il=W>nuxLk@`7h=+RI5T3iHvkM&Kp%GIh86wRm9F_5; zzc%Q935`0$V5XUqFa?>FQ$=V2B1$6SVGC*`aQJlteyv#^=fkOg;jVBa$I$K-fRSCn zVFpA?P%W%>J+1jQ*jBMWtq=1(HNbwx!6c6RiL*SPNT%k~=W&Rj9;C*xU9wK9wL*7H zE!tuhP52mjcMk*vD>t3eD%)=g)tKDvO&7uM9QV^2B}uvx;W?%|yW&WA5`^Zm>SNcS z2NSO(8w{>5Fz@FaIowq~HW2;-6lnxZfdcx6@S=@oFL2IW#hnA(Jpr~1cl0lrU*}N zeRtI>9*Aah}~kq z5d(cNNcuXVNhXlW$NOflqEf?MIk- z|J66ILxr00OY(BttzF$eo8zltUB9Bf*XWoCg5e{^Bh#T~EpNy+*VeuQ!mMqmo(CL1 z++FuO>Ulzx0A>N{Cst}B&VLuelK`M+EeHRzF;f~VH@D(pFH}ViexhS81n>)+j|Z0vB7Xy#@CK~I^B4N5ba z_BvNURswk!;DW!_)=GffTN0#1dOvyAK7dHXY1#G9ZhKyf$$&jYo1bh*T8Gvnl_ z)U&p$x3=tpp&Rqq@vyqv^n#$JGKT zrU6XEmopAhFF@w@O}@BTxki^sCZW>M;D57Ap3aUKMBA4RWP5z3ddFN&QIHjKiv4I? z`HI`9(H5ZG;mJU`K{5q?1TfR6{r0UJBu5*7F9Kg8P^9^TLd(G7qc^mWln6FvdY@|h zAoR_wfmN&)YleZy+~ErgW0wv>>T8JI*q3mOy67EP7lIm?bj^1wrubCxf4x$f$R0UAzCOGGzCO($s%A?>nX$5~9@f8a@ znlFWd-DuBr)c6NsEE^eN-qBB3{xIr(Iyv(WiEYJ{Jpx)OYZtEa%PZSG_uBI+V~pO# z@hUWvGu+m>l9NiA8JmFB1ui|F%XV2--=6?o1WXoi*P_U*wm~ zV*pat)zl2w;!pks>Un6k9WcOHcnCeT0e^lTPs-0D;SGpsfWcQ*I0Blh`)ZJ!)CMXq z0C?34@N2*j49>qRkM<^1Gp;{q!V)*tP)DNnB4JYO`TMiVDc2-15}50qZWMeu5}d0%Nf93#3f7~u=Ax(Bx0G`_47_lf z&{hom6K*k1^#dt2?1CO}e;7?{O@Vfu-ydLZ8xy4@njo9S;!*$mO)Lhd zWVLK<#)4to^#MKkvW&}N%+($SEs5b4(;tg5jwgz`pOKMBfpgPjuid)pPJpl(3US#q zERAt@ivu9X3tKqR6YS?&!-i5&bO7}(veF7-ZBS6uxBTNC#s0lngGT043DSZPZ z)e<|=^rn;0>=o=Oee3#VTKSZ4Jsw#iSmoGhc#f=WNY?0OB!*^rL1c5rs9{xefr?mF ze1?Wej=*;gYtKFj6fy)a0T)H7ZG-6ywm=3P2r+qrH^^#u2bt7!p*bZ8Yj453hrS!> z=v3i z>tyvt=7EibfOtg~=E8l^NEgxyqaqDJs76Lcwnqjy97XCe{byOmpXk_;$llc_fg?Np znmp4zaZ3gZwJcfop{ArnM*lxh_q+wEsw{zvXu&yFrkFks3hQHRSLCOkpSB?SazR6ws3((*q)!&P>Uj;CzNC*C@{^2<;3E{Xmr-S`gVM ze)Tz`chunp41(>R=x5@L?-47XEE%w5K%c1#M2q+az}?g4sF6j6Y;*v$b`P{0cJcv4 zoI-5oH3R&waaJGg~}|y8b9xuDmE&A{L4~!5gB@JhI1FPumDO-<*1rpa_xWYTq} z$$Ni5c0$b*{IB;w?$!a4EkHt#1F6a+3A#$tPUt|Z$%Bxw>B&D@n7fq?AJMn>*ss1; zYaQ19qMerL_sT|5=LUz9a%2Z8)7@0ws|(-m2Q%Tk`JwrfchA`ORA5Z0Lvc$Q3~3e5b-=%12$dI_q^ZzRLh_YDkzvGq8)SRIDE}L zw6||Z7%dI5A$btzF?Y_WJfZJx=-r-+Uak8$W49P{8gDuJL#29ogIgg!xsGeZTC!zx zk=3DJ=`&P7^?e@8UgrmzIZ)LZaLJ;}QIiC@6-b1Awje*x9sp3}OK}JeW9;Y`jxi{P z478PCGe8MbsObT;#3@rwb6))fYW~Z(+K~rEEWterhD+=pb#(36p_^B&R&RCQh`PL0 zd*2@ZLC?^6tKZwKk8;t+bhVx@(j~jj&L0xTOz`LBkpAdWda~ED%{rMXBN&urHfRrk ztM#4NRxH^*7oG(10!pO6fWo}vv=Lop04RTpJfDHi%lZX;i-AmOA`hocP*E7bzi+@B z72OJk2i=q3;NcZx&E4Z|V8lAN6?DG&QF&9mvLQ*ff16ujX9U4?=e|nK&a^WwmfDtD zb@%JZ%zDLKo~PSO5_;S5%h)?@<>Prp;0Tiuf_elH4f{xr6?WL3wvj0q(FtbxihCg$d?qJeFTMYy4}4P>zz!;O|r7USzqV z>RA_R$g*!##UONMrFpzPYP8}wRz}%{i9uwM4WM7FoW@jZ+I9^RAaRgTyQQsk`g;O> zDR7`6u>tZ89WaHlGsH$OSNV0n)tGXorrc0OTgh)zw_)og~yU?rh%GK#@a=IA$ z5miw$NW>MHe#2+2uDQn`Gn13{LxP=^!?9IH$O z;QmHShCU^ifZx5*^j34x``x>D+ALf^?vh#tuwCO!JA_cEsO`!x5Htw>?sT{Ap~p)- z&bd=G-_M*-apCoojO*jjOZQ9TPz|G*H=(f4ilbFY?{;fHxzyWjkL< zEJMhN0*;b$pTw4X)~Zkghczh<0Zwe{0F0S&`#T{7h(mq@O!J-z){fN^=yE0ev<9eq zz{ih29CJr(8-cnl&$VkAG=zIX;Gq@QfG|`%2b#oDz&_@13L7J@XF_?UiV#&NYE>hZ z7{VX>LZq&erVKhqXVZP?qT{e#XH+gH=UmLhxReekJXpHF0TF2Af})Vda0KGuI!Jy% zlfe&sv2?FFt)Cf{fu_7Suoft5!r_Xs@o5oup@p(O`ipI6KF8QdWZh^)j%v!S6jP;1uekW zL{fqJV45VApdy3kUv0b%NupIpKL3u2o~?67sO^Tg_+$*wOz14WFpI-x+T^b7(bcW0 zarv-@jUB_<+1W*>wt+))^TUONTKv5C4>f_`kFFs1jN|TF0-Ne`xcci?_fJ~qgS+nO z{@ZY=f9vKe4Y*$^N>H#OegXt{&JrS(^vT1Hreb!tu(FA}pg7y`+>V?x))u?IAQD^7)&U0uTCtOnLOpEo|-E?@Im8 zDJCxwdZ$~;S=mq2=A>s-7Q;v4fn;cwYwMSk!?t`Nc;*SdBkC#z>jdJ=BxrJNbogFC zCCQ!$I3m$KJ#!jj2|Az=GY=|2IJ<2i-su2!njwdf?J)3ThJvGNOweQv*>M~M9#DPn z`9o;$cF}Ne}-$PHiP47js z3P(C!6Rl_Dxz@L!nGR$e(3Dxz$LtOagW`sE!P5W?UX>{gv_!NCf)l~S>dl)sy1L+L z?y@UH+;;|sz0LP#kW;Q}D6yBuFX?-*`b~T^LW+VvVR`@BnLe;5rrn7S2f_5dc!4^(?RPiJg2hyg$aS19B$1wo4Is zkCIm`J$aUg^-G*!QXJ;oJ>;;1HTc6>j>+70t)Rjm$ z%{oY%6`d~yQk;CJPGj3XELSs-G0aS*bn=b)KIhxkCbvmo!rBDR!Z-T*(Bu^`#rLO8 zQ-%Hqq!RL><7EFuZ%3dl5;B6@ECwE{r@^MK84q zfmU{jCmb0sFoEVYB05cOHXzfHB6Xpc7sfcu1s+Q)lRmyL6H;89U(J?-fl?+%x9zwB zdgDBx9sPd29s1l57tr2}0fIXM8w~nHXerm}=MF|+Kqx{iy*5)V_I$q!njbUZSua4A z3OcW5pz8_j89;By7dW~N>|O=)7ZbjgpYKYd^=)H}r`j_nW?kw#zoRKeCvVQqvy2c8 z=kLL6T~=S+rLbJ7A7WSIQYp>fy<-y=Z`c6cc+ly*E5Tcyo|Z-d^SD*yR0e^e+1r;#Xp1gGYmj`;s$J6F)%;?jz!aPVxUg2c-$ct z(dxQHdF{&BRs9Loo_8CkUg7JC1*Z{jQPU^5yJNgKoGrXv?-mnysbK5C6$5J~ENPvj4lG>dnRJ{O8CxuT zWN>-&;TJJP&qNV=hu+WJXREH=AAu7G_*{TH6cR!fjR=C%A#hj-_;>ULnvsLj++wa7 zx?!uU!FLSxw!?N(Ledw7NA7|2IL@3#7^{o7+%yxR+Cgg9(Zx`&rLNl`>wis~_j%jX zYupgF>w1u6omaq2K~J9&jDmr%4L2~{dyr!+)y^N-Unzwz0!Yi1=I{|LyX1bl?(ETT zFs$kP^z;vmEr5PGNc3i$KjrpI5SXuk`2s3)zLviJpG`340oJo1tZv=tzIcLP!6Y*U z@q#6tHxIvmI}*N7R)Im@y3uV*jEa}?KFi&KY?8J@l|#ccN0`7G*80wBTsJ%|7)^`F zS+3y-W&gZO<%`p;>8$}M{jNe&{0aMY{O=SB#Gxf*z+XulYD$#AA zxmyEekuy0vXN&1=lmgalf!o+V(%l$qZ)DyCk{5$@H3wnVF`kNJyK@d#TwpE=(AXc!4u!R3v z0rmgC6AP+Bzu^*lBn}VRVXG+0TaaA(TxtmKzr zEF@QCR%J&X8xlNvxN#?A|7t7#AqKI@+ky%1p=>@dQz;!KmXxXCzoI%tFNqMtg~zvmxO(Da$JOG|@}eh&cVjK#5L{$CkpMD+ijcOS5QME)znho7mc~ zs3s0(BLBmc*VX_W1t^UrY37u^%%w5_i;K}4ak<))c1izh+i*z97qad+ty^hVARk!; zO}@WiV}ed8162~#;;W_xL!Bjw4QE9|Fn3^UgexYllX$P|J6iRH>f@>QR|8IOjt^NI z9l%k@$1TJyHJWj*@ao{fRh3GcQNL7e!8X)gGuqiOmhR8V>cbSaxYr@4FSp@um#jL< z_S;_+noKMyG0Y#Z7*b=H7x$KlVbT6Ru&7}+Qir~i+P7^TB2?S{!k5N4wW~(=XQ0vU zV?)dKO@u|zcdHy_+H^^up39G7m?tcbsBwJfDqK-|G9xXd{HBTq0tKxNHQQ=UM;R0v zVTfBdNIKdMSnz{%e`HVBCChP$2)$4*mOD-kEdpV3W5~(<#=wS|%^33%L>lK=!}Ug4 zgN66;zm0i&tc&^NYSc&a;gtI&QfDl2KRc4^r0(#n;qVuT1^z%?3#KCy0k>5(m0mH@ zrMwLh{ocKG1iFZ=3c;UggV!~!?gZgSX-fLPJzyPtm2O9!SQ0q1?KG`KCOY)tD9J;* zt7KDdw6g~aI0_7}oL)2oTg=XvDm8HpZ%XUgwTNtjxH1gfn|_+$eRg)Pt5#vvR4XzX zbqzVwu3QP^s%&v4UfdQLdj;6xGDS-Fd0s_z<1QzEm)F%MS?3aBzKHG)cF`4{G|iZ0V33QIyzNU@t5=N;WSa z$hq2omu9HGscf8e?dicH+~*NrwMWBO1+|zVtQsF`2^#1Ea!?NP3=I`Qj+0ySO|5Vwt5fxQ zV8)99Gj|Y<+)tMpKyw9K=c%%~`Xm6*P2FBFU6>CdSAHPjX!pHH{1#AxTq?pPeX!H{FzuYE05?yR|5vjm) z(r3&r#UHa-@6da`HF(g*SHoq)%@UULk%$rrXIU<*Ikj;^ClgI(N5-;xnd)pxJ}A3# z_teog$0ZjZXs8X(Whgvp!zzJm9Eu)b*@_Xtjd($DO#y(YARNVKuFtX6Y&E7E@75$S zf-$Y%h|o}&Hc*EVu-6;rqOuU%8i$wh`@^3i%_DU8UGuE$`L6Ixe{%FWIuy;&kjk2iJwa zTYtuR>CIOEoT|d8Y(*yL$S~NNc(hqv9TrU^XCHhF^TOhr!;xX2U;3}|xc$TsEiI=k zAU}7_hoXan^y0s-*p{0DlspHC<*YI}Vn}myC}hMz3WAr?Q9UnKiYXlIs#&#Lkhvy< z#p=Wi>Em)X6NrHAZ01=urC5gVsaA$#rLVIhl1a9fbZ=NAj(CM^)!DL(>@+({Bic6g z-N_E31)dDIRWus9@YpzsF%lj<+sSgbE4M*vgDpQ}BTVns-q^D7hc;D|GWt$$=^<76 zna z1hk(aYmG7nI!wJpWKw&-%fkAu&5RO_dHIq)Ys=@q;`jxVN}OaY9?9Ep<|b+5IdB=j z%n{MDGWeb-3_ZGA-aL!r1Mz$Hu4zOn+@vMhQ8&f#KN4A^<2!73!@A!-t@#cI^TG#| z9XmBA7TD$-Q88c(+vz7sJv}nt%znU(Q5BjY(6FV)6td1a=_}kF)$3Ug{MT4fNIs9` zw#lJ^!Xi+Z|C%Z#T{z8qsk^ts(mhI*18Ikv0V|`254Yw+PY4#c*UWJsMI!3~Ep$*g zex@RG#5B5|KW1r%Po;3O@bkwgsfbk^I=Sg7t<~Mk!{Dg@KBv|gR?xG?SEy8Kt4CsnE2#+JC0IoB-*tExrO8e(>}ojeV! z@WhF{U{Lgxa2^pqUyHzziID!>h#OU-nH5L8b%F`R>C+SE8c613GGa_$3eOqWwc8%U znb;<(!PaDy^B|D5HESS9QdOhlrId!p@@2rYsr-ZruP?5vtCp%D{`*dD$tl=1S|Hoj zZo}ZF>oG9X(S8T@4?uB_KuN)7103qOD=;rNF;-eGLXc09dGwf?O^Ck+$_T(B%~3>B z=z!=dkP6&KzSQ#=(*+0;73z8Abc}Bu5E;|kbh=G74>#Y+uE2H z^^I0#pbZNS4a?srX2uH1FQ{444jB*W$g&f!u^FR?Gv>+n{@f93eB>>xqLdX$Gpqj4 zZsV<_npgOt_Ft1fe2^z8sYvK>s-0nVwNf&bG{vMuFr=p`tuZ}X-CpwKFG78FcEYJ% z4~7U2&62|-qCzE{IU`oTX0 zysB23%5Pl%uoE{cp*r}?RjgJ#kNIRaF`Azbxjr3IjKgAlul!E2<=%n8dfiOJ6JjsH z=lEsr2sA-B$Kj)untSdDqhErlgj?+vy?Iq}QcPi8cx*7lh6vyny#?iMq)%Db`ii%8 z&)Z185JPJ z!O}L(5|dlp8L-+aW1x6Uq)9+jsSihu`Nsg)UUHO*>0s}U9M9TFhrY;he;-l+!y$uf zhr_Qxsllm0Agj;hrpZ-YMq_HZLK>s3v`&Zj$Ci39dp$&TnO^Rt{wnT8#sue;OS7e;&G8;?qu0Z+YO?q49*#uk%T|`cAEzfv zTX0>IW^8-?g^OIMj+c^(o86-AK#XaufyD5y$-5I?Bn9D4zSzwF#2c5J`a6$bj` zoS5=evyD(*bgOq9B7CLG9C^}18+mjh3SrpcJZ`daiSYE_J5vo%hUa8WtWJFljBpddEh zNaj!W$$L{sz`~!!3y&SmualCx>Vv&`haLDmSupr8d}~DwkL+*NKM})Gw(401)ZZd^ z+VMs1(PGdk55OB?ZQzxZqDY{k_K7C<{xcqQA16B zwJW}ImJUgsN{e6uH#*3~%ogV>7%H*E!i|QY)Z72~OjFG{CDc2H;k<;lgruc6kfF4~ zc$1Y}J!;D*10j>s=*gq@MXm3jPvycQ-Do*EzL+DGbxtaq!}Cn5tI{=)5$e!+cZjr= z$fWM}>u<8Ofga^(ESfcb3^M_lE@2ide?$F}{*vAn2Uc@KA;a3=iUEuW57f|`LT21L z=~=oGE>HiQ8b*d3Z8W~TWuXSUso5vvV@8SdQ`CeWM6Cm15@jkYp+hPqlhl-&zd08c zg%2Tf{Muhd@~`;}L|Yf@sT>7cx)gL}@#Q)^%PLpZAzEDx zqS#^070_Tij}r_%@sr?O^Ze7d3U6Z#9n;zJdLu_i!pQaoW-r~Wv<)Y8AL81xf;C>x z*-0BP6fq3wp0> zxOxxns*T&{_%NrVsybq31|%!AoM@+lWK(4Gs^kk9wW_plk4W#!cBUaU?H6-rf051j zZaiIk*Zj^DGFO~$BK1tG#r<9i4NR3sMbiWf^lMG8Ma6OwJSCY^)jKi52g3{2TY1s; zf+n$!@}4nHy??9Se=A2;#fTw)jm4Y)s?vO$$2x3t_^y079F8)3h(e*2p%f*YN4}(b zAR?&9-z`*uMgv86R}8c9Ph4YlO!zhoLZcjJ%W(7l%el)3|3w{%&vI%XO(GF|Y^U4(pv2H;=+OKe_M=gWNdWD` zDg=HJ&fXij6E4BKT`t+=RPwS^tngi%v=Z_v4KrlI&ZwO2uNUOcyeIYVUmHOaWR36H zbG{4k2e~^Psl2u}MN)7m6C7bQ=CEdhr_7Ve*O3~6vo$ri$rSTRQm zS);>`zm`XlpPwa*uze{nbKUg+qZ|%^FBoVp?n)TME(J*F$?$R34L%wY-l`mN0Z(l1oH@xq`d>k&hlZN)3aUkiOp zXKHNQwq8!7yrX_hyCnPcifQf-_6!jWsXk2lb)(Hbo}uL79!dnE@iOB}6QQrsO>z?0 zrA}k-eh_4PrTj68)`B6b>O)OKJS;wiJy*OYsbRt<%Ejms8KNzm=plx~{3Gube*aIx zmcLzZyztwX*wcZ|{Nh<($8V@QVtq-i{U#@J`f=x_#Wyd=xi4DL&Fr0FdwbMEVq@i- zpKv@~Y+QdW4gHo97)?2(j(MLP20SvoM8OxvlW)Xw1^>yl{cv3(6iHXNQ z5IMN0RxX!!>whu^Rc}SbGg!V=x?HM#FI|YP z&2Q>FZO7xCTL;jA;r*%YZNL?k|(emqwl5*@m6Vlr+e(6=Og!A zqDYZpIAF6Dhi-MC3}d{|^Nm{%Wi;YG3x$Ny5(OSknYNJ*+_9>8K$oV%LljQynDUjy zZxrv;kNnq6!<--!zy=e{7h-CT*>cIwWt`>QHkAL+xHr=Zz~h;N%8 z{BIUbZ{j2MGD^R+X23~Rec{b9b~Gy{p0T|_rxgl)ZajifCs5zf?j72b@yITAa+CKe z!oj|MYeD*MO_x%6m_->WI0rMSFSRN%#8JgThd6aJgT@*QM^%$m4MIs%l4+aEtCoc$Sn+`%{v(bs!R!RB47Ch4 z#wh)^iO~)hcNyX9&qS7fy;oaOIQl3&pVf+&bEE}ATj8TwyrqK<=z?Ji8A(DIRAGEu zWL^fAd?3g&19@i0bagwss-CnFZ4=zIv~HJ1dihF54sPQFH>1vMZ`JrC{g zWHG=)Jtv`LOFc=zpb@ktt7N7%wCT|bWrTT@>zF3NNhYZlVnFk^o$yG>kL}hr2{d6O zxj@PIf)`tgmMonT37blpPOCyV-iPbU9g4xjMQpBfnT`U7m(K)?^*bYC`nRCu0AAx# zX9XS2EY&+j3h9%7tXg^yO9Re~dCtVAL*kk>AGa8nnU%t|W%E}ioz&w8ic3;{>k&(X zQoC2ep9s>6F^@|R4&6#;lCaW%O zzT$A|W>qoh^Nsx0|3C@ui2AEKzMPlEDnduP$r(iKAN@J_q?l>k@GUSGGB(uBQc|&Y7eb$(X0&O993RvTgeGf(JqXC`cM0s=QPkUuv9!IH9V(N)s--yuJpB{4 zYY5l)Ay8S5b(nrqt-$f#zb@0;3Z{%cj`-(BUR&pj;C;y73Wuj%8SGL0tq-w3YwOQF zf}d{0kr3?A7(VGRjo=-Z)M18~kTAhr+7mqovju&==(gJv`pN7T)%?b^eb2!L9SJAo zaS9tDobjV%h(e;kx`JM?$oc`xbqKXc4P8Xs;ej3w>l3V8q#+zzVSbG)ugEV)2R?YK zcwJjG$GtNIjwh=G!WRq}SdnrbdR+?m$r_l~$uA$=LND*)1caAzXY_7unM5c$_h2Rn zYl0$``HT=POulCC75I5=YEbrMd>I^h8+R{89V8W))MfSopT9|@3U{QQ4WF+( z*U?i+4KJE{sg$)R5z$&W;4wrbWe#SL(eNc1p@Nr&js>}AU_alh((NpvVmF z7?vMhHQRs(U!xi<4%bQHMCAYl&D?iEqfx&%I&uD`hPl;7)>BFFu@qjkLGps4t2Z97 zp;uuaCq(d3VdfFFvQOOPGeUZ);b7`jxDS(9YpzS9lAv&?g*nKYQpJv`{@DE5h= z$ZECH|8<3sVh6WY7uy#fkE{7B5>}7j@>6VZt(3*``YLrH_7l44S32Te>>ax()YM6S zRzbuYlWFIJV6kx3I?T!sb3WF&a3c9TLLwtPJ!&e+YTUf!TIPu(ocgmU+QGO{S`k(G zBm}A5lu4jfk}V9QVUg09`Tg*|8=>5$-<5)oc)xQpw<076y<+|wKl(ih7l~syUhoe= zq`qJhsVsT~F?a)TgR`6yOm{V31}Q&hRCO}M3AYXp)y+rN**G6x`EXzk<-P}nH@gNd z0B77(GotN=MiAbd8f6h@XyJGEktm;T9>TpLQGdi7##Tjh*6D&8YzT98ijemYK|b_%KLw;p+}+h2v* zl0yiCt?^=c9m0i{;(@|KW)Ne8g`|lbj>ZT}SZ~#CN4oaBgD?cADS5wU*+w5< zlowg|6IMti#CgEIrnLrlC#RysDK}UJjm(`QWsev;V&f-07LK+IRnSIZNU|2{7;@VV zd+#xt#xN>L1|8qEym`}s9lNc6S5|{J%xyvFjg$vlT=X8(a6#s4A-b0Q0@xOuM6G59 zr8dqe0={9M*cv4rwXn1Qd@Q+>8Lif1EY}v)ZP)MEv$)M!a7&#?lo|dbhd7%zS>1>h z(*n@kfb}R@t9FGzuvJawI_8dNSd<+XVW|ut*&+QQ{1)-zHa6D;4hmE~WCNLdvjohT zT#h`wtwZ%*>P}w~?&+p(9ab{t;z{XJR5LQ-cXc@59HtuQ`H`uM6{`M53TulGRdcKs zp)C(Mrw}R~Z}j8kO~Wgx*Y>cSPeawOD#H~j^PFlu(uN-nqM@u-F;ftdxckrKuIu_2 zF?)B4eoU@Cvcw?Yo5O35_c^iku>|TZr%duFVfq9yu2a?!c}n5!&B@4I#`-de;G?ka zu$0>6J^?CyO63Wy@(nA}K0ax5e9zXs_E^J~n#oK~qZV7%2}ae^Q=zOJf^VMb>B*?Db ztgyC`#*r?WU@MYL%$YQ3CbW?*YqJ{32L}8gCrY`cWTyLE&W!PVtI3?~3Iwb!KRNTt z$@PX*hO09)v}L{3hg1ec3h}6wbH&)3IX$8vYm$#L->D6EEDSHR3KfaD5Q!+pMc%GT zoN{GNF-mnVIC#AEo~*bfWh4zt!?ci)%)glB!Mkp7kdP9%AV?x`fz>_dfugKw`fI zF7gdRW{~xsy9=QM5Zy!xOL^WV%Fg|t*NDSwdYxv`n*PGfy55V$kU59cwnj2O|2Q%h z36Q@SA_hn#Ky_w0Yk)p5VsK)01&M9Qg&2mmb-(=U+%foho}sC?_jdN*4Urv~Tfyp7 z@}fd^|FiEF0!b>|eRr&Cd<CrthM| z4Tm+A{Y_ZbJ;i<@ zs70CN*mgXw1Idr$nWiNRA44pzF$%j=CSxSyP?@yt!_UUqg|SYErYNQ;k|=<1e(rvj zr4L5Rz{ms!lpv`;P@&p!gYi7jrED9hCcs>zB>b195CXv<41IG zNIbi3WYUmXDNC|xI=ZgPBJz^Xn35!O;H%I8G2)SoVa9YF*G$tHDAN3*N8y zs2u^zFHk*vZ*>8Y(I&+$G+rU&jZ6kOGXTLB$bJxYwy1ujCClJJiGoGom$q$15Nvpk zV_UWwhu?$F{Bfa+DEsmSaeTr6JDY&Ow0b@HscYXm}&K*{ylR9JE6e|Y8X zREy7_q!0@Uq}4&B9DKBzA~J`w$RW>z`s0Fkht60}M|LI&%LdAHh^tb^u@ah+Ob83e zb0OOR;sJuA-USjv%zBUz^gtnJ!SevmRhEnk%Ty>FVhoPsGKpz1%_C&x9it41%w9%r zy5*6--u27A6{O7Z>Il7e`ldMxR{rtgF;!*cscGb`8L$5Nreiw9QkA;KiEI+`(i^K5 zE?e@OUmwBWtW|@+rfwR8sVkuHHQH&tExi9Wy<2J7GtVV6ApT4A4Z>^_I?PUbJ>Z+8BE){Gx$EaSa1ObvfN0o~ zS@YcU^;Z3mGfyQ|RduEviBej`lufF=!p}T(k>8Inby_-~2y>rRM6An9+j2rX$ zKmWGRug@c#Jap-GzrJ(|89mhV3|^MB8PSPEEG(Z>ux-41Bm(Ra>MoK3xNAVE1&=$x zsw-jaU)1<%$dEVR{^sA0CYCJe9@lD#S=p(h^q=7o}ey`nN%Y3cQ3p)_r+Jo4j3?J_U9+ub0_K2@!dZ^aLAQc zkoB9MK7Yz-f4G&X$~({gXTptF!}T?lXhL%mu_Or4V67ubD1Ra%6g*0-M0L$I*Zk$N zzvR@5!quhgdhg!7J9qAEm%8so%d&RN7yG~K{Lint(QIof`X_IYZGljM2I-2jqOf=Z)?G!1*xCvMFQ7_Lf#O0LvGX znd3r&prQgJi*NU2ZQUl9iUiB#m_fo(iSDSdMl8kp&Bp@uZ zELxXQzx|FRE91jPXyC-niyRetSdb0Y)Ob~xWP-gY>Y}P@#<3(qE7iDdrA`?;vCq1` zFMRYyWvn92B|GC}xLqaD(Y*(pI_A)kQqP#}0>Llf4`6j6wrE3J&!9YizigZXd)gz{nQg{>NBqEcIlLW zYRX0$hyG0E1{>i0j{=ZarJ zZ7a(zPgW2~a?*yZvQsblHGtUOe+&2!GAW6asYuRz;QkT&>*vy%{5eBsQ@(i8s*fv+ z1;6lVJ4xbVpaC>PO}ZmnR<3wi44?!SXSkvDFEb+P!ddN+{s~fNNEw${Hdh(dG$2D0 zu3&%f5_AJT)w*aoD4!@%CJxE650L^bUm?p^ktNI5?RRLu*~`eV?j%_zdoDR}Oy9r! z`~5E#9n`4{`OoVM_SI8aS7Qk(X)6Xh1+u(vauw88oFTWbaWuXH0)*%i7^9 zRyQ1fl6K22ub+F?!5!m-0c}#_`e`PKJQOPdRYj2Mi<$#mBGj@?Pm)z#ZP-$qjH&&Z zG_d=DC!D?xC4~Ls*c$ie)4|^ z<`!PgNLQDQ^7#_U&2*W^x_0jI?G{qwl8$k*;5))ht7k{jF-ac%X8BA1O@8s&mzVzLCm+1M=%`7% z_vk^=4&dQrDk~n*0URb$x)lf=f z(qe0C8!R)eDN>;&~7A^@ia}2>3}lr*O}CAS#Q=r@=3Wq{@6o5n?7~$$`K7)Hxt8}_wi>3 zUw8rO5O+P(06>6_zHs2`$Ed%bRhWGfibO~Nl?cT>fteW?IiLYKqY6X_@M+J^>Ba<58aYNU2mz0Qej z5s7yh*!zPAA4;v=aLADp$v2-asa`$i;NOrK<-`F_2{~H^DWnx40cTF|s})w0SHJ?? z=?t(RrT8yh?&bsji$9^@xrQ$hX1dLZDYR(VEngf>boX#S!3gt*G3O0)&U90aQB|jh zUlcFz3~IrBC)Hf`d}a8wO)L58Z=5}j0zUs1wH0&h51%k3g}U53o>pt^6D+?SNAAyxaG3Ls;bD?kz`<> ze)m7{tm{!V*6I3-CSdk|JT5gju-p2(9-73I*VQ(7E;(dhGNS*4wbkT^Bf7o+G3n8( z?~tL128WbMP%}s%<2(r{g(z`lSD>A-yk65M2oX;o5dwLRa0=99XRc+Mnx+{JHw;UU z>4ssHOL{|UYX?1XV2|DQAGkZf%DGi0#g&X5Cpve`4i%M~Yd5K~3a(!6>WZpM3L^M1 z2#)J1oUsk*fP$w51uDh}#N)ymfsObA0O&KxA;ivr6PPfPN|V6@^=?V_k3WCA>)^o) z7A@pQbj-#rI z<=6_7EPxk^I(>weCz7^fa$t8B+%=FN$E0k6dtub6&?5!+91_=y8NxRc6oR-0#dMuj zt3UYsqdf=o-#TO35;JwmFE1k_cBep115sodh>L`N zlr2)xWM;Apx+w|*aXyd@=t(kBnBovy1&0FP`235qX)K-f`I@@whNrAaSO1Rmlt@ek zt|-qYs<+j(q__@@fAR8Rla3@S*Ac@a<>jB>eb1?CK%LwL=BwQ-TX)?@g|HweOTGY8Dg0-77k2s|%PPCFt?S(9Z)*6} z-^yoCGqo7~`F&;IePX@yNam;4C&JGHdfQw0pE+5Kz3r`nFcQ60bYZ78?Jd|SB!##p zmnp9*(_x_CxE2-Cp!R79g75!BUK*@7h;+Fk_Xi^BjRn8p7uBVb?GVi-WXWps_PgI+ zan(5^cP9<0-ZxzF{MeUb5ajGnnr(dYP;}0J`aPTHbPM><@p54m{Nr?hU zGU?WtTzkoFj-b@cw31g5#*mfBniG7?vJC!f4>ITkdk^)1_)tha zGefBS@rNJvK79Nx=bS}kqu;DKZ~Wt*N8EM?QFTaS18xL`*c$WV6zG^Ji6=>)UD$vD zWX1Pi%wK%kH7BjP|GqWr*ILPn@fTej@7WXjWV&EX!c{IfmjzFOgJFTJ1F7j0K{5qA zXGNAg9fU2Ua z+Fi!&Gw6&{MTtyBCD6VXjq0T}_xr_*=0RuaiU!{uI993b#sC{G z+6Rwip1iXDkqhd+`$RZDg-am#<=sazPyDw2i7V=B*1J6h$-C?+pKx7b*L~!kgXQp_ zJqFA1a+axu%_dXl+K~26zJ1>sc^K@i1NTyU49Qg~;Ok?QaBl;~KyQP@pRsb5scX_( zBmS2gs!Yq{x-gOj?R}n#+~kiS`Peu7g(`r@@F$jzb#2!*A)kknEwzxRKA<1bL|lC6 zh>>eH*VS8O)|`bW{&fG&m83%%IsVuV)iw3YzR$2YNtv+s0ajX98ysee+Ab>4mGPto zvfE4p)8DKwzaKHIcZW(cXYSHpUwY_~$L;sGN8j3YVB>jdMwbI_d&g1vL@@a>ZPt5T-F%WukU$ zbIUWBR)1R*N{TC(jEM*KqnE8aBU37}w~4OFjt!WwxI(&BkkgOvIC@mMuF~;i%bYYx z#9`h{2OwF!I+0V3>`A=(uHBPeI}&CR)ge}cmn3A-H>tSJ$BrdaU;g66)5c6bXYcnu z{IYH&KU5{gX zoFoA!krh>!Rm~-WBb>^z2bf2XNx(%&C9uk&{t}e6CN6k4T*>7CAe3CHFin+Y70!W` z>@$o-?$4OzFa8C_QpjqOfDXyyUQCTa&VQ<5%7+X*$VRFbQZWnby>&pBgfLJX!c%x5|!(Uw+lrWvl9D&KAtdf~?wi`eBksYkUDa zrxo6!){aq$Ql0-hJP^|AXni>FUnGc7V%lLokTA#9Q3#pX3TBDTD|y!ejSpzjB~VKb zHU*o+D(D$^mTg(XpS`+a@P44S2OT*!>9(tM$S-5_-nHKTd#0=tP>`)z=#+I}$&RAZ zPxjBUj%?h?+LKq*SM`v#uI0yF1FnVYHGI+M&f+i9dV(GLtH$&sY~`Jpf?w|lU-yFE zIzs2r+i(5eju*WJenN#&O=YUuxMPH8sYtB^3N$$ZKRO=5%Bq6jlre=UmY3G0c*jbj z%HF0L!gW$-kY(#O_UhTmQAuU@u1i*v{l^iNlC2q%BBt6Ywjl%imEAyMWn|c(0jt)e z98w`MGWV-3{d-q}8$c$SCU31LufI0yR~H_u$V6sJMS}3ogn8+*SP+JgTn9thST85b zV;4MMKS?rdE5I)jlYqk6b4>*_sd_Lo=6Y{auP_K&`n#(hi`v=%u!<$nRI{vFGibJ6RBV>;;hAAOXd@8GBWzWQ^#^XR250U{&{sA&hRiSQH;n`@?paKmb%EhCU zz!HXNx*(aCDFnBPVGr*!Xv;UtJ9h2|(7EqcD^(SwJV{uRtlmm`R|584mQ%J3s7gtJ zb82cxjnS}Z9XVlF)2vr}bt9cSlWtwTO`8c&c8a1BA&4l!vdj{Dp2GT(gu9=ph&eZvL6<#LntD6g6o%1 z$7L1e6$AQ_wcnFL-HGe2SUj&|pT30K-j<9+ZIX-=$7P0@sjr7A@@$gI5MBN5t#{N8 zoydfPy|>>>_UlVzS?|;-<9Ow!LloVwAC*mubb(&5{XtS*3x&dVZtW|G*><>OK2o|Z zy_MKcj+D)J;IsmB(27sn(t>zy{v}5q24Vh=JtZ^Kh{y4Np=9vBpb!k%UtRkhxC=%c zqJ8=wbHG@i9yt8xJC9_Jz9R9HLe2I7IvG zIq`KN^5D;Bja|k-dDnMEZ|92MzWbz!VV8(&vLHSYF6vx2S|L*`%ULUd5+ImK1&U2% zVo|~5Bb=qga}+{$-Gv-|dhsKF`=WNs$^-Tvs>o!*0afq5`P`o$_~`Hd z{PMoLUpVE&1N!tLrb(tuxo^c%D@Msd`@*ENgz@3 zuSY)Jf8TL~`iL5LM)jMoHoW-EHwWxHigIGw2KX@v18L?Xp|N}lr)zVyZ2cRguX#I#^qroS{}ztO!$ z^%HE$T-0uJ$OrE(&eUxgF{H9WC&LDGcyHRO*I!uHy@S%B9AXqkhJf?lC9YjCK_>jp zA_fpV_@F;F`7~%iA}u_8j4?b}6DjY+`|JxR3K;C=ReAer9KlQ9rQ$u1pQCSUfy_g2k0MjI8<*(j#-@X$kkVIVX z)qVP`ndFtJWE3AJ@W^TkSx_%Hik43~%+)}rnYj9Zd5 z4OpQWa8m-B5R36ZEWsyu6a<5qc>ye$vZ5%u4*I(U)&5G6c1Zb$u#xP{U=p5R)X#QY zi*uGtl0LonIQ%ECPkVLVGta+v_dWBsZYVo&9HH!$=cYdY@28Xal&xY|)=qv>fymZzaZZno|*4LtN3J2iv&J=`mP@|2*>gz3HPb2M2(p zP(9HI&CO{bd$K-z5m+a4|7(t%s4V`%m&f5hH?82PD-0g5zWJ9lA@O(x8zhbl+E<unxbN_4ZcBmTkHUu?~@#=s7lBPtsD};5Y3-%5%578jU{Fe2jfZ&G>4FADf zFOYkL$~tU+9&_VNO1qBAuHQGom$1-9pk(lo7TzB7RY=ou_=10;GfEl-fc z4&J>_chG)Z?zlwm*}ZJ<{YNmHBov}c+~?J4OQ}C!gt?Y2U=uDGy2}}V zxM9wR?;8#ElP)@!4C+gmTrq6Gu9b1Qs)J{lG$v0x=k#=$s_QDJj+)N&KWJYvZVb^V z>DTL5x7_^E^f!Kb-mhZAcO!}-E26p*6qszPhGbVu;1tLmIsWvCjs#W@C@rBhBk~`E zK7=ZfxP71S0ID-Vg&n5I6!h^djIfC!9{og+>lYmoVnVYthGN-+*F&K);42l=NNI%i zJL`C6Fzj&JLG+2csTm46qm+_Z=yY}Q@6 zlJh5z;82wqFGi?m>xy#FzP*4{IOD2}_3A**JaHtGh{_=TBFh|ZiYS0Yi%A@)TZH4b z5s(_?^uo75IA*hP45G=ZDyud`_k9AxM#C2}&meRS3s+2`aBpL_WYOg8&~-yF)nNX^ zNz}ssE}Z|eL=udgbo_7-Dz>QND3N{l8ezI{*D9f@l-0Md+P!yINfyywP7a%}r$uc6 zItTx+ZGlVsCqEgHu}D&-CMT5|IsF6(V(A`X<0D_92k0wAiztp&w_BI9w<3(pt({{JCL4iZ`eU|L**H8*U)?gM(leYy^?0_m!{ zYyVw$>0?1EBS13uJmx4jsi#~=Qx&3WyPb3#T+Xp*EKYj%-v7c22|(U-Bp2Ww+=x#` z?Gqb|tiz%<15{EJlw$Zt<62WRLZL2&C@SS^NO-f7BvIhlhk}?P<_tfBzj2EEWW?xe zV?8Q?0}ud32(^fZuS$mFLE#9(R6Pb>AY#*DLr3qqCoCgK+J`mPfAmO^$^-Kj@7s4^ zpT6)a%8Fq+diS2Y^zP$%HZ4mKgLgjQzyrr0C}5nBDHNzXv;VlD00961Nkl5rc_nM{tjDEEqM7di0tc@F0UBMXTt_I>uxCcRx658oIf%O zDZY=axsD;22{fXM(6dH}We`mx7UfKmEKx5&AqufmvI3iPQm+7xAd^(ZVDB;v64wP7 zS&|7OM%vNh$mkAU&?a|{zK-yFgap25f(tnYTQ~(CGmS_b>dlF1$v#dY@_qA0=uTMJ z56r!malz)b@SI|gP-w_=TOAf@!|;~r7Ta@ zBqFm++E$n%DJ1QrEwi2}on}S~lReeC4ps1;IofcC)i7-vKO-ql(1nrgV*tQ(g z4983WnnB1#BB>Aul5B2K>d6XFP(y$51Vo;a6@*gmxROR?@bSakM+9=Rn@7asL*yeC z1@MI;^C(nQ#8JZ!1%gRFLAT4rQVI(1`ifED5Y6F@@P9!=n?{rb3yldZa8J07FryPv zY9tvFJ3Cea+k<6+_ui#MmFzg&&iMnGy9rWq5UTC6h@jiMZohI=opT`9jqR zWwz~5O)zH$%fQ!-AYRt|qRE-msK_sk@@ns#;s18DMdj@#`?s?uQ7F3K3i03ScGuB> z#=mNMRjYd#f0{o2G(G3wlu*+DWzYG)hnWD55-|d7S2wwOV-{cV=NWgFg%8Cm*-1A6 zYA>?2w6~xEPzmbqIZznBX=tX%3wH1E4TO+OrzSn>HYyIt{z0jXT(=Ma1R@5zE>N)-|{g3~@D)FYWo#NkkPRZ&zBUet@l6ylPM;b}1`R#t9i>XNZ!+HHt4oe>4{ zRy1(of>BBp;CuF&Wg}X6RfI)Up=_%#3uj>Qhl~7%yDgY?jMeEyT12*lK=!$c>*&dH zN0hwggsWO2%`J_|+~JT!NhMWM4bw?TI?)L?995S^NgGHG<(?PxVx~90U zrzYYCq@$>w3i!b+tEek$vXgW3;Fc-~jw-QvMG;^>6w|jY3X1PBi@f1xb&6&us7McO zYXMIdG($w9y~R z;wz>3TN1$Cqe~tHyE-1sDf3F1=ek^yAxT#SI0JWyr(mkIFtjL@g)I#A&OAzFUC;@- z0Bh4?3Rs=gRy8O@AW1Ox`Nsp^1@=R^1uA~UT63{C-77h9Ak zARd>oAiUs}@Zng(5h~EtV6HbBY?v|~NS*Nd6#V?67#@mwKUpxu_#)O-lZY@RJT!Vk z0Kf$|n&T%AmQ#?vjQP!?AUXtpgZ{!Jg(!%aLP~f7Zp(rgi>mr*)xt6b5E+3u2!Yf; z0}zW+3?!ex+WTyf9#KH4(i~!Gn&i5M=IN9x02u?gpA69q;9J1BHKgh1w;W@=>1&%! zR=`4nP1aI&Zn1D)lcm%|-4sU=JLx}23?)d6sf01d^+13D%tYivhA6v)XNVD|5GHd^ z^)wg$_TZ^lt|~LC>Zm(NDm-7m))l&Rz)02@^=yv;cGD*l3U(4#oFsl1YXS7#(B$}` z8{enl`-ig3{HULxX5(vVdBLabLHv5K+%N+)dwzKT;T#AP#j*if2=2N-wImk2tf`I* zrJa5DW-6(;Byfe zVUYnNj{DP(t}t-jP{>|vQfo1!yW~J(BR6ntIn*zB%UD4Gf9CA%g*2pAu-CU&E0Sf_ z?3vrp`rjGcCPgrM`C_}(mxwekLR(nw)x&B>p>Bi4*V2|l2)N^$7u2MOFrioQSqN5~ zkV+KVvcr5+uvGwKP{=}}o-A^+g_o8BT?zPz~jhThFIc8 zzE#*486%np<_d>oEJWbro-Gm%{B2b1QAnJQsna1+0}-r@T7->v7q+FE6jf*{`*Gyi zpMDfSltz)as(rj$&UG>B5MEdiK}MY;Ybi<~-969pHU>N5m_q&XaVl)obmXJ6Ut(tb?;xG zwXG1YA~^0N2Sc;bcuvSN39ys+GK$%1WNSvis^3W{QU{J9QNCA{rV{Z8ac81{HQc`h z3KsBEqN__FVVevBJH_`b%rpp>5fs`0I%GSGgz~lx8my4Ns)!$}5c6REnevZV_7`cT zqyLKBNcf{@E`cnlw$OEo44_!*ZGq&A96h%cTVYPic8;cP_ygZY2|o2ZDbP74zN29E znP($bCkgMxO>`sJBA`qRB*MXEWhBxSA%I3+K_OBcA-sXAEi1>-M+iH1$n51pdMjW* zi~1!FY$Sdi9N?jcA|xRC9N4{q0Z!~@NX?oR^tglqAG$vsNIQ#nojY$J@4}^%aVGtk zYojC+&L}Zs2*Sc@F8-4h4>!vtQ0Qsq%w$;9Ww~#|47dW$6?&SIpa~9ZA1-1p!2SnR z8R%PR_n`|$_%3i)N7fvKeL{Lf7RwM2I>H^}X(Ed&xJD5X9##TGCP?(+iELTGWqw6h zA;!VrYjl}F-oMM31Ihl1C@0~cDkcnUL~%P|LVlkZ0Ovy-am3mZ%aJn?*EjL8F~T5? z6H{Lbj25&dm`{8#fmYJwd5NO}0E_DeCs<4-Q1rEI+xBTKwE5_ zf<^~W`#0JWvoK1%-YHs%sD*99`M)#FLZoaCx26VqyA3#>ELyxsA{D%VgJ@@{^_QR$R+U)%fZN zhF=VV_i;d@lnR;4r067+-c|dyun>tHWRVdz0$>Z9b~~)(PfO>n@cG_0 zy>uZRu=v)~_;|7a!2jP$Yz3IkTr9f)N`P>v;<=unM;F@x+6@wvJriv1V)uq1z6;4_ zjbJDN%M-^%IuYzR0GC!ojKG14<)V}rn6QFnKy1(1*Tj#N!pHs|h)_C_Jn!2#S-T%L zz$gr$Khln;HYg!l3sELsC^tOu$dGf`_^; z6P|8Q24mJIBLvAV3E7_&bz;l*fDl4tXh1<)?E*-UAFubY!V2ugP)`O!km#dCAp=Mw@bD{Hkt1Mi7|4a42ykqoa+*!38XgV! zHUq0h2z$?yveZ1F9SEB+0C!3v&g%n}8cAF7bnt@lxgu*4!C(G97=2o}oK>)?g|*TU zpegG^1rtsH7SPs$5Ut==l>G81;y7@*VjVnT!$RU73LBvThG- zDkp+BLaPAZua6}3ITCYjp~#;tlI}$(5_A?s{58r0L74~MX~4B%+#>kQSH47rC8X&W ze~R39d~6~=B>+NzrwH9d62aU)tP15TS2$x?VtA!vE?LBhG`YD za{w%Y=#>Bm4%D~GM(iOq2Z*Tfg{0I49R_E^Czi@8oY`lELhR@+QbEK~_g%H~M!@Ws)g zjHfjXeK!ukwtW_tFgL^9$JU867+6;Va||g005MW*s>m5pZqvFNVePJl)#LYA>1ob#Rh=|`GnLFUAZLGEzJzEMK29pGb)ltihZZfuAboM{)&J zQCwOmIihl)?^#7t&L5fZ(F+q75P=T3Pe6i8gu|+al8^pPG3o-3y|C`HWD79CSgMh6 z-)rK+V-i|JV_cv)sh>R_p&~}YYy6NsE7+B&41{TNSvfB(RAkDNJeL9AQuYxFxr4&c z*PfbWWrB(*L7XV-x6fuM2<8Caor0uL_$$CEd?f_tL>R|e5g9b`1Ol!bUzHU$bQVO? z7%XoLA2cqK+*{5!va z#)d@r4JJ?mNRJ2@G1mc1C}c8VAizh#q7oJ2Kok&E|6T#~pCaz2LeZM-F~i_YYuTbN?!66$ zoOPEM+>3Fq347=ArppzD+=Q<=gopCMZ2o?Wxd8aPaJx2@oBoc6yzat`!3_)VU0fbM zbI~emtTZHO*)b~a1Le?3o|OezQyQK7S%UUbjo181ZT#tDo=}g$YCy2N3PTg^q1;Gz zel37tO0!E54>s0<$=*x0@LX=oPDB8SXnIy-JRFb~P@^GKo<)!>ivM5?0E|HtlA$pd znWDf9%{%V@%W07Z^IKTF1#2u@Nl?)6xWdQ{CI^#0eB&Jx-GY{nV1OADHJq{{3X>$V zn2O+wLOIM<2FFi>nuCpSw_pvldySjy+mrX)!%_eImOX(Zk3iFJXGe?tLJ%PG@t)%C z5QVQ{ezO%H#M_jeapA&i><9e@e$j&55&BsKt#35%rgjoRR>+n#rzGF-<%bkcO-8NY;XpRTB$vJQVp|}dv@EpZRY>ki)>`~zOx4Gdup2~pKok<#s z6ZAyG<{`*avk^$~b>B2pg~!chmYrMS6Pk0Gfe}F3FXE|s4EKKNg($e z43r4(wSeyiNufU80mlwx3_f>Z;Pw%ax2&ue&&|(OMJR^A5a3rty-vKeKcB+f4;0UA zl^Zc-sCpudq7|lTuF~eyQi7Ho^o9sr%d+rds1aq~N(@hdMgy}1OUbcp@Sv!`WFThF z*L!@BEq+gwEddmcc+vopMG{1;+exSfqzeMK8{&a#bbg^P1TV;eFd)-Qj?R*Zq@g7=nUzfjJ7Q7zlGe8%&ZQD69ry|#l^o!IY zdAXME*~&k&V9-QNLcUZhzI_K@X=KGKu+EW1uJ{P^`!Bsknri`RW)bX8i7lKW6`m-R z5aC%0&vQ5uoIg?dbK6p%<+f}k)qoC-(@zNroVfUI2Crnke>C!W*|r@_PC%B2>!*|~ zwQyHOM;md!NKhzAa3TDq#6s9!2-wIu*LB^pET9%uRl_jQ2cT(MX~Wntt&QW^wuyh+ zp=3h-<`^7_+yS>k1U)D9KfJM`*9hE-j)Nl!JWi!|-t0$(V2`EsQ%vD)(Q3=zRRiEu zpt2Ovfx^aa{`1ARJ=mQ?eadZFhxw}|=c6_*p3?P_BG+vqc(UuNkj0;SM?(DJ`QbEy z!c1Z96KpPY7hx^1$o!NzZ>6u#e8HbD>hNt1|5AEuFZHO9l2Q1ymcGBy>T-^j)2yRA zAD6|$jQ-Ot^}LJ^uG?TS~SIvj4M_Z(MW=(MapTtr(hGGJOlwtVpQLJuPU zgeb3T$bfW2Sr9breZT3HG55vkCCpR@SjbAN{z<@Z%vLm-ni8@RSR00{ZwdKhUtm& zVM9e44LNwA)LA^9PN(bY>P|W3l=II&fB*gW&tx*X-dJvJCy{9F+uAeszf(m-N--%) z?4flJz8Nzm`=7#7)}o8jmVdp4Uupa54|UM$z9Pj^zK+qF`G~$=oBN4AzpaxQR@rDv zUQhIz1bcMgr_I^5L-X8vYqgYCdR}`=E&H7Q3yJ?i-_6lif^P+y!S)gV+s?kqjyzZ1 zV~g|$M0S$n>Sq6TleL6>q1 z?d2}oMlHvp4V?cu;s-yv2U!zD3va=Z{X|+$enFRQGyeS0SaI1_O1LEdEy#b4#c5FU z4c|yTAaV{%8ExCwRVyZ^=)^X@gHf*g-2TJ6UycPIT%u{hZOm~6{!LcsoUnga!rnxGQ8lrg|G;%a{}jEMNDwXkc=ta z*36*+2o6R%;q%Se(${T?t)*6Sq32ade2N6ZW*nT652yfA#Q!BUY@aEpl)*R4U?2HO zKt@!G_z!xcO&=~|25W0;H*DA-Nm5;19cC5b`$Bf6){f(86#qqx(GnL&q*a>#QStYr zi1rt0C=}{VQ53|lhEc5fY30o#0nslS0L_AG&@B~**h6>*u(hE>x8qw^q^oAlubh@cSQea||FVP?g>bil9#G&C z+hjcy*Tz~g)%lqMEw#=gB}Z}XycBb`#1;v>C_dl}^DTa$Z>c)k(o7?IfuIf|W^CBSBk*|zO@o?#fKX;xQPmz9+nh9S!` zrf5Zuq1i`Y+?(6#R>-W%zi26Y;0`PKv#pzL1Ib0YGb zcC^BaTr2OR_SxI*3859jD?3= zyI0&OUOK&vAZhvVnWw3q4XT;PM$V7n3 zFHtSb`q#AQW`eS%w^H<^&{ekW8{2ZVtYryfVdrgl(!!XJeD7EID>>R~_%!h|*K%#P zfJG}D7?HLY*_JI|UN)-{Mz&IHySBjA@|J7k@s`313XDJZ5{4K_flLPZ%hA$W+0!8q;;~thz3w#7`Z!Mt1lYa zLnMN@S!ZYdrsN7SFG*6jZruXVU#mCg9p(KhGK?J~{)@*dw(0E!^*oRfA|U)~(L;^I zskOaQ-*#IQk;yhiPHw5STD;{*BJs1`YnE)+)?PuG;{E4GnhHhu!bghO@hycuwEB(; zdCRw_n%iD2-^t(hyD0K9M%DstwWlF+<87(&y<(32!4rn8P?vj$oD=Igv>2IgvvK{_ zO<;*%6kTI#yv$5I6w)J8=-~Hadx7J(ho_Frd z>^-fyz%Qd>AL7&IKWt~Txq5XRU9*&Pd7wkWN%fj1aaZQVzbKXgJ9 zFZ1{HmySJBHQY^g7uzO3y0*0fm>jG=wz0k$^QJiiQ)Hu6yTl}wwbRYb%qF)z?FS*3 zC}O(Z#NR%}eC*VHkT2!SmLwW$sbKfBuSh?y8mxkJ`y%XI`LKtFah@Glbvo^$VBRBj z{WCe~n!~=v5NW9K!ab3l({Vt|b)Bd?6>qz5z{#ePJ*}v|^O*@WkBm2263P)fK7g)S zP4|T2=eoRSurtuf-vQ%Bs?8q{_1)eK} zoy|`yni*bushUw#RgQuRr&*9F?^kKBp(ry9lh1uHSR(3HG?(pk)??ziny<;wWKooZ z{%bg6w#g(rRZUZ81s?j{ zrhAyw%Pugxj`7#l$M=~MKD*?nG?rDD*PKw;>5nJ0^Z!}D?G`f})za22quba0Fo@)Y z3WGV|ZG@qyd()L8LEFM{cvut&KtJ1Q7`v!|AHr7~RhWGRRH^`3Pll3kb=gD$4}$rJtRH(LH&jYUDBRDDTLX|k};zL^CwSBO)DQ$VeziZ&)b`{Nr7}$a*$Kw2HvB-k^#?Sna)FEj^T2ooQd`4{h;F1I*Q6ua{^nKZI2e3kkr)0A}dZHLY+yns|s47`kz)U zWm4&RS6kA~HLV`rij^b&uQe1@*SQVL{WvVe@xAP-C527U+I*GEe#QTRSO4PTPbYHU ztYEaXYT{9l+N^%>fD~=05tdOXSyduEr`eJl(0LGxzBAoVdcn-@vS{cm7NYV zX}5&=da&cblUZCF@m&Rq8)8yTNXbLmRxaF^$0JE=p?7ZFQ?f*V**HX;L!LX!Vw^EU zLn@jt`MfoHo1QgBfad|ej)uW}IZw<}%o%$}g=}_@S1?!c3m2E;L|yq4pf@)4x1L!U zqi0&OCErVVL}BKnIy#^wku(3aK$Dtss*B~j#R-p3{4vUgQq|_sf`b#7r+r&Q;5M}@ z9}U6Z2A3BnS64Ip6>hF$>V+t^hgKvS*V7~z*8pPmW)QB#5j3EEsXI3dlI?<87Tn4(&|TwdrBZiy#~CbB|0 zyD!oPL#x?VeAaEzjoyvsKsJ^!Z&)wFll4Y^=czzc1{dPf=v|~KP%?lYh6FLtvlt5l z_Mvj(mkP)Xn8Nwa4K`gXWx2G)`C}gwoSBq4eD-G-@9Pj3_$J|f;luPoV5_*II^qKC zuyU{O4c8ltG6<_#`%pbC@ssv5;b%tcR~r+PIqBFa8Wj~R?&c+v3rhC^SWO9-l0`c; z!_Li%4NjMbGn*R=cf3SjsroE0m(8<>JbzTi2id99Dm_culgE;La~7krexey<((R}I<3hh}L4ms(fZ7QyQd zywz{PQYSfs3oLaRzSj00Mq2*4m+$y6uY38kS?=Q_KRf-CmrU^b_5)shx4|v06X_2^ zRKm_;cyCwz(QtpF0@+@4aoc?Y&M4L!RLJXDl_i?xf8ThT&E9+*I%A+_5J zoPzQ2=A4|CIap=~yv!Jg*_CMd>-nu9q%SL>%gr+Baw%i{KVjn80z1|6)@ z)?v`dNCW3~+RPdM4jcF+RZeWc&=vLtPaLxJ0U@?GiEp(}B#j0HM)Rv1cRLPwl^(uw zu55B$c^p*CxyNXPt>@v8thZ$Q1}m38I7cjs_#cCg-|+&>e6QM^P7GC(`}}UYuKuF@ zNvXMOk+LJDcgD?D}gbN#=<}T7jJ8S)M z)6vZ0ZuQq~>xu0!;OZghn7eN`;P1O_=^WlpTw-jPP)$b!1y9Jy?=~VKiXTPVo44r% z?w&4P+iFEi8DTBj%<`Uo)*qYZA<`Ro%yPJ4v#4MZBjt~b4V}DsWDRA+(jXH?HfN5> zs~ky#ub7{`o2Kk_6Kp;z;>4xvB6kDhY-JMp23!VMAWU+ut~`Gms=2WKvdX3%cNRD8 zBu_LWNTw$WXPCLwb{}10T-$T+7af^PTZippt_N44h{~onNgm0NjamG4(cHjlxQ{gc z_@Z!m{>g_k`QBmT=%aJEvD3du7IAs(r$;(&*H2wq90y9Sm-E+pw!dP8O8k-}EfIeu zAnUYkhg57@Rx%9l!+Nidj^w~__7w3eI!pI~ORso$^14oBO1#C|XW4_abjfJTs5XcF zro;)>-&%d!p0z#w%u=;rHajpV_S{j)B5U@oCruHwP&N*He>Szp#!1e_nQ#y&&6hAE-3Zd9Hkzg0wP4U-^FTbFN8~BvgF-VZV=q*T z;-Ep)at^sBn-%2aXCK(-aPTs4-C3hKeL^t)Mn?f>n3W$#)xu&D zV^s<~M-eM!2g?*3X(z+mumaQ~Reo0b+6!hh9mE@u*J7vJ;<uZtNo%Eo-MOYOYZ(BtwHTqs*48W>4r8&fZ#4!f1TK^A?@)W+{S8%0IX zxb4&VPOjHgH?Ts4R4cXo(am|!N_XIhYHPYI(HiRswL0wC{uXuTylVs+Ph-`IU^`Av zC10{;Gk<~%5np>`7+Xc#mr_dAulV;@3#L-S`D+11gBPB9V)*6l*H5h zKfXAFq%IE$3eNtgkRFUwHY(;sWuY897YQ_@s}bD9uq1kEM7As1*(ITEbv8u7P*-q?9bD9wqZ6yww{66#eMk_Dr>y+_ln{VmlOSx1Jl+Dvu3}xjGW1J zkn#J&0X^5nOAZzC^1RFEP9pN7Q8eZc*?v*~yB0}j9MjvmJDo@7NaEUyz*GI6Y26z2 zr*E!;!^Ky zxF_jMh+~9a=$BJk#!}FI9>KMpo$Cs#6x1X0N1tl%bW=fw5bF5JqXYlo`xI38L4u~T z(VBrTNDh$49NbSrlv7Hj=$j**s&6 z-puof;zAJO0zZ52C>`s^N1+g}(#cNMzx)k*{Mo&Px8Gx7Z)MVo*KxxiO^)7s+%^9jdOahEq{w~obxnjC{_bST&^x|t<2#|R-g zPJ#%5<_|8ebgfa$__7}APvY?-cw5XkdifYtEzxJ2mV)k1ULuNU9K7hNqCs*}u8up$ zkbfKN2;1x67+Nvyew2kQ^y5f}ZQ-+6qz5S5d+b2Ao}$+t*ALw@nikfU_q)moPZqKo zNw^&FwfYsD$~JDtug(`~*7U7}*4Fz2GPf~=^Hnq#_?vM&Y;%N{9NYktuiob=UZ5S+ z_Q=P!zdoP4kHq6zZVWrD$3MS6Zf2l)t(UEOgdFb$J3~uN?j6|FXys0{DhW~fes8*v z=N?b5i@GCCdJ5SVk#d7Dni*CZ73vdwB5Rf@5BRIICqP^G$`{jmWSoSLOqtR#u9^6V zaT)mq#-v{<_oW@FQRi`KgCqvYch>niTOxE^y??(BLqLs(rC!TZ`8bhE(Q0#<_>oZk zt5v5~8}@p5=?tPiT0hPgkj5W$k!#(nf8p@(e_8^Jj>ssuj`x;|Q%tKP6w3ncm!B>p zW7JBfy4TDnzhstL;ls|K7n2*f+KFt_1svn@8AU*u3J~MFz`UJNNKml$?V-^($n!_KIac?7Ivph3D1!PO7TpatMGkVi2Aw-shQv6 zSp;7YksZ~~t1CgA+>;5x>-%7Wxd^Exyuf%}`at<8o7N|wHi&zc=IxD2bXxmZ6hw|# zP@{`>tdySz3V->eJ)6c@SYf&N;ANO7H4T34EeOzGtb}g`H$5L4qx{uuUX08C5*yx* z2;UdOij3i^^PoI)H2(E={b`Lo90tVk9TjW6!wAa`H=+EF^gGoxx{ieEB!V_M!Am)d zh#5@1OtVONNJ~_Z$Q98Z!dDNTUMglp?c(rIiH?iL4y0^EtqA>-vI;szi9Bpb^BJ-}(GcDusMl^J)weo=&;eBkJUz#tYOvrWpc{U_JDd>k{&Ywd@OqU*CDq*A3>haY( za6)XJ(P>J3zQagG`9=U!K&|(keCSgniV|=9EZ4uR5rQ0HKPD%{$fZTfs-<Pk?7&RanYY;{$GMS2ykB4!D_3T*kc(fp3Y@!gwfs!SQIYVnu+#MBK^W)NSZC(F zelXpjLz?Kmag9tn!S8uT_IT2c=rS2w|F|csJ;v{Os85m_96<1^d^zOVqx033u^w`s zhG?4>5T_Lpd70EDZUh;AyepCR$Yp=?jS&f7W$9si=of?NZN<0zh}aRfUj!|S`g&g$ zXb0XNSbO)|HjO(wS90s)U~%AAj?D3TFUH`_a>aWV9cd{)+87eOu^;Wp*p*_JDx2mi z+YY-L&dd=*YCYYcE9I(YHH?#xPZZ724Feb#;c5h}?CpFg`_(}Ay3H}OLT)fgInV4Er=w~caRLS`=8WO$3iONRAB>~b|evh9B9_W z>%j*rz`Q*?>ov@q+aT=a*{!CV*C8ihybSdvrqSVL-p}dFBqfG!%2mSPKwg7S_^@xj zqch+^ml*q&SZ}i)owyvAa1@fPMWRq+Onin2@Bg&VwBd$a_Zc?h9zAn;WIn7ClWE^y zY|P{5nYXUs`M4i!+V3Mp=2e=Deee!dr#P6eWWEfYLxdua!yvs)sn_i+RbOrMkhNNf z|6|d`@oc8zOSc0q@=!o~!Hi~Kngr_~7-wWSM~O+P2k{^U$Tx>fwc#ld(Sg#C$37#W zB8RXb%w6G`&_Ah=KTv5_b%hZA4D?g%c%de3ehnb)Osh{VVKQ*WF&Vl{-nO&|ImtVF6*GI((Ao)(pMl)O>X7CEd2Mv8;d zWPgHT-l*u z`spd36A3F|4WXyRHK`;-NI1~h43<0i^3e4sVQtn+m^az=EN*M{{nk;~=4WE$(7S>< zDOn*hE(218AaNMH#AWRnkGd03Np+<&$i9~>=Z3gKPlpwiz%U_xwA---f2_%&lVCk2 z^KPtmP)hE?!mec5n|V6cnGh_((cLOz6WnskVri^ZZCv|#T(I}k(>^e^E9r6E2=iC? z^%}IN5^gZKEw9~$sZ#0js7;GWvFR{?H&dD!!+o)HQggTuGpnkf5)+?^h(+4LTrBvZAbQz<{Eo0hNGENvf`)_v& z4le3b;FQ?np*>;vAW+aI5tuzurSkg?$8@kVJ`exKm4w&tGS)W@GUkUpApYqQ6U6@W zM;HVrMxa!OxDcqzM4pPoRe{ZAkShiWyQt@0-(wLgLQ1d2!)pa=QT)(RKW0Q8QIGoP zEX4+rePUPy9VAELi@ zbbEt?_Kiw;*2Da|4A(XLAxf?IF(6)W!eO0l%VK9eBsOG_dfP!AYL6@=8`a>E2wOKp zq14J2_E$%}BZ-_52lDOJUlCimmGyRMIM`uqDIRS(`#w^Sn^K{w^9LD@D=g!flGE(S zvs^aT)*M;#h02cAemLv#l=ruJSgjr%Gzu|@?R=QOb6;uF19|HCWe6gGE;o(Fn;+_gb7$b=a(>VYH7 z55}X@JgKip`X@AjFoxU~D0S4h=jm5=$BNoLaY{!p&fJB$L8n%6#^xO+E=WNX9Ur$T zDoJx2tm^)!Gt5t%LNb*o483(qqXs3#Wo}}&H&5V8Z4GHe64I#`Hb|_sdLTpK+Apc@ zJFEQZ+Bfs%?z}A$;wcHYz7XN$F5X|;kt8uoSo8~Ox7`%`7C2Qw%;Rmobw1hyCMnTrAGgJhz$^2+{|FR zZtG_v5zQ>RZPwiqS%n)|yIn8;yD!8E=a=Zi)_%&ML&p? z2N#0E0;Lrc!-v?BGyf7ql;z&N*kd2M9EoR*^A_YulT;Vy8HmVj9B?Us-wT-R?XiO# zKU2FvIGoVkUw(fHW<5I665I{nR_}v57y~oun_5mDpM$4Hm!>!?SM*#oR4bd(6zrWEnSso8%RWZ~Ct?oO^;+Su3Eqf8|TQca1 zNO08&;NKOn7L53B1?XH#yd{JRiV;aUH zV!E;L6pPiy__d#MOghNd(5il5dpjFSs-*h}T;M?Q#zk~!JT>o2q{u5PWg^O$SSD+L zxr!YT5Xcq&q!c)Hu3`m~w>9>}Oz1P?(%W>Hbk`r%(kXV2P#8whqn+D0DyU0hj$KA5 zE)~0dXg~;c0Hcqb2+#LFV=K*kxI)Mo)5#Yu5{Zzp%_wvBvqc?!~dRq7mhy!*i(_$ItBq_*_dz?+sr{1WD5GP|1I>JKM?&`1d z3N6}sLibvX~6mnBfULb$AHEntG5YdgfL*P}9Y@LZ5D%Jw%%5pKVI+Escnm?LvE#Np!+R8mhaqW;OGJ zS3*_Zu^dkUDnMVn*(y75wU4T?r;Yt58PC@YtM94);fdjjQQqh;+N%7nrUzn?#WRQY zmdR((;k5c<{+jsyFl%TLk!eoNeZMIyj0*HS-B1~$e{W=5L$I;X!2~i!ME=~8O*x*c zP@m3e=yP=D;g8#Q6Gm=vV({7VR(RN4G*fX?Xx#vSu%5Y}{c9|Ko#ixT>FbT(XWvhH z&ya>XSvA1aUEB3WoUGHW3yw;Qsz;-R=mhJ%0rS<|mCiy|;$D7fe4pf@TqBP>=R4|r zf$eLZ&wX8LK7N!C1s52fAclApDv)3(YSChFZG&ozKc@)R(NZg{5__FX!y58C=L{A@ zV*j`qa45*sUMRh|j-*Ru`U4Jst4i ztR_zF9HI^1!$X@1@g zv{>D@nd{Uuo5p%9n`f{zFG1QGB3c|b+a6m~?`@M~wj?o(;BR+Wf}Jo3ymU2_><>RX zkWbXJn0Pv55l#{ivtEvH+?rrR*y=)l4YoqsVz6{Z2*cv*<#Cm4wB=N?ghd)9rZW*k zi^QB^Awj||p-wc`F6v1WgUt%IMCur&AXz1ha825^!@BAC7nN>|ml*06%FGBt_fp(K z9ym@E$-{(?q-uL4AEGwe>CQGLORSVu;3B>EY8e^k8ZOtw676~! zBvVWM6P&j}*McN$0`2jF78O+XmbD8QcO-kMKwDfaO1TiD;(1c(=9qkFS=y`hwe+&# zpjwJSls&V^B5itzE6_@?PQ;kqDs>+fzr&P^CuIJw#6J2oXhR87VdeEtP7=lNy6ng? z3-Zt8(M2gxXvw0j6NEX!p4Ifx_788IT)^y{e%A~6#zBM2%TB-9Z%}i0ZIzJg!;NWm)P4T> ze#hEs0T(JAOwPs4J-@ivf1GB%Yz0A_L~Kk(S&3|Wamz#Bqy~Eyi=NG4c^F0_ms29J zG84^w2}!8}fnINGrY)TurYFq86r7#NO)|LKVNM~LWF|9p4deJr%k;A?k`s5-XF9sb z-w-a%qOM2H<(FOx4W4F3b6GUDZt`g`K`FZ&xV#3`#o^}Yt}(pSGKhVlJbfeS=1Bz@ zv50N73>BK}EGcSx+G25|IYtE1J1}bE-W#dZ%TsR7zQ@0{%ZqPIzQU6aMwEx}Xb3A` z!Q8M=E5^n@A;hGfA{Ege+;~Zy^5rg~iW)JtkYXdmotDNVGJA48v!055nHDE#utq3z2MEA`{ zm(iCUM*8WK=fw&Wc%Q6a}d`yk=1cyKC0Vv;V@R z#+`4e=py*X1ovc1l8?^N`j10LZRS#NTd5U! zpt?pHq6M-}x}=IacGTgMD?ii=bp{kBiVm)(MqjnEDstnNJ0-@=I-N?-U9>SiaABT5 zi4=^UFpLxi`5R9vUSuku8T9Fh6hv+siZI7z;*T_dIPAoZyHy(a3S(OZ=8tje^F;wl z2~Vj*VEjEjhhsi649Vu-fMW299N6}fp5Nx(m-cUezq6NyN&2!6OthjdlnW2voha(GW3McEv`-OdRi@QVvUo`ZTH>6CkcFAjQK4 zUG?F3FwPBI->GGSCj+auBqdb>QKE-U154;MU%oG<<^tNXTI}F)S#mpkVoE~$8 zv_M2LD*GZ@s)OI}A6`y3N9k}$IpA5-@iakk7zvesD@3s+xAF+6@M8ucmRFkrZBbcZ zg7)~nXBV$Zsg*^8&XUz1JyeR+e-=Ro%uXkJRT7jG@Ou2Yiqt0QVQ%Yz<*9!Ql+qD? zZDDta?aGadOd z&cNN?R#|MxOtOAPP5*M1S2T@gK3%bC33Y z+EN%>a|r20BYlHACR_&1xREH5ueo-~2aM=Lt>%tKR>ao{+Wpu)e?M7P`~ID28fR1Ytm0dja%U6DZ#%z2b|olBjMpfSz1F?j=qfmobqQQA9r;tDlexl#StCM%AuAc0U>R~pBX#XU%|0hoSz|gQ3dz$v9FW#Y_YC7x_9LwALqsP}U%AgLHFpCafsvp;l%3od6uH!fiS>Dz6L{=~^5eIIMAoh473!IY@K zqTkfpc1A3gDEY&KQO|_$kh)#H?6tvogAqPQR_0DA7IKQAGY`rm^PK@g&; z9#@{UDWet*#KXTT5Y@x%R_nC?vLhEH7T^r=t9h#^QDXv=k}!6)L1ZONf?sJhkV zWd<2ljx1vq>XfVdd&SIv)n?FEofTxfZ7v>6k#qwOrR{5IcPEk}6t2-+ zppg`TRnePJ1%kDNoN?4I&@%DYib zzBs)S)dM(t@MfU=hc$u~9Q-P12PP{M8;YGpYzwd4o(S^9B-Bg{b{lzk9|k@=f-6q% zQ?O{Yhz7+>Dy}HEK7w}Ni)?XaOeDuEh%BZAA=G~0ElHq z6iB`owo^r8Pes~9{OBC7flZ>Cemy@nn(E+?wj(*1kZ42w}4{4l4gK&&jf3p$HBI)8j6dn>ic{+-&TlX49vO zG|Z2ZdH8G^=MYWFUcpbvXq#fBsH>nyyC5}#)NTIR9=9*dd7%a3t{dYatOlCn9?@Pi z5E>_olv*kCrx9~nkLfo4F~Y)kE)Z*mBl5S&ph~35h5zk;?8@8kDuAXUK$X{4nk}ep znjK8v$7+J=GXM!wc*l$e-=yTk3!RhpXjy|5udFkwZ%fgByhmoQ9cf){l>k%wHzTUJD9NMnL8Vn6!#1!Jg) ze5JXhJTz&>HHPY*w^YKfy~n7l0AstEd78Jgk*zI?Wj30DGG_tPDunBwK?m4ASlibG zAD5m9Wi}bdn*?w6G&!6;n;I)R3I%9u+4>sEAQOZ`NyO}hDcaxL+C5q5YG=HvSSGV2 z(#jtrLaDB+)?dxH%Edj_k8|ynWIceOB7JuY=u{L*9F?jL?v=>X{*a}yBHyD-4+dlK zh_e1TQ$3pvqDzs{U(6EJ(a?CHHC~P7#^&#eOgUDPa95rH?n7jsp!qi|X9#PphcLRF; zH+^4U=ryvi_)|g$OCfu8*$*jR=aeJ+qqiP9%t22IGYk5kO0M^H_}pW4oX1>h+Md%c z99f}Rz>uFbMqp@`HA???4uT@hLbeKPc@3O4NaD=hUv=7T4YRaECHkf^)R?y6KB$WP zO-li7tX^Y;I0>CU(ffGwq~ZOKgE^LMU*m{X3dxA4rfr^ZU(KU4nRCg>S$Lv$2cl2MFQ|_m(YP;RzBbEpTj>$<(#; z`dH<|y?GodIc(}0>>&w1-*L?&R{r^zYFIbFS#p(CdnZ|O&T~1i_kr~=Qb+_>QgJbt z86Ve^vJG7lb0zmMsc7UIXcu9HG6%7fZ6sH*riT?nU|STOknf@MVa9ShqqQon{m?K$ zU+^qa#dMJA<8`NQn3VkJ?B(LyqM;ZPaLuXr){~A^h8|*!siZY(#xd$CSn8ZJ{S9mU zOX>CZJPrbzrG*AWzLVr?V=Siu?aX47dYbqVI$jgr-xxUEpud=WDcPSe8$?!j5P7Eg z2H2Zunr-zD!nEZJ)|(jaOKhRjz5xVc5{saV^XM0rXhzmgKwU-A>v226^s@<_`Kr4h zsmS}|?#JV>2r3Cb_G$m7&qZg3q5o5k%S4v{5W&Nm%Vc2$Y1`d`E^w0Uz1 z_Q&Ppz6^Z7Vp9VYNgr+aT)-Tkw_kMmT>7hZKc6tkP&R+-StNCvQyC_8!RB6<&G$a} zRhHvC=Dup@aW#l1a=&B>Ul@f&x8V-1s?@A{e!ZDw`?Hs<$0{YcW46jJj)mJFxSY}; zq_(29$G6(JY>P{e5D!*O5l|p2uQN!ZcAM$#$*s|@+X0ptHE04}sCqHG+JSOkvDzP- zZ)$3K^39uI$>Xw`c{;Mu#DkE^3GBfJszZ_)><&nHw z;1)l;YcT$iJnuS<-MIH##nrqx45{${?7JY5TPbK(hf~@NRdu_s=fbjPdXH+v;y-5wqX1H;^V!hv(p7= zdMtzV>N5zm7hAm3wf|%Oe0vN$*if+AqG7-Y{r3);=T=aLf%j zdLfV1klogXNnxM4aur*NiIPL!4%Zp#(5Zqi8xSyLx9+2)J`lqfZQCmQ=*4%#orRB^ z*p#sE2}J>m=AWANnY-!mEnXN6`$;6XLe$!kGaMxSbfrRI8selX>wZ0;DG^V4&N(kW zv>9WB{~Z!^5@Sj$uJe_MDp2lYn~o4nn9Echqt}y%(%u`QI43jKtgZerFg{sNNBhKA zwGcgziJL;Q9E)9Pb1}E@yI+Q)hQN(btPYxx-EoU;(kkl>_FEgJ_*_!mJEYQmgb^zK zT=4xmiCbw>@NJt!QD+kih7txt7&Tb%^Ddr@B4vyI{P@fY|g4H?b?+YOjg5-NYqIDV-!W{lHdB8XZ=7ey9jDwg&B{l}%C3<%Gy$+R+2A_gy(Dg0=* zoG!Ad{&R5f!{L2Y$v;^($x_jDD3)(GmUE$6-v=m;G4#C}q>_pk+)EViK_&kC42wbt zZR^CCrmEp~xz$^0bq`)-`|OgUdJ~B5a`^d53=f)0Crg>2{7zG7IOV26H zTnEpKYmXO-Mi< zzF1?pS!3Akv^(^vv+3=ARpf3?ZFeXRY6o@-L77OcRNl(kTDC+r-1r=@2qmFAY~qv% zQtudOYwm2A@;M23M<>rHQ(Jak9XH;5mz?oL^Yr0X|>8 zc(}Ve6Mx>aw6x6Rb)tm-FbXsxtMz@lUbSzxdwaeMgRC=xm=`8r0eVGokXgw5AGZN( z-v2K0LNMex7>(m{I6=Z=t4*77yjZhYZ$8eE_1C`rjCOoR;G)B25JyY!X)lGX`_;MI z|E-4{;`a@;jPL8!@ULT0rMSK2IfC#)dc+}odXWMvMWH*u1<`oHzZ;s@-E~DT=h*uF z0U=F(0Rh0Nze}n(^NE=Re;E#jK|1ez*!bW7!;N3h90J-;Dk}5*pO0hMx*qpZl=3*t z$7V|8ltkXR&}mNV$5|%@Z(_;e|KI9#U3;$~g6|zK=j{nVfd5sNXRV|8@&2Zg55hiO zEz1#l=r4qbR&1P)5-w6Q$yaPye9q8^`+I~}u3y$togxX32xumK^!|?0{|ut&C-&}w z;G;G@WKfE%v($xWD(_6|K%~`z4p1Ag&m3V#xUs~*WA4*sik8l~Va7bx+LEai&H?`w zy!El$i22p+3(L{er~5Iz;SY^hUuVi1XV7dtZw%^Z62if{gmqYWG4GQ4olM(^nr6TgcFZA1UVYtNr0_{J3RwLbF87Lf7XWVqLmp=!5M(^!9 zL3~~?J#WOrReFD}{~YE)rC1a|2*2H`ZN2DdHy-BsoUa1aW4E(PKzMgsZT=senrs%U z4ZGh@8z%jMh}ZjySUC51wd>G!TngB3E`T4oxw#GBCxtJ2pnkOzUJ+5l`lZ)--<`Nj z@O_`i;X}e@-r}`?y57G4UTOZv>-PhAxZn6|&CF-d$ux58Lx4*f>mUOjxw^8_wx6ym zMsS%6CFQu82E(w;^Dg$1gEaC4y+F_n2XlkYNncwdI{o?vL<~) z!_AG2u2~@R-~9VRm+Ly?_ptr|SV|WF)os-Q0Q71Zc$sr#bsU%eNFLoYjvxUbs_|-f z7%BL2Mve_Ie7Ab$|8W(7{gUR;QJb1h`^u_j4uyv1W=-_amQDYci-3=pStSu6vynv8 zcs`5MrMd`WXK9YCVha<<$giRUcG(BOt?BgK9GBykC~e6L@bsOlN5D_EY_iKJ-l`6#=RV9Q(9oX7tMXP>hsJbYx6*#8UufL%UB zK>_^wiZG3uAxT>F7V@&y6c4+0C0RYTm3W|}b#1uakb9j6VOP6H8^^w(&UARgZBZWx zW`l1Jo11HEme~Vu#_MfvE$i;9SW1G^OH10(B`O_PK$FNo?VX`-@DywJzZ|_8*~J`0Xv^m=3;v z@0E}Vp?3ETNd-1AHw`-C7y1icH%j}tXwwJX&d1`CKfn&HyQALQ%XAn zRoi-gbhHE*`2Ws%e}Avdys)+gAQ@iw&4d8ph+Tk<$BVpsib4Ti{;dfhpn1;^-5)ID zJ7Z}~|GzK5e6#fzs4KF zeP_N_h3h9l-i46wy}Jn_{%Bz&9&6KF|L(wx|I=I3;rPz?XE4YP1cPj?^DFrxZLgCX zB7i&;m(9SpALYv$u+q4=IDlI3|8VGH7$2C`M~`fVukIdgxj;PGB565mUVYys`xvokC*^{&^4+}lg&{e#ZvWpZ;MjiF>~oOy-T(b2e-q%}KyhquKbnXyz=6*?ZlEpo zlcSBBPeBn;RZvQ`eis}2>^1!t4k|gHrQk0H&Ac(C4&-$fS7EkoWzBd>Kz;~hR5vt)> z0#2hIh<^6-u9tH-QcqFz(116vVS>>85`eq#?VmHoN!*u=&(6-i=Xo78TFv(9n6)Ge z8(C_!j>FoKt`Sv)Mr)hUMI97H6gAFc1=9Y1#*i(mRjqVr#UN0~TE^k
nc#CF|; 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
w(V{|o@T*=Ax^jxpii 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 z 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 *K~Qcu@PibWN?Je;g|zA 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 RF{c0k002ovPDHLkV1g5ZV;ukh literal 0 HcmV?d00001 diff --git a/src/common/oneos-app-shell/OneOsAppShell.tsx b/src/common/oneos-app-shell/OneOsAppShell.tsx new file mode 100644 index 0000000..20bd998 --- /dev/null +++ b/src/common/oneos-app-shell/OneOsAppShell.tsx @@ -0,0 +1,413 @@ +import './oneos-app-shell.css'; + +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { + ChevronDown, + ChevronRight, + LayoutDashboard, + Menu, + Moon, + Sun, + PanelLeftClose, + RefreshCw, + Search, + Maximize, + X, + Sparkles, + type LucideIcon, + FolderOpen, + FileText, + Truck, + Shield, + Wallet, + Fuel, + BatteryCharging, + Users, + Workflow, + Settings, + ClipboardList, + BarChart3, + Wrench, +} from 'lucide-react'; +import navMenuJson from '../../prototypes/oneos-prototype-nav/nav-menu.json'; +import { + buildShellMenuFromNav, + collectOpenKeys, + findMenuPath, + hrefPathname, + type ShellMenuItem, +} from './nav-from-prototypes'; +import { + readStoredOneOsTheme, + setOneOsTheme, + type OneOsTheme, +} from './theme'; +import { ShellNoticeCenter } from './ShellNoticeCenter'; +import { + isNoticesSync, + postNoticeAction, + type ShellNoticeItem, +} from './notice-bridge'; + +const AVATAR_URL = 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp'; + +/** 演示壳自身,不在内容区嵌套打开 */ +export const PROTOTYPE_DEMO_HREF = '/prototypes/oneos-prototype-demo'; + +function iconForLabel(label: string): LucideIcon { + if (/原型演示|工作台/.test(label)) return LayoutDashboard; + if (/审批/.test(label)) return ClipboardList; + if (/车辆|运维|资产/.test(label)) return Truck; + if (/安全/.test(label)) return Shield; + if (/财务|应收|收款|应结/.test(label)) return Wallet; + if (/加氢|氢/.test(label)) return Fuel; + if (/充电/.test(label)) return BatteryCharging; + if (/用户|客户|机构|角色|部门/.test(label)) return Users; + if (/工作流|流程/.test(label)) return Workflow; + if (/系统|菜单|字典|参数|日志/.test(label)) return Settings; + if (/BI|统计|分析|台账|明细|盈亏|回款/.test(label)) return BarChart3; + if (/合同|业务|保险|供应商/.test(label)) return FileText; + if (/任务|工单|调度/.test(label)) return Wrench; + if (/配置|模板/.test(label)) return FolderOpen; + return FolderOpen; +} + +export type ShellTab = { + href: string; + title: string; +}; + +export type OneOsAppShellProps = { + children: React.ReactNode; + activeHref?: string; + pageTitle?: string; + breadcrumb?: string[]; + /** 自定义导航:用于演示壳在内容区切换;不传则整页跳转 */ + onNavigate?: (item: ShellMenuItem) => void; + /** 多页签(演示壳) */ + tabs?: ShellTab[]; + onTabSelect?: (href: string) => void; + onTabClose?: (href: string) => void; + onRefresh?: () => void; + brandHref?: string; + /** 原型演示:顶栏「版本更新」图标,打开更新日志 */ + onOpenReleaseNotes?: () => void; + /** 是否有未读版本更新(显示角标) */ + releaseNotesUnread?: boolean; +}; + +export function OneOsAppShell({ + children, + activeHref = PROTOTYPE_DEMO_HREF, + pageTitle = '原型演示', + breadcrumb, + onNavigate: onNavigateProp, + tabs, + onTabSelect, + onTabClose, + onRefresh, + brandHref = PROTOTYPE_DEMO_HREF, + onOpenReleaseNotes, + releaseNotesUnread = false, +}: OneOsAppShellProps) { + const menu = useMemo( + () => buildShellMenuFromNav(navMenuJson as Parameters[0]), + [], + ); + const path = useMemo(() => findMenuPath(menu, activeHref) || [], [menu, activeHref]); + const [collapsed, setCollapsed] = useState(false); + const [openKeys, setOpenKeys] = useState(() => collectOpenKeys(path)); + const [theme, setTheme] = useState(() => readStoredOneOsTheme()); + const [shellNotices, setShellNotices] = useState([]); + const [shellUnread, setShellUnread] = useState(0); + + useEffect(() => { + setOneOsTheme(theme); + }, [theme]); + + useEffect(() => { + const onMsg = (e: MessageEvent) => { + if (!isNoticesSync(e.data)) return; + setShellNotices(e.data.notices); + setShellUnread(e.data.unreadCount); + }; + window.addEventListener('message', onMsg); + return () => window.removeEventListener('message', onMsg); + }, []); + + useEffect(() => { + setShellNotices([]); + setShellUnread(0); + }, [activeHref]); + + const frameWindow = useCallback((): Window | null => { + if (typeof document === 'undefined') return null; + const frame = document.querySelector('.oneos-shell-frame'); + return frame?.contentWindow || null; + }, []); + + const onNoticeRead = useCallback( + (notice: ShellNoticeItem) => { + setShellNotices((prev) => + prev.map((n) => (n.id === notice.id ? { ...n, read: true } : n)), + ); + setShellUnread((c) => Math.max(0, c - (notice.read ? 0 : 1))); + const win = frameWindow(); + if (win) postNoticeAction(win, 'read', notice.id); + }, + [frameWindow], + ); + + const onNoticeOpen = useCallback( + (notice: ShellNoticeItem) => { + const win = frameWindow(); + if (win) postNoticeAction(win, 'open', notice.id); + }, + [frameWindow], + ); + + const onNoticeHandle = useCallback( + (notice: ShellNoticeItem) => { + setShellNotices((prev) => + prev.map((n) => (n.id === notice.id ? { ...n, read: true } : n)), + ); + setShellUnread((c) => Math.max(0, c - (notice.read ? 0 : 1))); + const win = frameWindow(); + if (win) postNoticeAction(win, 'handle', notice.id); + }, + [frameWindow], + ); + + useEffect(() => { + setOpenKeys((prev) => { + const next = collectOpenKeys(path); + return Array.from(new Set([...prev, ...next])); + }); + }, [path]); + + const toggleTheme = useCallback(() => { + setTheme((prev) => { + const next: OneOsTheme = prev === 'dark' ? 'light' : 'dark'; + setOneOsTheme(next); + return next; + }); + }, []); + + const crumbs = breadcrumb?.length + ? breadcrumb + : path.length + ? path.map((p) => p.label) + : ['OneOS', pageTitle]; + + const selectedKey = path[path.length - 1]?.key; + const showTabs = Array.isArray(tabs); + + const handleNavigate = useCallback( + (item: ShellMenuItem) => { + if (onNavigateProp) { + onNavigateProp(item); + return; + } + if (item.href && typeof window !== 'undefined') { + window.location.href = item.href; + } + }, + [onNavigateProp], + ); + + const toggleOpen = useCallback((key: string) => { + setOpenKeys((prev) => (prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key])); + }, []); + + const renderItems = (items: ShellMenuItem[], level = 0): React.ReactNode => + items.map((item) => { + const hasChildren = !!item.children?.length; + const opened = openKeys.includes(item.key); + const selected = + item.key === selectedKey || + (!!item.href && hrefPathname(item.href) === hrefPathname(activeHref)); + const Icon = iconForLabel(item.label); + + return ( +
  • + + {!collapsed && hasChildren && opened && ( +
      {renderItems(item.children!, level + 1)}
    + )} +
  • + ); + }); + + return ( +
    + ); +} + +export default OneOsAppShell; diff --git a/src/common/oneos-app-shell/README.md b/src/common/oneos-app-shell/README.md new file mode 100644 index 0000000..a62aae4 --- /dev/null +++ b/src/common/oneos-app-shell/README.md @@ -0,0 +1,12 @@ +# 羚牛 OneOS 应用外壳 + +> 实现:`src/common/oneos-app-shell/` +> 宿主页:`/prototypes/oneos-prototype-demo`(原型演示) + +## 用法 + +- **带外壳浏览所有原型**:打开「原型演示」,从左侧菜单点选;内容区 iframe 嵌入。 +- **单独打开某原型**:访问 `/prototypes/`,无侧栏顶栏。 +- **浅色 / 暗色**:顶栏月亮/太阳按钮切换;暗色参照原型导航页风格。对 OneOS 目录下原型生效(含直链),**不包括「旧 ONEOS」**。 + +菜单数据:`oneos-prototype-nav/nav-menu.json`。 diff --git a/src/common/oneos-app-shell/ShellNoticeCenter.tsx b/src/common/oneos-app-shell/ShellNoticeCenter.tsx new file mode 100644 index 0000000..fdd853b --- /dev/null +++ b/src/common/oneos-app-shell/ShellNoticeCenter.tsx @@ -0,0 +1,364 @@ +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { Bell, BellOff, X } from 'lucide-react'; +import type { ShellNoticeItem } from './notice-bridge'; + +/** 气泡/标签:x条新通知,请尽快处理(阿拉伯数字) */ +function formatNewMessageTip(count: number): string { + return `${count}条新通知,请尽快处理`; +} + +function compareNoticePriority(a: ShellNoticeItem, b: ShellNoticeItem): number { + const aUrge = a.type === '催办提醒' ? 0 : 1; + const bUrge = b.type === '催办提醒' ? 0 : 1; + if (aUrge !== bUrge) return aUrge - bUrge; + return b.time.localeCompare(a.time); +} + +type AllTab = 'unread' | 'read'; + +type ShellNoticeCenterProps = { + notices: ShellNoticeItem[]; + unreadCount: number; + onRead: (notice: ShellNoticeItem) => void; + onOpen: (notice: ShellNoticeItem) => void; + onHandle: (notice: ShellNoticeItem) => void; +}; + +function NoticeListItem({ + notice, + showHandle, + showSummary = false, + onSelect, + onHandle, +}: { + notice: ShellNoticeItem; + showHandle: boolean; + showSummary?: boolean; + onSelect: (notice: ShellNoticeItem) => void; + onHandle: (notice: ShellNoticeItem) => void; +}) { + const canHandle = !!(notice.href || notice.taskId); + return ( +
  • + + {showHandle && canHandle ? ( + + ) : null} +
  • + ); +} + +/** 顶栏铃铛 + 新消息闪烁提示 + 下拉通知中心(对齐工作台 NoticePanel) */ +export function ShellNoticeCenter({ + notices, + unreadCount, + onRead, + onOpen: _onOpen, + onHandle, +}: ShellNoticeCenterProps) { + const [open, setOpen] = useState(false); + const [allOpen, setAllOpen] = useState(false); + const [allTab, setAllTab] = useState('unread'); + const [detailNotice, setDetailNotice] = useState(null); + const wrapRef = useRef(null); + const allModalRef = useRef(null); + const [portalRoot, setPortalRoot] = useState(null); + + useEffect(() => { + const shell = wrapRef.current?.closest('.oneos-shell') as HTMLElement | null; + setPortalRoot(shell || document.body); + }, []); + + const pool = useMemo( + () => notices.filter((n) => !n.read).slice().sort(compareNoticePriority), + [notices], + ); + const unreadList = pool; + const readList = useMemo( + () => notices.filter((n) => n.read).slice().sort(compareNoticePriority), + [notices], + ); + const allTabList = allTab === 'unread' ? unreadList : readList; + + const closeAll = () => { + setOpen(false); + setAllOpen(false); + setDetailNotice(null); + }; + + useEffect(() => { + if (!open && !detailNotice && !allOpen) return; + const onDoc = (e: MouseEvent) => { + const target = e.target as Node; + if (allOpen) { + if (allModalRef.current && !allModalRef.current.contains(target)) { + setAllOpen(false); + } + return; + } + if (!wrapRef.current?.contains(target)) closeAll(); + }; + const onKey = (e: KeyboardEvent) => { + if (e.key !== 'Escape') return; + if (detailNotice) setDetailNotice(null); + else if (allOpen) setAllOpen(false); + else setOpen(false); + }; + document.addEventListener('mousedown', onDoc); + document.addEventListener('keydown', onKey); + return () => { + document.removeEventListener('mousedown', onDoc); + document.removeEventListener('keydown', onKey); + }; + }, [open, detailNotice, allOpen]); + + useEffect(() => { + if (!allOpen) return; + const lockEl = portalRoot || document.body; + const prev = lockEl.style.overflow; + lockEl.style.overflow = 'hidden'; + return () => { + lockEl.style.overflow = prev; + }; + }, [allOpen, portalRoot]); + + const handleSelect = (notice: ShellNoticeItem) => { + onRead(notice); + setDetailNotice(notice); + setOpen(false); + setAllOpen(false); + }; + + const handleAction = (notice: ShellNoticeItem) => { + onRead(notice); + onHandle(notice); + closeAll(); + }; + + return ( +
    + {unreadCount > 0 && !open && !detailNotice && !allOpen ? ( + + {formatNewMessageTip(unreadCount)} + + ) : null} + + + {open && !detailNotice && !allOpen ? ( +
    +
    +

    + 通知中心 + {unreadCount > 0 ? ( + {formatNewMessageTip(unreadCount)} + ) : null} +

    + +
    + +
    + {pool.length ? ( +
      + {pool.map((notice) => ( + + ))} +
    + ) : ( +
    + +

    暂无新消息

    +

    + 当前通知均已读完 +
    + 有新催办或业务提醒时会显示在这里 +

    +
    + )} +
    +
    + ) : null} + + {allOpen && portalRoot + ? createPortal( +
    +
    +
    +

    全部通知

    + +
    +
    + + +
    +
    + {allTabList.length ? ( +
      + {allTabList.map((notice) => ( + + ))} +
    + ) : ( +
    + +

    + {allTab === 'unread' ? '暂无未读通知' : '暂无已读通知'} +

    +

    + {allTab === 'unread' + ? '当前通知均已读完,有新催办或业务提醒时会显示在这里' + : '已读通知会显示在这里'} +

    +
    + )} +
    +
    +
    , + portalRoot, + ) + : null} + + {detailNotice ? ( +
    +
    +

    + {detailNotice.type === '催办提醒' ? ( + 催办提醒 + ) : null} + {detailNotice.bizTag} + {detailNotice.title} +

    + +
    +
    + {detailNotice.time} +

    {detailNotice.detail || detailNotice.title}

    + {detailNotice.summary ? ( +

    {detailNotice.summary}

    + ) : null} +
    +
    + + {(detailNotice.href || detailNotice.taskId) && ( + + )} +
    +
    + ) : null} +
    + ); +} diff --git a/src/common/oneos-app-shell/index.ts b/src/common/oneos-app-shell/index.ts new file mode 100644 index 0000000..182609e --- /dev/null +++ b/src/common/oneos-app-shell/index.ts @@ -0,0 +1,50 @@ +export { OneOsAppShell, PROTOTYPE_DEMO_HREF } from './OneOsAppShell'; +export type { OneOsAppShellProps, ShellTab } from './OneOsAppShell'; +export { ShellNoticeCenter } from './ShellNoticeCenter'; +export { + ONEOS_NOTICE_ACTION, + ONEOS_NOTICES_SYNC, + isNoticeAction, + isNoticesSync, + postNoticeAction, + postNoticesSync, + type NoticeActionPayload, + type NoticesSyncPayload, + type ShellNoticeItem, +} from './notice-bridge'; +export { + ONEOS_SHELL_NAV, + isShellNav, + requestShellNav, + type ShellNavPayload, +} from './nav-bridge'; +export { + buildShellMenuFromNav, + findMenuPath, + hrefPathname, + itemKeyToHref, + type ShellMenuItem, +} from './nav-from-prototypes'; +export { + ONEOS_RELEASE_DEMO_ACTION, + RELEASE_SEEN_STORAGE_KEY, + SHELL_CURRENT_RELEASE_VERSION, + clearAllReleaseSeen, + hasUnseenRelease, + isReleaseDemoAction, + postReleaseDemoAction, + postReleaseDemoOpen, + postReleaseDemoReset, + type ReleaseDemoAction, + type ReleaseDemoActionPayload, +} from './release-demo-bridge'; +export { + bootstrapOneOsTheme, + broadcastOneOsTheme, + isLegacyOneOsPrototype, + LEGACY_ONEOS_PROTO_IDS, + readStoredOneOsTheme, + setOneOsTheme, + toggleOneOsTheme, + type OneOsTheme, +} from './theme'; diff --git a/src/common/oneos-app-shell/nav-bridge.ts b/src/common/oneos-app-shell/nav-bridge.ts new file mode 100644 index 0000000..741571b --- /dev/null +++ b/src/common/oneos-app-shell/nav-bridge.ts @@ -0,0 +1,29 @@ +/** iframe 内页面 ↔ 外壳侧栏/页签导航 postMessage 协议 */ + +export const ONEOS_SHELL_NAV = 'ONEOS_SHELL_NAV'; + +export type ShellNavPayload = { + type: typeof ONEOS_SHELL_NAV; + href: string; + title?: string; +}; + +export function isShellNav(data: unknown): data is ShellNavPayload { + return ( + !!data && + typeof data === 'object' && + (data as ShellNavPayload).type === ONEOS_SHELL_NAV && + typeof (data as ShellNavPayload).href === 'string' && + (data as ShellNavPayload).href.startsWith('/prototypes/') + ); +} + +/** 在 iframe 内跳转时优先通知外壳;直链打开则本页跳转 */ +export function requestShellNav(href: string, title?: string): boolean { + if (typeof window === 'undefined' || !href.startsWith('/prototypes/')) return false; + if (window.parent && window.parent !== window) { + window.parent.postMessage({ type: ONEOS_SHELL_NAV, href, title } satisfies ShellNavPayload, '*'); + return true; + } + return false; +} diff --git a/src/common/oneos-app-shell/nav-from-prototypes.ts b/src/common/oneos-app-shell/nav-from-prototypes.ts new file mode 100644 index 0000000..3dd5af2 --- /dev/null +++ b/src/common/oneos-app-shell/nav-from-prototypes.ts @@ -0,0 +1,104 @@ +/** + * 将 oneos-prototype-nav/nav-menu.json 转为羚牛 OneOS 侧栏菜单树 + */ + +export type NavMenuNode = { + id: string; + kind: 'folder' | 'item'; + title: string; + itemKey?: string; + children?: NavMenuNode[]; +}; + +export type ShellMenuItem = { + key: string; + label: string; + href?: string; + children?: ShellMenuItem[]; +}; + +type NavMenuFile = { + prototypes?: NavMenuNode[]; +}; + +export function itemKeyToHref(itemKey?: string): string | undefined { + if (!itemKey) return undefined; + if (itemKey.startsWith('/prototypes/')) return itemKey; + if (itemKey.startsWith('prototypes/')) return `/${itemKey}`; + return `/prototypes/${itemKey}`; +} + +function convertNode(node: NavMenuNode): ShellMenuItem { + if (node.kind === 'item') { + return { + key: node.id || node.itemKey || node.title, + label: node.title, + href: itemKeyToHref(node.itemKey), + }; + } + return { + key: node.id || node.title, + label: node.title, + children: (node.children || []).map(convertNode), + }; +} + +/** 取「OneOS」根目录下的子树作为侧栏;若无则用整棵 prototypes */ +export function buildShellMenuFromNav( + nav: NavMenuFile, + options?: { excludeHrefs?: string[] }, +): ShellMenuItem[] { + const roots = nav.prototypes || []; + const oneos = roots.find((n) => n.kind === 'folder' && /oneos/i.test(n.title)); + const source = oneos?.children?.length ? oneos.children : roots; + const exclude = new Set( + (options?.excludeHrefs ?? ['/prototypes/oneos-prototype-demo']).map((h) => h.replace(/\/$/, '')), + ); + + function filterTree(items: ShellMenuItem[]): ShellMenuItem[] { + return items + .map((it) => { + if (it.href && exclude.has(it.href.replace(/\/$/, ''))) return null; + if (it.children?.length) { + const children = filterTree(it.children); + if (!it.href && children.length === 0) return null; + return { ...it, children }; + } + return it; + }) + .filter(Boolean) as ShellMenuItem[]; + } + + return filterTree(source.map(convertNode)); +} + +/** 去掉 query/hash 与尾部斜杠,供菜单高亮与路径匹配 */ +export function hrefPathname(href: string): string { + return href.split(/[?#]/)[0]!.replace(/\/$/, '') || href; +} + +export function findMenuPath( + items: ShellMenuItem[], + activeHref: string, + trail: ShellMenuItem[] = [], +): ShellMenuItem[] | null { + const activePath = hrefPathname(activeHref); + for (const item of items) { + const next = [...trail, item]; + if (item.href) { + const itemPath = hrefPathname(item.href); + if (activePath === itemPath || activePath.endsWith(itemPath)) { + return next; + } + } + if (item.children?.length) { + const hit = findMenuPath(item.children, activeHref, next); + if (hit) return hit; + } + } + return null; +} + +export function collectOpenKeys(path: ShellMenuItem[]): string[] { + return path.filter((n) => n.children?.length).map((n) => n.key); +} diff --git a/src/common/oneos-app-shell/notice-bridge.ts b/src/common/oneos-app-shell/notice-bridge.ts new file mode 100644 index 0000000..0e34a95 --- /dev/null +++ b/src/common/oneos-app-shell/notice-bridge.ts @@ -0,0 +1,60 @@ +/** 工作台 iframe ↔ 外壳顶栏通知中心 postMessage 协议 */ + +export const ONEOS_NOTICES_SYNC = 'ONEOS_NOTICES_SYNC'; +export const ONEOS_NOTICE_ACTION = 'ONEOS_NOTICE_ACTION'; + +export type ShellNoticeItem = { + id: string; + type: string; + bizTag: string; + title: string; + summary: string; + detail: string; + time: string; + read: boolean; + href?: string; + taskId?: string; +}; + +export type NoticesSyncPayload = { + type: typeof ONEOS_NOTICES_SYNC; + unreadCount: number; + notices: ShellNoticeItem[]; + source?: string; +}; + +export type NoticeActionPayload = { + type: typeof ONEOS_NOTICE_ACTION; + action: 'read' | 'handle' | 'open'; + noticeId: string; +}; + +export function isNoticesSync(data: unknown): data is NoticesSyncPayload { + return ( + !!data && + typeof data === 'object' && + (data as NoticesSyncPayload).type === ONEOS_NOTICES_SYNC && + Array.isArray((data as NoticesSyncPayload).notices) + ); +} + +export function isNoticeAction(data: unknown): data is NoticeActionPayload { + return ( + !!data && + typeof data === 'object' && + (data as NoticeActionPayload).type === ONEOS_NOTICE_ACTION && + typeof (data as NoticeActionPayload).noticeId === 'string' + ); +} + +export function postNoticesSync(target: Window, payload: Omit) { + target.postMessage({ type: ONEOS_NOTICES_SYNC, ...payload }, '*'); +} + +export function postNoticeAction( + target: Window, + action: NoticeActionPayload['action'], + noticeId: string, +) { + target.postMessage({ type: ONEOS_NOTICE_ACTION, action, noticeId }, '*'); +} diff --git a/src/common/oneos-app-shell/oneos-app-shell.css b/src/common/oneos-app-shell/oneos-app-shell.css new file mode 100644 index 0000000..cbe84ab --- /dev/null +++ b/src/common/oneos-app-shell/oneos-app-shell.css @@ -0,0 +1,1564 @@ +/** + * 羚牛 OneOS 应用壳(侧栏 + 顶栏) + * Enterprise SaaS:精致侧栏、玻璃顶栏、流畅微交互 + */ + +@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400&display=swap'); + +.oneos-shell { + --oneos-aside-w: 232px; + --oneos-aside-w-collapsed: 68px; + --oneos-header-h: 52px; + --oneos-tabs-h: 40px; + --oneos-primary: #32a06e; + --oneos-primary-soft: #3fb87c; + --oneos-ink: #1e293b; + --oneos-ink-soft: rgba(30, 41, 59, 0.88); + --oneos-muted: #64748b; + --oneos-border: #e2e8f0; + --oneos-bg: #f1f5f9; + --oneos-surface: #ffffff; + --oneos-aside-bg: linear-gradient(180deg, #ffffff 0%, #f8fafc 48%, #f1f5f9 100%); + --oneos-ease: cubic-bezier(0.22, 1, 0.36, 1); + --oneos-shadow-aside: 4px 0 24px rgba(15, 23, 42, 0.04); + --oneos-shadow-chrome: 0 1px 0 rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.03); + + display: flex; + width: 100%; + min-height: 100dvh; + height: 100dvh; + overflow: hidden; + background: var(--oneos-bg); + color: var(--oneos-ink); + font-family: + 'Plus Jakarta Sans', + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + 'PingFang SC', + 'Microsoft YaHei', + sans-serif; + font-size: 14px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +/* 暗色:参照原型导航页(VoltAgent 深色画布) */ +.oneos-shell[data-oneos-theme='dark'], +html[data-oneos-theme='dark'] .oneos-shell { + --oneos-primary: #00d992; + --oneos-primary-soft: #2fd6a1; + --oneos-ink: #f2f2f2; + --oneos-ink-soft: rgba(242, 242, 242, 0.88); + --oneos-muted: #8b949e; + --oneos-border: #3d3a39; + --oneos-bg: #101010; + --oneos-surface: #1a1a1a; + --oneos-aside-bg: linear-gradient(180deg, #121212 0%, #101010 48%, #0c0c0c 100%); + --oneos-shadow-aside: 4px 0 24px rgba(0, 0, 0, 0.35); + --oneos-shadow-chrome: 0 1px 0 rgba(255, 255, 255, 0.04), 0 8px 24px rgba(0, 0, 0, 0.25); + color-scheme: dark; +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-brand { + background: linear-gradient(180deg, rgba(26, 26, 26, 0.95) 0%, rgba(16, 16, 16, 0.6) 100%); +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-brand__text { + background: linear-gradient(135deg, #f2f2f2 0%, #00d992 55%, #2fd6a1 100%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-icon-btn:hover { + background: color-mix(in srgb, var(--oneos-primary) 12%, #1a1a1a); +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-search { + background: linear-gradient(180deg, #1a1a1a 0%, #161616 100%); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-search:focus-within { + background: #1a1a1a; + box-shadow: 0 0 0 3px color-mix(in srgb, var(--oneos-primary) 18%, transparent); +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-search kbd { + background: #222; + border-color: #3d3a39; + color: #8b949e; + box-shadow: none; +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-tabs { + background: linear-gradient(180deg, #141414 0%, #101010 100%); +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-tab.is-active { + box-shadow: 0 -1px 0 #1a1a1a, 0 0 0 1px color-mix(in srgb, var(--oneos-border) 80%, transparent); +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-tab.is-active::after { + box-shadow: none; +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-breadcrumb__sep { + color: #565656; +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-avatar img { + border-color: #1a1a1a; +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-frame { + background: #101010; +} + +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-menu__btn:focus-visible, +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-icon-btn:focus-visible, +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-brand__link:focus-visible, +.oneos-shell[data-oneos-theme='dark'] .oneos-shell-avatar:focus-visible { + box-shadow: 0 0 0 2px #101010, 0 0 0 4px color-mix(in srgb, var(--oneos-primary) 45%, transparent); +} + +.oneos-shell *, +.oneos-shell *::before, +.oneos-shell *::after { + box-sizing: border-box; +} + +/* ——— Sidebar ——— */ +.oneos-shell-aside { + flex: 0 0 var(--oneos-aside-w); + width: var(--oneos-aside-w); + display: flex; + flex-direction: column; + background: var(--oneos-aside-bg); + border-right: 1px solid color-mix(in srgb, var(--oneos-border) 85%, transparent); + box-shadow: var(--oneos-shadow-aside); + z-index: 40; + transition: + width 0.28s var(--oneos-ease), + flex-basis 0.28s var(--oneos-ease); + position: relative; +} + +.oneos-shell-aside::after { + content: ''; + position: absolute; + inset: 0 0 0 auto; + width: 1px; + background: linear-gradient( + 180deg, + transparent 0%, + color-mix(in srgb, var(--oneos-primary) 12%, transparent) 20%, + color-mix(in srgb, var(--oneos-border) 90%, transparent) 50%, + transparent 100% + ); + pointer-events: none; +} + +.oneos-shell--collapsed .oneos-shell-aside { + flex-basis: var(--oneos-aside-w-collapsed); + width: var(--oneos-aside-w-collapsed); +} + +.oneos-shell-brand { + height: var(--oneos-header-h); + display: flex; + align-items: center; + justify-content: center; + padding: 0 12px; + border-bottom: 1px solid color-mix(in srgb, var(--oneos-border) 55%, transparent); + flex-shrink: 0; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.95) 0%, rgba(248, 250, 252, 0.6) 100%); + backdrop-filter: blur(8px); +} + +.oneos-shell-brand__link { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 6px 10px; + min-height: 44px; + width: 100%; + border-radius: 12px; + text-decoration: none; + color: var(--oneos-ink); + overflow: hidden; + cursor: pointer; + transition: + background 0.2s var(--oneos-ease), + transform 0.2s var(--oneos-ease); +} + +.oneos-shell-brand__link:hover { + background: color-mix(in srgb, var(--oneos-primary) 6%, transparent); +} + +.oneos-shell-brand__link:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #fff, 0 0 0 4px color-mix(in srgb, var(--oneos-primary) 45%, transparent); +} + +.oneos-shell-brand__mark { + display: grid; + place-items: center; + flex-shrink: 0; + filter: drop-shadow(0 2px 6px color-mix(in srgb, var(--oneos-primary) 35%, transparent)); + transition: transform 0.25s var(--oneos-ease), filter 0.25s var(--oneos-ease); +} + +.oneos-shell-brand__link:hover .oneos-shell-brand__mark { + transform: translateY(-1px) scale(1.04); + filter: drop-shadow(0 4px 10px color-mix(in srgb, var(--oneos-primary) 45%, transparent)); +} + +.oneos-shell-brand__text { + font-size: 15px; + font-weight: 700; + letter-spacing: -0.03em; + white-space: nowrap; + background: linear-gradient(135deg, #18181b 0%, #2b9260 55%, #32a06e 100%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.oneos-shell-nav { + flex: 1; + min-height: 0; + overflow: auto; + padding: 10px 0 20px; + scrollbar-width: thin; + scrollbar-color: color-mix(in srgb, var(--oneos-muted) 35%, transparent) transparent; +} + +.oneos-shell-nav::-webkit-scrollbar { + width: 4px; +} + +.oneos-shell-nav::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--oneos-muted) 30%, transparent); + border-radius: 999px; +} + +.oneos-shell-menu, +.oneos-shell-menu__sub { + list-style: none; + margin: 0; + padding: 0; +} + +.oneos-shell-menu__item { + margin: 0 10px 3px; +} + +.oneos-shell-menu__btn { + position: relative; + display: flex; + align-items: center; + gap: 10px; + width: 100%; + min-height: 42px; + padding: 8px 12px; + border: none; + border-radius: 10px; + background: transparent; + color: var(--oneos-ink-soft); + font: inherit; + text-align: left; + cursor: pointer; + transition: + background 0.2s var(--oneos-ease), + color 0.2s var(--oneos-ease), + box-shadow 0.2s var(--oneos-ease), + transform 0.15s var(--oneos-ease); +} + +.oneos-shell-menu__btn::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + width: 3px; + height: 0; + border-radius: 0 3px 3px 0; + background: linear-gradient(180deg, var(--oneos-primary-soft), var(--oneos-primary)); + transform: translateY(-50%); + opacity: 0; + transition: + height 0.22s var(--oneos-ease), + opacity 0.22s var(--oneos-ease); +} + +.oneos-shell-menu__btn:hover { + background: color-mix(in srgb, var(--oneos-primary) 7%, transparent); + color: var(--oneos-ink); +} + +.oneos-shell-menu__btn:active { + transform: scale(0.985); +} + +.oneos-shell-menu__btn:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #fff, 0 0 0 4px color-mix(in srgb, var(--oneos-primary) 40%, transparent); +} + +.oneos-shell-menu__btn.is-active { + color: var(--oneos-primary); + background: linear-gradient( + 90deg, + color-mix(in srgb, var(--oneos-primary) 14%, transparent) 0%, + color-mix(in srgb, var(--oneos-primary) 6%, transparent) 100% + ); + font-weight: 600; + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--oneos-primary) 12%, transparent); +} + +.oneos-shell-menu__btn.is-active::before { + height: 18px; + opacity: 1; +} + +.oneos-shell-menu__btn.is-active .oneos-shell-menu__icon { + opacity: 1; + color: var(--oneos-primary); +} + +.oneos-shell-menu__icon { + flex-shrink: 0; + opacity: 0.72; + transition: + opacity 0.2s var(--oneos-ease), + color 0.2s var(--oneos-ease), + transform 0.2s var(--oneos-ease); +} + +.oneos-shell-menu__btn:hover .oneos-shell-menu__icon { + opacity: 1; + transform: scale(1.06); +} + +.oneos-shell-menu__label { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + letter-spacing: -0.01em; +} + +.oneos-shell-menu__arrow { + display: inline-flex; + color: var(--oneos-muted); + flex-shrink: 0; + transition: transform 0.22s var(--oneos-ease), color 0.2s ease; +} + +.oneos-shell-menu__btn.is-parent[aria-expanded='true'] .oneos-shell-menu__arrow { + color: var(--oneos-primary); +} + +.oneos-shell-menu__sub { + padding: 2px 0 6px 8px; + animation: oneos-shell-sub-in 0.22s var(--oneos-ease); +} + +@keyframes oneos-shell-sub-in { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.oneos-shell-menu__item--lv1 .oneos-shell-menu__btn { + min-height: 38px; + padding-left: 14px; + font-size: 13px; +} + +.oneos-shell-menu__item--lv2 .oneos-shell-menu__btn { + min-height: 36px; + padding-left: 18px; + font-size: 13px; +} + +.oneos-shell--collapsed .oneos-shell-brand__link { + justify-content: center; + padding: 6px; +} + +.oneos-shell--collapsed .oneos-shell-menu__btn { + justify-content: center; + padding-left: 0; + padding-right: 0; +} + +.oneos-shell--collapsed .oneos-shell-menu__btn::before { + display: none; +} + +.oneos-shell--collapsed .oneos-shell-menu__item { + margin-left: 8px; + margin-right: 8px; +} + +/* ——— Main / Chrome ——— */ +.oneos-shell-main { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.oneos-shell-chrome { + flex-shrink: 0; + background: color-mix(in srgb, var(--oneos-surface) 88%, transparent); + backdrop-filter: blur(14px) saturate(1.2); + -webkit-backdrop-filter: blur(14px) saturate(1.2); + border-bottom: 1px solid color-mix(in srgb, var(--oneos-border) 80%, transparent); + box-shadow: var(--oneos-shadow-chrome); + z-index: 30; +} + +.oneos-shell-header { + height: var(--oneos-header-h); + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 0 14px 0 10px; +} + +.oneos-shell-header__left, +.oneos-shell-header__right { + display: flex; + align-items: center; + gap: 4px; + min-width: 0; +} + +.oneos-shell-header__right { + gap: 6px; +} + +.oneos-shell-text-btn { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 36px; + padding: 0 10px; + border: 1px dashed color-mix(in srgb, var(--oneos-ink) 22%, transparent); + border-radius: 10px; + background: transparent; + color: color-mix(in srgb, var(--oneos-ink) 68%, transparent); + font-size: 0.75rem; + font-weight: 500; + line-height: 1.2; + white-space: nowrap; + cursor: pointer; + transition: + color 0.18s var(--oneos-ease), + border-color 0.18s var(--oneos-ease), + background 0.18s var(--oneos-ease); +} + +.oneos-shell-text-btn:hover { + color: var(--oneos-primary); + border-color: color-mix(in srgb, var(--oneos-primary) 55%, transparent); + background: color-mix(in srgb, var(--oneos-primary) 8%, transparent); +} + +.oneos-shell-text-btn:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #fff, 0 0 0 4px color-mix(in srgb, var(--oneos-primary) 40%, transparent); +} + +.oneos-shell-release-btn { + position: relative; + display: inline-grid; + place-items: center; + width: 36px; + height: 36px; + min-width: 36px; + border: none; + border-radius: 12px; + background: transparent; + color: var(--oneos-primary); + cursor: pointer; + transition: + transform 0.18s var(--oneos-ease), + box-shadow 0.18s var(--oneos-ease); +} + +.oneos-shell-release-btn__ring { + position: absolute; + inset: 2px; + border-radius: 11px; + background: + linear-gradient(135deg, color-mix(in srgb, var(--oneos-primary) 22%, #fff), color-mix(in srgb, #6366f1 18%, #fff)); + box-shadow: + inset 0 1px 0 color-mix(in srgb, #fff 70%, transparent), + 0 4px 12px color-mix(in srgb, var(--oneos-primary) 18%, transparent); + transition: opacity 0.18s var(--oneos-ease), transform 0.18s var(--oneos-ease); +} + +.oneos-shell-release-btn__icon { + position: relative; + z-index: 1; + display: grid; + place-items: center; + color: var(--oneos-primary); + filter: drop-shadow(0 1px 0 color-mix(in srgb, #fff 50%, transparent)); +} + +.oneos-shell-release-btn__dot { + position: absolute; + top: 5px; + right: 5px; + z-index: 2; + width: 8px; + height: 8px; + border-radius: 999px; + background: #f97316; + box-shadow: 0 0 0 2px #fff; +} + +.oneos-shell-release-btn:hover { + transform: translateY(-1px); +} + +.oneos-shell-release-btn:hover .oneos-shell-release-btn__ring { + transform: scale(1.04); + box-shadow: + inset 0 1px 0 color-mix(in srgb, #fff 80%, transparent), + 0 6px 16px color-mix(in srgb, var(--oneos-primary) 28%, transparent); +} + +.oneos-shell-release-btn:active { + transform: translateY(0) scale(0.96); +} + +.oneos-shell-release-btn:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #fff, 0 0 0 4px color-mix(in srgb, var(--oneos-primary) 40%, transparent); +} + +.oneos-shell-release-btn.has-unread .oneos-shell-release-btn__ring { + animation: oneos-release-pulse 2.4s var(--oneos-ease) infinite; +} + +@media (prefers-reduced-motion: reduce) { + .oneos-shell-text-btn, + .oneos-shell-release-btn, + .oneos-shell-release-btn__ring { + transition: none; + } + + .oneos-shell-release-btn.has-unread .oneos-shell-release-btn__ring { + animation: none; + } +} + +@keyframes oneos-release-pulse { + 0%, + 100% { + box-shadow: + inset 0 1px 0 color-mix(in srgb, #fff 70%, transparent), + 0 4px 12px color-mix(in srgb, var(--oneos-primary) 18%, transparent); + } + 50% { + box-shadow: + inset 0 1px 0 color-mix(in srgb, #fff 80%, transparent), + 0 0 0 4px color-mix(in srgb, var(--oneos-primary) 12%, transparent), + 0 8px 18px color-mix(in srgb, var(--oneos-primary) 28%, transparent); + } +} + +.oneos-shell-icon-btn { + display: inline-grid; + place-items: center; + width: 36px; + height: 36px; + min-width: 36px; + border: none; + border-radius: 10px; + background: transparent; + color: color-mix(in srgb, var(--oneos-ink) 72%, transparent); + cursor: pointer; + transition: + background 0.18s var(--oneos-ease), + color 0.18s var(--oneos-ease), + transform 0.15s var(--oneos-ease), + box-shadow 0.18s var(--oneos-ease); +} + +.oneos-shell-icon-btn:hover { + background: color-mix(in srgb, var(--oneos-primary) 8%, #f1f5f9); + color: var(--oneos-ink); + transform: translateY(-1px); +} + +.oneos-shell-icon-btn:active { + transform: translateY(0) scale(0.96); +} + +.oneos-shell-icon-btn:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #fff, 0 0 0 4px color-mix(in srgb, var(--oneos-primary) 40%, transparent); +} + +.oneos-shell-icon-btn--notify { + position: relative; +} + +.oneos-shell-icon-btn--notify::after { + display: none; +} + +.oneos-shell-icon-btn--notify.has-unread::after { + display: none; +} + +.oneos-shell-notify { + position: relative; + display: inline-flex; + align-items: center; +} + +.oneos-shell-notify__callout { + position: absolute; + top: calc(100% + 8px); + right: 0; + left: auto; + transform: none; + z-index: 30; + white-space: nowrap; + padding: 6px 10px; + border-radius: 8px; + font-size: 12px; + font-weight: 600; + color: #9a3412; + background: #fff7ed; + border: 1px solid #fdba74; + box-shadow: 0 4px 14px rgba(249, 115, 22, 0.18); + pointer-events: none; + animation: oneos-shell-notify-blink 1.6s ease-in-out infinite; +} + +.oneos-shell-notify__callout::after { + content: ''; + position: absolute; + right: 13px; + left: auto; + top: -5px; + width: 10px; + height: 10px; + transform: rotate(45deg); + background: #fff7ed; + border-left: 1px solid #fdba74; + border-top: 1px solid #fdba74; +} + +@keyframes oneos-shell-notify-blink { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.55; + } +} + +.oneos-shell-notify__badge { + position: absolute; + top: 4px; + right: 4px; + min-width: 16px; + height: 16px; + padding: 0 4px; + border-radius: 999px; + font-size: 10px; + font-weight: 700; + line-height: 16px; + text-align: center; + color: #fff; + background: #f97316; + box-shadow: 0 0 0 2px var(--oneos-surface); + font-variant-numeric: tabular-nums; +} + +.oneos-shell-notify__panel { + position: absolute; + top: calc(100% + 8px); + right: 0; + z-index: 40; + width: min(420px, calc(100vw - 32px)); + max-height: min(520px, 70vh); + display: flex; + flex-direction: column; + border-radius: 14px; + border: 1px solid color-mix(in srgb, var(--oneos-primary) 12%, #e2e8f0); + background: #fff; + box-shadow: 0 12px 40px rgba(15, 23, 42, 0.14); + overflow: hidden; +} + +.oneos-shell-notify__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 12px 14px; + border-bottom: 1px solid #e2e8f0; +} + +.oneos-shell-notify__title { + margin: 0; + display: inline-flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 700; + color: #0f172a; +} + +.oneos-shell-notify__tag { + display: inline-flex; + align-items: center; + padding: 2px 8px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + color: #9a3412; + background: #fff7ed; + border: 1px solid #fdba74; + animation: oneos-shell-notify-blink 1.6s ease-in-out infinite; + font-variant-numeric: tabular-nums; +} + +.oneos-shell-notify__link { + border: 0; + background: transparent; + color: var(--oneos-primary); + font-size: 12px; + font-weight: 600; + cursor: pointer; + padding: 4px 0; +} + +.oneos-shell-notify__link:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.oneos-shell-notify__link:focus-visible { + outline: 2px solid color-mix(in srgb, var(--oneos-primary) 45%, transparent); + outline-offset: 2px; +} + +.oneos-shell-notify__body { + flex: 1; + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; + padding: 8px; +} + +.oneos-shell-notify__list { + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: column; + gap: 8px; +} + +.oneos-shell-notify__item { + position: relative; + border-radius: 10px; + border: 1px solid #e2e8f0; + background: #f8fafc; + overflow: hidden; +} + +.oneos-shell-notify__item.is-unread { + background: #fff; + border-color: color-mix(in srgb, var(--oneos-primary) 22%, #e2e8f0); +} + +.oneos-shell-notify__item-main { + display: block; + width: 100%; + text-align: left; + border: 0; + background: transparent; + padding: 10px 12px; + cursor: pointer; + color: inherit; +} + +.oneos-shell-notify__item-main:hover { + background: color-mix(in srgb, var(--oneos-primary) 5%, #fff); +} + +.oneos-shell-notify__meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; + margin-bottom: 6px; +} + +.oneos-shell-notify__dot { + width: 8px; + height: 8px; + border-radius: 999px; + background: #cbd5e1; + flex-shrink: 0; +} + +.oneos-shell-notify__item.is-unread .oneos-shell-notify__dot { + background: #f97316; + box-shadow: 0 0 0 3px rgba(249, 115, 22, 0.18); +} + +.oneos-shell-notify__time { + font-size: 11px; + color: #64748b; + font-variant-numeric: tabular-nums; +} + +.oneos-shell-notify__chip { + display: inline-flex; + align-items: center; + padding: 1px 6px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + color: #334155; + background: #e2e8f0; +} + +.oneos-shell-notify__chip--urge { + color: #9a3412; + background: #ffedd5; +} + +.oneos-shell-notify__text { + margin: 0; + font-size: 13px; + line-height: 1.5; + color: #0f172a; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.oneos-shell-notify__item.is-expanded .oneos-shell-notify__text { + -webkit-line-clamp: unset; +} + +.oneos-shell-notify__summary { + margin: 6px 0 0; + font-size: 12px; + line-height: 1.45; + color: #64748b; +} + +.oneos-shell-notify__handle { + position: absolute; + top: 10px; + right: 10px; + border: 0; + border-radius: 8px; + padding: 4px 10px; + font-size: 12px; + font-weight: 600; + color: #fff; + background: var(--oneos-primary); + cursor: pointer; +} + +.oneos-shell-notify__handle:hover { + filter: brightness(0.96); +} + +.oneos-shell-notify__handle:focus-visible { + outline: 2px solid color-mix(in srgb, var(--oneos-primary) 45%, transparent); + outline-offset: 2px; +} + +.oneos-shell-notify__item-main { + padding-right: 72px; +} + +/* 挂到 document.body,避开顶栏 backdrop-filter 对 fixed 的包含块影响 */ +.oneos-shell-notify__all-overlay { + position: fixed; + inset: 0; + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; + padding: 24px 16px; + background: rgba(15, 23, 42, 0.45); + box-sizing: border-box; +} + +.oneos-shell-notify__all-modal { + width: min(560px, calc(100vw - 32px)); + max-height: min(720px, calc(100vh - 48px)); + display: flex; + flex-direction: column; + border-radius: 14px; + border: 1px solid color-mix(in srgb, var(--oneos-primary) 12%, #e2e8f0); + background: #fff; + box-shadow: 0 20px 56px rgba(15, 23, 42, 0.22); + overflow: hidden; +} + +.oneos-shell-notify__all-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 14px 16px; + border-bottom: 1px solid #e2e8f0; +} + +.oneos-shell-notify__all-title { + margin: 0; + font-size: 16px; + font-weight: 700; + color: #0f172a; +} + +.oneos-shell-notify__all-close { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: 0; + border-radius: 8px; + background: transparent; + color: #64748b; + cursor: pointer; +} + +.oneos-shell-notify__all-close:hover { + background: #f1f5f9; + color: #0f172a; +} + +.oneos-shell-notify__all-close:focus-visible { + outline: 2px solid color-mix(in srgb, var(--oneos-primary) 45%, transparent); + outline-offset: 2px; +} + +.oneos-shell-notify__all-tabs { + display: flex; + gap: 4px; + padding: 10px 16px 0; + border-bottom: 1px solid #e2e8f0; +} + +.oneos-shell-notify__all-tab { + display: inline-flex; + align-items: center; + gap: 6px; + border: 0; + border-bottom: 2px solid transparent; + margin-bottom: -1px; + background: transparent; + padding: 8px 12px; + font-size: 13px; + font-weight: 600; + color: #64748b; + cursor: pointer; + min-height: 40px; +} + +.oneos-shell-notify__all-tab:hover { + color: #0f172a; +} + +.oneos-shell-notify__all-tab.is-active { + color: var(--oneos-primary); + border-bottom-color: var(--oneos-primary); +} + +.oneos-shell-notify__all-tab:focus-visible { + outline: 2px solid color-mix(in srgb, var(--oneos-primary) 45%, transparent); + outline-offset: 2px; +} + +.oneos-shell-notify__all-count { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 18px; + height: 18px; + padding: 0 5px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + font-variant-numeric: tabular-nums; + color: #475569; + background: #e2e8f0; +} + +.oneos-shell-notify__all-tab.is-active .oneos-shell-notify__all-count { + color: #fff; + background: var(--oneos-primary); +} + +.oneos-shell-notify__all-body { + flex: 1; + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; + padding: 12px; +} + +.oneos-shell-notify__detail { + position: absolute; + top: calc(100% + 8px); + right: 0; + z-index: 50; + width: min(440px, calc(100vw - 32px)); + display: flex; + flex-direction: column; + border-radius: 14px; + border: 1px solid color-mix(in srgb, var(--oneos-primary) 12%, #e2e8f0); + background: #fff; + box-shadow: 0 12px 40px rgba(15, 23, 42, 0.16); + overflow: hidden; +} + +.oneos-shell-notify__detail-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 14px 16px; + border-bottom: 1px solid #e2e8f0; +} + +.oneos-shell-notify__detail-title { + margin: 0; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; + font-size: 14px; + font-weight: 700; + color: #0f172a; + line-height: 1.45; +} + +.oneos-shell-notify__detail-name { + flex: 1 1 100%; + font-size: 15px; + font-weight: 700; + color: #0f172a; +} + +.oneos-shell-notify__detail-close { + flex-shrink: 0; + border: 0; + background: transparent; + color: #64748b; + font-size: 12px; + font-weight: 600; + cursor: pointer; + padding: 4px 0; +} + +.oneos-shell-notify__detail-close:hover { + color: #0f172a; +} + +.oneos-shell-notify__detail-close:focus-visible { + outline: 2px solid color-mix(in srgb, var(--oneos-primary) 45%, transparent); + outline-offset: 2px; +} + +.oneos-shell-notify__detail-body { + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 10px; + max-height: min(360px, 50vh); + overflow-y: auto; +} + +.oneos-shell-notify__detail-text { + margin: 0; + font-size: 14px; + line-height: 1.65; + color: #334155; + white-space: pre-wrap; +} + +.oneos-shell-notify__detail-summary { + margin: 0; + font-size: 13px; + line-height: 1.5; + color: #64748b; +} + +.oneos-shell-notify__detail-foot { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 12px 16px; + border-top: 1px solid #e2e8f0; + background: #f8fafc; +} + +.oneos-shell-notify__detail-secondary, +.oneos-shell-notify__detail-primary { + border: 0; + border-radius: 8px; + padding: 8px 14px; + font-size: 13px; + font-weight: 600; + cursor: pointer; + min-height: 36px; +} + +.oneos-shell-notify__detail-secondary { + color: #334155; + background: #e2e8f0; +} + +.oneos-shell-notify__detail-secondary:hover { + background: #cbd5e1; +} + +.oneos-shell-notify__detail-primary { + color: #fff; + background: var(--oneos-primary); +} + +.oneos-shell-notify__detail-primary:hover { + filter: brightness(0.96); +} + +.oneos-shell-notify__detail-secondary:focus-visible, +.oneos-shell-notify__detail-primary:focus-visible { + outline: 2px solid color-mix(in srgb, var(--oneos-primary) 45%, transparent); + outline-offset: 2px; +} + +.oneos-shell-notify__empty { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + padding: 36px 20px 32px; + text-align: center; + animation: oneos-shell-empty-in 220ms ease-out; +} + +.oneos-shell-notify__empty-visual { + position: relative; + width: 64px; + height: 64px; + margin-bottom: 2px; +} + +.oneos-shell-notify__empty-ring { + position: absolute; + inset: 0; + border-radius: 999px; + background: + radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--oneos-primary) 14%, #fff) 0%, transparent 68%), + #f1f5f9; + border: 1px solid #e2e8f0; +} + +.oneos-shell-notify__empty-icon { + position: absolute; + inset: 0; + display: grid; + place-items: center; + color: #64748b; +} + +.oneos-shell-notify__empty-title { + margin: 0; + font-size: 14px; + font-weight: 700; + letter-spacing: 0.01em; + color: #0f172a; +} + +.oneos-shell-notify__empty-desc { + margin: 0; + max-width: 220px; + font-size: 12px; + line-height: 1.65; + color: #64748b; +} + +@keyframes oneos-shell-empty-in { + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@media (prefers-reduced-motion: reduce) { + .oneos-shell-notify__empty { + animation: none; + } +} + +@keyframes oneos-shell-pulse { + 0%, + 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.15); + opacity: 0.85; + } +} + +.oneos-shell-breadcrumb { + margin-left: 6px; + min-width: 0; +} + +.oneos-shell-breadcrumb ol { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0; + margin: 0; + padding: 0; + list-style: none; + font-size: 13px; + color: var(--oneos-muted); +} + +.oneos-shell-breadcrumb li { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.oneos-shell-breadcrumb__sep { + margin: 0 6px; + color: #cbd5e1; + font-weight: 400; +} + +.oneos-shell-breadcrumb .is-current { + color: var(--oneos-ink); + font-weight: 600; + letter-spacing: -0.01em; +} + +.oneos-shell-search { + display: inline-flex; + align-items: center; + gap: 8px; + height: 36px; + min-width: 180px; + padding: 0 12px; + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--oneos-border) 90%, transparent); + background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); + color: var(--oneos-muted); + cursor: text; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9); + transition: + border-color 0.2s var(--oneos-ease), + box-shadow 0.2s var(--oneos-ease), + background 0.2s var(--oneos-ease); +} + +.oneos-shell-search:hover { + border-color: color-mix(in srgb, var(--oneos-primary) 28%, var(--oneos-border)); +} + +.oneos-shell-search:focus-within { + border-color: color-mix(in srgb, var(--oneos-primary) 55%, transparent); + box-shadow: + 0 0 0 3px color-mix(in srgb, var(--oneos-primary) 16%, transparent), + inset 0 1px 0 rgba(255, 255, 255, 0.9); + background: #fff; +} + +.oneos-shell-search input { + flex: 1; + min-width: 0; + border: none; + background: transparent; + font: inherit; + font-size: 13px; + color: var(--oneos-ink); + outline: none; +} + +.oneos-shell-search input::placeholder { + color: #94a3b8; +} + +.oneos-shell-search kbd { + font-family: inherit; + font-size: 11px; + font-weight: 500; + line-height: 1; + padding: 3px 6px; + border-radius: 6px; + border: 1px solid var(--oneos-border); + background: #fff; + color: #94a3b8; + box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04); +} + +.oneos-shell-avatar { + width: 36px; + height: 36px; + padding: 2px; + border: none; + border-radius: 999px; + overflow: hidden; + background: linear-gradient(135deg, var(--oneos-primary-soft), var(--oneos-primary)); + cursor: pointer; + transition: + transform 0.2s var(--oneos-ease), + box-shadow 0.2s var(--oneos-ease); +} + +.oneos-shell-avatar:hover { + transform: translateY(-1px) scale(1.04); + box-shadow: 0 4px 12px color-mix(in srgb, var(--oneos-primary) 35%, transparent); +} + +.oneos-shell-avatar:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #fff, 0 0 0 4px color-mix(in srgb, var(--oneos-primary) 40%, transparent); +} + +.oneos-shell-avatar img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 999px; + border: 2px solid #fff; +} + +/* ——— Tabs ——— */ +.oneos-shell-tabs { + display: flex; + align-items: flex-end; + gap: 4px; + min-height: var(--oneos-tabs-h); + padding: 0 14px; + background: linear-gradient(180deg, #f1f5f9 0%, #e8eef5 100%); + border-top: 1px solid color-mix(in srgb, var(--oneos-border) 70%, transparent); +} + +.oneos-shell-tab { + display: inline-flex; + align-items: center; + gap: 4px; + min-height: 32px; + padding: 0 4px 0 12px; + margin-bottom: 0; + border-radius: 10px 10px 0 0; + background: transparent; + color: var(--oneos-muted); + font-size: 13px; + transition: + background 0.18s var(--oneos-ease), + color 0.18s var(--oneos-ease), + box-shadow 0.18s var(--oneos-ease); +} + +.oneos-shell-tab:hover:not(.is-active) { + background: color-mix(in srgb, var(--oneos-surface) 55%, transparent); + color: var(--oneos-ink); +} + +.oneos-shell-tab.is-active { + background: var(--oneos-surface); + color: var(--oneos-ink); + font-weight: 600; + box-shadow: + 0 -1px 0 #fff, + 0 0 0 1px color-mix(in srgb, var(--oneos-border) 60%, transparent); + position: relative; +} + +.oneos-shell-tab.is-active::after { + content: ''; + position: absolute; + left: 12px; + right: 12px; + bottom: 0; + height: 2px; + border-radius: 2px 2px 0 0; + background: linear-gradient(90deg, var(--oneos-primary-soft), var(--oneos-primary)); +} + +.oneos-shell-tab__label { + border: none; + background: transparent; + color: inherit; + font: inherit; + cursor: pointer; + padding: 7px 4px; + max-width: 160px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.oneos-shell-tab__close { + display: inline-grid; + place-items: center; + width: 18px; + height: 18px; + min-width: 18px; + border: none; + border-radius: 6px; + background: transparent; + color: var(--oneos-muted); + cursor: pointer; + padding: 0; + opacity: 0.65; + transition: + background 0.15s ease, + color 0.15s ease, + opacity 0.15s ease; +} + +.oneos-shell-tab:hover .oneos-shell-tab__close, +.oneos-shell-tab.is-active .oneos-shell-tab__close { + opacity: 1; +} + +.oneos-shell-tab__close:hover { + background: color-mix(in srgb, var(--oneos-ink) 8%, transparent); + color: var(--oneos-ink); +} + +.oneos-shell-content { + flex: 1; + min-height: 0; + overflow: hidden; + background: var(--oneos-bg); + display: flex; + flex-direction: column; +} + +.oneos-shell-frame { + flex: 1; + width: 100%; + height: 100%; + min-height: 0; + border: 0; + background: #fff; +} + +.oneos-shell-welcome { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; + padding: 48px 24px; + text-align: center; + color: var(--oneos-ink); +} + +.oneos-shell-welcome h1 { + margin: 0; + font-size: 1.5rem; + font-weight: 700; + letter-spacing: -0.02em; +} + +.oneos-shell-welcome p { + margin: 0; + max-width: 420px; + color: var(--oneos-muted); + line-height: 1.6; + font-size: 14px; +} + +.oneos-shell-content > .vm-page, +.oneos-shell-content > .wb-page { + min-height: 100%; + height: auto; + max-height: none; + overflow: auto; +} + +@media (max-width: 900px) { + .oneos-shell-search { + min-width: 0; + width: 36px; + padding: 0; + justify-content: center; + } + + .oneos-shell-search input, + .oneos-shell-search kbd { + display: none; + } + + .oneos-shell-breadcrumb { + display: none; + } +} + +@media (prefers-reduced-motion: reduce) { + .oneos-shell-aside, + .oneos-shell-menu__btn, + .oneos-shell-menu__icon, + .oneos-shell-menu__arrow, + .oneos-shell-brand__link, + .oneos-shell-brand__mark, + .oneos-shell-icon-btn, + .oneos-shell-avatar, + .oneos-shell-tab, + .oneos-shell-search, + .oneos-shell-menu__sub { + transition: none !important; + animation: none !important; + } + + .oneos-shell-icon-btn--notify::after { + animation: none; + } + + .oneos-shell-notify__callout, + .oneos-shell-notify__tag { + animation: none !important; + } +} diff --git a/src/common/oneos-app-shell/oneos-theme-content.css b/src/common/oneos-app-shell/oneos-theme-content.css new file mode 100644 index 0000000..b30f3ff --- /dev/null +++ b/src/common/oneos-app-shell/oneos-theme-content.css @@ -0,0 +1,163 @@ +/** + * OneOS 内容区暗色 token(参照原型导航 / VoltAgent 深色画布) + * 由 html[data-oneos-theme="dark"] 驱动;浅色保持各页原有 :root 定义。 + */ + +html[data-oneos-theme='dark'] { + color-scheme: dark; + + --ln-primary: #00d992; + --ln-primary-hover: #2fd6a1; + --ln-primary-focus: #10b981; + --ln-primary-active: #10b981; + --ln-primary-soft: rgba(0, 217, 146, 0.12); + --ln-primary-soft-strong: rgba(0, 217, 146, 0.2); + --ln-ink: #f2f2f2; + --ln-body: #bdbdbd; + --ln-body-strong: #f2f2f2; + --ln-muted: #8b949e; + --ln-muted-soft: #6b7280; + --ln-muted-80: #d1d5db; + --ln-divider-soft: #1a1a1a; + --ln-hairline: #3d3a39; + --ln-hairline-strong: #565656; + --ln-hairline-tertiary: #3d3a39; + --ln-canvas: #101010; + --ln-canvas-parchment: #101010; + --ln-surface-pearl: #1a1a1a; + --ln-surface-card: #1a1a1a; + --ln-surface-strong: #222222; + --ln-on-primary: #101010; + --ln-success: #00d992; + --ln-warning: #f59e0b; + --ln-error: #f87171; + --ln-brand-secure: #2fd6a1; + --ln-shadow-soft: none; + --ln-shadow-hover: none; + --ln-shadow-float: none; + --ln-shadow-modal: 0 20px 40px rgba(0, 0, 0, 0.45); + --ln-link: #00d992; + --ln-canvas-soft: #1a1a1a; + + --vm-primary: var(--ln-primary); + --vm-primary-dark: var(--ln-primary-focus); + --vm-primary-soft: var(--ln-primary-soft); + --vm-bg: var(--ln-canvas-parchment); + --vm-surface: var(--ln-surface-card); + --vm-border: var(--ln-hairline); + --vm-text: var(--ln-ink); + --vm-muted: var(--ln-muted); + --vm-danger: var(--ln-error); + --vm-warning: var(--ln-warning); + --vm-link: var(--ln-link); +} + +html[data-oneos-theme='dark'], +html[data-oneos-theme='dark'] body { + background-color: #101010 !important; + color: #f2f2f2; +} + +html[data-oneos-theme='dark'] #root { + background-color: #101010; + color: #f2f2f2; + min-height: 100%; +} + +/* 常见页面根容器 */ +html[data-oneos-theme='dark'] .vm-page, +html[data-oneos-theme='dark'] .wb-page, +html[data-oneos-theme='dark'] .page, +html[data-oneos-theme='dark'] .ant-layout, +html[data-oneos-theme='dark'] .ant-layout-content { + background: #101010 !important; + color: #f2f2f2; +} + +html[data-oneos-theme='dark'] .ant-card, +html[data-oneos-theme='dark'] .ant-table, +html[data-oneos-theme='dark'] .ant-table-container, +html[data-oneos-theme='dark'] .ant-table-thead > tr > th, +html[data-oneos-theme='dark'] .ant-modal-content, +html[data-oneos-theme='dark'] .ant-drawer-content, +html[data-oneos-theme='dark'] .ant-input, +html[data-oneos-theme='dark'] .ant-input-affix-wrapper, +html[data-oneos-theme='dark'] .ant-select-selector, +html[data-oneos-theme='dark'] .ant-picker { + background: #1a1a1a !important; + border-color: #3d3a39 !important; + color: #f2f2f2 !important; +} + +html[data-oneos-theme='dark'] .ant-table-thead > tr > th { + background: #161616 !important; + color: #bdbdbd !important; +} + +html[data-oneos-theme='dark'] .ant-table-tbody > tr > td { + border-color: #3d3a39 !important; + color: #f2f2f2 !important; +} + +html[data-oneos-theme='dark'] .ant-table-tbody > tr:hover > td { + background: #222222 !important; +} + +html[data-oneos-theme='dark'] .ant-btn-default { + background: #1a1a1a !important; + border-color: #3d3a39 !important; + color: #f2f2f2 !important; +} + +html[data-oneos-theme='dark'] .ant-btn-primary { + background: #00d992 !important; + border-color: #00d992 !important; + color: #101010 !important; +} + +/* 工作台 / 通用卡片:覆盖硬编码浅色渐变与表头 */ +html[data-oneos-theme='dark'] .wb-hero--welcome, +html[data-oneos-theme='dark'] .wb-hero--personal, +html[data-oneos-theme='dark'] .wb-panel, +html[data-oneos-theme='dark'] .wb-insights, +html[data-oneos-theme='dark'] .wb-section--flow, +html[data-oneos-theme='dark'] .wb-section--approval, +html[data-oneos-theme='dark'] .wb-kpi-tile, +html[data-oneos-theme='dark'] .wb-notice-card { + background: #1a1a1a !important; + box-shadow: none; + color: #f2f2f2; +} + +html[data-oneos-theme='dark'] .wb-kpi-chip { + background: #222222 !important; + box-shadow: none; + color: #f2f2f2; +} + +html[data-oneos-theme='dark'] .wb-panel-header, +html[data-oneos-theme='dark'] .wb-section-head, +html[data-oneos-theme='dark'] .wb-todo-table thead th, +html[data-oneos-theme='dark'] .wb-insight-table th, +html[data-oneos-theme='dark'] .wb-insight-metric { + background: #161616 !important; + color: #bdbdbd; +} + +html[data-oneos-theme='dark'] .wb-page--layout-personal { + background: #101010 !important; +} + +html[data-oneos-theme='dark'] .wb-todo-table tbody tr:hover td, +html[data-oneos-theme='dark'] .wb-kpi-chip:hover { + background: #222222 !important; +} + +html[data-oneos-theme='dark'] .wb-tabs, +html[data-oneos-theme='dark'] .wb-drawer, +html[data-oneos-theme='dark'] .wb-modal, +html[data-oneos-theme='dark'] .wb-overlay-panel { + background: #1a1a1a; + color: #f2f2f2; + border-color: #3d3a39; +} diff --git a/src/common/oneos-app-shell/release-demo-bridge.ts b/src/common/oneos-app-shell/release-demo-bridge.ts new file mode 100644 index 0000000..7ef51ba --- /dev/null +++ b/src/common/oneos-app-shell/release-demo-bridge.ts @@ -0,0 +1,64 @@ +/** 原型演示外壳 ↔ 工作台 iframe:版本更新日志 */ + +export const ONEOS_RELEASE_DEMO_ACTION = 'ONEOS_RELEASE_DEMO_ACTION'; + +export type ReleaseDemoAction = 'open' | 'reset'; + +export type ReleaseDemoActionPayload = { + type: typeof ONEOS_RELEASE_DEMO_ACTION; + action: ReleaseDemoAction; +}; + +export function isReleaseDemoAction(data: unknown): data is ReleaseDemoActionPayload { + if (!data || typeof data !== 'object') return false; + const payload = data as ReleaseDemoActionPayload; + return ( + payload.type === ONEOS_RELEASE_DEMO_ACTION && + (payload.action === 'open' || payload.action === 'reset') + ); +} + +export function postReleaseDemoAction(target: Window, action: ReleaseDemoAction) { + target.postMessage( + { type: ONEOS_RELEASE_DEMO_ACTION, action } satisfies ReleaseDemoActionPayload, + '*', + ); +} + +/** @deprecated 使用 postReleaseDemoAction(target, 'open') */ +export function postReleaseDemoReset(target: Window) { + postReleaseDemoAction(target, 'reset'); +} + +export function postReleaseDemoOpen(target: Window) { + postReleaseDemoAction(target, 'open'); +} + +/** 与工作台 `utils/release-seen.ts` 同源 key */ +export const RELEASE_SEEN_STORAGE_KEY = 'oneos-wb-release-seen-v1'; + +/** 当前发布版本(外壳「未读」点用;与 workbench data/release-notes 保持一致) */ +export const SHELL_CURRENT_RELEASE_VERSION = '1.1.5'; + +export function clearAllReleaseSeen(): void { + if (typeof window === 'undefined') return; + try { + window.localStorage.removeItem(RELEASE_SEEN_STORAGE_KEY); + } catch { + /* ignore */ + } +} + +export function hasUnseenRelease(operatorName?: string): boolean { + if (typeof window === 'undefined') return false; + try { + const raw = window.localStorage.getItem(RELEASE_SEEN_STORAGE_KEY); + if (!raw) return true; + const map = JSON.parse(raw) as Record; + if (!map || typeof map !== 'object') return true; + if (operatorName) return map[operatorName] !== SHELL_CURRENT_RELEASE_VERSION; + return !Object.values(map).includes(SHELL_CURRENT_RELEASE_VERSION); + } catch { + return true; + } +} diff --git a/src/common/oneos-app-shell/theme.ts b/src/common/oneos-app-shell/theme.ts new file mode 100644 index 0000000..c5cf3a9 --- /dev/null +++ b/src/common/oneos-app-shell/theme.ts @@ -0,0 +1,122 @@ +/** + * OneOS 浅色 / 暗色主题(localStorage + 跨 iframe postMessage) + * 暗色参照原型导航页(VoltAgent 深色画布)。 + * 不对「旧 ONEOS」目录下原型生效。 + */ + +import './oneos-theme-content.css'; + +export type OneOsTheme = 'light' | 'dark'; + +export const ONEOS_THEME_STORAGE_KEY = 'oneos-ui-theme'; +export const ONEOS_THEME_ATTR = 'data-oneos-theme'; +export const ONEOS_THEME_MESSAGE = 'ONEOS_THEME_CHANGE'; + +/** sidebar「旧ONEOS」目录下的原型,不接入主题切换 */ +export const LEGACY_ONEOS_PROTO_IDS = new Set([ + 'oneos-web-business', + 'oneos-web-data-analysis', + 'oneos-web-finance', + 'oneos-web-help-center', + 'oneos-web-lease-contract', + 'oneos-web-ledger-data', + 'oneos-web-ops', + 'oneos-web-procurement', +]); + +export function isLegacyOneOsPrototype(id?: string | null): boolean { + if (!id) return false; + return LEGACY_ONEOS_PROTO_IDS.has(id); +} + +export function readStoredOneOsTheme(): OneOsTheme { + if (typeof window === 'undefined') return 'light'; + try { + const v = window.localStorage.getItem(ONEOS_THEME_STORAGE_KEY); + if (v === 'dark' || v === 'light') return v; + } catch { + /* ignore */ + } + return 'light'; +} + +export function applyOneOsTheme(theme: OneOsTheme, root: HTMLElement = document.documentElement) { + root.setAttribute(ONEOS_THEME_ATTR, theme); + if (root === document.documentElement) { + document.documentElement.style.colorScheme = theme; + } +} + +export function persistOneOsTheme(theme: OneOsTheme) { + try { + window.localStorage.setItem(ONEOS_THEME_STORAGE_KEY, theme); + } catch { + /* ignore */ + } +} + +export function broadcastOneOsTheme(theme: OneOsTheme) { + if (typeof window === 'undefined') return; + window.dispatchEvent(new CustomEvent(ONEOS_THEME_MESSAGE, { detail: { theme } })); + try { + window.parent?.postMessage({ type: ONEOS_THEME_MESSAGE, theme }, '*'); + } catch { + /* ignore */ + } + const frames = document.querySelectorAll('iframe'); + frames.forEach((frame) => { + try { + frame.contentWindow?.postMessage({ type: ONEOS_THEME_MESSAGE, theme }, '*'); + } catch { + /* ignore */ + } + }); +} + +export function setOneOsTheme(theme: OneOsTheme) { + applyOneOsTheme(theme); + persistOneOsTheme(theme); + broadcastOneOsTheme(theme); +} + +export function toggleOneOsTheme(current?: OneOsTheme): OneOsTheme { + const next: OneOsTheme = (current ?? readStoredOneOsTheme()) === 'dark' ? 'light' : 'dark'; + setOneOsTheme(next); + return next; +} + +type BootstrapOptions = { + prototypeId?: string; + /** 强制跳过(如旧 ONEOS) */ + skip?: boolean; +}; + +/** + * 在原型预览入口调用:同步 localStorage / URL / 父页 postMessage。 + */ +export function bootstrapOneOsTheme(options: BootstrapOptions = {}) { + if (typeof window === 'undefined') return; + if (options.skip || isLegacyOneOsPrototype(options.prototypeId)) return; + + const fromQuery = new URLSearchParams(window.location.search).get('oneosTheme'); + const initial: OneOsTheme = + fromQuery === 'dark' || fromQuery === 'light' ? fromQuery : readStoredOneOsTheme(); + + applyOneOsTheme(initial); + persistOneOsTheme(initial); + + window.addEventListener('message', (event) => { + const data = event.data; + if (!data || data.type !== ONEOS_THEME_MESSAGE) return; + if (data.theme !== 'dark' && data.theme !== 'light') return; + applyOneOsTheme(data.theme); + persistOneOsTheme(data.theme); + }); + + window.addEventListener('storage', (event) => { + if (event.key !== ONEOS_THEME_STORAGE_KEY) return; + if (event.newValue === 'dark' || event.newValue === 'light') { + applyOneOsTheme(event.newValue); + } + }); +} diff --git a/src/common/selfOperatedLogisticsBridge.js b/src/common/selfOperatedLogisticsBridge.js new file mode 100644 index 0000000..2adff2c --- /dev/null +++ b/src/common/selfOperatedLogisticsBridge.js @@ -0,0 +1,567 @@ +/** + * 自营物流闭环桥:自营合同 ↔ 轻量调度任务 ↔ 车辆运营态 ↔ 物流业务明细自动生成 + * 原型本地种子 + localStorage,未接真实 API。 + * 规则见:src/resources/self-operated-logistics/assumptions.md + */ + +export const SO_CONTRACT_STORAGE_KEY = 'oneos.self-operated.contracts.v1'; +export const SO_DISPATCH_STORAGE_KEY = 'oneos.self-operated.dispatch-tasks.v1'; +export const SO_LEDGER_AUTO_STORAGE_KEY = 'oneos.self-operated.ledger-auto.v1'; +export const SO_VEHICLE_OPS_STORAGE_KEY = 'oneos.self-operated.vehicle-ops.v1'; + +export const CONTRACT_STATUS = { + draft: { key: 'draft', label: '草稿', color: 'default' }, + active: { key: 'active', label: '生效', color: 'processing' }, + performing: { key: 'performing', label: '履约中', color: 'success' }, + ended: { key: 'ended', label: '已结束', color: 'default' }, + terminated: { key: 'terminated', label: '已终止', color: 'error' }, +}; + +export const DISPATCH_STATUS = { + pending_assign: { key: 'pending_assign', label: '待派', color: 'default' }, + assigned: { key: 'assigned', label: '已派', color: 'processing' }, + in_progress: { key: 'in_progress', label: '执行中', color: 'warning' }, + completed: { key: 'completed', label: '已办结', color: 'success' }, + cancelled: { key: 'cancelled', label: '已取消', color: 'error' }, +}; + +/** inventory | self_operated */ +export const VEHICLE_OPS = { + inventory: 'inventory', + self_operated: 'self_operated', +}; + +export const SEED_VEHICLES = [ + { plateNo: '粤B·H2001', brandModel: '佛山飞驰·35T', opsStatus: VEHICLE_OPS.inventory }, + { plateNo: '粤B·H2002', brandModel: '佛山飞驰·35T', opsStatus: VEHICLE_OPS.self_operated }, + { plateNo: '粤B·H2003', brandModel: '未势能源·49T', opsStatus: VEHICLE_OPS.inventory }, + { plateNo: '粤B·H2008', brandModel: '未势能源·49T', opsStatus: VEHICLE_OPS.inventory }, +]; + +export const SEED_DRIVERS = [ + { name: '陈志强', phone: '13800138001' }, + { name: '李伟', phone: '13800138002' }, + { name: '王海涛', phone: '13800138003' }, +]; + +function nowStamp() { + const d = new Date(); + const p = (n) => String(n).padStart(2, '0'); + return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`; +} + +function todayDate() { + return nowStamp().slice(0, 10); +} + +function readJson(key, fallback) { + try { + const raw = localStorage.getItem(key); + if (!raw) return fallback; + const parsed = JSON.parse(raw); + return parsed == null ? fallback : parsed; + } catch { + return fallback; + } +} + +function writeJson(key, value) { + localStorage.setItem(key, JSON.stringify(value)); + try { + window.dispatchEvent(new CustomEvent('oneos-self-operated-bridge', { detail: { key } })); + } catch { + /* ignore */ + } +} + +function roundMoney(value) { + return Math.round(Number(value || 0) * 100) / 100; +} + +function computeAmount(unitPrice, quantity) { + return roundMoney(Number(unitPrice || 0) * Number(quantity || 0)); +} + +function computeTotalCost(row) { + return roundMoney( + Number(row.hydrogenFee || 0) + + Number(row.manualReimbursement || 0) + + Number(row.etcFee || 0) + + Number(row.electricityFee || 0) + + Number(row.salary || 0) + + Number(row.vehicleFee || 0), + ); +} + +function brandModelOf(plateNo) { + const hit = SEED_VEHICLES.find((v) => v.plateNo === plateNo); + return hit ? hit.brandModel : ''; +} + +// —— 车辆运营态 —— + +export function loadVehicleOpsMap() { + const stored = readJson(SO_VEHICLE_OPS_STORAGE_KEY, null); + if (stored && typeof stored === 'object') return stored; + const map = {}; + SEED_VEHICLES.forEach((v) => { + map[v.plateNo] = v.opsStatus; + }); + writeJson(SO_VEHICLE_OPS_STORAGE_KEY, map); + return map; +} + +export function getVehicleOpsStatus(plateNo) { + const map = loadVehicleOpsMap(); + return map[plateNo] || VEHICLE_OPS.inventory; +} + +export function setVehicleOpsStatus(plateNo, status) { + const map = loadVehicleOpsMap(); + map[plateNo] = status; + writeJson(SO_VEHICLE_OPS_STORAGE_KEY, map); + return map; +} + +/** + * 模拟运维交车:库存 → 自营 + */ +export function simulateHandover(plateNo) { + const plate = String(plateNo || '').trim(); + if (!plate) return { ok: false, message: '请选择车牌' }; + const current = getVehicleOpsStatus(plate); + if (current === VEHICLE_OPS.self_operated) { + return { ok: true, message: `${plate} 已在自营运营态`, already: true }; + } + setVehicleOpsStatus(plate, VEHICLE_OPS.self_operated); + return { ok: true, message: `已模拟交车:${plate} → 自营运营态` }; +} + +/** + * 模拟运维还车:自营 → 库存;存在未办结任务时拦截 + */ +export function simulateReturn(plateNo) { + const plate = String(plateNo || '').trim(); + if (!plate) return { ok: false, message: '请选择车牌' }; + const open = loadDispatchTasks().filter( + (t) => t.plateNo === plate && !['completed', 'cancelled'].includes(t.status), + ); + if (open.length > 0) { + return { + ok: false, + message: `${plate} 仍有 ${open.length} 条未办结调度任务,无法还车`, + }; + } + setVehicleOpsStatus(plate, VEHICLE_OPS.inventory); + return { ok: true, message: `已模拟还车:${plate} → 库存` }; +} + +// —— 合同 —— + +function seedContracts() { + return [ + { + id: 'soc-seed-001', + contractCode: 'ZY-2026-0001', + customerName: '深圳城配物流有限公司', + businessName: '深圳南山短驳项目', + vehicleType: '35T 氢能重卡', + vehicleCount: 2, + serviceStart: '2026-07-01', + serviceEnd: '2026-12-31', + handoverDate: '2026-07-05', + handoverPlace: '深圳坪山运维基地', + pricingSummary: '线路计价 · 元/趟', + unitPriceDefault: 1800, + status: 'active', + attachmentName: '自营服务合同-南山短驳.pdf', + createdAt: '2026-06-28 10:00', + activatedAt: '2026-06-28 15:20', + remark: '种子合同:已生效,可创建调度任务', + }, + { + id: 'soc-seed-002', + contractCode: 'ZY-2026-0002', + customerName: '广州港务运输集团', + businessName: '南沙港区接驳', + vehicleType: '49T 氢能重卡', + vehicleCount: 1, + serviceStart: '2026-08-01', + serviceEnd: '2027-01-31', + handoverDate: '2026-08-03', + handoverPlace: '广州南沙停车场', + pricingSummary: '线路计价 · 元/趟', + unitPriceDefault: 2200, + status: 'draft', + attachmentName: '', + createdAt: '2026-07-10 09:30', + activatedAt: '', + remark: '草稿:需业务确认生效', + }, + ]; +} + +export function loadContracts() { + const stored = readJson(SO_CONTRACT_STORAGE_KEY, null); + if (Array.isArray(stored) && stored.length > 0) return stored; + const seed = seedContracts(); + writeJson(SO_CONTRACT_STORAGE_KEY, seed); + return seed; +} + +export function saveContracts(list) { + writeJson(SO_CONTRACT_STORAGE_KEY, list || []); +} + +export function getContractById(id) { + return loadContracts().find((c) => c.id === id) || null; +} + +export function getActiveContracts() { + return loadContracts().filter((c) => c.status === 'active' || c.status === 'performing'); +} + +export function createContract(payload) { + const list = loadContracts(); + const n = list.length + 1; + const row = { + id: `soc-${Date.now()}`, + contractCode: payload.contractCode || `ZY-2026-${String(n).padStart(4, '0')}`, + customerName: String(payload.customerName || '').trim(), + businessName: String(payload.businessName || '').trim(), + vehicleType: String(payload.vehicleType || '').trim(), + vehicleCount: Number(payload.vehicleCount) || 1, + serviceStart: payload.serviceStart || '', + serviceEnd: payload.serviceEnd || '', + handoverDate: payload.handoverDate || '', + handoverPlace: payload.handoverPlace || '', + pricingSummary: payload.pricingSummary || '线路计价', + unitPriceDefault: Number(payload.unitPriceDefault) || 0, + status: 'draft', + attachmentName: payload.attachmentName || '', + createdAt: nowStamp(), + activatedAt: '', + remark: payload.remark || '', + }; + list.unshift(row); + saveContracts(list); + return row; +} + +/** + * 草稿 → 生效;进入可派车;标记可发起交车(原型 toast/handoverHint) + */ +export function activateContract(contractId) { + const list = loadContracts(); + const idx = list.findIndex((c) => c.id === contractId); + if (idx < 0) return { ok: false, message: '合同不存在' }; + const c = list[idx]; + if (c.status !== 'draft') { + return { ok: false, message: `当前状态「${CONTRACT_STATUS[c.status]?.label || c.status}」不可生效` }; + } + list[idx] = { + ...c, + status: 'active', + activatedAt: nowStamp(), + }; + saveContracts(list); + return { + ok: true, + contract: list[idx], + message: `合同 ${c.contractCode} 已生效,可创建调度任务;交车请在调度侧「模拟交车」或运维交车办理`, + handoverHint: { + contractCode: c.contractCode, + handoverDate: c.handoverDate, + handoverPlace: c.handoverPlace, + vehicleCount: c.vehicleCount, + }, + }; +} + +export function endContract(contractId) { + const list = loadContracts(); + const idx = list.findIndex((c) => c.id === contractId); + if (idx < 0) return { ok: false, message: '合同不存在' }; + const open = loadDispatchTasks().filter( + (t) => t.contractId === contractId && !['completed', 'cancelled'].includes(t.status), + ); + if (open.length > 0) { + return { ok: false, message: `仍有 ${open.length} 条未办结调度任务,无法结束合同` }; + } + list[idx] = { ...list[idx], status: 'ended' }; + saveContracts(list); + return { ok: true, message: `合同 ${list[idx].contractCode} 已结束` }; +} + +// —— 调度任务 —— + +function seedDispatchTasks() { + return [ + { + id: 'sdt-seed-001', + taskCode: 'DD-2026-0001', + contractId: 'soc-seed-001', + contractCode: 'ZY-2026-0001', + businessName: '深圳南山短驳项目', + customerName: '深圳城配物流有限公司', + planDate: '2026-07-18', + routeDesc: '坪山基地 → 南山仓 A', + multiTrip: '否', + routePricing: '线路计价', + unitPrice: 1800, + quantity: 1, + plateNo: '粤B·H2002', + driver: '陈志强', + phone: '13800138001', + status: 'assigned', + salary: 350, + remark: '种子任务:车辆已在自营态,可直接办结演示自动台账', + createdAt: '2026-07-15 11:00', + assignedAt: '2026-07-15 11:05', + completedAt: '', + ledgerRowId: '', + }, + ]; +} + +export function loadDispatchTasks() { + const stored = readJson(SO_DISPATCH_STORAGE_KEY, null); + if (Array.isArray(stored) && stored.length > 0) return stored; + const seed = seedDispatchTasks(); + writeJson(SO_DISPATCH_STORAGE_KEY, seed); + return seed; +} + +export function saveDispatchTasks(list) { + writeJson(SO_DISPATCH_STORAGE_KEY, list || []); +} + +export function createDispatchTask(payload) { + const contract = getContractById(payload.contractId); + if (!contract) return { ok: false, message: '请选择自营合同' }; + if (!['active', 'performing'].includes(contract.status)) { + return { ok: false, message: '仅生效/履约中的合同可创建调度任务' }; + } + const list = loadDispatchTasks(); + const n = list.length + 1; + const plateNo = String(payload.plateNo || '').trim(); + const hasPlate = Boolean(plateNo); + const row = { + id: `sdt-${Date.now()}`, + taskCode: `DD-2026-${String(n).padStart(4, '0')}`, + contractId: contract.id, + contractCode: contract.contractCode, + businessName: contract.businessName, + customerName: contract.customerName, + planDate: payload.planDate || todayDate(), + routeDesc: String(payload.routeDesc || '').trim(), + multiTrip: payload.multiTrip === '是' ? '是' : '否', + routePricing: payload.routePricing || contract.pricingSummary || '线路计价', + unitPrice: Number(payload.unitPrice ?? contract.unitPriceDefault) || 0, + quantity: Number(payload.quantity) || 1, + plateNo, + driver: String(payload.driver || '').trim(), + phone: String(payload.phone || '').trim(), + status: hasPlate ? 'assigned' : 'pending_assign', + salary: Number(payload.salary) || 0, + remark: String(payload.remark || '').trim(), + createdAt: nowStamp(), + assignedAt: hasPlate ? nowStamp() : '', + completedAt: '', + ledgerRowId: '', + }; + list.unshift(row); + saveDispatchTasks(list); + if (contract.status === 'active') { + const contracts = loadContracts(); + const cidx = contracts.findIndex((c) => c.id === contract.id); + if (cidx >= 0) { + contracts[cidx] = { ...contracts[cidx], status: 'performing' }; + saveContracts(contracts); + } + } + return { ok: true, task: row, message: `已创建调度任务 ${row.taskCode}` }; +} + +export function assignDispatchTask(taskId, payload) { + const list = loadDispatchTasks(); + const idx = list.findIndex((t) => t.id === taskId); + if (idx < 0) return { ok: false, message: '任务不存在' }; + const t = list[idx]; + if (['completed', 'cancelled'].includes(t.status)) { + return { ok: false, message: '已办结/已取消任务不可改派' }; + } + const plateNo = String(payload.plateNo || '').trim(); + if (!plateNo) return { ok: false, message: '请选择车牌' }; + list[idx] = { + ...t, + plateNo, + driver: String(payload.driver || t.driver || '').trim(), + phone: String(payload.phone || t.phone || '').trim(), + status: 'assigned', + assignedAt: nowStamp(), + }; + saveDispatchTasks(list); + return { ok: true, task: list[idx], message: `已派车 ${plateNo}` }; +} + +export function startDispatchTask(taskId) { + const list = loadDispatchTasks(); + const idx = list.findIndex((t) => t.id === taskId); + if (idx < 0) return { ok: false, message: '任务不存在' }; + const t = list[idx]; + if (t.status !== 'assigned') { + return { ok: false, message: '仅「已派」任务可开始执行' }; + } + if (!t.plateNo) return { ok: false, message: '请先派车' }; + list[idx] = { ...t, status: 'in_progress' }; + saveDispatchTasks(list); + return { ok: true, task: list[idx], message: '任务已进入执行中' }; +} + +export function cancelDispatchTask(taskId) { + const list = loadDispatchTasks(); + const idx = list.findIndex((t) => t.id === taskId); + if (idx < 0) return { ok: false, message: '任务不存在' }; + const t = list[idx]; + if (t.status === 'completed') { + return { ok: false, message: '已办结任务不可取消(台账行已生成)' }; + } + if (t.status === 'cancelled') return { ok: true, task: t, message: '任务已是取消状态' }; + list[idx] = { ...t, status: 'cancelled' }; + saveDispatchTasks(list); + return { ok: true, task: list[idx], message: `已取消 ${t.taskCode}` }; +} + +/** + * 办结 → 自动生成台账行(严格:车辆须已交车) + */ +export function completeDispatchTask(taskId, confirmPayload) { + const list = loadDispatchTasks(); + const idx = list.findIndex((t) => t.id === taskId); + if (idx < 0) return { ok: false, message: '任务不存在' }; + const t = list[idx]; + if (t.status === 'completed') { + return { ok: false, message: '任务已办结', ledgerRowId: t.ledgerRowId }; + } + if (t.status === 'cancelled') { + return { ok: false, message: '已取消任务不可办结' }; + } + if (!t.plateNo) return { ok: false, message: '请先派车再办结' }; + + const ops = getVehicleOpsStatus(t.plateNo); + if (ops !== VEHICLE_OPS.self_operated) { + return { + ok: false, + code: 'NEED_HANDOVER', + message: `${t.plateNo} 尚未交车(当前非自营运营态),请先「模拟交车」或完成运维交车后再办结`, + }; + } + + const dispatchDate = (confirmPayload && confirmPayload.dispatchDate) || t.planDate || todayDate(); + const quantity = Number(confirmPayload?.quantity ?? t.quantity) || 1; + const unitPrice = Number(confirmPayload?.unitPrice ?? t.unitPrice) || 0; + const salary = Number(confirmPayload?.salary ?? t.salary) || 0; + const remark = String(confirmPayload?.remark ?? t.remark ?? '').trim(); + + const year = dispatchDate.slice(0, 4); + const month = String(Number(dispatchDate.slice(5, 7)) || 1); + const amount = computeAmount(unitPrice, quantity); + const ledgerDraft = { + id: `auto-${t.id}`, + year, + month, + dispatchDate, + businessName: t.businessName, + driver: t.driver, + phone: t.phone, + plateNo: t.plateNo, + systemVehicleType: brandModelOf(t.plateNo), + unitPrice, + quantity, + amount, + hydrogenFee: 0, + etcFee: 0, + salary, + electricityFee: 0, + manualReimbursement: 0, + dailySocialSecurityFee: 0, + dailyTrailerServiceFee: 0, + dailyTrailerFee: 0, + dailyParkingFee: 0, + dailyTireFee: 0, + vehicleFee: 0, + totalCost: 0, + profitLoss: 0, + multiTrip: t.multiTrip || '否', + routePricing: t.routePricing || '线路计价', + remark: remark ? `[调度自动·${t.taskCode}] ${remark}` : `[调度自动·${t.taskCode}]`, + rowSource: 'auto', + dispatchTaskId: t.id, + dispatchTaskCode: t.taskCode, + contractCode: t.contractCode, + }; + ledgerDraft.totalCost = computeTotalCost(ledgerDraft); + ledgerDraft.profitLoss = roundMoney(amount - ledgerDraft.totalCost); + + const autoRows = loadAutoLedgerRows(); + const withoutDup = autoRows.filter((r) => r.dispatchTaskId !== t.id && r.id !== ledgerDraft.id); + withoutDup.unshift(ledgerDraft); + saveAutoLedgerRows(withoutDup); + + list[idx] = { + ...t, + status: 'completed', + quantity, + unitPrice, + salary, + remark, + completedAt: nowStamp(), + ledgerRowId: ledgerDraft.id, + }; + saveDispatchTasks(list); + + return { + ok: true, + task: list[idx], + ledgerRow: ledgerDraft, + message: `已办结 ${t.taskCode},并自动生成物流业务明细 1 行`, + }; +} + +// —— 台账自动行 —— + +export function loadAutoLedgerRows() { + const stored = readJson(SO_LEDGER_AUTO_STORAGE_KEY, null); + return Array.isArray(stored) ? stored : []; +} + +export function saveAutoLedgerRows(list) { + writeJson(SO_LEDGER_AUTO_STORAGE_KEY, list || []); +} + +export function removeAutoLedgerRow(rowId) { + const next = loadAutoLedgerRows().filter((r) => r.id !== rowId); + saveAutoLedgerRows(next); + return next; +} + +/** + * 合并种子/当前列表与自动生成行:自动行按 id 覆盖同 id,其余追加到顶部 + */ +export function mergeLedgerWithAutoRows(baseRows) { + const auto = loadAutoLedgerRows(); + const autoIds = new Set(auto.map((r) => r.id)); + const rest = (baseRows || []).filter((r) => !autoIds.has(r.id)); + return [...auto, ...rest]; +} + +export function subscribeSelfOperatedBridge(handler) { + const fn = (e) => handler(e?.detail); + window.addEventListener('oneos-self-operated-bridge', fn); + window.addEventListener('storage', fn); + return () => { + window.removeEventListener('oneos-self-operated-bridge', fn); + window.removeEventListener('storage', fn); + }; +} diff --git a/src/prototypes/business-dept-ledger/.spec/requirements-prd.md b/src/prototypes/business-dept-ledger/.spec/requirements-prd.md new file mode 100644 index 0000000..c86cf9f --- /dev/null +++ b/src/prototypes/business-dept-ledger/.spec/requirements-prd.md @@ -0,0 +1,40 @@ +# 业务部台账 · 产品需求说明(PRD) + +> 原型路径:`/prototypes/business-dept-ledger` +> 实现页:`src/prototypes/oneos-web-data-analysis/pages/05-业务部台账.jsx` + +--- + +## 1. 模块定位 + +| 项 | 说明 | +|---|---| +| 目标用户 | 业务部业管 / 运营查看汇总 | +| 核心任务 | 按业务条线汇总查看业绩、成本、盈亏,并支持租赁/氢费下钻 | +| 内容来源 | 各业务明细模块按月汇总(原型本地种子,未接真实 API) | + +--- + +## 2. 各业务条线数据统计来源(摘要) + +主表分组表头在业务名称后方标注最小颗粒度来源;悬停可查看业绩/成本/盈亏字段口径。 + +| 业务条线 | 最小颗粒度 | 业绩 | 成本 | 盈亏 | +|---|---|---|---|---| +| 物流业务 | 物流业务明细 | 「金额」列 | 「总成本」列 | 金额 − 总成本 | +| 租赁业务 | 租赁业务明细 | 「实收金额」列 | 「车辆实际成本」列 | 实收金额 − 车辆实际成本 | +| 氢费业务 | 车辆氢费明细 | 「加氢总价(元)」列 | 「成本总价(元)」列 | 加氢总价 − 成本总价 | +| 电费业务 | 充电订单 | 「对客费用(元)」列 | 「成本费用(元)」列 | 对客费用 − 成本费用 | +| ETC业务 | ETC业务 | 字段口径待补充 | 字段口径待补充 | 字段口径待补充 | + +完整判定、前置条件与代码映射见 [sector-data-source.md](./sector-data-source.md)。 + +--- + +## 3. 验收重点 + +- [ ] 主表五条业务线后方分别显示:`物流业务明细` / `租赁业务明细` / `车辆氢费明细` / `充电订单` / `ETC业务`(不再显示「预留明细」) +- [ ] 悬停业务名称或来源标注,可看到该条线最小颗粒度与业绩/成本/盈亏口径 +- [ ] 悬停「业绩」「成本」「盈亏」列头,提示对应明细字段 +- [ ] 电费来源为「充电订单」;ETC 来源为「ETC业务」 +- [ ] 文档与页面口径一致:`.spec/sector-data-source.md`、本 PRD diff --git a/src/prototypes/business-dept-ledger/.spec/sector-data-source.md b/src/prototypes/business-dept-ledger/.spec/sector-data-source.md new file mode 100644 index 0000000..46ec61d --- /dev/null +++ b/src/prototypes/business-dept-ledger/.spec/sector-data-source.md @@ -0,0 +1,60 @@ +# 业务部台账 · 各业务条线数据统计来源 + +> 实现:`src/prototypes/oneos-web-data-analysis/pages/05-业务部台账.jsx` +> 常量:`SECTOR_DATA_SOURCE`、`SECTOR_METRIC_SOURCE` +> UI:主表分组表头「业务名称 ← 明细来源」;业绩/成本/盈亏列悬停展示字段口径 + +## 对照数据源 + +| 业务条线 | 最小颗粒度数据来源 | 原型本地种子 | 真实 API | +|---|---|---|---| +| 物流业务 | 物流业务明细 | 是(汇总种子) | 未接 | +| 租赁业务 | 租赁业务明细 | 是(汇总种子) | 未接 | +| 氢费业务 | 车辆氢费明细 | 是(汇总种子) | 未接 | +| 电费业务 | 充电订单 | 是(汇总种子) | 未接 | +| ETC业务 | ETC业务 | 是(汇总种子) | 未接 | + +主表当前展示的是**按月(或客户/事业部)汇总后的结果**;上表「最小颗粒度」指最终应回溯到的明细模块。 + +## 业绩 / 成本 / 盈亏口径(判定顺序) + +按业务条线取数;同一条线内先取业绩、成本字段,再计算盈亏。 + +| 优先级 | 指标 | 规则 | +|---|---|---| +| 1 | 业绩 | 取对应明细模块指定金额列(见下表) | +| 2 | 成本 | 取对应明细模块指定成本列(见下表) | +| 3 | 盈亏 | 业绩 − 成本(见下表公式) | + +### 分条线字段映射 + +| 业务条线 | 业绩来源列 | 成本来源列 | 盈亏公式 | +|---|---|---|---| +| 物流业务 | 物流业务明细「金额」 | 物流业务明细「总成本」 | 金额 − 总成本 | +| 租赁业务 | 租赁业务明细「实收金额」 | 租赁业务明细「车辆实际成本」 | 实收金额 − 车辆实际成本 | +| 氢费业务 | 车辆氢费明细「加氢总价(元)」 | 车辆氢费明细「成本总价(元)」 | 加氢总价(元)− 成本总价(元) | +| 电费业务 | 充电订单「对客费用(元)」 | 充电订单「成本费用(元)」 | 对客费用(元)− 成本费用(元) | +| ETC业务 | ETC业务(字段口径待补充) | ETC业务(字段口径待补充) | ETC业务(字段口径待补充) | + +## 前置条件 + +- 仅在业务部台账主表分组列展示来源标注;钻取子页「数据来源」仍使用 `SECTOR_DATA_SOURCE` 短名。 +- ETC 业务本期仅确认最小颗粒度模块为「ETC业务」,业绩/成本/盈亏字段列名待业务补充后更新本规格与代码常量。 + +## 用户可见结果 + +| 位置 | 展示 | +|---|---| +| 分组表头业务名称后方 | `←物流业务明细` / `←租赁业务明细` / `←车辆氢费明细` / `←充电订单` / `←ETC业务` | +| 悬停业务名称或来源标注 | 最小颗粒度 + 业绩/成本/盈亏口径全文 | +| 悬停「业绩」「成本」「盈亏」列头 | 该列对应字段口径 | + +## 与代码映射 + +| 常量 / 函数 | 作用 | +|---|---| +| `SECTOR_DATA_SOURCE` | 表头后方短名;钻取页数据来源短名 | +| `SECTOR_METRIC_SOURCE` | 业绩/成本/盈亏字段口径 | +| `buildSectorSourceTooltip` | 分组表头悬停全文 | +| `renderSectorMetricTitle` | 指标列头悬停 | +| `renderSectorGroupTitle` | 渲染「业务名 ← 来源」 | diff --git a/src/prototypes/business-dept-ledger/annotation-source.json b/src/prototypes/business-dept-ledger/annotation-source.json new file mode 100644 index 0000000..7db424d --- /dev/null +++ b/src/prototypes/business-dept-ledger/annotation-source.json @@ -0,0 +1,69 @@ +{ + "documentVersion": 1, + "format": "axhub-annotation-source", + "data": { + "version": 2, + "prototypeName": "business-dept-ledger", + "pageId": "main", + "updatedAt": 1783938000000, + "nodes": [ + { + "id": "bdl-list-table", + "index": 1, + "title": "主表 · 业务条线数据来源", + "pageId": "main", + "locator": { + "selectors": [ + "[data-annotation-id=\"bdl-list-table\"]" + ], + "fingerprint": "bdl-list-table", + "path": [] + }, + "aiPrompt": "业务部台账主表各业务条线业绩/成本/盈亏数据来源口径。", + "annotationText": "## 各业务条线数据统计来源\n\n分组表头业务名称后方标注最小颗粒度来源;悬停查看业绩/成本/盈亏字段口径。\n\n| 业务条线 | 最小颗粒度 | 业绩 | 成本 | 盈亏 |\n|---|---|---|---|---|\n| 物流业务 | 物流业务明细 | 金额 | 总成本 | 金额 − 总成本 |\n| 租赁业务 | 租赁业务明细 | 实收金额 | 车辆实际成本 | 实收金额 − 车辆实际成本 |\n| 氢费业务 | 车辆氢费明细 | 加氢总价(元) | 成本总价(元) | 加氢总价 − 成本总价 |\n| 电费业务 | 充电订单 | 对客费用(元) | 成本费用(元) | 对客费用 − 成本费用 |\n| ETC业务 | ETC业务 | 待补充 | 待补充 | 待补充 |\n\n完整规格见 `.spec/sector-data-source.md`。原型本地种子,未接真实 API。", + "hasMarkdown": true, + "color": "#2563eb", + "images": [], + "createdAt": 1783938000000, + "updatedAt": 1783938000000 + } + ], + "notes": { + "bdl-list-table": "## 各业务条线数据统计来源\n\n分组表头业务名称后方标注最小颗粒度来源;悬停查看业绩/成本/盈亏字段口径。\n\n| 业务条线 | 最小颗粒度 | 业绩 | 成本 | 盈亏 |\n|---|---|---|---|---|\n| 物流业务 | 物流业务明细 | 金额 | 总成本 | 金额 − 总成本 |\n| 租赁业务 | 租赁业务明细 | 实收金额 | 车辆实际成本 | 实收金额 − 车辆实际成本 |\n| 氢费业务 | 车辆氢费明细 | 加氢总价(元) | 成本总价(元) | 加氢总价 − 成本总价 |\n| 电费业务 | 充电订单 | 对客费用(元) | 成本费用(元) | 对客费用 − 成本费用 |\n| ETC业务 | ETC业务 | 待补充 | 待补充 | 待补充 |\n\n完整规格见 `.spec/sector-data-source.md`。原型本地种子,未接真实 API。", + "bdl-doc-sector-data-source": "# 业务部台账 · 各业务条线数据统计来源\n\n见 `.spec/sector-data-source.md`。" + }, + "directory": [ + { + "type": "group", + "id": "bdl-dir-logic", + "title": "业务逻辑规格", + "children": [ + { + "type": "note", + "id": "bdl-doc-sector-data-source", + "title": "各业务条线数据统计来源", + "markdownPath": ".spec/sector-data-source.md" + }, + { + "type": "note", + "id": "bdl-doc-prd", + "title": "产品需求说明(PRD)", + "markdownPath": ".spec/requirements-prd.md" + } + ] + }, + { + "type": "group", + "id": "bdl-dir-page", + "title": "页面", + "children": [ + { + "type": "annotation", + "id": "bdl-list-table", + "title": "主表 · 业务条线数据来源" + } + ] + } + ] + } +} diff --git a/src/prototypes/insurance-procurement/.spec/requirements-prd.md b/src/prototypes/insurance-procurement/.spec/requirements-prd.md new file mode 100644 index 0000000..fe65ade --- /dev/null +++ b/src/prototypes/insurance-procurement/.spec/requirements-prd.md @@ -0,0 +1,376 @@ +# 保险采购 · 产品需求说明(全模块) + +| 项 | 内容 | +|---|---| +| 模块名称 | 保险采购 | +| 所属系统 | ONE-OS(PC) | +| 交互原型 | `/prototypes/insurance-procurement` | +| 业务条线 | 运维管理条线(证照 / 保险相关) | +| 文档状态 | AutoPRD · 已对齐可运行原型 | + +--- + +## 1. 一句话与目标 + +**一句话** +在同一模块内管理车队**保单台账**与**采购前比价审批**两条业务线,保证续保不遗漏、变更可追溯,并为交车提供交强险 + 商业险合规校验。 + +**要解决的问题** +- 保险信息散落在表格、邮件与线下批单中,台账不全、临期易漏 +- 停保 / 复驶 / 退保缺少统一留痕 +- 采购前多方比价难追溯,审批结果难跟踪 +- 交车时无法与车辆管理共用同一套「保险是否有效」口径 + +**本期目标** +1. 一车一档维护五类险种台账;支持手工新增、批量识别、批量导入 +2. 支持停保 / 复驶 / 退保办理与操作历史 +3. 输出与车辆管理一致的车辆级保险状态,支撑交车拦截 +4. 比价单批次管理:多方报价、最晚付费预警、勾选提交采购审批(本页只读跟踪) + +**非目标(本期不做)** +- 比价单与保单台账**自动双向同步**;审批通过后**不自动写入**正式保单 +- 在本模块内办理审批(通过 / 驳回 / 撤回均在审批中心) +- 维护行驶证、道路运输证等证照档案(见证照管理) +- 事故赔付、保险上浮等事故链路台账(见事故管理) + +--- + +## 2. 模块边界(最重要) + +```mermaid +flowchart LR + IPC[保险采购] + VM[车辆管理] + OPS[交车管理] + APPR[审批中心] + CERT[证照管理] + WB[工作台] + IPC -->|输出保险状态口径| VM + VM -->|交车前置校验| OPS + CERT -.->|并列交车前置| OPS + IPC -->|发起采购审批| APPR + APPR -->|回写采购状态| IPC + WB -->|临期 KPI / 快捷入口| IPC +``` + +| 业务线 / 子域 | 做什么 | 不做什么 | +|---------------|--------|----------| +| 保单管理 | 一车一档、多通道录入、停保/复驶/退保、状态与交车口径 | 不替代证照管理;不办事故赔付 | +| 比价单 | 批次比价、付费预警、提交采购、只读跟踪审批 | 不自动生成正式保单;不在本页审批 | +| 与车辆 / 交车 | 提供交强 + 商业是否有效的统一口径 | 不在本页完成交车动作 | + +**外部依赖(产品口径)** + +| 依赖方 | 交互方式 | 产品要求 | +|--------|----------|----------| +| 车辆管理 | 读取保险状态 / 到期日 | 口径与本模块一致:异常则禁止交车 | +| 交车管理 | 交付前校验交强 + 商业有效 | 不合规须可理解拦截原因 | +| 审批中心 | 接收采购申请并回写状态 / 当前审批人 | 本页只读展示,不办理审批 | +| 工作台 | KPI、快捷「发起保险采购」 | 跳转至本模块对应能力 | +| 证照管理 | 并列交车前置 | 证照与保险各自独立维护 | + +**关键约束** +- **保单台账 ↔ 比价单互不自动同步**;审批通过后须运营在保单管理侧另行录入正式保单 +- **交车只看交强险 + 商业险**;超赔 / 货物 / 驾意仅参与临期预警,不参与交车判定 +- **最晚付费临期 / 超期**仅服务比价采购节奏,不写入保单台账 + +--- + +## 3. 用户与角色 + +| 角色 | 主要目标 | +|------|----------| +| 运维部 | 维护保单台账、办理停保/复驶/退保;交车前确认保险有效 | +| 安全部 | 从合规视角关注保险与证照并列的交车前置 | +| 采购部 | 创建比价单、录入报价、提交采购申请;关注临期与付费节奏 | +| 业务管理组(业管) | 在审批中心确认报价等节点(本页只读看到结果) | + +> 部门名对齐业务条线说明;`module-role-mapping` 将本模块主责记为运维部、安全部,采购部为比价线主操作用户。 + +--- + +## 4. 用户故事与故事点(业务条线说明口径) + +> 故事点供排期参考,可按团队基准调整。合计约 **28 SP**。 + +### Epic A · 保单台账与交车合规(约 15 SP) + +#### A1 · 一车一档台账与检索(约 3 SP) +- **角色**:运维部、采购部 +- **起点**:需要按车牌 / VIN / 运营状态 / 保险状态等快速定位车辆保险档案。 +- **怎么运作**: + 1. 在列表筛选或点击 KPI 卡片缩小范围。 + 2. 查看五类险种到期日与车辆级保险状态。 + 3. 进入「管理」打开车辆保险档案。 +- **关键结果**:`可按条件定位车辆` / `空态提示无可匹配台账` +- **闭环**:台账可检索、可下钻,与 KPI 口径一致。 +- **排期(可选)**:US-IPC-01 · 规模 M · 作为运维/采购,我想快速筛出台账车辆,以便处理续保与异常。 + +#### A2 · 多通道录入正式保单(约 5 SP) +- **角色**:运维部、采购部 +- **起点**:拿到新保或续保材料(纸质 / 电子保单、批量表格)。 +- **怎么运作**: + 1. 选择新增、保单批量识别或批量导入。 + 2. 识别类须人工确认后再落库;导入仅处理新保/续保。 + 3. 按车牌/VIN(或批单场景按保单号)匹配车辆并写入对应险种台账。 +- **关键结果**:`确认后落库` / `识别失败可理解提示` / `车牌不一致则拦截` +- **闭环**:正式保单进入一车一档,列表与车辆管理可见最新到期与状态。 +- **排期(可选)**:US-IPC-02 · 规模 L · 作为运维/采购,我想用识别或导入补齐台账,以便续保不遗漏。 + +#### A3 · 停保 / 复驶 / 退保留痕(约 4 SP) +- **角色**:运维部 +- **起点**:车辆需临时停保、恢复行驶或办理退保。 +- **怎么运作**: + 1. 在车辆保险档案对**当前有效**记录发起对应办理。 + 2. 填写时间、金额等业务字段并上传批单附件。 + 3. 写入操作历史;更新险种与车辆状态展示。 +- **关键结果**:`变更可追溯` / `归档记录只读` +- **闭环**:停保/复驶/退保全程留痕,交车口径随之更新。 +- **排期(可选)**:US-IPC-03 · 规模 M。 + +#### A4 · 交强 + 商业决定是否可交车(约 3 SP) +- **角色**:运维部、采购部 +- **起点**:采购/运维维护交强险与商业险台账,作为交车前置合规数据。 +- **怎么运作**: + 1. 台账维护一车一档核心险种。 + 2. 交车时按「交强 + 商业均为正常或临期」判定是否放行。 + 3. 异常(未购买 / 已到期 / 已退保视同无效)则拦截并提示原因。 +- **关键结果**:`允许交车` / `禁止交车 · 保险异常` +- **闭环**:交车合规与台账一致、可追溯;异常车辆不能进入履约运营。 +- **排期(可选)**:US-17 · 规模 M · 作为采购/运维,我想维护交强+商业险并在交车时校验,以便不合规车辆无法交车。 + +### Epic B · 比价采购与审批跟踪(约 13 SP) + +#### B1 · 比价单批次与付费预警(约 4 SP) +- **角色**:采购部 +- **起点**:需要按批次组织待购车辆与险种,并盯住最晚付费日。 +- **怎么运作**: + 1. 在比价单管理新建或编辑批次;可从临期预警一键带入。 + 2. 用全部 / 临期(≤3 天)/ 超期看板筛选批次。 + 3. 保存时校验购买记录、备注与附件。 +- **关键结果**:`可按付费节奏跟进` / `未满足保存条件则拦截` +- **闭环**:采购节奏可视、可预警;不与保单台账自动混写。 +- **排期(可选)**:US-IPC-04 · 规模 M。 + +#### B2 · 多方报价与提交采购(约 5 SP) +- **角色**:采购部 +- **起点**:同一购买需求需比较多家保司报价后发起采购。 +- **怎么运作**: + 1. 为每行录入多家报价并指定唯一最终比价。 + 2. 填写最晚付费日,勾选可提交行。 + 3. 提交采购申请进入审批中心。 +- **关键结果**:`审批中/已通过行不可再提` / `撤回或驳回后可再提` +- **闭环**:比价过程可追溯;正式保单仍须审批通过后在台账侧补录。 +- **排期(可选)**:US-IPC-05 · 规模 L。 + +#### B3 · 审批只读回显(约 4 SP) +- **角色**:采购部、业务管理组 +- **起点**:采购申请已在审批中心流转。 +- **怎么运作**: + 1. 本页展示采购状态与当前审批人(可为空)。 + 2. 业管等角色在审批中心办理节点。 + 3. 结果回写本页;通过后运营去保单管理录入正式保单。 +- **关键结果**:`本页不可办理审批` / `状态与审批中心一致` +- **闭环**:采购决策可跟踪;台账仍以人工录入为准。 +- **排期(可选)**:US-IPC-06 · 规模 M。 + +--- + +## 5. 功能模块说明(正向 / 逆向) + +### 5.1 保险台账列表与 KPI + +**正向** +1. 按车牌、VIN、品牌型号、运营状态、保险状态、险种 + 到期区间筛选。 +2. 点击 KPI(台账车辆、状态正常、核心逾期、核心待购等)联动列表或打开预警。 +3. 行内「管理」打开车辆保险档案。 + +**逆向 / 边界** + +| 情况 | 系统表现 | +|------|----------| +| 无匹配数据 | 空态提示无可匹配台账车辆 | +| 退出运营车辆 | 列表可查看但样式弱化 | +| 险种到期筛选未选险种 | 须先选险种再筛到期区间 | +| 车牌与多车牌条件互斥 | 按产品约定互斥,避免条件冲突 | + +### 5.2 保单录入(新增 / 识别 / 导入) + +**正向** +1. 新增:完整字段手工录入后保存。 +2. 批量识别:选业务类型 → 上传 → 识别 → **人工确认** → 落库。 +3. 批量导入:下载模板填写 → 上传 → 仅新保/续保入库。 +4. 识别任务记录:查看进度,对待确认结果继续确认。 + +**逆向 / 边界** + +| 情况 | 系统表现 | +|------|----------| +| 识别失败 | 提示清晰度 / 格式 / 损坏等原因 | +| 无成功结果可确认 | 提示暂无识别成功结果 | +| 识别车牌与所选车不一致 | 拦截并提示两边车牌 | +| 导入中的停保/复驶/退保行 | 自动跳过 | +| OCR 确认页 | 不展示承保险种明细、分项保费(手工路径仍可填) | + +### 5.3 车辆保险档案 · 停保 / 复驶 / 退保 + +**正向** +1. 分险种查看历史;对当前有效记录办理停保、复驶或退保。 +2. 填写业务时间 / 金额等并上传批单附件。 +3. 全周期时间轴与操作历史可追溯。 + +**逆向 / 边界** + +| 情况 | 系统表现 | +|------|----------| +| 历史归档记录 | 只读,不可再办停保/退保 | +| 已退保 | 仅允许复驶 | +| 已停保 | 可复驶或退保 | +| 新保覆盖已退保险种 | 旧记录归档后再写入新保单 | + +### 5.4 比价单管理与编辑 + +**正向** +1. 新建 / 编辑比价单,添加购买记录与多方报价。 +2. 设最终比价、最晚付费日;保存备注与附件。 +3. 勾选行提交采购;跟踪审批状态。 +4. 可从险种临期预警一键生成比价单(附件须补传)。 + +**逆向 / 边界** + +| 情况 | 系统表现 | +|------|----------| +| 保存校验不通过 | 拦截并提示缺记录 / 备注 / 附件等 | +| 审批中或已通过行 | 勾选禁用,不可重复提交 | +| 撤回 / 驳回 | 恢复可提交 | +| 修改行内险种 | 清空该行报价与最终比价 | +| 删除比价单 | 二次确认,删除后不可恢复 | +| 一键生成时已有审批中/通过的同车同险 | 跳过该条 | + +--- + +## 6. 关键业务逻辑(必须对齐) + +### 6.1 险种范围 + +| 险种 | 台账 | 临期预警 | 交车判定 | +|------|------|----------|----------| +| 交强险 | 是 | 是 | **是(核心)** | +| 商业险 | 是 | 是 | **是(核心)** | +| 超赔险 | 是 | 是 | 否 | +| 驾意险 | 是 | 是 | 否 | +| 货物险 | 是 | 是 | 否 | + +### 6.2 单险种状态(按优先级命中即止) + +1. 已退保 +2. 无保单号 → 未购买 +3. 停保标记 → 已停保 +4. 有号但无到期日 → 未购买 +5. 到期日 ≤ 今天 → 已到期 +6. 到期日 ≤ 今天 + 30 天 → 临期 +7. 否则 → 正常 + +列表到期日取同车同险种**最新一条**记录。 + +### 6.3 车辆级保险状态(交车联动) + +| 条件 | 车辆保险状态 | 交车 | +|------|--------------|------| +| 交强与商业均为正常或临期(仍在有效期内) | 正常 / 临期 | **允许**(临期可提示尽快续保) | +| 交强或商业任一未购买或已到期 | 异常 | **禁止** | +| 已退保 | 视同无有效保障 | **禁止** | + +### 6.4 新保 / 续保录入必填(业务口径) + +- 车牌与车架号至少填一项 +- 必填:险种、保险公司、保单号、生效日、到期日、保险费合计 +- 承保险种明细、分项保费:非必填 + +### 6.5 批单匹配方式 + +| 业务 | 匹配方式 | +|------|----------| +| 保单录入 | 按车牌 / 车架号匹配车辆 | +| 停保 / 复驶 / 退保 | 按**保单号**在全库匹配已有记录 | + +### 6.6 比价保存与提交 + +**保存须同时满足**:至少 1 条购买记录;每行已选车辆;整单备注非空;整单至少 1 个附件。 + +**提交采购另须**:至少勾选 1 行;行内已有报价并设最终比价;已填最晚付费日;采购状态为未提交 / 撤回 / 驳回;未保存须先保存。 + +**报价**:保司 + 金额大于 0;同一保司不可对同行重复报价。 + +**最晚付费预警(仅比价)**:≤ 3 个自然日为临期;已过为超期。 + +### 6.7 采购状态(购买记录行级) + +| 状态 | 可否再提交 | 说明 | +|------|------------|------| +| 未提交 | 可以 | 默认 | +| 审批中 | 不可以 | 本页提交后写入;勾选禁用 | +| 审批通过 | 不可以 | 审批中心回写;勾选禁用 | +| 撤回 / 审批驳回 | 可以 | 审批中心回写后可再提 | + +> 审批办理不在本页;当前审批人由工作流回写,可为空。 + +--- + +## 7. 总览流程图 + +```mermaid +flowchart TB + entry[保险采购入口] + entry --> policy[保单管理] + entry --> compare[比价单] + + policy --> input[新增 / 识别 / 导入] + input --> confirm[人工确认落库] + confirm --> archive[车辆保险档案] + archive --> change[停保 / 复驶 / 退保] + archive --> status[更新车辆保险状态] + status --> handover[交车校验交强+商业] + + compare --> batch[新建或预警生成批次] + batch --> quote[多方报价与最终比价] + quote --> save[保存备注与附件] + save --> submit[勾选提交采购] + submit --> appr[审批中心流转] + appr --> readonly[本页只读回显] + readonly --> manual[审批通过后人工录入正式保单] + manual --> policy +``` + +--- + +## 8. 验收清单(产品 / 测试) + +**保单台账** +- [ ] 筛选与 KPI 口径一致;空态文案正确 +- [ ] 五类险种独立台账;列表到期日取最新一条 +- [ ] 新增 / OCR 确认 / 导入(仅新保续保)均可落库 +- [ ] OCR 失败、车牌不一致有明确拦截 +- [ ] 停保 / 复驶 / 退保须附件并写入操作历史;归档只读 + +**交车合规** +- [ ] 仅交强 + 商业决定车辆级状态 +- [ ] 异常禁止交车;正常/临期允许(临期可提示续保) +- [ ] 超赔 / 货物 / 驾意不参与交车判定 + +**比价采购** +- [ ] 保存 / 提交校验完整 +- [ ] 最晚付费临期 ≤3 天、超期看板正确 +- [ ] 审批中 / 通过行不可再提;撤回 / 驳回可再提 +- [ ] 审批通过**不**自动写入保单台账 +- [ ] 本页不可办理审批,状态只读回显 + +**边界** +- [ ] 保单与比价互不自动同步 +- [ ] 一键生成跳过已审批中/通过的同车同险 + +--- + +## 9. 交付口径 + +> 「保险采购」在 OneOS PC 承载**保单台账**与**比价采购**两条独立业务线:运维部、采购部可一车一档维护交强 / 商业等五类险种并办理停保复驶退保,车辆管理与交车据此校验核心险种有效性;采购部可在比价单中完成多方报价、付费预警与采购审批跟踪,审批通过后须人工补录正式保单——台账与比价互不同步,审批不在本页办理。 diff --git a/src/prototypes/insurance-procurement/annotation-source.json b/src/prototypes/insurance-procurement/annotation-source.json index 4912131..266ac4b 100644 --- a/src/prototypes/insurance-procurement/annotation-source.json +++ b/src/prototypes/insurance-procurement/annotation-source.json @@ -5,7 +5,7 @@ "version": 2, "prototypeName": "insurance-procurement", "pageId": "list", - "updatedAt": 1783800844965, + "updatedAt": 1784545564192, "nodes": [ { "id": "ipc-header", @@ -502,6 +502,13 @@ "title": "保险采购说明", "defaultExpanded": true, "children": [ + { + "type": "markdown", + "id": "ipc-doc-prd", + "title": "产品需求说明(PRD)", + "markdownPath": ".spec/requirements-prd.md", + "markdown": "# 保险采购 · 产品需求说明(全模块)\n\n| 项 | 内容 |\n|---|---|\n| 模块名称 | 保险采购 |\n| 所属系统 | ONE-OS(PC) |\n| 交互原型 | `/prototypes/insurance-procurement` |\n| 业务条线 | 运维管理条线(证照 / 保险相关) |\n| 文档状态 | AutoPRD · 已对齐可运行原型 |\n\n---\n\n## 1. 一句话与目标\n\n**一句话** \n在同一模块内管理车队**保单台账**与**采购前比价审批**两条业务线,保证续保不遗漏、变更可追溯,并为交车提供交强险 + 商业险合规校验。\n\n**要解决的问题** \n- 保险信息散落在表格、邮件与线下批单中,台账不全、临期易漏 \n- 停保 / 复驶 / 退保缺少统一留痕 \n- 采购前多方比价难追溯,审批结果难跟踪 \n- 交车时无法与车辆管理共用同一套「保险是否有效」口径 \n\n**本期目标** \n1. 一车一档维护五类险种台账;支持手工新增、批量识别、批量导入 \n2. 支持停保 / 复驶 / 退保办理与操作历史 \n3. 输出与车辆管理一致的车辆级保险状态,支撑交车拦截 \n4. 比价单批次管理:多方报价、最晚付费预警、勾选提交采购审批(本页只读跟踪) \n\n**非目标(本期不做)** \n- 比价单与保单台账**自动双向同步**;审批通过后**不自动写入**正式保单 \n- 在本模块内办理审批(通过 / 驳回 / 撤回均在审批中心) \n- 维护行驶证、道路运输证等证照档案(见证照管理) \n- 事故赔付、保险上浮等事故链路台账(见事故管理) \n\n---\n\n## 2. 模块边界(最重要)\n\n```mermaid\nflowchart LR\n IPC[保险采购]\n VM[车辆管理]\n OPS[交车管理]\n APPR[审批中心]\n CERT[证照管理]\n WB[工作台]\n IPC -->|输出保险状态口径| VM\n VM -->|交车前置校验| OPS\n CERT -.->|并列交车前置| OPS\n IPC -->|发起采购审批| APPR\n APPR -->|回写采购状态| IPC\n WB -->|临期 KPI / 快捷入口| IPC\n```\n\n| 业务线 / 子域 | 做什么 | 不做什么 |\n|---------------|--------|----------|\n| 保单管理 | 一车一档、多通道录入、停保/复驶/退保、状态与交车口径 | 不替代证照管理;不办事故赔付 |\n| 比价单 | 批次比价、付费预警、提交采购、只读跟踪审批 | 不自动生成正式保单;不在本页审批 |\n| 与车辆 / 交车 | 提供交强 + 商业是否有效的统一口径 | 不在本页完成交车动作 |\n\n**外部依赖(产品口径)**\n\n| 依赖方 | 交互方式 | 产品要求 |\n|--------|----------|----------|\n| 车辆管理 | 读取保险状态 / 到期日 | 口径与本模块一致:异常则禁止交车 |\n| 交车管理 | 交付前校验交强 + 商业有效 | 不合规须可理解拦截原因 |\n| 审批中心 | 接收采购申请并回写状态 / 当前审批人 | 本页只读展示,不办理审批 |\n| 工作台 | KPI、快捷「发起保险采购」 | 跳转至本模块对应能力 |\n| 证照管理 | 并列交车前置 | 证照与保险各自独立维护 |\n\n**关键约束** \n- **保单台账 ↔ 比价单互不自动同步**;审批通过后须运营在保单管理侧另行录入正式保单 \n- **交车只看交强险 + 商业险**;超赔 / 货物 / 驾意仅参与临期预警,不参与交车判定 \n- **最晚付费临期 / 超期**仅服务比价采购节奏,不写入保单台账 \n\n---\n\n## 3. 用户与角色\n\n| 角色 | 主要目标 |\n|------|----------|\n| 运维部 | 维护保单台账、办理停保/复驶/退保;交车前确认保险有效 |\n| 安全部 | 从合规视角关注保险与证照并列的交车前置 |\n| 采购部 | 创建比价单、录入报价、提交采购申请;关注临期与付费节奏 |\n| 业务管理组(业管) | 在审批中心确认报价等节点(本页只读看到结果) |\n\n> 部门名对齐业务条线说明;`module-role-mapping` 将本模块主责记为运维部、安全部,采购部为比价线主操作用户。\n\n---\n\n## 4. 用户故事与故事点(业务条线说明口径)\n\n> 故事点供排期参考,可按团队基准调整。合计约 **28 SP**。\n\n### Epic A · 保单台账与交车合规(约 15 SP)\n\n#### A1 · 一车一档台账与检索(约 3 SP)\n- **角色**:运维部、采购部 \n- **起点**:需要按车牌 / VIN / 运营状态 / 保险状态等快速定位车辆保险档案。 \n- **怎么运作**: \n 1. 在列表筛选或点击 KPI 卡片缩小范围。 \n 2. 查看五类险种到期日与车辆级保险状态。 \n 3. 进入「管理」打开车辆保险档案。 \n- **关键结果**:`可按条件定位车辆` / `空态提示无可匹配台账` \n- **闭环**:台账可检索、可下钻,与 KPI 口径一致。 \n- **排期(可选)**:US-IPC-01 · 规模 M · 作为运维/采购,我想快速筛出台账车辆,以便处理续保与异常。\n\n#### A2 · 多通道录入正式保单(约 5 SP)\n- **角色**:运维部、采购部 \n- **起点**:拿到新保或续保材料(纸质 / 电子保单、批量表格)。 \n- **怎么运作**: \n 1. 选择新增、保单批量识别或批量导入。 \n 2. 识别类须人工确认后再落库;导入仅处理新保/续保。 \n 3. 按车牌/VIN(或批单场景按保单号)匹配车辆并写入对应险种台账。 \n- **关键结果**:`确认后落库` / `识别失败可理解提示` / `车牌不一致则拦截` \n- **闭环**:正式保单进入一车一档,列表与车辆管理可见最新到期与状态。 \n- **排期(可选)**:US-IPC-02 · 规模 L · 作为运维/采购,我想用识别或导入补齐台账,以便续保不遗漏。\n\n#### A3 · 停保 / 复驶 / 退保留痕(约 4 SP)\n- **角色**:运维部 \n- **起点**:车辆需临时停保、恢复行驶或办理退保。 \n- **怎么运作**: \n 1. 在车辆保险档案对**当前有效**记录发起对应办理。 \n 2. 填写时间、金额等业务字段并上传批单附件。 \n 3. 写入操作历史;更新险种与车辆状态展示。 \n- **关键结果**:`变更可追溯` / `归档记录只读` \n- **闭环**:停保/复驶/退保全程留痕,交车口径随之更新。 \n- **排期(可选)**:US-IPC-03 · 规模 M。\n\n#### A4 · 交强 + 商业决定是否可交车(约 3 SP)\n- **角色**:运维部、采购部 \n- **起点**:采购/运维维护交强险与商业险台账,作为交车前置合规数据。 \n- **怎么运作**: \n 1. 台账维护一车一档核心险种。 \n 2. 交车时按「交强 + 商业均为正常或临期」判定是否放行。 \n 3. 异常(未购买 / 已到期 / 已退保视同无效)则拦截并提示原因。 \n- **关键结果**:`允许交车` / `禁止交车 · 保险异常` \n- **闭环**:交车合规与台账一致、可追溯;异常车辆不能进入履约运营。 \n- **排期(可选)**:US-17 · 规模 M · 作为采购/运维,我想维护交强+商业险并在交车时校验,以便不合规车辆无法交车。\n\n### Epic B · 比价采购与审批跟踪(约 13 SP)\n\n#### B1 · 比价单批次与付费预警(约 4 SP)\n- **角色**:采购部 \n- **起点**:需要按批次组织待购车辆与险种,并盯住最晚付费日。 \n- **怎么运作**: \n 1. 在比价单管理新建或编辑批次;可从临期预警一键带入。 \n 2. 用全部 / 临期(≤3 天)/ 超期看板筛选批次。 \n 3. 保存时校验购买记录、备注与附件。 \n- **关键结果**:`可按付费节奏跟进` / `未满足保存条件则拦截` \n- **闭环**:采购节奏可视、可预警;不与保单台账自动混写。 \n- **排期(可选)**:US-IPC-04 · 规模 M。\n\n#### B2 · 多方报价与提交采购(约 5 SP)\n- **角色**:采购部 \n- **起点**:同一购买需求需比较多家保司报价后发起采购。 \n- **怎么运作**: \n 1. 为每行录入多家报价并指定唯一最终比价。 \n 2. 填写最晚付费日,勾选可提交行。 \n 3. 提交采购申请进入审批中心。 \n- **关键结果**:`审批中/已通过行不可再提` / `撤回或驳回后可再提` \n- **闭环**:比价过程可追溯;正式保单仍须审批通过后在台账侧补录。 \n- **排期(可选)**:US-IPC-05 · 规模 L。\n\n#### B3 · 审批只读回显(约 4 SP)\n- **角色**:采购部、业务管理组 \n- **起点**:采购申请已在审批中心流转。 \n- **怎么运作**: \n 1. 本页展示采购状态与当前审批人(可为空)。 \n 2. 业管等角色在审批中心办理节点。 \n 3. 结果回写本页;通过后运营去保单管理录入正式保单。 \n- **关键结果**:`本页不可办理审批` / `状态与审批中心一致` \n- **闭环**:采购决策可跟踪;台账仍以人工录入为准。 \n- **排期(可选)**:US-IPC-06 · 规模 M。\n\n---\n\n## 5. 功能模块说明(正向 / 逆向)\n\n### 5.1 保险台账列表与 KPI\n\n**正向** \n1. 按车牌、VIN、品牌型号、运营状态、保险状态、险种 + 到期区间筛选。 \n2. 点击 KPI(台账车辆、状态正常、核心逾期、核心待购等)联动列表或打开预警。 \n3. 行内「管理」打开车辆保险档案。 \n\n**逆向 / 边界**\n\n| 情况 | 系统表现 |\n|------|----------|\n| 无匹配数据 | 空态提示无可匹配台账车辆 |\n| 退出运营车辆 | 列表可查看但样式弱化 |\n| 险种到期筛选未选险种 | 须先选险种再筛到期区间 |\n| 车牌与多车牌条件互斥 | 按产品约定互斥,避免条件冲突 |\n\n### 5.2 保单录入(新增 / 识别 / 导入)\n\n**正向** \n1. 新增:完整字段手工录入后保存。 \n2. 批量识别:选业务类型 → 上传 → 识别 → **人工确认** → 落库。 \n3. 批量导入:下载模板填写 → 上传 → 仅新保/续保入库。 \n4. 识别任务记录:查看进度,对待确认结果继续确认。 \n\n**逆向 / 边界**\n\n| 情况 | 系统表现 |\n|------|----------|\n| 识别失败 | 提示清晰度 / 格式 / 损坏等原因 |\n| 无成功结果可确认 | 提示暂无识别成功结果 |\n| 识别车牌与所选车不一致 | 拦截并提示两边车牌 |\n| 导入中的停保/复驶/退保行 | 自动跳过 |\n| OCR 确认页 | 不展示承保险种明细、分项保费(手工路径仍可填) |\n\n### 5.3 车辆保险档案 · 停保 / 复驶 / 退保\n\n**正向** \n1. 分险种查看历史;对当前有效记录办理停保、复驶或退保。 \n2. 填写业务时间 / 金额等并上传批单附件。 \n3. 全周期时间轴与操作历史可追溯。 \n\n**逆向 / 边界**\n\n| 情况 | 系统表现 |\n|------|----------|\n| 历史归档记录 | 只读,不可再办停保/退保 |\n| 已退保 | 仅允许复驶 |\n| 已停保 | 可复驶或退保 |\n| 新保覆盖已退保险种 | 旧记录归档后再写入新保单 |\n\n### 5.4 比价单管理与编辑\n\n**正向** \n1. 新建 / 编辑比价单,添加购买记录与多方报价。 \n2. 设最终比价、最晚付费日;保存备注与附件。 \n3. 勾选行提交采购;跟踪审批状态。 \n4. 可从险种临期预警一键生成比价单(附件须补传)。 \n\n**逆向 / 边界**\n\n| 情况 | 系统表现 |\n|------|----------|\n| 保存校验不通过 | 拦截并提示缺记录 / 备注 / 附件等 |\n| 审批中或已通过行 | 勾选禁用,不可重复提交 |\n| 撤回 / 驳回 | 恢复可提交 |\n| 修改行内险种 | 清空该行报价与最终比价 |\n| 删除比价单 | 二次确认,删除后不可恢复 |\n| 一键生成时已有审批中/通过的同车同险 | 跳过该条 |\n\n---\n\n## 6. 关键业务逻辑(必须对齐)\n\n### 6.1 险种范围\n\n| 险种 | 台账 | 临期预警 | 交车判定 |\n|------|------|----------|----------|\n| 交强险 | 是 | 是 | **是(核心)** |\n| 商业险 | 是 | 是 | **是(核心)** |\n| 超赔险 | 是 | 是 | 否 |\n| 驾意险 | 是 | 是 | 否 |\n| 货物险 | 是 | 是 | 否 |\n\n### 6.2 单险种状态(按优先级命中即止)\n\n1. 已退保 \n2. 无保单号 → 未购买 \n3. 停保标记 → 已停保 \n4. 有号但无到期日 → 未购买 \n5. 到期日 ≤ 今天 → 已到期 \n6. 到期日 ≤ 今天 + 30 天 → 临期 \n7. 否则 → 正常 \n\n列表到期日取同车同险种**最新一条**记录。\n\n### 6.3 车辆级保险状态(交车联动)\n\n| 条件 | 车辆保险状态 | 交车 |\n|------|--------------|------|\n| 交强与商业均为正常或临期(仍在有效期内) | 正常 / 临期 | **允许**(临期可提示尽快续保) |\n| 交强或商业任一未购买或已到期 | 异常 | **禁止** |\n| 已退保 | 视同无有效保障 | **禁止** |\n\n### 6.4 新保 / 续保录入必填(业务口径)\n\n- 车牌与车架号至少填一项 \n- 必填:险种、保险公司、保单号、生效日、到期日、保险费合计 \n- 承保险种明细、分项保费:非必填 \n\n### 6.5 批单匹配方式\n\n| 业务 | 匹配方式 |\n|------|----------|\n| 保单录入 | 按车牌 / 车架号匹配车辆 |\n| 停保 / 复驶 / 退保 | 按**保单号**在全库匹配已有记录 |\n\n### 6.6 比价保存与提交\n\n**保存须同时满足**:至少 1 条购买记录;每行已选车辆;整单备注非空;整单至少 1 个附件。 \n\n**提交采购另须**:至少勾选 1 行;行内已有报价并设最终比价;已填最晚付费日;采购状态为未提交 / 撤回 / 驳回;未保存须先保存。 \n\n**报价**:保司 + 金额大于 0;同一保司不可对同行重复报价。 \n\n**最晚付费预警(仅比价)**:≤ 3 个自然日为临期;已过为超期。\n\n### 6.7 采购状态(购买记录行级)\n\n| 状态 | 可否再提交 | 说明 |\n|------|------------|------|\n| 未提交 | 可以 | 默认 |\n| 审批中 | 不可以 | 本页提交后写入;勾选禁用 |\n| 审批通过 | 不可以 | 审批中心回写;勾选禁用 |\n| 撤回 / 审批驳回 | 可以 | 审批中心回写后可再提 |\n\n> 审批办理不在本页;当前审批人由工作流回写,可为空。\n\n---\n\n## 7. 总览流程图\n\n```mermaid\nflowchart TB\n entry[保险采购入口]\n entry --> policy[保单管理]\n entry --> compare[比价单]\n\n policy --> input[新增 / 识别 / 导入]\n input --> confirm[人工确认落库]\n confirm --> archive[车辆保险档案]\n archive --> change[停保 / 复驶 / 退保]\n archive --> status[更新车辆保险状态]\n status --> handover[交车校验交强+商业]\n\n compare --> batch[新建或预警生成批次]\n batch --> quote[多方报价与最终比价]\n quote --> save[保存备注与附件]\n save --> submit[勾选提交采购]\n submit --> appr[审批中心流转]\n appr --> readonly[本页只读回显]\n readonly --> manual[审批通过后人工录入正式保单]\n manual --> policy\n```\n\n---\n\n## 8. 验收清单(产品 / 测试)\n\n**保单台账** \n- [ ] 筛选与 KPI 口径一致;空态文案正确 \n- [ ] 五类险种独立台账;列表到期日取最新一条 \n- [ ] 新增 / OCR 确认 / 导入(仅新保续保)均可落库 \n- [ ] OCR 失败、车牌不一致有明确拦截 \n- [ ] 停保 / 复驶 / 退保须附件并写入操作历史;归档只读 \n\n**交车合规** \n- [ ] 仅交强 + 商业决定车辆级状态 \n- [ ] 异常禁止交车;正常/临期允许(临期可提示续保) \n- [ ] 超赔 / 货物 / 驾意不参与交车判定 \n\n**比价采购** \n- [ ] 保存 / 提交校验完整 \n- [ ] 最晚付费临期 ≤3 天、超期看板正确 \n- [ ] 审批中 / 通过行不可再提;撤回 / 驳回可再提 \n- [ ] 审批通过**不**自动写入保单台账 \n- [ ] 本页不可办理审批,状态只读回显 \n\n**边界** \n- [ ] 保单与比价互不自动同步 \n- [ ] 一键生成跳过已审批中/通过的同车同险 \n\n---\n\n## 9. 交付口径\n\n> 「保险采购」在 OneOS PC 承载**保单台账**与**比价采购**两条独立业务线:运维部、采购部可一车一档维护交强 / 商业等五类险种并办理停保复驶退保,车辆管理与交车据此校验核心险种有效性;采购部可在比价单中完成多方报价、付费预警与采购审批跟踪,审批通过后须人工补录正式保单——台账与比价互不同步,审批不在本页办理。\n" + }, { "type": "markdown", "id": "ipc-doc-overview", diff --git a/src/prototypes/oneos-h5-h2-order/.spec/flows.md b/src/prototypes/oneos-h5-h2-order/.spec/flows.md new file mode 100644 index 0000000..1091269 --- /dev/null +++ b/src/prototypes/oneos-h5-h2-order/.spec/flows.md @@ -0,0 +1,68 @@ +# 流程图 + +> 标注面板支持 Mermaid 渲染。下列流程与当前原型行为一致(本地 Store,未接真实 API)。 + +--- + +## 1. 主路径:从打开到上报 + +```mermaid +flowchart TD + A[打开加氢订单 H5] --> B[查看本站列表与 KPI] + B --> C{前一日手工台账已上传?} + C -->|否| D[提示:手工台账有缺失…] + D --> E[进入手工台账 · 选中昨日] + E --> F[拍照/相册上传并确认] + F --> C + C -->|是| G[点新增] + G --> H[填写时间/车牌/面板/量价] + H --> I{同站同车牌同时间已存在?} + I -->|是| J[拦截提示] + J --> H + I -->|否| K[保存写入 Bridge] + K --> B +``` + +--- + +## 2. 手工台账补传 + +```mermaid +flowchart LR + A[手工台账 Tab] --> B[月历:绿已传 / 橙未传] + B --> C[点选 ≤今日 且 ≥首笔日] + C --> D[上传图片] + D --> E[二次确认] + E --> F[归档不可删 · 可追加] +``` + +--- + +## 3. 编辑 / 删除与锁定 + +```mermaid +flowchart TD + A[打开详情] --> L{羚牛车辆?} + L -->|否| D[可编辑 / 可删除] + L -->|是| B{已核对或已对账?} + B -->|是| C[只读 · 禁改删] + B -->|否| D + D --> E[写回 Bridge] +``` + +--- + +## 4. 与 Web / 台账联动(概念) + +```mermaid +sequenceDiagram + participant H5 as 加氢订单 H5 + participant BR as Bridge Store + participant WEB as 加氢记录 Web + participant LED as 车辆氢费明细 + participant SITE as 站点对账单 + H5->>BR: 新增/改删流水 · 上传台账 + WEB->>BR: 同 Store 读写 · 导出 + LED->>BR: 完成核对 → 已核对 + SITE->>BR: 提交对账单 → 已对账 +``` diff --git a/src/prototypes/oneos-h5-h2-order/.spec/key-logic.md b/src/prototypes/oneos-h5-h2-order/.spec/key-logic.md new file mode 100644 index 0000000..b3f4c48 --- /dev/null +++ b/src/prototypes/oneos-h5-h2-order/.spec/key-logic.md @@ -0,0 +1,52 @@ +# 关键逻辑索引 + +本页汇总加氢订单(H5)复杂判定入口;细则见各规格全文。原型数据均为本地种子 / 内存 Store,**未接真实 API**。 + +--- + +## 1. 判定主题一览 + +| 主题 | 一句话 | 全文 | +|---|---|---| +| 手工台账门禁 | **昨日**未传 → 今日禁新增;固定提示文案并直达台账 | [feature-manual-ledger.md](./feature-manual-ledger.md) | +| 核对 ≠ 对账 | 能源部核对 / 站点对账单提交,触发方不同 | [reconcile-linkage.md](./reconcile-linkage.md) | +| 记录锁定 | **仅羚牛**:已核对或已对账 → 站端不可改删;非羚牛不锁 | [feature-detail.md](./feature-detail.md)、[reconcile-linkage.md](./reconcile-linkage.md)、[fleet-plate-tag.md](./fleet-plate-tag.md) | +| 重复键 | 同站 + 同车牌 + 同加氢时间 | Bridge / [Web record-data-model](../oneos-web-h2-station/.spec/record-data-model.md) | +| 总额计算 | 总额 = 单价 × 量(两位小数) | [feature-create.md](./feature-create.md) | +| 羚牛车辆 | 本地系统车牌集合 | [fleet-plate-tag.md](./fleet-plate-tag.md) | + +--- + +## 2. 手工台账门禁(摘要) + +| 优先级 | 条件 | 结果 | +|--------|------|------| +| 1 | 编辑已有记录 | 不校验 | +| 2 | 昨日早于本站首笔加氢日 | 不要求昨日台账,可新增 | +| 3 | 昨日已有 ≥1 张台账图 | 可新增 | +| 4 | 昨日未上传 | 提示并跳转手工台账(选中昨日) | + +用户可见文案:`手工台账有缺失,作为对账重要依据,请先上传手工台账` + +--- + +## 3. 核对 / 对账(摘要) + +| 状态 | 谁触发 | 站端影响 | +|---|---|---| +| 已核对 | 车辆氢费明细「完成核对」 | **羚牛**不可改删 | +| 已对账 | 站点信息对账单提交 | **羚牛**不可改删;回写对账时间 | +| 非羚牛 | — | 不参与核对/对账展示与锁定 | + +H5 列表主要展示核对状态(仅羚牛);对账细节见联动规格。 + +--- + +## 4. 代码路径 + +| 能力 | 路径 | +|---|---| +| Bridge | `src/common/h2VehicleLedgerBridge.js` | +| 门禁工具 | `utils/manual-ledger.ts` → `isManualLedgerGateBlocked` | +| 订单 CRUD | `utils/orders.ts` | +| 页面 | `index.tsx` / `ManualLedgerPanel` / `CreateWizard` | diff --git a/src/prototypes/oneos-h5-h2-order/.spec/prototype-review.md b/src/prototypes/oneos-h5-h2-order/.spec/prototype-review.md new file mode 100644 index 0000000..43ceac6 --- /dev/null +++ b/src/prototypes/oneos-h5-h2-order/.spec/prototype-review.md @@ -0,0 +1,64 @@ +# Prototype Review + +- 审查目标:`src/prototypes/oneos-h5-h2-order`(加氢站管理 → **加氢订单** H5;入口 `index.tsx`) +- 用户资料/参考资料: + - 本轮用户说明:对「加氢订单」执行 Prototype Review / 需求评审(未另附独立需求附件) + - `.spec/`:`requirements-prd.md`(v1.7)、`roles.md`、`user-stories.md`、`key-logic.md`、`flows.md`、`feature-list.md`、`feature-create.md`、`feature-detail.md`、`feature-manual-ledger.md`、`fleet-plate-tag.md`、`reconcile-linkage.md`;早期 `2026-07-14-requirements-design.md`(已标注作废条目) + - `src/resources/`:`oneos-web-legacy/…/车辆氢费明细-需求文档.md`(文首已注明以 verify-reconcile / reconcile-linkage 为准) + - 关联规格:`oneos-web-h2-station/.spec/fleet-vehicle-verify.md`、`record-data-model.md`;`vehicle-h2-fee-ledger/.spec/verify-reconcile.md` + - 源码:`index.tsx`、`components/*`、`utils/*`、`types.ts`、`annotation-source.json`;共享 Bridge / 品牌 Store +- 生成时间:2026-07-18 20:14 +- 修复闭环:2026-07-18 20:32(按 Review 处理全部冲突与 P0–P3 项) +- 资料冲突/待确认:**无**(已对齐) +- 独立评估:`full` + +## 总体点评 + +加氢订单 H5 主路径完整可演示。评审指出的冲突已闭环:**昨日门禁文案**、**列表顶栏橙条**、**锁定仅羚牛**(规格与实现一致)、**早期切站稿作废标注**、**删除确认「未锁定」**、历史 resources 权威顺序说明。剩余为原型边界内 Mock(OCR / 车机 / 无真实 API),不构成业务冲突。 + +## P0-P3 优先级问题 + +### P1 - 手工台账页文案把「今日上传」当成新增门禁 — **已修复** + +- 处理:改为「今日不作为新增门禁,仅要求前一日已上传」;昨日缺失时台账页单独橙条并可选中昨日。 + +### P2 - 「记录锁定」规格未写明羚牛例外 — **已修复** + +- 处理:`feature-detail.md` / `reconcile-linkage.md` / `key-logic.md` / `flows.md` / PRD v1.7 / US-04 统一为「锁定仅羚牛」;与 `isOrderLocked` 实现一致。 + +### P2 - 门禁提示缺列表顶栏橙条 — **已修复** + +- 处理:订单列表顶部增加可点橙条(跳转台账并选中昨日);保留 FAB 旁提示 + Tab 红点。 + +### P3 - 早期设计文档仍写切站 — **已修复** + +- 处理:`2026-07-14-requirements-design.md` 文首与表格标注作废/覆盖项;权威以 PRD 为准。台账规格区分 H5 固定本站 vs Web 多站。 + +### P3 - 删除确认文案偏「未对账」 — **已修复** + +- 处理:确认语改为「确认删除车牌 … 的未锁定记录?」;台账二次确认与 Web 文案对齐。 + +## 完整性与项目对齐 + +| 维度 | 结论 | +|------|------| +| 核心用户 / 主流程 | 对齐 PRD v1.7 | +| 范围边界 | 真登录 / 真 OCR / PLC / 原生 App / 后端联调仍不做 | +| 资料冲突 | 已消除;resources 旧文档文首指向现行核对≠对账规格 | +| 双端口径 | 门禁昨日、锁定羚牛、Bridge 共享 — 与 Web / 台账一致 | + +## 业务逻辑连贯性 + +- 台账门禁:昨日未传 → 顶栏橙条 / Toast / 跳转台账选中昨日;今日状态 ≠ 门禁。 +- 锁定:先判羚牛,再判核对∨对账。 +- 总额偏差二次确认、重复键拦截:与 Web 共用规则。 + +## 状态、异常、边界与恢复 + +评审所列空态、门禁、必填、重复、脏返回、锁定、归档路径仍成立;本轮仅修正文案/提示位与规格一致性,未改变 Mock 边界。 + +## 证据与评估说明 + +- 用户资料优先级:无新附件;以 `.spec` PRD v1.7 为基线。 +- 读取范围:见文首;闭环改动见 `ManualLedgerPanel.tsx`、`index.tsx` 及上述规格文件。 +- 独立评估:`full`。 diff --git a/src/prototypes/oneos-h5-h2-order/.spec/roles.md b/src/prototypes/oneos-h5-h2-order/.spec/roles.md new file mode 100644 index 0000000..4e685f4 --- /dev/null +++ b/src/prototypes/oneos-h5-h2-order/.spec/roles.md @@ -0,0 +1,38 @@ +# 需求角色 + +| 项 | 说明 | +|---|---| +| 适用原型 | 加氢订单(H5)`oneos-h5-h2-order` | +| 关联 Web | 加氢记录 `oneos-web-h2-station`(同一业务、不同端) | +| 数据 | 原型本地种子 + 共享 Bridge(未接真实登录 / API) | + +--- + +## 1. 角色一览 + +| 角色 | 端 | 核心诉求 | 本原型是否为主用户 | +|---|---|---|---| +| **加氢站操作人员** | H5(手机浏览器) | 在站场快速上报本站加氢流水;补传手工台账照片 | **是** | +| **加氢站站长 / 多站管理员** | Web 为主,H5 可看本站 | 多站台账、导出、手工台账补传与督导 | 间接(H5 固定单站演示) | +| **能源部核对人员** | 车辆氢费明细 | 完成核对,回写「已核对」 | 否(触发方在台账) | +| **站点对账人员** | 站点信息 · 对账单 | 提交对账单,回写「已对账」 | 否(触发方在站点信息) | +| **超级管理员** | Web | 查看全部站点流水与台账 | 否(Web 标注状态演示) | + +--- + +## 2. 本原型主角色:加氢站操作人员 + +| 项 | 说明 | +|---|---| +| 场景 | 户外 / 站场,手机竖屏 | +| 权限边界 | 仅本站数据;不可切站(原型固定演示站) | +| 日常动作 | 看列表 KPI → 上传昨日手工台账(若缺失)→ 新增加氢记录 → 查看/编辑未锁定记录 | +| 约束 | 前一日手工台账未上传则今日不可新增;已核对或已对账不可改删 | + +--- + +## 3. 角色边界(本期不做) + +- 真登录与组织权限中心对接 +- 能源部 / 对账人员在 H5 内操作核对或对账 +- 跨站汇总与导出(属 Web「加氢记录」) diff --git a/src/prototypes/oneos-h5-h2-order/.spec/user-stories.md b/src/prototypes/oneos-h5-h2-order/.spec/user-stories.md new file mode 100644 index 0000000..538a2e5 --- /dev/null +++ b/src/prototypes/oneos-h5-h2-order/.spec/user-stories.md @@ -0,0 +1,84 @@ +# 用户故事 + +> 以加氢站操作人员为主;验收口径见 [requirements-prd.md](./requirements-prd.md)。 + +--- + +## US-01 查看本站加氢订单 + +**作为** 加氢站操作人员, +**我希望** 打开加氢订单即看到本站列表与 KPI, +**以便** 快速了解今日/近期上报情况。 + +**验收要点** + +- 默认已登录本站;无切站 +- KPI:订单数 / 加氢量 / 加氢金额随列表汇总 +- 卡片展示加氢时间、车牌、量、额、核对状态等 + +--- + +## US-02 补传缺失的手工台账后再新增 + +**作为** 加氢站操作人员, +**我希望** 在前一日手工台账未上传时被明确拦截,并直达台账页补传, +**以便** 保证纸质台账作为对账依据不缺失。 + +**验收要点** + +- 顶栏橙条 + 底栏提示:「手工台账有缺失,作为对账重要依据,请先上传手工台账」 +- 点「新增」或顶栏橙条跳转手工台账并选中昨日 +- 补传昨日确认后可新增 + +--- + +## US-03 新增加氢记录 + +**作为** 加氢站操作人员, +**我希望** 用车牌键盘 + 面板拍照/OCR 快速填单, +**以便** 在站场少打字完成上报。 + +**验收要点** + +- 加氢时间默认=点击新增时刻(到分) +- 站名只读为本站 +- 总额 = 单价 × 加氢量(界面不展示公式) +- 同站同车牌同时间重复保存拦截 + +--- + +## US-04 查看详情并编辑/删除未锁定记录 + +**作为** 加氢站操作人员, +**我希望** 打开详情查看字段与识别原图,并对未核对未对账记录改删, +**以便** 纠正录入错误。 + +**验收要点** + +- 羚牛车辆已核对或已对账:不可编辑/删除,有锁定说明;非羚牛不锁定 +- 详情展示车牌/面板含水印原图 + +--- + +## US-05 按日历管理手工台账 + +**作为** 加氢站操作人员, +**我希望** 在月历上看已传/未传并补传过往日, +**以便** 多日未操作后仍能补齐缺口。 + +**验收要点** + +- 自首笔加氢日起绿/橙标记;首笔前不要求 +- 禁选未来日;已归档图不可删,仅可追加 + +--- + +## US-06 识别羚牛车辆 + +**作为** 加氢站操作人员, +**我希望** 在列表/详情看到是否羚牛车辆, +**以便** 理解后续核对对账是否适用该车。 + +**验收要点** + +- 标签:羚牛车辆 / 非羚牛车辆(本地车辆集合判定) diff --git a/src/prototypes/oneos-h5-h2-order/components/CreateWizard.tsx b/src/prototypes/oneos-h5-h2-order/components/CreateWizard.tsx index 2597bd8..86e2a97 100644 --- a/src/prototypes/oneos-h5-h2-order/components/CreateWizard.tsx +++ b/src/prototypes/oneos-h5-h2-order/components/CreateWizard.tsx @@ -16,17 +16,6 @@ import { import { PhoneShell } from './PhoneShell'; import { PlateKeyboardSheet } from './PlateKeyboardSheet'; -function listDispenserBrandHints(): string[] { - const store = (window as unknown as { H2DispenserBrandStore?: { labels?: () => string[] } }) - .H2DispenserBrandStore; - if (!store || typeof store.labels !== 'function') return []; - try { - return store.labels().slice(0, 4); - } catch { - return []; - } -} - type Props = { mode: 'create' | 'edit'; draft: CreateDraft; @@ -107,7 +96,6 @@ export function CreateWizard({ const [plateOpen, setPlateOpen] = useState(false); const vehicleTag = useMemo(() => getVehicleTag(draft.plateNo), [draft.plateNo]); - const brandHints = listDispenserBrandHints(); function patchDraft(patch: Partial) { const nextPrice = patch.unitPrice !== undefined ? patch.unitPrice : draft.unitPrice; @@ -380,12 +368,6 @@ export function CreateWizard({

    请拍摄清晰的加氢机照片

    - {brandHints.length ? ( -

    - OCR 参考品牌型号(站点信息维护):{brandHints.join('、')} - {brandHints.length >= 4 ? '…' : ''} -

    - ) : null}
    原型本地演示种子:[`utils/mileage-task.ts`](../utils/mileage-task.ts) +> **未接真实采购合同 / 任务工单 API** + +## 业务背景 + +采购部发起车辆采购合同时,合同可关联对应批次车辆的**里程要求**与**时间起止**;里程任务通过**任务工单**下发到车辆。手机列表卡片展示该车当前里程任务完成情况,便于外勤扫一眼进度与是否赶得上截止。 + +## 展示位置 + +车辆卡片中,在「来源 / 客户名」等标签行**下方**,全宽分隔区: + +1. 标题「里程任务」(有任务时右侧展示完成百分比) +2. 有任务:进度条 +3. 有任务:剩余公里 + 按近 7 活跃日推算的预计完成天数 +4. 进行中任务:文案后方完成预判标签(对照任务截止剩余日历天) + +## 判定 / 展示优先级 + +| 顺序 | 条件 | 结果 | +|------|------|------| +| 1 | 车辆未挂接有效里程任务工单 | 展示标题 +「暂无里程任务」(无进度条、无剩余/预计、无预判标签) | +| 2 | 已完成里程 ≥ 目标里程(或进度 ≥ 100%) | 进度 100%、成功色进度条与百分比;文案「已完成」;**不**展示预判标签 | +| 3 | 有任务且未完成,且近 7 个活跃日均里程可算 | 进度条 +「剩余 N km · 预计 D 天内完成」+ 预判标签;预判为「无法完成」时进度条/百分比为危险红;「有完成风险」为警告橙 | +| 4 | 有任务且未完成,但无活跃日(近窗内日里程均 ≤ 0) | 进度条(警告橙)+「剩余 N km · 暂无法预计」+ 预判标签「剩余 x 天,预计有完成风险」 | + +## 口径公式 + +| 项 | 口径 | +|----|------| +| 完成百分比 | `min(100, 保留 1 位小数(任务内已完成里程 / 合同目标里程 × 100))` | +| 剩余公里 | `max(0, 目标里程 − 已完成里程)` | +| 活跃日 | 自然日内该车行驶里程 **> 0 km** | +| 近 7 活跃日均 | 取最近最多 **7** 个活跃日的日里程算术平均;不足 7 个则有几个算几个 | +| 预计天数 D | `ceil(剩余公里 / 近 7 活跃日均)`;均里程为 0 或无活跃日 → 无法预计 | +| 截止剩余日历天 x | 任务/合同时间窗截止日相对今天的剩余自然日(下限 0) | + +## 完成预判标签(文案后方) + +用预计天数 D(近 7 活跃日推算)对照截止剩余日历天 x: + +| 顺序 | 条件 | 标签文案 | 色调 | 进度条 / 百分比 | +|------|------|----------|------|----------------| +| 1 | D 无法算出(无活跃日) | 剩余 x 天,预计有完成风险 | 警告橙 | 同警告橙 | +| 2 | x ≤ 0 或 D > x | 剩余 x 天,预计无法完成 | 危险红 | 同危险红 | +| 3 | D > x × 85%,或 (x − D) ≤ 7 | 剩余 x 天,预计有完成风险 | 警告橙 | 同警告橙 | +| 4 | 其余 | 剩余 x 天,预计可完成 | 成功绿 | 默认主色蓝 | + +## 前置条件 + +- 仅列表卡片只读展示;不提供下发、编辑、跳转工单详情(本期不做) +- 筛选 / KPI / 搜索不影响本区展示规则,只影响哪些车出现在列表 + +## 数据源(原型) + +| 来源 | 说明 | +|------|------| +| 演示种子 | 按车牌稳定 hash:约 65% 挂接演示任务(目标里程、已完成、近窗活跃日里程序列、截止剩余日历天);其余为无任务 | +| 台账导表 | 仅借用车牌标识做稳定种子;**不**把导表「当前总里程」直接当作任务进度真相 | + +正式环境应由采购合同批次里程要求 + 时间起止 + 任务工单执行回写提供目标 / 已完成 / 日里程明细 / 截止日。 + +## 用户可见文案 + +| 场景 | 文案 | +|------|------| +| 无任务 | 暂无里程任务 | +| 进行中 | 剩余 {N} km · 预计 {D} 天内完成 | +| 进行中且无法预计 | 剩余 {N} km · 暂无法预计 | +| 预判标签 | 剩余 {x} 天,预计无法完成 / 预计可完成 / 预计有完成风险 | +| 已完成 | 已完成(无预判标签) | + +## 代码路径 + +- 解析:`utils/mileage-task.ts` → `resolveMileageTaskCard` / `resolveMileageForecast` +- 展示:`components/VehicleCard.tsx` + `styles/index.css`(`.h5-va-mileage-task*` / `.h5-va-mileage-forecast`) diff --git a/src/prototypes/oneos-h5-vehicle-assets/.spec/more-filters.md b/src/prototypes/oneos-h5-vehicle-assets/.spec/more-filters.md new file mode 100644 index 0000000..0519449 --- /dev/null +++ b/src/prototypes/oneos-h5-vehicle-assets/.spec/more-filters.md @@ -0,0 +1,38 @@ +# 更多筛选 · 判定规格 + +「更多筛选」底部面板内的条件与列表筛选项行(品牌车型 / 运营城市 / 车辆状态)叠加生效;任一非「全部」即点亮更多筛选角标。 + +## 面板项与匹配口径 + +| 优先级 | 筛选项 | 选项 | 匹配规则 | 数据源 | +|--------|--------|------|----------|--------| +| 1 | 证照状态 | 全部 / 正常 / 异常 | 异常 = 检验有效期早于今天;正常 = 非异常 | 台账证照有效期 | +| 2 | 保险状态 | 全部 / 正常 / 异常 | 异常 = 保险状态为「异常」;正常 = 非异常 | 台账保险状态 | +| 3 | 出库状态 | 全部 / 未出库 / 已出库 | 见下表(由车辆状态推导,无独立出库字段) | 台账车辆状态 | +| 4 | 车辆来源 | 全部 / 自有 / 外租 / 挂靠 | 精确等于台账「车辆来源」 | 台账车辆来源 | +| 5 | 在线状态 | 全部 / 在线 / 离线 | 精确等于台账「在线状态」 | 台账在线状态 | + +以上条件与 KPI、搜索、品牌车型、运营城市、车辆状态为 **AND**。 + +## 出库状态推导(原型本地) + +| 出库选项 | 车辆状态集合 | +|----------|----------------| +| 未出库 | 待验车、未备车、已备车、待交车 | +| 已出库 | 有明确车辆状态,且不属于「未出库」集合(如已交车、待还车、异动中等) | + +**前置条件** + +- 车辆状态为空、`-`、`无`:不匹配「未出库」也不匹配「已出库」(选任一侧时该车被滤掉) +- 正式环境若上线独立「出库」字段,以字段为准,本表仅作原型推导 + +## 交互与布局 + +- 面板挂在手机壳操作区内,宽度不超过壳层;内容区可滚动,底部「重置 / 完成」固定 +- 「重置」只清更多筛选五项,不清筛选项行与 KPI / 搜索 +- 「完成」关闭面板并保留当前选择;列表已随选择即时刷新 + +## 代码路径(实现对照) + +- 匹配:`utils/filter.ts` → `matchOutboundFilter` / `matchVehicleSourceFilter` / `matchOnlineStatusFilter` 等 +- UI:`components/FilterSheet.tsx` diff --git a/src/prototypes/oneos-h5-vehicle-assets/.spec/requirements-prd.md b/src/prototypes/oneos-h5-vehicle-assets/.spec/requirements-prd.md new file mode 100644 index 0000000..136c93f --- /dev/null +++ b/src/prototypes/oneos-h5-vehicle-assets/.spec/requirements-prd.md @@ -0,0 +1,276 @@ +# 车辆资产(H5)· 产品需求说明(全模块) + +## 1. 一句话与目标 + +**一句话** +在 App / 手机浏览器提供与 PC「车辆资产」口径一致的只读查车与异常浏览能力。 + +**要解决的问题** +- 外勤与业务人员无法在手机上快速按车牌查车、看营运分类与证照/保险异常 +- PC 宽表页不适合塞进 WebView,需要独立手机信息架构 + +**本期目标** +1. 列表:车牌搜索、KPI 分类横滑、证照/保险异常筛选、车辆卡片(含里程任务进度) +2. 详情:多 Tab 对齐 PC,全程只读浏览 +3. 全角色同一套界面;手机端不做编辑 + +**非目标(本期不做)** +- 任意字段编辑(含 Admin) +- 导入 / 导出、催办、跳转证照管理或保险采购等业务处理页 +- 里程任务工单下发 / 编辑、从卡片跳转工单详情、按截止日逾期标红 +- 在租车型占比等 PC 尾部分析入口 +- 将 PC 页做响应式自适应 + +## 2. 模块边界 + +```mermaid +flowchart LR + App[App WebView] --> H5[车辆资产 H5] + H5 -.口径一致.-> PC[车辆资产 PC] + H5 -.只读.-> Data[车辆台账与关联记录] +``` + +| 业务线 / 子域 | 做什么 | 不做什么 | +|---------------|--------|----------| +| 查车列表 | 搜索、KPI、异常筛选、卡片进详情、卡片里程任务进度 | 批量操作、写操作、下发/编辑工单 | +| 车辆详情 | 11 个 Tab 只读浏览 | 行内编辑、跳转处理 | + +**外部依赖(产品口径)** + +| 依赖方 | 交互方式 | 产品要求 | +|--------|----------|----------| +| 车辆资产 PC | 同一套台账与统计口径 | KPI / 证照异常 / 保险异常定义一致;台账数据同源(业务导表) | +| 采购合同 / 任务工单 | 合同关联批次里程要求与起止;工单下发后回写进度 | 列表卡片只读展示完成百分比、剩余公里与预计天数;无工单时仍展示「暂无里程任务」 | +| App 壳 | 内嵌打开本 H5 | 固定打开手机页,不打开 PC 宽表地址 | + +**关键约束** +- 手机端明确提示:修改请在 PC 完成 +- 筛选与 KPI 叠加:先按 KPI,再按证照/保险条件 +- 列表车辆数据与 PC「车辆资产」共用业务导表台账(含真实车牌、客户、运营状态分布) + +## 3. 用户与角色 + +| 角色 | 主要目标 | +|------|----------| +| 运维 / 业务 / 管理等全角色 | 同一套界面查车、看异常与档案摘要 | +| Admin | 与普通用户相同;手机端仍无编辑(改数回 PC) | + +## 4. 用户故事与故事点(业务条线说明口径) + +合计约 **15 SP**。 + +### Epic A · 列表查车(约 10 SP) + +#### A1 · 车牌搜索与卡片列表 +- **角色**:全体手机用户 +- **起点**:从 App 进入车辆资产 H5 +- **怎么运作**: + 1. 顶栏左对齐「车辆资产」;搜索栏提示「搜索车牌 / VIN / 停车场 / 客户名称」,支持按这些关键词过滤;右侧扫码为占位提示 + 2. KPI 横滑摘要卡(每屏 3 张、共 2 页;图标 + 计数 + 占比);下方固定筛选项行:品牌车型 / 运营城市(省→市,可只选省)/ 车辆状态 +「更多筛选」(证照 / 保险 / 出库 / 来源 / 在线);前三项为 App 原生底部选择器,选项来自业务导表;更多筛选面板限制在手机操作区内并可滚动 + 3. 列表横卡含车牌、车型、来源标签、按运营状态变化的次要标签(租赁/自营→客户名省略;库存→未备车警告/已备车正常;非运营→非运营车辆;退出→三方退租/销售/报废)、营运状态圆点、里程与异常角标(不含车辆照片);标签下方展示「里程任务」分隔区(有任务:百分比 + 进度条 + 剩余公里/预计天数 + 完成预判标签;无任务:「暂无里程任务」);列表不展示总车辆数,不提供翻页按钮,滚动触底异步加载更多 + 4. 底部 App Tab 为单条悬浮胶囊壳层预览:车辆 / 业务 / 工作台(中间圆形仅图标)/ 消息 / 我的;非车辆入口仅提示;点卡片进详情 +- **闭环**:找到目标车并进入详情 +- **排期**:US-01 · M + +#### A2 · KPI 分类与异常筛选 +- **角色**:运维 / 业务 +- **起点**:需要按营运分类或证照/保险异常缩小范围 +- **怎么运作**: + 1. 横滑点选 KPI 摘要卡(全部营运/租赁/物流/库存/非运营/退出;每屏完整显示 3 张,翻页看其余 3 张;显示计数与相对占比) + 2. 筛选项行:品牌车型(品牌→车型)、运营城市(省→市,可只选省)、车辆状态(与 PC 字典一致,含调拨中 / 替换中等;无数据时计数为 0 仍可选);点「更多筛选」可再调证照状态、保险状态、出库状态、车辆来源、在线状态 + 3. 列表随条件刷新并从顶部重新加载;不展示列表总辆数 +- **闭环**:得到与 PC 同口径的筛选结果 +- **排期**:US-02 · M + +#### A3 · 卡片里程任务进度 +- **角色**:运维 / 业务 / 管理 +- **起点**:采购合同已为批次车辆关联里程要求与起止时间,并通过任务工单下发 +- **怎么运作**: + 1. 列表每张卡片固定展示「里程任务」区 + 2. 有工单:展示完成百分比、进度条、剩余公里;预计完成天数按近 7 个活跃日(日里程 > 0)平均日里程推算;文案后方展示完成预判标签(对照任务截止剩余日历天:预计无法完成 / 可完成 / 有完成风险) + 3. 无工单:仍保留区块,文案「暂无里程任务」 +- **闭环**:不进详情也能判断该车里程任务是否在轨、还差多少 +- **排期**:US-04 · S + +### Epic B · 详情只读(约 5 SP) + +#### B1 · 多 Tab 档案浏览 +- **角色**:全体手机用户 +- **起点**:从列表进入某车详情 +- **怎么运作**: + 1. 详情与列表共用一体浅蓝渐变底;顶栏透明,仅返回 + 车牌;不挂底部悬浮 Tab + 2. 摘要为浮起白卡:车牌、品牌型号、营运状态(与列表同文案/色点)、来源·城市·项目等标签、里程、证照/保险异常角标(无车辆实景大图) + 3. 横滑切换与 PC 一致的 11 个 Tab;选中态为浅底主色字(对齐列表 Chip) + 4. 各 Tab 仅展示信息(白卡 Section / 记录列表),无编辑按钮;检验有效期后方展示「剩余 x 天」标签;沪牌车在档案信息额外展示等级评定时间与剩余天数;证照信息 Tab 对齐证照管理八类证照;保险记录 Tab 对齐保险采购五险种与采购历史;租赁记录为纵向时间轴(近→远,未还车标注),并支持按客户名称 / 项目名称搜索;事故等业务 Tab 无种子时展示演示用例;无数据时为空态插画 +- **闭环**:不回 PC 也能看完档案与业务记录摘要 +- **排期**:US-03 · M + +## 5. 功能模块说明(正向 / 逆向) + +### 5.1 列表 + +**正向** +1. 默认展示「全部营运」KPI 下的卡片列表(首屏一批,触底异步加载更多;不含退出运营、不含非运营车辆) +2. 搜索、切 KPI、改筛选后列表刷新并从顶部重新加载;不展示总车辆数、无翻页按钮 +3. 证照/保险异常在卡片上可见 +4. 每张卡片标签下方展示「里程任务」:有工单则百分比 + 进度条 + 剩余/预计 + 完成预判标签;无工单则「暂无里程任务」 + +**逆向 / 边界** + +| 情况 | 系统表现 | +|------|----------| +| 无匹配 | 展示「暂无匹配车辆」+ 引导文案;可清空搜索或重置筛选 | +| 仅筛选异常 | 只保留对应异常车辆 | +| 更多筛选多项叠加 | 与 KPI / 搜索 / 筛选项行条件同时生效(AND) | +| 已加载全部 | 底部提示「没有更多了」 | +| 里程任务已完成 | 进度 100%,文案「已完成」,无预判标签 | +| 有任务但近窗无活跃日 | 仍显示剩余公里,预计侧「暂无法预计」,预判标签为「剩余 x 天,预计有完成风险」 | +| 按活跃日推算赶不上截止 | 预判标签「剩余 x 天,预计无法完成」 | + +### 5.2 详情 + +**正向** +1. 进入详情后背景与列表一体连续;摘要卡可见状态、标签、里程与异常 +2. 11 个 Tab 均可切换;内容区白卡展示字段或记录 +3. 检验有效期后方展示剩余/已过期天数标签;沪牌车额外展示等级评定时间与剩余天数 +4. 证照信息含八类证照(行驶证、道路运输证、登记证、特种设备使用登记证、特种设备使用标识、加氢卡、安全阀、压力表),字段与证照管理对齐;顶部索引单行横滑点选锚点 +5. 保险记录对齐保险采购:五险种卡片 + 当前保单摘要 + 采购/批单历史(纵向时间轴,只读) +6. 租赁 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审均为纵向时间轴(近→远);事故及右侧 Tab 支持时间快捷/自定义筛选(自定义为手机端原生滚轮选日);调拨卡片展示出发城市、到达城市、停车场、接收人、接收日(不展示路线与调出人);租赁可搜客户/项目,未还车显示「未还车」 +7. 业务 Tab 无种子时展示演示用例;租赁不足 3 条时补足 +8. 无记录时展示美化空态;无底部 App Tab,避免挡内容 + +**逆向 / 边界** + +| 情况 | 系统表现 | +|------|----------| +| 任意角色 | 均无编辑、删除、导入入口 | +| 返回 | 顶栏返回或系统返回回到列表;列表底栏样式不受详情影响 | +| 检验有效期为空 | 显示「未上传」,无剩余天数标签 | +| 非沪牌 | 不展示等级评定时间 / 剩余天数 | +| 未绑定加氢卡 | 显示「尚未绑定加氢卡」 | +| 某险种无采购记录 | 显示「暂无记录」,引导回 PC 保险采购 | +| 租赁未还车 | 还车字段显示「未还车」 | +| 租赁搜索无匹配 | 展示「暂无匹配记录」,可清空搜索 | +| 业务 Tab 无种子 | 展示稳定演示用例(非真实业务单据) | + +## 6. 关键业务逻辑 + +### 6.1 KPI 口径(与 PC 一致) + +| KPI | 口径 | +|-----|------| +| 全部营运(原「所有营运车辆」) | 非「非运营车辆」,且运营状态不为「退出运营」 | +| 租赁 / 物流 | 运营状态等于对应值 | +| 库存 | 可运营或待运营 | +| 非运营 | 台账类型为非运营车辆 | +| 退出运营 | 运营状态为退出运营 | + +### 6.2 异常口径 + +| 异常 | 口径 | +|------|------| +| 证照异常 | 检验有效期早于今天 | +| 保险异常 | 保险状态为「异常」 | + +### 6.3 更多筛选口径 + +| 筛选项 | 口径 | +|--------|------| +| 证照 / 保险 | 同 §6.2 | +| 出库状态 | 由车辆状态推导:未出库 = 待验车/未备车/已备车/待交车;已出库 = 其余有状态车辆(原型无独立出库字段) | +| 车辆来源 | 自有 / 外租 / 挂靠,与台账一致 | +| 在线状态 | 在线 / 离线,与台账一致 | + +规格全文见 [more-filters.md](./more-filters.md)。 + +### 6.4 里程任务进度(列表卡片) + +| 项 | 口径 | +|----|------| +| 来源 | 采购合同批次里程要求 + 时间起止 + 任务工单下发(原型为本地演示种子,未接真实 API) | +| 完成百分比 | 任务内已完成里程 / 目标里程,封顶 100%,展示保留 1 位小数 | +| 剩余公里 | 目标 − 已完成,下限 0 | +| 活跃日 | 当日行驶里程 > 0 km | +| 预计天数 D | 剩余 ÷ 近最多 7 个活跃日平均日里程,向上取整;无活跃日则「暂无法预计」 | +| 截止剩余日历天 x | 任务时间窗相对今天的剩余自然日 | +| 预判标签 | 用 D 对照 x:无法完成 / 有完成风险 / 可完成(见规格全文);无法完成时进度条与百分比为危险红,有完成风险为警告橙 | +| 无工单 | 仍展示区块,文案「暂无里程任务」 | + +规格全文见 [mileage-task.md](./mileage-task.md)。 + +### 6.5 检验有效期剩余天数 · 沪牌等级评定 + +| 项 | 口径 | +|----|------| +| 检验有效期 | 日期后方标签:剩余 x 天 / 已过期 x 天;空为「未上传」 | +| 沪牌判定 | 车牌以「沪」开头 | +| 等级评定时间 | 仅沪牌展示;空为「未上传」 | +| 剩余天数 | 仅沪牌展示;相对等级评定日计算 | + +规格全文见 [expire-remain-shanghai.md](./expire-remain-shanghai.md)。 + +### 6.6 证照信息(八类) + +与证照管理一致:行驶证、道路运输证、登记证、特种设备使用登记证、特种设备使用标识、加氢卡、安全阀、压力表。规格全文见 [license-certs.md](./license-certs.md)。 + +### 6.7 保险记录(对齐保险采购) + +五险种(交强 / 商业 / 超赔 / 驾意 / 货物)+ 当前保单摘要 + 采购历史(纵向时间轴)。规格全文见 [insurance-records.md](./insurance-records.md)。 + +### 6.8 业务记录纵向时间轴 + +租赁 / 保险历史 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审统一近→远时间轴。 + +| 能力 | 口径 | +|------|------| +| 时间筛选 | 事故及右侧 Tab:全部 / 近30天 / 近90天 / 近1年 / 自定义;按各 Tab 关键时间过滤;无匹配可重置 | +| 自定义选日 | 手机端底部滚轮(年 / 月 / 日,精确到日);起日不得晚于止日;本模块选日入口统一该交互 | +| 调拨卡片 | 出发城市、到达城市、停车场、接收人、接收日;不展示「路线」合并行与「调出」人 | + +规格全文见 [biz-timeline.md](./biz-timeline.md)、[lease-records.md](./lease-records.md)。 + +### 6.9 业务记录演示用例与空态 + +无种子时 H5 本地生成演示用例;真正无数据时展示美化空态。规格全文见 [demo-biz-records.md](./demo-biz-records.md)。 + +## 7. 总览流程图 + +```mermaid +flowchart TB + entry[App 打开车辆资产 H5] --> list[列表:搜索 / KPI / 筛选] + list --> card[点车辆卡片] + card --> detail[详情摘要] + detail --> tabs[切换只读 Tab] + tabs --> back[返回列表] +``` + +## 8. 验收清单(产品 / 测试) + +- [ ] 独立原型可打开,桌面 PhoneShell / 窄屏可用 +- [ ] KPI 六类计数与同数据 PC 一致 +- [ ] 证照异常、保险异常筛选结果与 PC 规则一致 +- [ ] 「更多筛选」含证照 / 保险 / 出库 / 来源 / 在线;面板不超出手机操作区,内容可滚动;启用后角标点亮 +- [ ] 出库:未出库对应待验车/未备车/已备车/待交车;来源与在线与台账字段一致 +- [ ] 车辆状态选项与 PC 字典一致,含「调拨中」「替换中」 +- [ ] 搜索支持车牌 / VIN / 停车场 / 客户名称过滤列表;可一键清空搜索 +- [ ] 无匹配时有引导操作(清空搜索 / 重置筛选) +- [ ] 列表无总辆数、无翻页按钮;滚动触底可异步加载更多,到底提示「没有更多了」 +- [ ] 列表进详情:一体渐变底连续,无白顶栏色带;摘要为浮起白卡(状态/标签/里程/异常与列表同语义);详情无底栏 +- [ ] 详情检验有效期后方有「剩余 x 天」或「已过期 x 天」标签;空为「未上传」 +- [ ] 沪牌详情可见「等级评定时间」「剩余天数」;非沪牌不出现 +- [ ] 证照信息 Tab 可见八类证照,与证照管理种类一致 +- [ ] 保险记录可见五险种卡片、当前保单摘要与历史时间轴;整体状态口径与保险采购一致 +- [ ] 保单附件可预览与下载(原型演示) +- [ ] 租赁 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审为近→远时间轴;租赁未还车显示「未还车」 +- [ ] 调拨卡片可见出发城市、到达城市、停车场、接收人、接收日;无「路线」「调出」 +- [ ] 事故及右侧业务 Tab 可按时间快捷或自定义区间筛选;自定义起止为底部滚轮选日(精确到日),无匹配可重置 +- [ ] 租赁记录可按客户名称 / 项目名称搜索;无匹配可清空搜索 +- [ ] 任意车辆打开上述业务 Tab 可见列表(种子或演示用例);空态有插画与说明 +- [ ] 详情 11 个 Tab 可进入且只读,无编辑按钮 +- [ ] 列表卡片标签下有「里程任务」区:有任务可见百分比、进度条、剩余公里与预计天数;无任务显示「暂无里程任务」 +- [ ] 预计天数按近 7 活跃日均里程推算;无活跃日时显示「暂无法预计」 +- [ ] 进行中任务文案后方有完成预判标签(剩余 x 天 + 预计无法完成 / 可完成 / 有完成风险) +- [ ] 页脚说明「修改请在 PC」可见 + +**交付口径** +本期交付可嵌入 App 的车辆资产只读 H5:能查、能筛异常、能看详情全 Tab,并在列表卡片扫读里程任务进度;写操作与异常处理仍回 PC。 diff --git a/src/prototypes/oneos-h5-vehicle-assets/annotation-source.json b/src/prototypes/oneos-h5-vehicle-assets/annotation-source.json new file mode 100644 index 0000000..7f1343c --- /dev/null +++ b/src/prototypes/oneos-h5-vehicle-assets/annotation-source.json @@ -0,0 +1,293 @@ +{ + "documentVersion": 1, + "format": "axhub-annotation-source", + "data": { + "version": 2, + "prototypeName": "oneos-h5-vehicle-assets", + "pageId": "list", + "updatedAt": 1784600555394, + "nodes": [ + { + "id": "h5-va-search", + "index": 1, + "title": "车牌搜索", + "pageId": "list", + "color": "#32a06e", + "hasMarkdown": true, + "annotationText": "支持按车牌关键词过滤列表。", + "locator": { + "selectors": [ + "[data-annotation-id='h5-va-search']" + ], + "fingerprint": "search|list", + "path": [] + }, + "images": [], + "controls": [], + "createdAt": 1784557399047, + "updatedAt": 1784557399047 + }, + { + "id": "h5-va-kpi", + "index": 2, + "title": "KPI 分类", + "pageId": "list", + "color": "#2b9260", + "hasMarkdown": true, + "annotationText": "与 PC 车辆资产 KPI 口径一致;「全部营运」不含退出运营、不含非运营车辆;摘要卡展示计数与占比。", + "locator": { + "selectors": [ + "[data-annotation-id='h5-va-kpi']" + ], + "fingerprint": "kpi|list", + "path": [] + }, + "images": [], + "controls": [], + "createdAt": 1784557399047, + "updatedAt": 1784570335522 + }, + { + "id": "h5-va-filter", + "index": 3, + "title": "异常筛选", + "pageId": "list", + "color": "#d97706", + "hasMarkdown": true, + "annotationText": "筛选项行:品牌车型 / 运营城市(先省后市,可只选省)/ 车辆状态 + 更多筛选(证照 / 保险 / 出库 / 来源 / 在线)。", + "locator": { + "selectors": [ + "[data-annotation-id='h5-va-filter']" + ], + "fingerprint": "filter|list", + "path": [] + }, + "images": [], + "controls": [], + "createdAt": 1784557399047, + "updatedAt": 1784570606914 + }, + { + "id": "h5-va-card", + "index": 4, + "title": "车辆卡片", + "pageId": "list", + "color": "#52525b", + "hasMarkdown": true, + "annotationText": "Boutique 横卡:车牌、车型、标签、营运状态、里程与异常角标;点击进入详情。", + "locator": { + "selectors": [ + "[data-annotation-id='h5-va-card']" + ], + "fingerprint": "card|list", + "path": [] + }, + "images": [], + "controls": [], + "createdAt": 1784557399047, + "updatedAt": 1784557399047 + }, + { + "id": "h5-va-detail", + "index": 5, + "title": "详情摘要", + "pageId": "detail", + "color": "#32a06e", + "hasMarkdown": true, + "annotationText": "车牌、车型、营运状态与证照/保险异常;全程只读。", + "locator": { + "selectors": [ + "[data-annotation-id='h5-va-detail']" + ], + "fingerprint": "detail|detail", + "path": [] + }, + "images": [], + "controls": [], + "createdAt": 1784557399047, + "updatedAt": 1784557399047 + }, + { + "id": "h5-va-tabs", + "index": 6, + "title": "详情 Tab", + "pageId": "detail", + "color": "#2b9260", + "hasMarkdown": true, + "annotationText": "11 个 Tab 对齐 PC,内容只读。", + "locator": { + "selectors": [ + "[data-annotation-id='h5-va-tabs']" + ], + "fingerprint": "tabs|detail", + "path": [] + }, + "images": [], + "controls": [], + "createdAt": 1784557399047, + "updatedAt": 1784557399047 + }, + { + "id": "h5-va-mileage-task", + "index": 7, + "title": "里程任务进度", + "pageId": "list", + "color": "#2563eb", + "hasMarkdown": true, + "annotationText": "标签下方展示里程任务:有工单显示百分比、进度条、剩余公里与预计天数,文案后方完成预判标签(剩余x天+预计无法完成/可完成/有完成风险);无工单显示「暂无里程任务」。", + "locator": { + "selectors": [ + "[data-annotation-id='h5-va-mileage-task']" + ], + "fingerprint": "mileage-task|list", + "path": [] + }, + "images": [], + "controls": [], + "createdAt": 1784568570662, + "updatedAt": 1784568901056 + } + ] + }, + "markdownMap": {}, + "assetMap": {}, + "directory": { + "nodes": [ + { + "type": "folder", + "id": "h5-va-doc-root", + "title": "车辆资产(H5)说明", + "defaultExpanded": true, + "children": [ + { + "type": "markdown", + "id": "h5-va-doc-prd", + "title": "产品需求说明(PRD)", + "markdownPath": ".spec/requirements-prd.md", + "markdown": "# 车辆资产(H5)· 产品需求说明(全模块)\n\n## 1. 一句话与目标\n\n**一句话** \n在 App / 手机浏览器提供与 PC「车辆资产」口径一致的只读查车与异常浏览能力。\n\n**要解决的问题** \n- 外勤与业务人员无法在手机上快速按车牌查车、看营运分类与证照/保险异常 \n- PC 宽表页不适合塞进 WebView,需要独立手机信息架构 \n\n**本期目标** \n1. 列表:车牌搜索、KPI 分类横滑、证照/保险异常筛选、车辆卡片(含里程任务进度) \n2. 详情:多 Tab 对齐 PC,全程只读浏览 \n3. 全角色同一套界面;手机端不做编辑 \n\n**非目标(本期不做)** \n- 任意字段编辑(含 Admin) \n- 导入 / 导出、催办、跳转证照管理或保险采购等业务处理页 \n- 里程任务工单下发 / 编辑、从卡片跳转工单详情、按截止日逾期标红 \n- 在租车型占比等 PC 尾部分析入口 \n- 将 PC 页做响应式自适应 \n\n## 2. 模块边界\n\n```mermaid\nflowchart LR\n App[App WebView] --> H5[车辆资产 H5]\n H5 -.口径一致.-> PC[车辆资产 PC]\n H5 -.只读.-> Data[车辆台账与关联记录]\n```\n\n| 业务线 / 子域 | 做什么 | 不做什么 |\n|---------------|--------|----------|\n| 查车列表 | 搜索、KPI、异常筛选、卡片进详情、卡片里程任务进度 | 批量操作、写操作、下发/编辑工单 |\n| 车辆详情 | 11 个 Tab 只读浏览 | 行内编辑、跳转处理 |\n\n**外部依赖(产品口径)** \n\n| 依赖方 | 交互方式 | 产品要求 |\n|--------|----------|----------|\n| 车辆资产 PC | 同一套台账与统计口径 | KPI / 证照异常 / 保险异常定义一致;台账数据同源(业务导表) |\n| 采购合同 / 任务工单 | 合同关联批次里程要求与起止;工单下发后回写进度 | 列表卡片只读展示完成百分比、剩余公里与预计天数;无工单时仍展示「暂无里程任务」 |\n| App 壳 | 内嵌打开本 H5 | 固定打开手机页,不打开 PC 宽表地址 |\n\n**关键约束** \n- 手机端明确提示:修改请在 PC 完成 \n- 筛选与 KPI 叠加:先按 KPI,再按证照/保险条件 \n- 列表车辆数据与 PC「车辆资产」共用业务导表台账(含真实车牌、客户、运营状态分布) \n\n## 3. 用户与角色\n\n| 角色 | 主要目标 |\n|------|----------|\n| 运维 / 业务 / 管理等全角色 | 同一套界面查车、看异常与档案摘要 |\n| Admin | 与普通用户相同;手机端仍无编辑(改数回 PC) |\n\n## 4. 用户故事与故事点(业务条线说明口径)\n\n合计约 **15 SP**。\n\n### Epic A · 列表查车(约 10 SP)\n\n#### A1 · 车牌搜索与卡片列表\n- **角色**:全体手机用户 \n- **起点**:从 App 进入车辆资产 H5 \n- **怎么运作**:\n 1. 顶栏左对齐「车辆资产」;搜索栏提示「搜索车牌 / VIN / 停车场 / 客户名称」,支持按这些关键词过滤;右侧扫码为占位提示 \n 2. KPI 横滑摘要卡(每屏 3 张、共 2 页;图标 + 计数 + 占比);下方固定筛选项行:品牌车型 / 运营城市(省→市,可只选省)/ 车辆状态 +「更多筛选」(证照 / 保险 / 出库 / 来源 / 在线);前三项为 App 原生底部选择器,选项来自业务导表;更多筛选面板限制在手机操作区内并可滚动 \n 3. 列表横卡含车牌、车型、来源标签、按运营状态变化的次要标签(租赁/自营→客户名省略;库存→未备车警告/已备车正常;非运营→非运营车辆;退出→三方退租/销售/报废)、营运状态圆点、里程与异常角标(不含车辆照片);标签下方展示「里程任务」分隔区(有任务:百分比 + 进度条 + 剩余公里/预计天数 + 完成预判标签;无任务:「暂无里程任务」);列表不展示总车辆数,不提供翻页按钮,滚动触底异步加载更多 \n 4. 底部 App Tab 为单条悬浮胶囊壳层预览:车辆 / 业务 / 工作台(中间圆形仅图标)/ 消息 / 我的;非车辆入口仅提示;点卡片进详情 \n- **闭环**:找到目标车并进入详情 \n- **排期**:US-01 · M \n\n#### A2 · KPI 分类与异常筛选\n- **角色**:运维 / 业务 \n- **起点**:需要按营运分类或证照/保险异常缩小范围 \n- **怎么运作**:\n 1. 横滑点选 KPI 摘要卡(全部营运/租赁/物流/库存/非运营/退出;每屏完整显示 3 张,翻页看其余 3 张;显示计数与相对占比) \n 2. 筛选项行:品牌车型(品牌→车型)、运营城市(省→市,可只选省)、车辆状态(与 PC 字典一致,含调拨中 / 替换中等;无数据时计数为 0 仍可选);点「更多筛选」可再调证照状态、保险状态、出库状态、车辆来源、在线状态 \n 3. 列表随条件刷新并从顶部重新加载;不展示列表总辆数 \n- **闭环**:得到与 PC 同口径的筛选结果 \n- **排期**:US-02 · M \n\n#### A3 · 卡片里程任务进度\n- **角色**:运维 / 业务 / 管理 \n- **起点**:采购合同已为批次车辆关联里程要求与起止时间,并通过任务工单下发 \n- **怎么运作**:\n 1. 列表每张卡片固定展示「里程任务」区 \n 2. 有工单:展示完成百分比、进度条、剩余公里;预计完成天数按近 7 个活跃日(日里程 > 0)平均日里程推算;文案后方展示完成预判标签(对照任务截止剩余日历天:预计无法完成 / 可完成 / 有完成风险) \n 3. 无工单:仍保留区块,文案「暂无里程任务」 \n- **闭环**:不进详情也能判断该车里程任务是否在轨、还差多少 \n- **排期**:US-04 · S \n\n### Epic B · 详情只读(约 5 SP)\n\n#### B1 · 多 Tab 档案浏览\n- **角色**:全体手机用户 \n- **起点**:从列表进入某车详情 \n- **怎么运作**:\n 1. 详情与列表共用一体浅蓝渐变底;顶栏透明,仅返回 + 车牌;不挂底部悬浮 Tab \n 2. 摘要为浮起白卡:车牌、品牌型号、营运状态(与列表同文案/色点)、来源·城市·项目等标签、里程、证照/保险异常角标(无车辆实景大图) \n 3. 横滑切换与 PC 一致的 11 个 Tab;选中态为浅底主色字(对齐列表 Chip) \n 4. 各 Tab 仅展示信息(白卡 Section / 记录列表),无编辑按钮;检验有效期后方展示「剩余 x 天」标签;沪牌车在档案信息额外展示等级评定时间与剩余天数;证照信息 Tab 对齐证照管理八类证照;保险记录 Tab 对齐保险采购五险种与采购历史;租赁记录为纵向时间轴(近→远,未还车标注),并支持按客户名称 / 项目名称搜索;事故等业务 Tab 无种子时展示演示用例;无数据时为空态插画 \n- **闭环**:不回 PC 也能看完档案与业务记录摘要 \n- **排期**:US-03 · M \n\n## 5. 功能模块说明(正向 / 逆向)\n\n### 5.1 列表\n\n**正向** \n1. 默认展示「全部营运」KPI 下的卡片列表(首屏一批,触底异步加载更多;不含退出运营、不含非运营车辆) \n2. 搜索、切 KPI、改筛选后列表刷新并从顶部重新加载;不展示总车辆数、无翻页按钮 \n3. 证照/保险异常在卡片上可见 \n4. 每张卡片标签下方展示「里程任务」:有工单则百分比 + 进度条 + 剩余/预计 + 完成预判标签;无工单则「暂无里程任务」 \n\n**逆向 / 边界** \n\n| 情况 | 系统表现 |\n|------|----------|\n| 无匹配 | 展示「暂无匹配车辆」+ 引导文案;可清空搜索或重置筛选 |\n| 仅筛选异常 | 只保留对应异常车辆 |\n| 更多筛选多项叠加 | 与 KPI / 搜索 / 筛选项行条件同时生效(AND) |\n| 已加载全部 | 底部提示「没有更多了」 |\n| 里程任务已完成 | 进度 100%,文案「已完成」,无预判标签 |\n| 有任务但近窗无活跃日 | 仍显示剩余公里,预计侧「暂无法预计」,预判标签为「剩余 x 天,预计有完成风险」 |\n| 按活跃日推算赶不上截止 | 预判标签「剩余 x 天,预计无法完成」 |\n\n### 5.2 详情\n\n**正向** \n1. 进入详情后背景与列表一体连续;摘要卡可见状态、标签、里程与异常 \n2. 11 个 Tab 均可切换;内容区白卡展示字段或记录 \n3. 检验有效期后方展示剩余/已过期天数标签;沪牌车额外展示等级评定时间与剩余天数 \n4. 证照信息含八类证照(行驶证、道路运输证、登记证、特种设备使用登记证、特种设备使用标识、加氢卡、安全阀、压力表),字段与证照管理对齐;顶部索引单行横滑点选锚点 \n5. 保险记录对齐保险采购:五险种卡片 + 当前保单摘要 + 采购/批单历史(纵向时间轴,只读) \n6. 租赁 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审均为纵向时间轴(近→远);事故及右侧 Tab 支持时间快捷/自定义筛选(自定义为手机端原生滚轮选日);调拨卡片展示出发城市、到达城市、停车场、接收人、接收日(不展示路线与调出人);租赁可搜客户/项目,未还车显示「未还车」 \n7. 业务 Tab 无种子时展示演示用例;租赁不足 3 条时补足 \n8. 无记录时展示美化空态;无底部 App Tab,避免挡内容 \n\n**逆向 / 边界** \n\n| 情况 | 系统表现 |\n|------|----------|\n| 任意角色 | 均无编辑、删除、导入入口 |\n| 返回 | 顶栏返回或系统返回回到列表;列表底栏样式不受详情影响 |\n| 检验有效期为空 | 显示「未上传」,无剩余天数标签 |\n| 非沪牌 | 不展示等级评定时间 / 剩余天数 |\n| 未绑定加氢卡 | 显示「尚未绑定加氢卡」 |\n| 某险种无采购记录 | 显示「暂无记录」,引导回 PC 保险采购 |\n| 租赁未还车 | 还车字段显示「未还车」 |\n| 租赁搜索无匹配 | 展示「暂无匹配记录」,可清空搜索 |\n| 业务 Tab 无种子 | 展示稳定演示用例(非真实业务单据) |\n\n## 6. 关键业务逻辑\n\n### 6.1 KPI 口径(与 PC 一致)\n\n| KPI | 口径 |\n|-----|------|\n| 全部营运(原「所有营运车辆」) | 非「非运营车辆」,且运营状态不为「退出运营」 |\n| 租赁 / 物流 | 运营状态等于对应值 |\n| 库存 | 可运营或待运营 |\n| 非运营 | 台账类型为非运营车辆 |\n| 退出运营 | 运营状态为退出运营 |\n\n### 6.2 异常口径\n\n| 异常 | 口径 |\n|------|------|\n| 证照异常 | 检验有效期早于今天 |\n| 保险异常 | 保险状态为「异常」 |\n\n### 6.3 更多筛选口径\n\n| 筛选项 | 口径 |\n|--------|------|\n| 证照 / 保险 | 同 §6.2 |\n| 出库状态 | 由车辆状态推导:未出库 = 待验车/未备车/已备车/待交车;已出库 = 其余有状态车辆(原型无独立出库字段) |\n| 车辆来源 | 自有 / 外租 / 挂靠,与台账一致 |\n| 在线状态 | 在线 / 离线,与台账一致 |\n\n规格全文见 [more-filters.md](./more-filters.md)。\n\n### 6.4 里程任务进度(列表卡片)\n\n| 项 | 口径 |\n|----|------|\n| 来源 | 采购合同批次里程要求 + 时间起止 + 任务工单下发(原型为本地演示种子,未接真实 API) |\n| 完成百分比 | 任务内已完成里程 / 目标里程,封顶 100%,展示保留 1 位小数 |\n| 剩余公里 | 目标 − 已完成,下限 0 |\n| 活跃日 | 当日行驶里程 > 0 km |\n| 预计天数 D | 剩余 ÷ 近最多 7 个活跃日平均日里程,向上取整;无活跃日则「暂无法预计」 |\n| 截止剩余日历天 x | 任务时间窗相对今天的剩余自然日 |\n| 预判标签 | 用 D 对照 x:无法完成 / 有完成风险 / 可完成(见规格全文);无法完成时进度条与百分比为危险红,有完成风险为警告橙 |\n| 无工单 | 仍展示区块,文案「暂无里程任务」 |\n\n规格全文见 [mileage-task.md](./mileage-task.md)。\n\n### 6.5 检验有效期剩余天数 · 沪牌等级评定\n\n| 项 | 口径 |\n|----|------|\n| 检验有效期 | 日期后方标签:剩余 x 天 / 已过期 x 天;空为「未上传」 |\n| 沪牌判定 | 车牌以「沪」开头 |\n| 等级评定时间 | 仅沪牌展示;空为「未上传」 |\n| 剩余天数 | 仅沪牌展示;相对等级评定日计算 |\n\n规格全文见 [expire-remain-shanghai.md](./expire-remain-shanghai.md)。\n\n### 6.6 证照信息(八类)\n\n与证照管理一致:行驶证、道路运输证、登记证、特种设备使用登记证、特种设备使用标识、加氢卡、安全阀、压力表。规格全文见 [license-certs.md](./license-certs.md)。\n\n### 6.7 保险记录(对齐保险采购)\n\n五险种(交强 / 商业 / 超赔 / 驾意 / 货物)+ 当前保单摘要 + 采购历史(纵向时间轴)。规格全文见 [insurance-records.md](./insurance-records.md)。\n\n### 6.8 业务记录纵向时间轴\n\n租赁 / 保险历史 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审统一近→远时间轴。\n\n| 能力 | 口径 |\n|------|------|\n| 时间筛选 | 事故及右侧 Tab:全部 / 近30天 / 近90天 / 近1年 / 自定义;按各 Tab 关键时间过滤;无匹配可重置 |\n| 自定义选日 | 手机端底部滚轮(年 / 月 / 日,精确到日);起日不得晚于止日;本模块选日入口统一该交互 |\n| 调拨卡片 | 出发城市、到达城市、停车场、接收人、接收日;不展示「路线」合并行与「调出」人 |\n\n规格全文见 [biz-timeline.md](./biz-timeline.md)、[lease-records.md](./lease-records.md)。\n\n### 6.9 业务记录演示用例与空态\n\n无种子时 H5 本地生成演示用例;真正无数据时展示美化空态。规格全文见 [demo-biz-records.md](./demo-biz-records.md)。\n\n## 7. 总览流程图\n\n```mermaid\nflowchart TB\n entry[App 打开车辆资产 H5] --> list[列表:搜索 / KPI / 筛选]\n list --> card[点车辆卡片]\n card --> detail[详情摘要]\n detail --> tabs[切换只读 Tab]\n tabs --> back[返回列表]\n```\n\n## 8. 验收清单(产品 / 测试)\n\n- [ ] 独立原型可打开,桌面 PhoneShell / 窄屏可用 \n- [ ] KPI 六类计数与同数据 PC 一致 \n- [ ] 证照异常、保险异常筛选结果与 PC 规则一致 \n- [ ] 「更多筛选」含证照 / 保险 / 出库 / 来源 / 在线;面板不超出手机操作区,内容可滚动;启用后角标点亮 \n- [ ] 出库:未出库对应待验车/未备车/已备车/待交车;来源与在线与台账字段一致 \n- [ ] 车辆状态选项与 PC 字典一致,含「调拨中」「替换中」 \n- [ ] 搜索支持车牌 / VIN / 停车场 / 客户名称过滤列表;可一键清空搜索 \n- [ ] 无匹配时有引导操作(清空搜索 / 重置筛选) \n- [ ] 列表无总辆数、无翻页按钮;滚动触底可异步加载更多,到底提示「没有更多了」 \n- [ ] 列表进详情:一体渐变底连续,无白顶栏色带;摘要为浮起白卡(状态/标签/里程/异常与列表同语义);详情无底栏 \n- [ ] 详情检验有效期后方有「剩余 x 天」或「已过期 x 天」标签;空为「未上传」 \n- [ ] 沪牌详情可见「等级评定时间」「剩余天数」;非沪牌不出现 \n- [ ] 证照信息 Tab 可见八类证照,与证照管理种类一致 \n- [ ] 保险记录可见五险种卡片、当前保单摘要与历史时间轴;整体状态口径与保险采购一致 \n- [ ] 保单附件可预览与下载(原型演示) \n- [ ] 租赁 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审为近→远时间轴;租赁未还车显示「未还车」 \n- [ ] 调拨卡片可见出发城市、到达城市、停车场、接收人、接收日;无「路线」「调出」 \n- [ ] 事故及右侧业务 Tab 可按时间快捷或自定义区间筛选;自定义起止为底部滚轮选日(精确到日),无匹配可重置 \n- [ ] 租赁记录可按客户名称 / 项目名称搜索;无匹配可清空搜索 \n- [ ] 任意车辆打开上述业务 Tab 可见列表(种子或演示用例);空态有插画与说明 \n- [ ] 详情 11 个 Tab 可进入且只读,无编辑按钮 \n- [ ] 列表卡片标签下有「里程任务」区:有任务可见百分比、进度条、剩余公里与预计天数;无任务显示「暂无里程任务」 \n- [ ] 预计天数按近 7 活跃日均里程推算;无活跃日时显示「暂无法预计」 \n- [ ] 进行中任务文案后方有完成预判标签(剩余 x 天 + 预计无法完成 / 可完成 / 有完成风险) \n- [ ] 页脚说明「修改请在 PC」可见 \n\n**交付口径** \n本期交付可嵌入 App 的车辆资产只读 H5:能查、能筛异常、能看详情全 Tab,并在列表卡片扫读里程任务进度;写操作与异常处理仍回 PC。\n" + }, + { + "type": "folder", + "id": "h5-va-doc-prd-chapters", + "title": "PRD 分章", + "defaultExpanded": true, + "children": [ + { + "type": "markdown", + "id": "h5-va-prd-ch-01", + "title": "1. 一句话与目标", + "markdown": "## 1. 一句话与目标\n\n**一句话** \n在 App / 手机浏览器提供与 PC「车辆资产」口径一致的只读查车与异常浏览能力。\n\n**要解决的问题** \n- 外勤与业务人员无法在手机上快速按车牌查车、看营运分类与证照/保险异常 \n- PC 宽表页不适合塞进 WebView,需要独立手机信息架构 \n\n**本期目标** \n1. 列表:车牌搜索、KPI 分类横滑、证照/保险异常筛选、车辆卡片(含里程任务进度) \n2. 详情:多 Tab 对齐 PC,全程只读浏览 \n3. 全角色同一套界面;手机端不做编辑 \n\n**非目标(本期不做)** \n- 任意字段编辑(含 Admin) \n- 导入 / 导出、催办、跳转证照管理或保险采购等业务处理页 \n- 里程任务工单下发 / 编辑、从卡片跳转工单详情、按截止日逾期标红 \n- 在租车型占比等 PC 尾部分析入口 \n- 将 PC 页做响应式自适应\n" + }, + { + "type": "markdown", + "id": "h5-va-prd-ch-02", + "title": "2. 模块边界", + "markdown": "## 2. 模块边界\n\n```mermaid\nflowchart LR\n App[App WebView] --> H5[车辆资产 H5]\n H5 -.口径一致.-> PC[车辆资产 PC]\n H5 -.只读.-> Data[车辆台账与关联记录]\n```\n\n| 业务线 / 子域 | 做什么 | 不做什么 |\n|---------------|--------|----------|\n| 查车列表 | 搜索、KPI、异常筛选、卡片进详情、卡片里程任务进度 | 批量操作、写操作、下发/编辑工单 |\n| 车辆详情 | 11 个 Tab 只读浏览 | 行内编辑、跳转处理 |\n\n**外部依赖(产品口径)** \n\n| 依赖方 | 交互方式 | 产品要求 |\n|--------|----------|----------|\n| 车辆资产 PC | 同一套台账与统计口径 | KPI / 证照异常 / 保险异常定义一致;台账数据同源(业务导表) |\n| 采购合同 / 任务工单 | 合同关联批次里程要求与起止;工单下发后回写进度 | 列表卡片只读展示完成百分比、剩余公里与预计天数;无工单时仍展示「暂无里程任务」 |\n| App 壳 | 内嵌打开本 H5 | 固定打开手机页,不打开 PC 宽表地址 |\n\n**关键约束** \n- 手机端明确提示:修改请在 PC 完成 \n- 筛选与 KPI 叠加:先按 KPI,再按证照/保险条件 \n- 列表车辆数据与 PC「车辆资产」共用业务导表台账(含真实车牌、客户、运营状态分布)\n" + }, + { + "type": "markdown", + "id": "h5-va-prd-ch-03", + "title": "3. 用户与角色", + "markdown": "## 3. 用户与角色\n\n| 角色 | 主要目标 |\n|------|----------|\n| 运维 / 业务 / 管理等全角色 | 同一套界面查车、看异常与档案摘要 |\n| Admin | 与普通用户相同;手机端仍无编辑(改数回 PC) |\n" + }, + { + "type": "markdown", + "id": "h5-va-prd-ch-04", + "title": "4. 用户故事与故事点(业务条线说明口径)", + "markdown": "## 4. 用户故事与故事点(业务条线说明口径)\n\n合计约 **15 SP**。\n\n### Epic A · 列表查车(约 10 SP)\n\n#### A1 · 车牌搜索与卡片列表\n- **角色**:全体手机用户 \n- **起点**:从 App 进入车辆资产 H5 \n- **怎么运作**:\n 1. 顶栏左对齐「车辆资产」;搜索栏提示「搜索车牌 / VIN / 停车场 / 客户名称」,支持按这些关键词过滤;右侧扫码为占位提示 \n 2. KPI 横滑摘要卡(每屏 3 张、共 2 页;图标 + 计数 + 占比);下方固定筛选项行:品牌车型 / 运营城市(省→市,可只选省)/ 车辆状态 +「更多筛选」(证照 / 保险 / 出库 / 来源 / 在线);前三项为 App 原生底部选择器,选项来自业务导表;更多筛选面板限制在手机操作区内并可滚动 \n 3. 列表横卡含车牌、车型、来源标签、按运营状态变化的次要标签(租赁/自营→客户名省略;库存→未备车警告/已备车正常;非运营→非运营车辆;退出→三方退租/销售/报废)、营运状态圆点、里程与异常角标(不含车辆照片);标签下方展示「里程任务」分隔区(有任务:百分比 + 进度条 + 剩余公里/预计天数 + 完成预判标签;无任务:「暂无里程任务」);列表不展示总车辆数,不提供翻页按钮,滚动触底异步加载更多 \n 4. 底部 App Tab 为单条悬浮胶囊壳层预览:车辆 / 业务 / 工作台(中间圆形仅图标)/ 消息 / 我的;非车辆入口仅提示;点卡片进详情 \n- **闭环**:找到目标车并进入详情 \n- **排期**:US-01 · M \n\n#### A2 · KPI 分类与异常筛选\n- **角色**:运维 / 业务 \n- **起点**:需要按营运分类或证照/保险异常缩小范围 \n- **怎么运作**:\n 1. 横滑点选 KPI 摘要卡(全部营运/租赁/物流/库存/非运营/退出;每屏完整显示 3 张,翻页看其余 3 张;显示计数与相对占比) \n 2. 筛选项行:品牌车型(品牌→车型)、运营城市(省→市,可只选省)、车辆状态(与 PC 字典一致,含调拨中 / 替换中等;无数据时计数为 0 仍可选);点「更多筛选」可再调证照状态、保险状态、出库状态、车辆来源、在线状态 \n 3. 列表随条件刷新并从顶部重新加载;不展示列表总辆数 \n- **闭环**:得到与 PC 同口径的筛选结果 \n- **排期**:US-02 · M \n\n#### A3 · 卡片里程任务进度\n- **角色**:运维 / 业务 / 管理 \n- **起点**:采购合同已为批次车辆关联里程要求与起止时间,并通过任务工单下发 \n- **怎么运作**:\n 1. 列表每张卡片固定展示「里程任务」区 \n 2. 有工单:展示完成百分比、进度条、剩余公里;预计完成天数按近 7 个活跃日(日里程 > 0)平均日里程推算;文案后方展示完成预判标签(对照任务截止剩余日历天:预计无法完成 / 可完成 / 有完成风险) \n 3. 无工单:仍保留区块,文案「暂无里程任务」 \n- **闭环**:不进详情也能判断该车里程任务是否在轨、还差多少 \n- **排期**:US-04 · S \n\n### Epic B · 详情只读(约 5 SP)\n\n#### B1 · 多 Tab 档案浏览\n- **角色**:全体手机用户 \n- **起点**:从列表进入某车详情 \n- **怎么运作**:\n 1. 详情与列表共用一体浅蓝渐变底;顶栏透明,仅返回 + 车牌;不挂底部悬浮 Tab \n 2. 摘要为浮起白卡:车牌、品牌型号、营运状态(与列表同文案/色点)、来源·城市·项目等标签、里程、证照/保险异常角标(无车辆实景大图) \n 3. 横滑切换与 PC 一致的 11 个 Tab;选中态为浅底主色字(对齐列表 Chip) \n 4. 各 Tab 仅展示信息(白卡 Section / 记录列表),无编辑按钮;检验有效期后方展示「剩余 x 天」标签;沪牌车在档案信息额外展示等级评定时间与剩余天数;证照信息 Tab 对齐证照管理八类证照;保险记录 Tab 对齐保险采购五险种与采购历史;租赁记录为纵向时间轴(近→远,未还车标注),并支持按客户名称 / 项目名称搜索;事故等业务 Tab 无种子时展示演示用例;无数据时为空态插画 \n- **闭环**:不回 PC 也能看完档案与业务记录摘要 \n- **排期**:US-03 · M\n" + }, + { + "type": "markdown", + "id": "h5-va-prd-ch-05", + "title": "5. 功能模块说明(正向 / 逆向)", + "markdown": "## 5. 功能模块说明(正向 / 逆向)\n\n### 5.1 列表\n\n**正向** \n1. 默认展示「全部营运」KPI 下的卡片列表(首屏一批,触底异步加载更多;不含退出运营、不含非运营车辆) \n2. 搜索、切 KPI、改筛选后列表刷新并从顶部重新加载;不展示总车辆数、无翻页按钮 \n3. 证照/保险异常在卡片上可见 \n4. 每张卡片标签下方展示「里程任务」:有工单则百分比 + 进度条 + 剩余/预计 + 完成预判标签;无工单则「暂无里程任务」 \n\n**逆向 / 边界** \n\n| 情况 | 系统表现 |\n|------|----------|\n| 无匹配 | 展示「暂无匹配车辆」+ 引导文案;可清空搜索或重置筛选 |\n| 仅筛选异常 | 只保留对应异常车辆 |\n| 更多筛选多项叠加 | 与 KPI / 搜索 / 筛选项行条件同时生效(AND) |\n| 已加载全部 | 底部提示「没有更多了」 |\n| 里程任务已完成 | 进度 100%,文案「已完成」,无预判标签 |\n| 有任务但近窗无活跃日 | 仍显示剩余公里,预计侧「暂无法预计」,预判标签为「剩余 x 天,预计有完成风险」 |\n| 按活跃日推算赶不上截止 | 预判标签「剩余 x 天,预计无法完成」 |\n\n### 5.2 详情\n\n**正向** \n1. 进入详情后背景与列表一体连续;摘要卡可见状态、标签、里程与异常 \n2. 11 个 Tab 均可切换;内容区白卡展示字段或记录 \n3. 检验有效期后方展示剩余/已过期天数标签;沪牌车额外展示等级评定时间与剩余天数 \n4. 证照信息含八类证照(行驶证、道路运输证、登记证、特种设备使用登记证、特种设备使用标识、加氢卡、安全阀、压力表),字段与证照管理对齐;顶部索引单行横滑点选锚点 \n5. 保险记录对齐保险采购:五险种卡片 + 当前保单摘要 + 采购/批单历史(纵向时间轴,只读) \n6. 租赁 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审均为纵向时间轴(近→远);事故及右侧 Tab 支持时间快捷/自定义筛选(自定义为手机端原生滚轮选日);调拨卡片展示出发城市、到达城市、停车场、接收人、接收日(不展示路线与调出人);租赁可搜客户/项目,未还车显示「未还车」 \n7. 业务 Tab 无种子时展示演示用例;租赁不足 3 条时补足 \n8. 无记录时展示美化空态;无底部 App Tab,避免挡内容 \n\n**逆向 / 边界** \n\n| 情况 | 系统表现 |\n|------|----------|\n| 任意角色 | 均无编辑、删除、导入入口 |\n| 返回 | 顶栏返回或系统返回回到列表;列表底栏样式不受详情影响 |\n| 检验有效期为空 | 显示「未上传」,无剩余天数标签 |\n| 非沪牌 | 不展示等级评定时间 / 剩余天数 |\n| 未绑定加氢卡 | 显示「尚未绑定加氢卡」 |\n| 某险种无采购记录 | 显示「暂无记录」,引导回 PC 保险采购 |\n| 租赁未还车 | 还车字段显示「未还车」 |\n| 租赁搜索无匹配 | 展示「暂无匹配记录」,可清空搜索 |\n| 业务 Tab 无种子 | 展示稳定演示用例(非真实业务单据) |\n" + }, + { + "type": "markdown", + "id": "h5-va-prd-ch-06", + "title": "6. 关键业务逻辑", + "markdown": "## 6. 关键业务逻辑\n\n### 6.1 KPI 口径(与 PC 一致)\n\n| KPI | 口径 |\n|-----|------|\n| 全部营运(原「所有营运车辆」) | 非「非运营车辆」,且运营状态不为「退出运营」 |\n| 租赁 / 物流 | 运营状态等于对应值 |\n| 库存 | 可运营或待运营 |\n| 非运营 | 台账类型为非运营车辆 |\n| 退出运营 | 运营状态为退出运营 |\n\n### 6.2 异常口径\n\n| 异常 | 口径 |\n|------|------|\n| 证照异常 | 检验有效期早于今天 |\n| 保险异常 | 保险状态为「异常」 |\n\n### 6.3 更多筛选口径\n\n| 筛选项 | 口径 |\n|--------|------|\n| 证照 / 保险 | 同 §6.2 |\n| 出库状态 | 由车辆状态推导:未出库 = 待验车/未备车/已备车/待交车;已出库 = 其余有状态车辆(原型无独立出库字段) |\n| 车辆来源 | 自有 / 外租 / 挂靠,与台账一致 |\n| 在线状态 | 在线 / 离线,与台账一致 |\n\n规格全文见 [more-filters.md](./more-filters.md)。\n\n### 6.4 里程任务进度(列表卡片)\n\n| 项 | 口径 |\n|----|------|\n| 来源 | 采购合同批次里程要求 + 时间起止 + 任务工单下发(原型为本地演示种子,未接真实 API) |\n| 完成百分比 | 任务内已完成里程 / 目标里程,封顶 100%,展示保留 1 位小数 |\n| 剩余公里 | 目标 − 已完成,下限 0 |\n| 活跃日 | 当日行驶里程 > 0 km |\n| 预计天数 D | 剩余 ÷ 近最多 7 个活跃日平均日里程,向上取整;无活跃日则「暂无法预计」 |\n| 截止剩余日历天 x | 任务时间窗相对今天的剩余自然日 |\n| 预判标签 | 用 D 对照 x:无法完成 / 有完成风险 / 可完成(见规格全文);无法完成时进度条与百分比为危险红,有完成风险为警告橙 |\n| 无工单 | 仍展示区块,文案「暂无里程任务」 |\n\n规格全文见 [mileage-task.md](./mileage-task.md)。\n\n### 6.5 检验有效期剩余天数 · 沪牌等级评定\n\n| 项 | 口径 |\n|----|------|\n| 检验有效期 | 日期后方标签:剩余 x 天 / 已过期 x 天;空为「未上传」 |\n| 沪牌判定 | 车牌以「沪」开头 |\n| 等级评定时间 | 仅沪牌展示;空为「未上传」 |\n| 剩余天数 | 仅沪牌展示;相对等级评定日计算 |\n\n规格全文见 [expire-remain-shanghai.md](./expire-remain-shanghai.md)。\n\n### 6.6 证照信息(八类)\n\n与证照管理一致:行驶证、道路运输证、登记证、特种设备使用登记证、特种设备使用标识、加氢卡、安全阀、压力表。规格全文见 [license-certs.md](./license-certs.md)。\n\n### 6.7 保险记录(对齐保险采购)\n\n五险种(交强 / 商业 / 超赔 / 驾意 / 货物)+ 当前保单摘要 + 采购历史(纵向时间轴)。规格全文见 [insurance-records.md](./insurance-records.md)。\n\n### 6.8 业务记录纵向时间轴\n\n租赁 / 保险历史 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审统一近→远时间轴。\n\n| 能力 | 口径 |\n|------|------|\n| 时间筛选 | 事故及右侧 Tab:全部 / 近30天 / 近90天 / 近1年 / 自定义;按各 Tab 关键时间过滤;无匹配可重置 |\n| 自定义选日 | 手机端底部滚轮(年 / 月 / 日,精确到日);起日不得晚于止日;本模块选日入口统一该交互 |\n| 调拨卡片 | 出发城市、到达城市、停车场、接收人、接收日;不展示「路线」合并行与「调出」人 |\n\n规格全文见 [biz-timeline.md](./biz-timeline.md)、[lease-records.md](./lease-records.md)。\n\n### 6.9 业务记录演示用例与空态\n\n无种子时 H5 本地生成演示用例;真正无数据时展示美化空态。规格全文见 [demo-biz-records.md](./demo-biz-records.md)。\n" + }, + { + "type": "markdown", + "id": "h5-va-prd-ch-07", + "title": "7. 总览流程图", + "markdown": "## 7. 总览流程图\n\n```mermaid\nflowchart TB\n entry[App 打开车辆资产 H5] --> list[列表:搜索 / KPI / 筛选]\n list --> card[点车辆卡片]\n card --> detail[详情摘要]\n detail --> tabs[切换只读 Tab]\n tabs --> back[返回列表]\n```\n" + }, + { + "type": "markdown", + "id": "h5-va-prd-ch-08", + "title": "8. 验收清单(产品 / 测试)", + "markdown": "## 8. 验收清单(产品 / 测试)\n\n- [ ] 独立原型可打开,桌面 PhoneShell / 窄屏可用 \n- [ ] KPI 六类计数与同数据 PC 一致 \n- [ ] 证照异常、保险异常筛选结果与 PC 规则一致 \n- [ ] 「更多筛选」含证照 / 保险 / 出库 / 来源 / 在线;面板不超出手机操作区,内容可滚动;启用后角标点亮 \n- [ ] 出库:未出库对应待验车/未备车/已备车/待交车;来源与在线与台账字段一致 \n- [ ] 车辆状态选项与 PC 字典一致,含「调拨中」「替换中」 \n- [ ] 搜索支持车牌 / VIN / 停车场 / 客户名称过滤列表;可一键清空搜索 \n- [ ] 无匹配时有引导操作(清空搜索 / 重置筛选) \n- [ ] 列表无总辆数、无翻页按钮;滚动触底可异步加载更多,到底提示「没有更多了」 \n- [ ] 列表进详情:一体渐变底连续,无白顶栏色带;摘要为浮起白卡(状态/标签/里程/异常与列表同语义);详情无底栏 \n- [ ] 详情检验有效期后方有「剩余 x 天」或「已过期 x 天」标签;空为「未上传」 \n- [ ] 沪牌详情可见「等级评定时间」「剩余天数」;非沪牌不出现 \n- [ ] 证照信息 Tab 可见八类证照,与证照管理种类一致 \n- [ ] 保险记录可见五险种卡片、当前保单摘要与历史时间轴;整体状态口径与保险采购一致 \n- [ ] 保单附件可预览与下载(原型演示) \n- [ ] 租赁 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审为近→远时间轴;租赁未还车显示「未还车」 \n- [ ] 调拨卡片可见出发城市、到达城市、停车场、接收人、接收日;无「路线」「调出」 \n- [ ] 事故及右侧业务 Tab 可按时间快捷或自定义区间筛选;自定义起止为底部滚轮选日(精确到日),无匹配可重置 \n- [ ] 租赁记录可按客户名称 / 项目名称搜索;无匹配可清空搜索 \n- [ ] 任意车辆打开上述业务 Tab 可见列表(种子或演示用例);空态有插画与说明 \n- [ ] 详情 11 个 Tab 可进入且只读,无编辑按钮 \n- [ ] 列表卡片标签下有「里程任务」区:有任务可见百分比、进度条、剩余公里与预计天数;无任务显示「暂无里程任务」 \n- [ ] 预计天数按近 7 活跃日均里程推算;无活跃日时显示「暂无法预计」 \n- [ ] 进行中任务文案后方有完成预判标签(剩余 x 天 + 预计无法完成 / 可完成 / 有完成风险) \n- [ ] 页脚说明「修改请在 PC」可见 \n\n**交付口径** \n本期交付可嵌入 App 的车辆资产只读 H5:能查、能筛异常、能看详情全 Tab,并在列表卡片扫读里程任务进度;写操作与异常处理仍回 PC。\n" + } + ] + }, + { + "type": "folder", + "id": "h5-va-doc-specs", + "title": "专题规格", + "defaultExpanded": true, + "children": [ + { + "type": "markdown", + "id": "h5-va-doc-biz-timeline", + "title": "业务记录时间轴", + "markdownPath": ".spec/biz-timeline.md", + "markdown": "# 业务记录 · 纵向时间轴\n\n详情中租赁 / 保险历史 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审等业务记录,统一为**近 → 远纵向时间轴**展示(与租赁记录同一视觉与交互骨架)。\n\n## 结构\n\n1. **时间筛选**(事故及右侧 Tab):快捷「全部 / 近30天 / 近90天 / 近1年 / 自定义起止」;按各 Tab 关键时间过滤;无匹配可重置 \n - **自定义起止**:底部原生滚轮日期选择(年 / 月 / 日,精确到日);起日不得晚于止日 \n - 本模块凡需选日的入口,统一用该原生日期选择器,不使用浏览器原生 `date` 控件\n2. **时间节点**:左侧圆点 + 连线;节点日期取该条业务关键时间(近 → 远) \n3. **记录卡片**:主标题(多为完整时间)+ 键值行 + 角标状态 \n4. **未闭环强调**:进行中 / 未还车 / 未解决 / 未处理等节点用强调色圆点与日期 \n\n## 各 Tab 关键时间\n\n| Tab | 节点日期 / 筛选字段 | 卡片要点 |\n|-----|---------------------|----------|\n| 租赁 | 交车/还车较近者 | 合同、项目、客户、交还车;未还车文案;客户/项目搜索 |\n| 保险历史 | 采购时间(无则到期) | 保单号、公司、状态、到期;附件预览/下载 |\n| 事故 | 事故时间 | 标题为事故时间;下侧:单号、位置、等级、类型、客户、状态 |\n| 故障 | 上报时间 | 标题为上报时间;下侧:编号、等级、类型、解决;故障说明用只读文本域 |\n| 违章 | 违章时间 | 标题为违章时间;下侧:行为、位置、缴费、处理、客户 |\n| 异动 | 开始日 | 标题为开始时间;下侧:类型、目的地、预计结束 |\n| 调拨 | 调拨日 | 标题为调拨日期;下侧:出发城市、到达城市、停车场、接收人、接收日 |\n| 年审 | 执行时间 | 标题为执行时间;下侧:有效期至、检测站、执行人 |\n\n保险 Tab 仍保留总览 + 五险种索引 + 当前摘要;**采购/批单历史**改为时间轴。\n\n## 数据源\n\n种子优先;不足时 H5 演示兜底(见 [demo-biz-records.md](./demo-biz-records.md))。\n\n## 代码路径\n\n- 通用:`components/RecordTimeline.tsx`、`TimelineDateFilter.tsx`、`NativeDatePickerSheet.tsx`\n- 租赁:`LeaseRecordsPanel.tsx`\n- 保险:`InsuranceRecordsPanel.tsx`\n- 其余:`BizTimelinePanels.tsx`\n" + }, + { + "type": "markdown", + "id": "h5-va-doc-lease", + "title": "租赁记录(时间轴)", + "markdownPath": ".spec/lease-records.md", + "markdown": "# 租赁记录 · 纵向时间轴\n\n详情「租赁记录」以**时间为主轴**展示,由近到远;支持按客户名称 / 项目名称即时搜索。\n\n## 结构\n\n1. **搜索栏**(时间轴上方):提示「搜索客户名称 / 项目名称」;可一键清空 \n2. **纵向时间轴**:左侧圆点 + 连线;节点日期取该条记录交车/还车中较近的日期 \n3. **记录卡片**(节点右侧): \n - 客户合同编码 \n - 项目名称 \n - **客户**(交车上方,始终展示) \n - 交车时间 \n - 还车时间;未还车时显示 **未还车**(强调色) \n - 业务类型角标(租赁 / 物流)\n\n## 排序\n\n按节点日期降序(近 → 远)。搜索只过滤可见项,不改变排序规则。\n\n## 搜索匹配\n\n| 顺序 | 条件 | 结果 |\n|------|------|------|\n| 1 | 关键词为空 | 展示该车全部租赁记录 |\n| 2 | 客户名称或项目名称包含关键词(不区分大小写) | 保留该条 |\n| 3 | 均不包含 | 过滤掉;全部无匹配时展示「暂无匹配记录」+ 清空搜索 |\n\n**前置条件**\n\n- 该车无任何租赁记录:只展示「暂无记录」,不展示搜索栏 \n- 仅匹配客户名称、项目名称;合同编码等不参与搜索(本期)\n\n## 数据源\n\n- `resolveVehicleLeaseRecords`(租赁记录种子;无种子时由车辆台账交还车信息推导)\n- 原型本地种子,未接真实租赁合同 API\n\n## 代码路径\n\n- UI:`components/LeaseRecordsPanel.tsx`\n- 数据:`vehicle-management/utils/leaseRecords.ts`\n" + }, + { + "type": "markdown", + "id": "h5-va-doc-demo-biz", + "title": "业务记录演示与空态", + "markdownPath": ".spec/demo-biz-records.md", + "markdown": "# 业务记录 · 演示用例数据(H5)\n\n详情中租赁 / 事故 / 故障 / 违章 / 异动 / 调拨 / 年审等业务 Tab,在**台账种子无该车记录**时,由 H5 本地按车牌稳定生成演示记录;租赁不足 3 条时补足,便于查看完整时间轴。\n\n## 判定优先级\n\n| 顺序 | 条件 | 结果 |\n|------|------|------|\n| 1 | PC 同源种子已有该车牌记录且条数足够 | 使用种子(不覆盖) |\n| 2 | 租赁可由车辆台账交还车字段推导 | 使用推导记录,不足 3 条再补历史演示 |\n| 3 | 仍为空,或条数不足 3 | 生成 / 补足演示用例(租赁至少 3 条:1 条在租 + 2 条已还车历史) |\n\n## 前置条件\n\n- 仅 H5「车辆资产」详情生效;不写入真实 API\n- 演示字段取自该车台账(客户、项目、城市、停车场、里程等)+ 车牌哈希,换车内容会变、同车刷新稳定\n\n## 空状态\n\n无任何可展示记录时(极少):插画空态 + 标题「暂无记录」+ 引导回 PC 录入。\n\n## 代码路径\n\n- 兜底:`utils/demo-biz-records.ts`\n- 空态:`components/RecordsEmpty.tsx`\n" + }, + { + "type": "markdown", + "id": "h5-va-doc-insurance", + "title": "保险记录", + "markdownPath": ".spec/insurance-records.md", + "markdown": "# 保险记录 · 与保险采购对齐\n\n详情「保险记录」Tab 数据与险种口径对齐 **保险采购 / PC 车辆资产保险记录**。\n\n## 结构\n\n1. **车辆保险档案总览**:整体保险状态(正常 / 临期 / 异常)及说明(交强险 + 商业险判定,与保险采购联动口径一致)\n2. **五险种索引**(单行横滑密排):交强险、商业险、超赔险、驾意险、货物险 \n - 展示险种名 + 简短状态(正常/临期/未购等)+ 历史条数;到期日在下方详情区 \n3. **当前险种详情** \n - 摘要:保单号、保险公司、到期日期、保费 \n - 历史:纵向时间轴(近→远);节点取采购时间;卡片含保单号、公司、状态、到期、保单附件(预览 / 下载);不展示导入时间与付款时间 \n\n手机端保单附件:\n- **预览**:壳内底部弹层展示文件名与演示影像(正式环境预览真实 PDF)\n- **下载**:触发本地下载演示文件并 Toast「已开始下载」\n- 无附件显示「无附件」\n\n## 状态口径(摘要)\n\n| 整体状态 | 条件 |\n|----------|------|\n| 正常 | 交强险、商业险均在有效期内(含临期、停保仍可交车语义与 PC 一致时按 PC) |\n| 临期 | 交强或商业有效但临期 |\n| 异常 | 交强或商业未购买 / 已到期 / 已退保等 |\n\n手机端只读录入;保单附件支持预览与下载(原型演示)。某险种无历史时展示带插画的紧凑空态,引导回 PC「保险采购」录入。\n\n## 数据源\n\n- `resolveVehicleInsuranceOverview`(保险采购记录 + 车辆保险台账种子)\n- 原型本地种子,未接正式保险采购写接口\n\n## 代码路径\n\n- UI:`components/InsuranceRecordsPanel.tsx`\n- 数据:`vehicle-management/utils/vehicleInsuranceRecords.ts`\n" + }, + { + "type": "markdown", + "id": "h5-va-doc-license-certs", + "title": "证照信息(八类)", + "markdownPath": ".spec/license-certs.md", + "markdown": "# 证照信息 · 与证照管理对齐\n\n详情「证照信息」Tab 证照种类、字段与状态口径对齐 **证照管理 / PC 车辆资产证照 Tab**(八类)。\n\n## 八类证照\n\n| 顺序 | 证照 | 展示内容 | 状态角标 |\n|------|------|----------|----------|\n| 1 | 行驶证 | 影像 + 注册/发证/强制报废/检验有效期至;沪牌另显下次等级评定时间与剩余天数 | 已上传 / 未上传 |\n| 2 | 道路运输证 | 影像 + 经营许可证号、核发时间、证件有效期、审验有效期 | 已上传 / 未上传 |\n| 3 | 登记证 | 影像 | 已上传 / 未上传 |\n| 4 | 特种设备使用登记证 | 影像 | 已上传 / 未上传 |\n| 5 | 特种设备使用标识 | 影像 + 下次检验日期 | 已上传 / 未上传 |\n| 6 | 加氢卡 | 卡号/余额/配发信息;未绑定文案「尚未绑定加氢卡」 | 已绑定 / 未绑定 |\n| 7 | 安全阀 | 影像 + 本次检验日期、下次检测日期 | 已上传 / 未上传 |\n| 8 | 压力表 | 影像 + 本次检验日期、下次检验日期 | 已上传 / 未上传 |\n\n顶部提供八类名称**单行横向滚动**索引;点选后自动锚点滚动到对应证照区块,选中项高亮。\n\n## 日期与剩余天数\n\n有明确到期/检验日的字段,日期后方可展示「剩余 x 天 / 已过期 x 天」(与检验有效期规则一致)。\n\n## 数据源\n\n- 与 PC 车辆资产同一套证照包解析(原型种子 + 台账回填)\n- 影像为原型演示样例图;正式环境以证照管理上传为准\n- 手机端只读,不提供编辑 / 跳转证照管理\n\n## 代码路径\n\n- UI:`components/LicenseCertPanel.tsx`\n- 数据:`vehicle-management/utils/vehicleLicenses.ts` → `LICENSE_CERT_NAV` / `resolveVehicleLicenseBundle`\n" + }, + { + "type": "markdown", + "id": "h5-va-doc-expire-shanghai", + "title": "检验有效期与沪牌", + "markdownPath": ".spec/expire-remain-shanghai.md", + "markdown": "# 检验有效期剩余天数 · 沪牌等级评定\n\n## 检验有效期 · 剩余天数标签\n\n| 项 | 口径 |\n|----|------|\n| 展示位置 | 基本信息「证照 / 保险概览」· 检验有效期;证照信息「行驶证」· 检验有效期 |\n| 有有效日期 | 日期后方标签:`剩余x天`;已过期则为 `已过期x天` |\n| 无日期 | 文案「未上传」,无标签 |\n| 临期着色 | 剩余 ≤ 30 天:警告色;已过期:危险色;其余:主色浅底 |\n| 计算 | 到期日相对今天的日历天数差(与 PC 一致) |\n\n## 沪牌等级评定(仅车牌以「沪」开头)\n\n| 字段 | 展示 |\n|------|------|\n| 等级评定时间 | 日期;空为「未上传」 |\n| 剩余天数 | `剩余x天` / `已过期x天`;无日期为「—」 |\n\n- 证照信息 Tab 在行驶证内展示沪牌等级评定(与 PC 一致);基本信息档案区亦有等级评定字段 \n- 非沪牌不展示 \n\n## 数据源\n\n- 检验有效期:台账检验有效期 / 行驶证检验有效期 \n- 等级评定:台账等级评定时间(空时证照 Tab 可回退行驶证下次评定日) \n- 原型本地计算剩余天数,未接实时服务端日历接口 \n\n## 代码路径\n\n- `components/detailTabs.tsx` → `ExpireValue` / `formatRemainDaysLabel` / `isShanghaiPlate`\n" + }, + { + "type": "markdown", + "id": "h5-va-doc-more-filters", + "title": "更多筛选", + "markdownPath": ".spec/more-filters.md", + "markdown": "# 更多筛选 · 判定规格\n\n「更多筛选」底部面板内的条件与列表筛选项行(品牌车型 / 运营城市 / 车辆状态)叠加生效;任一非「全部」即点亮更多筛选角标。\n\n## 面板项与匹配口径\n\n| 优先级 | 筛选项 | 选项 | 匹配规则 | 数据源 |\n|--------|--------|------|----------|--------|\n| 1 | 证照状态 | 全部 / 正常 / 异常 | 异常 = 检验有效期早于今天;正常 = 非异常 | 台账证照有效期 |\n| 2 | 保险状态 | 全部 / 正常 / 异常 | 异常 = 保险状态为「异常」;正常 = 非异常 | 台账保险状态 |\n| 3 | 出库状态 | 全部 / 未出库 / 已出库 | 见下表(由车辆状态推导,无独立出库字段) | 台账车辆状态 |\n| 4 | 车辆来源 | 全部 / 自有 / 外租 / 挂靠 | 精确等于台账「车辆来源」 | 台账车辆来源 |\n| 5 | 在线状态 | 全部 / 在线 / 离线 | 精确等于台账「在线状态」 | 台账在线状态 |\n\n以上条件与 KPI、搜索、品牌车型、运营城市、车辆状态为 **AND**。\n\n## 出库状态推导(原型本地)\n\n| 出库选项 | 车辆状态集合 |\n|----------|----------------|\n| 未出库 | 待验车、未备车、已备车、待交车 |\n| 已出库 | 有明确车辆状态,且不属于「未出库」集合(如已交车、待还车、异动中等) |\n\n**前置条件**\n\n- 车辆状态为空、`-`、`无`:不匹配「未出库」也不匹配「已出库」(选任一侧时该车被滤掉)\n- 正式环境若上线独立「出库」字段,以字段为准,本表仅作原型推导\n\n## 交互与布局\n\n- 面板挂在手机壳操作区内,宽度不超过壳层;内容区可滚动,底部「重置 / 完成」固定\n- 「重置」只清更多筛选五项,不清筛选项行与 KPI / 搜索\n- 「完成」关闭面板并保留当前选择;列表已随选择即时刷新\n\n## 代码路径(实现对照)\n\n- 匹配:`utils/filter.ts` → `matchOutboundFilter` / `matchVehicleSourceFilter` / `matchOnlineStatusFilter` 等\n- UI:`components/FilterSheet.tsx`\n" + }, + { + "type": "markdown", + "id": "h5-va-doc-mileage-task", + "title": "里程任务进度", + "markdownPath": ".spec/mileage-task.md", + "markdown": "# 列表卡片 · 里程任务进度\n\n> 原型本地演示种子:[`utils/mileage-task.ts`](../utils/mileage-task.ts) \n> **未接真实采购合同 / 任务工单 API**\n\n## 业务背景\n\n采购部发起车辆采购合同时,合同可关联对应批次车辆的**里程要求**与**时间起止**;里程任务通过**任务工单**下发到车辆。手机列表卡片展示该车当前里程任务完成情况,便于外勤扫一眼进度与是否赶得上截止。\n\n## 展示位置\n\n车辆卡片中,在「来源 / 客户名」等标签行**下方**,全宽分隔区:\n\n1. 标题「里程任务」(有任务时右侧展示完成百分比)\n2. 有任务:进度条\n3. 有任务:剩余公里 + 按近 7 活跃日推算的预计完成天数\n4. 进行中任务:文案后方完成预判标签(对照任务截止剩余日历天)\n\n## 判定 / 展示优先级\n\n| 顺序 | 条件 | 结果 |\n|------|------|------|\n| 1 | 车辆未挂接有效里程任务工单 | 展示标题 +「暂无里程任务」(无进度条、无剩余/预计、无预判标签) |\n| 2 | 已完成里程 ≥ 目标里程(或进度 ≥ 100%) | 进度 100%、成功色进度条与百分比;文案「已完成」;**不**展示预判标签 |\n| 3 | 有任务且未完成,且近 7 个活跃日均里程可算 | 进度条 +「剩余 N km · 预计 D 天内完成」+ 预判标签;预判为「无法完成」时进度条/百分比为危险红;「有完成风险」为警告橙 |\n| 4 | 有任务且未完成,但无活跃日(近窗内日里程均 ≤ 0) | 进度条(警告橙)+「剩余 N km · 暂无法预计」+ 预判标签「剩余 x 天,预计有完成风险」 |\n\n## 口径公式\n\n| 项 | 口径 |\n|----|------|\n| 完成百分比 | `min(100, 保留 1 位小数(任务内已完成里程 / 合同目标里程 × 100))` |\n| 剩余公里 | `max(0, 目标里程 − 已完成里程)` |\n| 活跃日 | 自然日内该车行驶里程 **> 0 km** |\n| 近 7 活跃日均 | 取最近最多 **7** 个活跃日的日里程算术平均;不足 7 个则有几个算几个 |\n| 预计天数 D | `ceil(剩余公里 / 近 7 活跃日均)`;均里程为 0 或无活跃日 → 无法预计 |\n| 截止剩余日历天 x | 任务/合同时间窗截止日相对今天的剩余自然日(下限 0) |\n\n## 完成预判标签(文案后方)\n\n用预计天数 D(近 7 活跃日推算)对照截止剩余日历天 x:\n\n| 顺序 | 条件 | 标签文案 | 色调 | 进度条 / 百分比 |\n|------|------|----------|------|----------------|\n| 1 | D 无法算出(无活跃日) | 剩余 x 天,预计有完成风险 | 警告橙 | 同警告橙 |\n| 2 | x ≤ 0 或 D > x | 剩余 x 天,预计无法完成 | 危险红 | 同危险红 |\n| 3 | D > x × 85%,或 (x − D) ≤ 7 | 剩余 x 天,预计有完成风险 | 警告橙 | 同警告橙 |\n| 4 | 其余 | 剩余 x 天,预计可完成 | 成功绿 | 默认主色蓝 |\n\n## 前置条件\n\n- 仅列表卡片只读展示;不提供下发、编辑、跳转工单详情(本期不做)\n- 筛选 / KPI / 搜索不影响本区展示规则,只影响哪些车出现在列表\n\n## 数据源(原型)\n\n| 来源 | 说明 |\n|------|------|\n| 演示种子 | 按车牌稳定 hash:约 65% 挂接演示任务(目标里程、已完成、近窗活跃日里程序列、截止剩余日历天);其余为无任务 |\n| 台账导表 | 仅借用车牌标识做稳定种子;**不**把导表「当前总里程」直接当作任务进度真相 |\n\n正式环境应由采购合同批次里程要求 + 时间起止 + 任务工单执行回写提供目标 / 已完成 / 日里程明细 / 截止日。\n\n## 用户可见文案\n\n| 场景 | 文案 |\n|------|------|\n| 无任务 | 暂无里程任务 |\n| 进行中 | 剩余 {N} km · 预计 {D} 天内完成 |\n| 进行中且无法预计 | 剩余 {N} km · 暂无法预计 |\n| 预判标签 | 剩余 {x} 天,预计无法完成 / 预计可完成 / 预计有完成风险 |\n| 已完成 | 已完成(无预判标签) |\n\n## 代码路径\n\n- 解析:`utils/mileage-task.ts` → `resolveMileageTaskCard` / `resolveMileageForecast`\n- 展示:`components/VehicleCard.tsx` + `styles/index.css`(`.h5-va-mileage-task*` / `.h5-va-mileage-forecast`)\n" + } + ] + } + ] + } + ] + } +} diff --git a/src/prototypes/oneos-h5-vehicle-assets/components/AppToast.tsx b/src/prototypes/oneos-h5-vehicle-assets/components/AppToast.tsx new file mode 100644 index 0000000..936883d --- /dev/null +++ b/src/prototypes/oneos-h5-vehicle-assets/components/AppToast.tsx @@ -0,0 +1,37 @@ +import React, { useEffect, useState } from 'react'; + +type ToastState = { id: number; message: string } | null; + +let pushToast: ((message: string) => void) | null = null; + +export function showAppToast(message: string) { + pushToast?.(message); +} + +export function AppToastHost() { + const [toast, setToast] = useState(null); + + useEffect(() => { + let timer = 0; + pushToast = (message: string) => { + const id = Date.now(); + setToast({ id, message }); + window.clearTimeout(timer); + timer = window.setTimeout(() => { + setToast((current) => (current?.id === id ? null : current)); + }, 1800); + }; + return () => { + pushToast = null; + window.clearTimeout(timer); + }; + }, []); + + if (!toast) return null; + + return ( +
    + {toast.message} +
    + ); +} diff --git a/src/prototypes/oneos-h5-vehicle-assets/components/BizTimelinePanels.tsx b/src/prototypes/oneos-h5-vehicle-assets/components/BizTimelinePanels.tsx new file mode 100644 index 0000000..be68ed2 --- /dev/null +++ b/src/prototypes/oneos-h5-vehicle-assets/components/BizTimelinePanels.tsx @@ -0,0 +1,283 @@ +import React, { useMemo } from 'react'; +import type { VehicleRecord } from '../utils/filter'; +import { resolveVehicleAccidentRecords } from '../../vehicle-management/utils/accidentRecords'; +import { resolveVehicleFaultRecords } from '../../vehicle-management/utils/faultRecords'; +import { resolveVehicleViolationRecords } from '../../vehicle-management/utils/violationRecords'; +import { resolveVehicleMovementRecords } from '../../vehicle-management/utils/movementRecords'; +import { resolveVehicleTransferRecords } from '../../vehicle-management/utils/transferRecords'; +import { resolveVehicleAnnualReviewRecords } from '../../vehicle-management/utils/annualReviewRecords'; +import { + ensureAccidentRecords, + ensureAnnualReviewRecords, + ensureFaultRecords, + ensureMovementRecords, + ensureTransferRecords, + ensureViolationRecords, +} from '../utils/demo-biz-records'; +import { RecordsEmpty } from './RecordsEmpty'; +import { + RecordTimeline, + sortByDateDesc, + timelineDatePart, + type TimelineItem, +} from './RecordTimeline'; +import { + matchTimelineDate, + useTimelineDateFilter, +} from './TimelineDateFilter'; + +function PanelShell({ + label, + emptyDesc, + items, + dateFieldLabel, +}: { + label: string; + emptyDesc: string; + items: TimelineItem[]; + dateFieldLabel: string; +}) { + const { range, filterActive, filterNode, reset } = useTimelineDateFilter(dateFieldLabel); + + const filtered = useMemo( + () => items.filter((item) => matchTimelineDate(item.date, range)), + [items, range], + ); + + if (!items.length) { + return ; + } + + return ( +
    + {filterNode} + {!filtered.length ? ( +
    +

    暂无匹配记录

    +

    调整时间范围,或重置筛选查看全部

    + {filterActive ? ( +
    + +
    + ) : null} +
    + ) : ( + + )} +
    + ); +} + +export function AccidentRecordsPanel({ record }: { record: VehicleRecord }) { + const items = useMemo(() => { + const rows = sortByDateDesc( + ensureAccidentRecords(record, resolveVehicleAccidentRecords(record)), + (row) => timelineDatePart(row.accidentTime), + ); + return rows.map((row): TimelineItem => ({ + id: row.id, + date: timelineDatePart(row.accidentTime) || '—', + accent: row.status === '处理中', + badge: row.status, + title: row.accidentTime || '—', + meta: [ + { label: '单号', value: row.accidentCode || '—' }, + { label: '位置', value: row.accidentLocation || '—' }, + { label: '等级', value: row.accidentLevel || '—' }, + { label: '类型', value: row.accidentType || '—' }, + ...(row.customerName ? [{ label: '客户', value: row.customerName }] : []), + ], + })); + }, [record]); + + return ( + + ); +} + +export function FaultRecordsPanel({ record }: { record: VehicleRecord }) { + const items = useMemo(() => { + const rows = sortByDateDesc( + ensureFaultRecords(record, resolveVehicleFaultRecords(record)), + (row) => timelineDatePart(row.reportedAt), + ); + return rows.map((row): TimelineItem => { + const open = row.resolutionStatus !== '已解决'; + const description = (row.faultDescription || '').trim() || '—'; + return { + id: row.id, + date: timelineDatePart(row.reportedAt) || '—', + accent: open, + badge: row.resolutionStatus || '未解决', + title: row.reportedAt || '—', + meta: [ + { label: '编号', value: row.faultNo || '—' }, + { label: '等级', value: row.faultLevel || '—' }, + { label: '类型', value: row.faultType || '—' }, + ...(row.resolvedAt + ? [{ label: '解决', value: row.resolvedAt }] + : [{ label: '解决', value: '未解决', pending: true }]), + ], + footer: ( +
    +
    故障说明
    +
    + + +
    +
    +
    +
    + + + +
    +
    + + {onOpenReleaseNotes ? ( + + ) : null} + + + + +
    +
    + + {showTabs && tabs!.length > 0 && ( +
    + {tabs!.map((tab) => { + const active = hrefPathname(tab.href) === hrefPathname(activeHref); + return ( +
    + + {onTabClose && ( + + )} +
    + ); + })} +
    + )} +
    + +
    {children}
    +
    +