Files
platforms/docs/superpowers/plans/2026-07-27-platform-admin-full-audit.md

366 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 平台总后台全量审计与修复 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 让后台模型、API、菜单、路由和 Vue 页面完整符合需求分析,并以自动检查证明逐资源连通。
**Architecture:** 后端以受约束的资源定义复用分页、按 `identity` 查询、状态更新和归档;账户、角色、菜单、事件处置与审批保留专用处理器。前端通过同一资源声明驱动标准 CRUD、只读和树页面专用看板与轨迹页面独立实现。
**Tech Stack:** Go 1.26、Gin、GORM、Vue 3、TypeScript、Arco Design Vue、Vite、Biome、pnpm。
## Global Constraints
- 将未提交的 `backend/api/internal/logic/platform` 文件视为本次可修改的审计基线。
- 气站名称和路由固定为 `gas_basic`,不得改用 `gas_station`
- 设备管理是一级领域,涵盖瓶阀、绑定、遥测、安全规则、事件、处置和安检。
- 所有模型和字段均保留准确的中文注释。
- HTTP 只传 UUID V7 `identity`,字段使用 `snake_case`,金额使用最小货币单位整数。
- 主数据以 `status=archived` 归档;钱包、流水、充值、提现、报表和审计只读。
- 每项功能先写失败测试或审计断言,再写最小实现;每领域独立验证并提交。
## File Structure
- `backend/api/internal/logic/platform/resource.go`:资源定义、通用 CRUD、只读限制和字段白名单。
- `backend/api/internal/logic/platform/{organization,device,commerce,finance,content,audit}.go`:领域资源和专用动作。
- `backend/api/internal/routers/platform.go`:领域 API 注册。
- `backend/api/internal/models/*.go`:模型字段、关联、索引和中文注释修订。
- `backend/api/internal/{logic/platform,routers}/*_test.go`:资源契约、路由、只读和角色保护测试。
- `frontend/platform_admin/src/api/resources.ts`:资源键、路径、页面类型和表单字段的唯一声明。
- `frontend/platform_admin/src/views/shared/{CrudListPage,ReadOnlyListPage,TreePage}.vue`:三类可复用页面。
- `frontend/platform_admin/src/views/**/ListPage.vue`:资源声明页面;看板和轨迹使用专用组件。
- `frontend/platform_admin/src/router/routes/modules/platform.ts`:完整菜单和路由树。
- `frontend/platform_admin/scripts/audit-check.mjs`:模型、路由、前端 API、页面的静态一致性检查。
## Resource Contract
可写资源:`gas_basic``gas_account``delivery_basic``delivery_account``staff_account``staff_credential``user_account``user_address``user_service_relation``dev_smart_cylinder_valve``dev_device_binding``saf_rule``saf_event``saf_inspection``ec_category``ec_product``ec_product_attribute``ec_product_image``ec_cart``ec_order``ec_review``delivery_task``delivery_track``delivery_track_point``fin_payment``fin_settlement``fin_reconciliation``cnt_content``ntf_template``cs_ticket``platfrom_account``platform_role``platform_menu`
只读资源:`dev_telemetry``wallet``wallet_ledger``wallet_recharge``wallet_withdrawal``report``report_item``report_metric_snapshot``aud_operation_log``aud_export_log``aud_approval``saf_event_disposal` 是仅追加的事件动作记录。
### Task 1: 建立资源契约和审计基线
**Files:**
- Create: `backend/api/internal/logic/platform/resource.go`
- Create: `backend/api/internal/logic/platform/resource_test.go`
- Create: `backend/api/internal/routers/platform_test.go`
- Modify: `frontend/platform_admin/scripts/audit-check.mjs`
- Modify: `frontend/platform_admin/package.json`
**Interfaces:** `ExpectedResources() []ResourceContract` 返回领域、资源名、读写模式和页面类型;`pnpm audit:platform` 缺层时输出 `domain/name: missing <layer>`
- [ ] **Step 1: 写失败的资源契约测试。**
```go
func TestExpectedResources(t *testing.T) {
assertContract(t, ExpectedResources(), "gas", "gas_basic", Writable, "list")
assertContract(t, ExpectedResources(), "device", "saf_event", Writable, "list")
assertContract(t, ExpectedResources(), "wallet", "wallet_ledger", ReadOnly, "list")
}
```
- [ ] **Step 2: 运行失败测试。**
Run: `go test ./internal/logic/platform ./internal/routers -run TestExpectedResources -v`
Expected: FAIL提示 `ExpectedResources``ResourceContract` 或资源模式尚未定义。
- [ ] **Step 3: 实现完整资源清单和审计命令。**
```go
type ResourceMode string
const ( Writable ResourceMode = "writable"; ReadOnly ResourceMode = "readonly" )
type ResourceContract struct { Domain, Name, PageKind string; Mode ResourceMode }
func ExpectedResources() []ResourceContract { return []ResourceContract{{"gas", "gas_basic", "list", Writable}} }
```
将完整 `Resource Contract` 清单写入函数和 JSON 可读清单;脚本核对 `resources.ts`、路由模块及页面路径。
- [ ] **Step 4: 运行资源测试和审计命令。**
Run: `go test ./internal/logic/platform ./internal/routers -run TestExpectedResources -v; pnpm audit:platform`
Expected: 测试 PASS审计命令列出当前缺失层作为后续任务的失败清单。
- [ ] **Step 5: 提交基线。**
```powershell
git add backend/api/internal/logic/platform/resource.go backend/api/internal/logic/platform/resource_test.go backend/api/internal/routers/platform_test.go frontend/platform_admin/scripts/audit-check.mjs frontend/platform_admin/package.json
git commit -m "test: add platform resource contract audit"
```
### Task 2: 修订模型和通用后端资源处理器
**Files:**
- Modify: `backend/api/internal/logic/platform/platform.go`
- Modify: `backend/api/internal/logic/platform/resource.go`
- Modify: `backend/api/internal/models/entity.go`
- Modify: `backend/api/internal/models/*.go`
- Test: `backend/api/internal/logic/platform/resource_test.go`
**Interfaces:** `ResourceDefinition{Domain, Name, Model, Mode, CreateFields, UpdateFields}``RegisterResource(*gin.RouterGroup, ResourceDefinition)`;通用列表、详情、创建、更新、状态和归档处理器。
- [ ] **Step 1: 写失败的只读、字段白名单和归档测试。**
```go
func TestReadOnlyResourceRejectsWrite(t *testing.T) {
if (ResourceDefinition{Mode: ReadOnly}).Allows(http.MethodPost) { t.Fatal("readonly POST") }
}
func TestArchiveUsesStatus(t *testing.T) {
if archiveValues()["status"] != "archived" { t.Fatal("must archive") }
}
```
- [ ] **Step 2: 运行失败测试。**
Run: `go test ./internal/logic/platform -run 'Test(ReadOnly|Archive)' -v`
Expected: FAIL因为 `Allows``archiveValues` 不存在。
- [ ] **Step 3: 实现最小通用处理器和模型审计。**
```go
func (d ResourceDefinition) Allows(method string) bool { return d.Mode == Writable || method == http.MethodGet }
func archiveValues() gin.H { return gin.H{"status": "archived"} }
```
逐模型核对 `Entity`、业务编码唯一索引、关联字段和中文注释。仅当字段不在需求、无 GORM 关联或索引、无 HTTP 和前端消费者时才删除,并将理由写入最终报告。
- [ ] **Step 4: 验证通用层。**
Run: `gofmt -w internal/logic/platform internal/models; go test ./...; go build ./cmd/main`
Expected: PASS。
- [ ] **Step 5: 提交通用层。**
```powershell
git add backend/api/internal/logic/platform backend/api/internal/models
git commit -m "feat: add audited platform resource foundation"
```
### Task 3: 完成组织、账户、人员和用户领域
**Files:**
- Create: `backend/api/internal/logic/platform/organization.go`
- Modify: `backend/api/internal/logic/platform/{gas,delivery,staff,user,role}.go`
- Modify: `backend/api/internal/routers/platform.go`
- Test: `backend/api/internal/logic/platform/organization_test.go`
**Interfaces:** 注册 `/gas/gas_basic``/gas/gas_account``/delivery/delivery_basic``/delivery/delivery_account``/staff/account``/staff/credential``/user/account``/user/address``/user/service_relation``/platform/platfrom_account``/platform/platform_role``/platform/platform_menu`
- [ ] **Step 1: 写失败的名称和角色保护测试。**
```go
func TestOrganizationRoutesKeepGasBasic(t *testing.T) { assertRoute(t, routes, http.MethodGet, "/gas/gas_basic") }
func TestSystemRoleCannotMutate(t *testing.T) { if mayMutateRole(models.PlatformRole{IsSystem: true}) { t.Fatal("system role") } }
func TestAccountMasksPhone(t *testing.T) { if maskPhone("13812345678") != "138****5678" { t.Fatal("mask") } }
```
- [ ] **Step 2: 运行失败测试。**
Run: `go test ./internal/logic/platform ./internal/routers -run 'Test(Organization|SystemRole|Account)' -v`
Expected: FAIL直至路由、系统角色保护和账户视图完成。
- [ ] **Step 3: 实现领域资源和专用动作。**
账户创建及角色菜单覆盖使用事务;更新只接受字段白名单;系统角色拒绝更新、状态变更与归档;平台账户只返回脱敏电话和非密码字段。
- [ ] **Step 4: 验证领域。**
Run: `go test ./internal/logic/platform ./internal/routers -v; go build ./cmd/main`
Expected: PASS。
- [ ] **Step 5: 提交领域。**
```powershell
git add backend/api/internal/logic/platform backend/api/internal/routers/platform.go
git commit -m "feat: complete organization account APIs"
```
### Task 4: 完成设备管理、电商和配送领域
**Files:**
- Create: `backend/api/internal/logic/platform/{device,commerce}.go`
- Modify: `backend/api/internal/logic/platform/delivery.go`
- Modify: `backend/api/internal/routers/platform.go`
- Test: `backend/api/internal/logic/platform/{device,commerce}_test.go`
**Interfaces:** 注册 `/device/*``/safety/*``/ec/*``/delivery/delivery_task``/delivery/delivery_track``/delivery/delivery_track_point`
- [ ] **Step 1: 写失败的安全和订单轨迹测试。**
```go
func TestDeviceManagementIncludesSafety(t *testing.T) { assertContract(t, ExpectedResources(), "safety", "saf_event", Writable, "list") }
func TestSafetyDisposalIsAppendOnly(t *testing.T) { if disposalDefinition.Allows(http.MethodDelete) { t.Fatal("disposal delete") } }
func TestTrackPointRoute(t *testing.T) { assertRoute(t, routes, http.MethodPost, "/delivery/delivery_track_point") }
```
- [ ] **Step 2: 运行失败测试。**
Run: `go test ./internal/logic/platform ./internal/routers -run 'Test(Device|Safety|Track)' -v`
Expected: FAIL直至领域资源与事件动作完成。
- [ ] **Step 3: 实现资源。**
设备、绑定、规则、事件和安检采用可写资源,遥测只读;事件处置在事务中更新事件并追加含 `operator_identity``action``reason` 的记录。商品、属性、图片、购物车、订单、订单项、评论、配送任务、轨迹和轨迹点全部注册;订单详情包含订单项,轨迹详情按时间包含轨迹点。
- [ ] **Step 4: 验证领域。**
Run: `go test ./...; go build ./cmd/main`
Expected: PASS。
- [ ] **Step 5: 提交领域。**
```powershell
git add backend/api/internal/logic/platform backend/api/internal/routers/platform.go
git commit -m "feat: complete device commerce delivery APIs"
```
### Task 5: 完成财务、钱包、报表、内容和审计领域
**Files:**
- Create: `backend/api/internal/logic/platform/{finance,content,audit}.go`
- Modify: `backend/api/internal/logic/platform/health.go`
- Modify: `backend/api/internal/models/query.go`
- Modify: `backend/api/internal/routers/platform.go`
- Test: `backend/api/internal/logic/platform/{finance,content,audit}_test.go`
**Interfaces:** 注册 `/finance/*``/wallet/*``/report/*``/content/*``/customer_service/*``/audit/*` 与扩展的 `/dashboard/overview`
- [ ] **Step 1: 写失败的只读与审批审计测试。**
```go
func TestWalletAndReportAreReadOnly(t *testing.T) { assertNoRoute(t, routes, http.MethodPost, "/wallet/wallet") }
func TestApprovalStoresOperator(t *testing.T) { if approvalValues("approved", "op-1")["operator_identity"] != "op-1" { t.Fatal("operator") } }
```
- [ ] **Step 2: 运行失败测试。**
Run: `go test ./internal/logic/platform ./internal/routers -run 'Test(Wallet|Approval)' -v`
Expected: FAIL直至只读路由和审批动作存在。
- [ ] **Step 3: 实现资源和看板聚合。**
支付、结算、对账、内容、模板和工单可写;钱包、资金流水、充值、提现、报表、指标快照与审计仅注册 GET。审批仅更新状态、意见、处理人和时间并追加操作审计空数据看板返回零值。
- [ ] **Step 4: 验证领域。**
Run: `go test ./...; go build ./cmd/main`
Expected: PASS。
- [ ] **Step 5: 提交领域。**
```powershell
git add backend/api/internal/logic/platform backend/api/internal/models/query.go backend/api/internal/routers/platform.go
git commit -m "feat: complete finance content audit APIs"
```
### Task 6: 建立前端资源契约并补齐页面
**Files:**
- Create: `frontend/platform_admin/src/api/resources.ts`
- Create: `frontend/platform_admin/src/views/shared/{ReadOnlyListPage,TreePage}.vue`
- Create: `frontend/platform_admin/src/views/{gas,delivery,staff,user,device,ec,finance,wallet,report,content,audit}/**/ListPage.vue`
- Modify: `frontend/platform_admin/src/views/shared/CrudListPage.vue`
- Modify: `frontend/platform_admin/src/api/{resource,platform,gas,delivery,staff,user}.ts`
- Modify: `frontend/platform_admin/src/router/routes/modules/platform.ts`
- Test: `frontend/platform_admin/scripts/audit-check.mjs`
**Interfaces:** `ResourceUiDefinition { key, resource, title, mode, pageKind, fields }`;每个后端契约对应一个前端资源、菜单路由和页面。
- [ ] **Step 1: 写失败的前端资源断言。**
```js
assertResource(resources, { key: 'gas-basic', resource: '/gas/gas_basic', mode: 'writable', pageKind: 'list' });
assertResource(resources, { key: 'wallet-ledger', resource: '/wallet/wallet_ledger', mode: 'readonly', pageKind: 'list' });
assertRouteFile('src/views/device/safety-event/ListPage.vue');
```
- [ ] **Step 2: 运行失败断言。**
Run: `pnpm audit:platform`
Expected: FAIL列出缺失资源、页面或菜单路由。
- [ ] **Step 3: 实现资源声明、共享组件和页面。**
```ts
export type ResourceUiDefinition = { key: string; resource: string; title: string; mode: 'writable' | 'readonly'; pageKind: 'list' | 'tree' | 'dashboard'; fields: Array<{ key: string; label: string; required?: boolean }> };
export const resources: ResourceUiDefinition[] = [{ key: 'gas-basic', resource: '/gas/gas_basic', title: '气站管理', mode: 'writable', pageKind: 'list', fields: [{ key: 'code', label: '气站编码', required: true }, { key: 'name', label: '气站名称', required: true }] }];
```
`CrudListPage` 增加详情请求、归档二次确认、服务端筛选和类型化字段;`ReadOnlyListPage` 不渲染写操作;`TreePage` 支持父子节点、新增、编辑、排序和归档。每个页面只传资源声明,禁止复制 HTTP 调用。
- [ ] **Step 4: 验证前端。**
Run: `pnpm type:check; pnpm audit:platform; pnpm build`
Expected: PASS且审计报告每个契约都有页面和菜单路由。
- [ ] **Step 5: 提交前端。**
```powershell
git add frontend/platform_admin/src frontend/platform_admin/scripts/audit-check.mjs frontend/platform_admin/package.json
git commit -m "feat: complete platform administration pages"
```
### Task 7: 全量映射、冗余清理和最终验收
**Files:**
- Modify: `backend/api/internal/logic/platform/*`
- Modify: `backend/api/internal/models/*`
- Modify: `backend/api/internal/routers/platform.go`
- Modify: `frontend/platform_admin/src/{api,router,views}/**/*`
- Modify: `frontend/platform_admin/scripts/audit-check.mjs`
- Create: `docs/平台总后台审计报告-2026-07-27.md`
**Interfaces:** 完整资源清单、后端注册路由、前端资源、页面与菜单必须一一对应。
- [ ] **Step 1: 写失败的全量映射测试。**
```go
func TestEveryContractHasRegisteredRoute(t *testing.T) {
for _, contract := range ExpectedResources() { assertResourceRoutes(t, registeredRoutes, contract) }
}
```
```js
for (const contract of expectedContracts) { assertBackendRoute(contract); assertFrontendResource(contract); assertPage(contract); assertMenuRoute(contract); }
```
- [ ] **Step 2: 运行全量映射测试。**
Run: `go test ./...; pnpm audit:platform`
Expected: 任一遗漏失败为 `domain/name: missing <layer>`
- [ ] **Step 3: 修复遗漏并清理可证明冗余。**
仅删除同时满足“不在需求、无后端调用、无前端消费者、无测试或迁移依赖”的代码或字段;对保留的快照、审计和授权字段在报告说明用途。
- [ ] **Step 4: 运行最终验证。**
Run: `go test ./...; go build ./cmd/main; pnpm type:check; pnpm lint; pnpm audit:platform; pnpm build`
Expected: 全部 PASS报告记录命令退出码及资源数量。
- [ ] **Step 5: 提交最终修复和审计报告。**
```powershell
git add backend/api frontend/platform_admin docs/平台总后台审计报告-2026-07-27.md
git commit -m "fix: complete platform admin audit remediation"
```
## Plan Self-Review
- 需求覆盖Task 2 审计模型与基础语义Task 3 至 5 覆盖所有后端领域Task 6 覆盖菜单、路由和页面Task 7 验证端到端映射并产出审计报告。
- 命名一致:后端和前端共享同一资源契约,气站唯一资源为 `/gas/gas_basic`
- 非目标:不实现需求明确后置的双人复核、资金审批、敏感导出审批和精确轨迹回放;保留相关模型和扩展边界。
- 无占位项:资源、路径、命令、测试与提交范围均已给出。