366 lines
18 KiB
Markdown
366 lines
18 KiB
Markdown
# 平台总后台全量审计与修复 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`。
|
||
- 非目标:不实现需求明确后置的双人复核、资金审批、敏感导出审批和精确轨迹回放;保留相关模型和扩展边界。
|
||
- 无占位项:资源、路径、命令、测试与提交范围均已给出。
|