422 lines
12 KiB
Markdown
422 lines
12 KiB
Markdown
# P3: muse-admin 管理端搭建 — 执行计划
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task.
|
||
|
||
**目标:** 在 Vben Admin fork 基础上搭建 muse-admin,实现 MetaSchema 管理、系统治理、AI 配置、市场治理、全局知识管理 5 个功能域。
|
||
|
||
**架构:** Vue 3 + Vben Admin + TypeScript,Composition API + SFC script setup,defHttp API 集成,Vben 内置表格/表单组件复用。
|
||
|
||
**技术栈:** Vue 3.5, TypeScript 5, Vben Admin 5, Pinia, Vitest + Vue Test Utils + Playwright
|
||
|
||
注: muse-admin 已 fork 完成,存在 `apps/web-antd` 和 `packages/` 结构。
|
||
|
||
---
|
||
|
||
## Step 1: 工程调整
|
||
|
||
### Task 1.1: 业务目录结构建立
|
||
|
||
**仓库路径:** `muse-admin/`
|
||
|
||
- [ ] **Step 1: 创建业务模块目录**
|
||
|
||
```bash
|
||
mkdir -p muse-admin/apps/web-antd/src/views/{governance,ai,knowledge,market,account,jobs,audit}
|
||
mkdir -p muse-admin/apps/web-antd/src/api/muse/{governance,ai,knowledge,market,account,jobs,audit}
|
||
```
|
||
|
||
- [ ] **Step 2: 检查现有结构**
|
||
|
||
```bash
|
||
ls muse-admin/apps/web-antd/src/views/
|
||
ls muse-admin/packages/@core/
|
||
```
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add muse-admin/apps/web-antd/src/views/governance/ muse-admin/apps/web-antd/src/api/muse/
|
||
git commit -m "feat(struct): 建立管理端业务模块目录(governance/ai/knowledge/market/account)"
|
||
```
|
||
|
||
### Task 1.2: API 类型集成
|
||
|
||
- [ ] **Step 1: 复制 OpenAPI 生成的类型**
|
||
|
||
```bash
|
||
cp docs/api-contracts/generated/typescript/*.ts muse-admin/apps/web-antd/src/api/types/
|
||
```
|
||
|
||
- [ ] **Step 2: 编写 API 客户端**
|
||
|
||
```typescript
|
||
// muse-admin/apps/web-antd/src/api/muse/client.ts
|
||
import { requestClient } from '#/api/request';
|
||
|
||
const BASE = '/admin-api/muse';
|
||
|
||
export const museAdminApi = {
|
||
get: <T>(url: string, params?: Record<string, unknown>) =>
|
||
requestClient.get<T>(`${BASE}${url}`, { params }),
|
||
|
||
post: <T>(url: string, data?: unknown) =>
|
||
requestClient.post<T>(`${BASE}${url}`, data),
|
||
|
||
put: <T>(url: string, data?: unknown) =>
|
||
requestClient.put<T>(`${BASE}${url}`, data),
|
||
|
||
delete: <T>(url: string) =>
|
||
requestClient.delete<T>(`${BASE}${url}`),
|
||
};
|
||
```
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add -A && git commit -m "feat(api): 集成管理端 API 客户端 + OpenAPI 类型包"
|
||
```
|
||
|
||
---
|
||
|
||
## Step 2: 管理页面开发(按优先级)
|
||
|
||
### Task 2.1: MetaSchema 管理 — 列表页
|
||
|
||
**文件:**
|
||
- 创建: `apps/web-antd/src/views/governance/MetaSchemaList.vue`
|
||
- 创建: `apps/web-antd/src/api/muse/governance/meta-schema.ts`
|
||
|
||
- [ ] **Step 1: 编写 API service**
|
||
|
||
```typescript
|
||
// apps/web-antd/src/api/muse/governance/meta-schema.ts
|
||
import { museAdminApi } from '../client';
|
||
|
||
export interface MetaSchemaSummary {
|
||
schemaKey: string;
|
||
displayName: string;
|
||
fieldType: string;
|
||
scope: string;
|
||
activeVersion: number;
|
||
status: string;
|
||
}
|
||
|
||
export function listMetaSchemas(params: {
|
||
pageNo: number;
|
||
pageSize: number;
|
||
scope?: string;
|
||
}) {
|
||
return museAdminApi.get<{
|
||
total: number;
|
||
list: MetaSchemaSummary[];
|
||
}>('/governance/meta-schemas', params);
|
||
}
|
||
|
||
export function getMetaSchema(schemaKey: string) {
|
||
return museAdminApi.get<any>(`/governance/meta-schemas/${schemaKey}`);
|
||
}
|
||
|
||
export function createMetaSchemaDraft(schemaKey: string, data: any) {
|
||
return museAdminApi.post<any>(`/governance/meta-schemas/${schemaKey}/drafts`, data);
|
||
}
|
||
|
||
export function publishMetaSchemaDraft(schemaKey: string, draftVersion: number, data: any) {
|
||
return museAdminApi.post<any>(
|
||
`/governance/meta-schemas/${schemaKey}/drafts/${draftVersion}/publish`,
|
||
data
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 编写列表页**
|
||
|
||
```vue
|
||
<!-- apps/web-antd/src/views/governance/MetaSchemaList.vue -->
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue';
|
||
import { BasicTable, useTable, BasicColumn } from '@vben/common-ui';
|
||
import { listMetaSchemas, type MetaSchemaSummary } from '#/api/muse/governance/meta-schema';
|
||
import { useRouter } from 'vue-router';
|
||
|
||
const router = useRouter();
|
||
|
||
const [registerTable, { reload }] = useTable({
|
||
api: async ({ page, size, scope }) => {
|
||
const res = await listMetaSchemas({ pageNo: page, pageSize: size, scope });
|
||
return { items: res.list, total: res.total };
|
||
},
|
||
columns: [
|
||
{ title: 'Schema Key', dataIndex: 'schemaKey', width: 200 },
|
||
{ title: '显示名称', dataIndex: 'displayName', width: 200 },
|
||
{ title: '字段类型', dataIndex: 'fieldType', width: 100 },
|
||
{ title: '作用域', dataIndex: 'scope', width: 100 },
|
||
{ title: '活跃版本', dataIndex: 'activeVersion', width: 100 },
|
||
{ title: '状态', dataIndex: 'status', width: 100 },
|
||
] as BasicColumn[],
|
||
});
|
||
|
||
function handleDetail(record: MetaSchemaSummary) {
|
||
router.push(`/governance/meta-schemas/${record.schemaKey}`);
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="p-4">
|
||
<BasicTable @register="registerTable" @row-click="handleDetail">
|
||
<template #toolbar>
|
||
<a-button type="primary">新建 MetaSchema</a-button>
|
||
</template>
|
||
</BasicTable>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
- [ ] **Step 3: 编写测试**
|
||
|
||
```typescript
|
||
// apps/web-antd/src/views/governance/__tests__/MetaSchemaList.spec.ts
|
||
import { describe, it, expect, vi } from 'vitest';
|
||
import { mount } from '@vue/test-utils';
|
||
import MetaSchemaList from '../MetaSchemaList.vue';
|
||
|
||
vi.mock('#/api/muse/governance/meta-schema', () => ({
|
||
listMetaSchemas: vi.fn().mockResolvedValue({
|
||
total: 2,
|
||
list: [
|
||
{ schemaKey: 'character_name', displayName: '角色名', fieldType: 'string', scope: 'work', activeVersion: 3, status: 'active' },
|
||
{ schemaKey: 'world_setting', displayName: '世界设定', fieldType: 'text', scope: 'global', activeVersion: 1, status: 'active' },
|
||
],
|
||
}),
|
||
}));
|
||
|
||
describe('MetaSchemaList', () => {
|
||
it('should render table', () => {
|
||
const wrapper = mount(MetaSchemaList);
|
||
expect(wrapper.find('.p-4').exists()).toBe(true);
|
||
});
|
||
});
|
||
```
|
||
|
||
- [ ] **Step 4: 提交**
|
||
|
||
```bash
|
||
git add -A && git commit -m "feat(governance): 实现 MetaSchema 列表页(Vben Table + API 集成)"
|
||
```
|
||
|
||
### Task 2.2: MetaSchema 管理 — 详情/草稿编辑页
|
||
|
||
实现 MetaSchema 详情页,包含:
|
||
- 版本历史时间线
|
||
- 当前 active 版本字段展示
|
||
- 草稿编辑 + 校验 + 影响预览 + 发布流程
|
||
- 灰度规则配置
|
||
|
||
使用 Vben 内置表单组件 + 动态表单渲染。
|
||
|
||
### Task 2.3-2.5: 其余管理页面
|
||
|
||
按优先级依次实现:
|
||
|
||
**Task 2.3: 系统治理** — 用户管理(复用 Yudao system 模块)、角色权限配置、审计日志查看
|
||
|
||
**Task 2.4: AI 配置** — Prompt 模板管理、质量门控维度配置、保护节点注册管理、Tool Grant 配置
|
||
|
||
**Task 2.5: 市场治理 + 全局知识管理** — 资产审核(上架/驳回)、下架/召回、申诉处理、全局知识库维护、知识来源管理
|
||
|
||
每个页面遵循相同模式:API service → 组件实现 → 测试 → 提交。
|
||
|
||
---
|
||
|
||
### Task 2.6: Account 管理页面
|
||
|
||
**文件:**
|
||
- 创建: `apps/web-antd/src/api/muse/account/index.ts`
|
||
|
||
**页面清单:**
|
||
|
||
| 页面 | 路由 | 说明 |
|
||
|------|------|------|
|
||
| 用户权益列表 | `/account/entitlements` | 表格:用户 + 权益类型 + 上限 + 已用 + 过期时间 |
|
||
| 配额调整表单 | `/account/quota-adjustments` | 弹窗:用户 + 资源类型 + 调整量 + 原因 + commandId |
|
||
| New-API 绑定状态 | `/account/new-api-bindings` | 表格:用户 + 绑定状态 + 同步状态 |
|
||
| 购买/使用记录 | `/account/records` | 只读汇总表格 |
|
||
|
||
**API endpoints:**
|
||
|
||
```
|
||
GET /admin-api/muse/account/users # 用户列表
|
||
GET /admin-api/muse/account/users/{userId}/entitlements # 用户权益
|
||
POST /admin-api/muse/account/users/{userId}/quota-adjustments # 配额调整
|
||
GET /admin-api/muse/account/new-api-bindings # New-API 绑定状态
|
||
```
|
||
|
||
**代码示例 — API service:**
|
||
|
||
```typescript
|
||
// apps/web-antd/src/api/muse/account/index.ts
|
||
import { museAdminApi } from '../client';
|
||
|
||
export interface UserEntitlement {
|
||
userId: string;
|
||
entitlementType: string;
|
||
limit: number;
|
||
used: number;
|
||
expiry: string;
|
||
}
|
||
|
||
export interface QuotaAdjustment {
|
||
userId: string;
|
||
resourceType: string;
|
||
adjustmentAmount: number;
|
||
reason: string;
|
||
commandId: string;
|
||
}
|
||
|
||
export function getAccountUsers(params: { pageNo: number; pageSize: number }) {
|
||
return museAdminApi.get<{ total: number; list: any[] }>('/account/users', params);
|
||
}
|
||
|
||
export function getUserEntitlements(userId: string) {
|
||
return museAdminApi.get<UserEntitlement[]>(`/account/users/${userId}/entitlements`);
|
||
}
|
||
|
||
export function adjustQuota(userId: string, data: QuotaAdjustment) {
|
||
return museAdminApi.post<void>(`/account/users/${userId}/quota-adjustments`, data);
|
||
}
|
||
|
||
export function getNewApiBindings(params: { pageNo: number; pageSize: number }) {
|
||
return museAdminApi.get<{ total: number; list: any[] }>('/account/new-api-bindings', params);
|
||
}
|
||
```
|
||
|
||
### Task 2.7: 任务监控页面
|
||
|
||
**文件:**
|
||
- 创建: `apps/web-antd/src/api/muse/jobs/index.ts`
|
||
|
||
**页面清单:**
|
||
|
||
| 页面 | 路由 | 说明 |
|
||
|------|------|------|
|
||
| 任务列表 | `/jobs` | 表格:job ID + 类型 + 状态 + 创建时间 + 重试次数 |
|
||
| 任务详情 | `/jobs/:jobId` | 状态 + 失败原因 + 重试/取消按钮 |
|
||
| 源事件列表 | `/jobs/source-events` | 表格:事件类型 + 来源 + 状态 + 影响摘要 |
|
||
|
||
**API endpoints:**
|
||
|
||
```
|
||
GET /admin-api/muse/jobs # 任务列表
|
||
GET /admin-api/muse/jobs/{jobId} # 任务详情
|
||
POST /admin-api/muse/jobs/{jobId}/retry # 重试任务
|
||
GET /admin-api/muse/source-events # 源事件列表
|
||
```
|
||
|
||
**代码示例 — API service:**
|
||
|
||
```typescript
|
||
// apps/web-antd/src/api/muse/jobs/index.ts
|
||
import { museAdminApi } from '../client';
|
||
|
||
export interface JobSummary {
|
||
jobId: string;
|
||
jobType: string;
|
||
status: string;
|
||
createdAt: string;
|
||
retryCount: number;
|
||
}
|
||
|
||
export interface JobDetail extends JobSummary {
|
||
failureReason?: string;
|
||
payload?: Record<string, unknown>;
|
||
result?: Record<string, unknown>;
|
||
}
|
||
|
||
export interface SourceEvent {
|
||
eventType: string;
|
||
source: string;
|
||
status: string;
|
||
impactSummary: string;
|
||
createdAt: string;
|
||
}
|
||
|
||
export function listJobs(params: { pageNo: number; pageSize: number; status?: string }) {
|
||
return museAdminApi.get<{ total: number; list: JobSummary[] }>('/jobs', params);
|
||
}
|
||
|
||
export function getJob(jobId: string) {
|
||
return museAdminApi.get<JobDetail>(`/jobs/${jobId}`);
|
||
}
|
||
|
||
export function retryJob(jobId: string) {
|
||
return museAdminApi.post<void>(`/jobs/${jobId}/retry`);
|
||
}
|
||
|
||
export function listSourceEvents(params: { pageNo: number; pageSize: number }) {
|
||
return museAdminApi.get<{ total: number; list: SourceEvent[] }>('/source-events', params);
|
||
}
|
||
```
|
||
|
||
### Task 2.8: 审计日志页面
|
||
|
||
**文件:**
|
||
- 创建: `apps/web-antd/src/api/muse/audit/index.ts`
|
||
|
||
**页面清单:**
|
||
|
||
| 页面 | 路由 | 说明 |
|
||
|------|------|------|
|
||
| 业务审计事件列表 | `/audit/business-events` | 表格:时间戳 + 操作者 + 操作 + 目标 + 结果 |
|
||
| 审计详情 | `/audit/business-events/:eventId` | 完整 before/after 快照对比 |
|
||
|
||
**API endpoints:**
|
||
|
||
```
|
||
GET /admin-api/muse/audit/business-events # 业务审计事件列表
|
||
```
|
||
|
||
**代码示例 — API service:**
|
||
|
||
```typescript
|
||
// apps/web-antd/src/api/muse/audit/index.ts
|
||
import { museAdminApi } from '../client';
|
||
|
||
export interface BusinessAuditEvent {
|
||
eventId: string;
|
||
timestamp: string;
|
||
actor: string;
|
||
action: string;
|
||
target: string;
|
||
result: string;
|
||
}
|
||
|
||
export interface BusinessAuditDetail extends BusinessAuditEvent {
|
||
beforeSnapshot?: Record<string, unknown>;
|
||
afterSnapshot?: Record<string, unknown>;
|
||
}
|
||
|
||
export function listBusinessAuditEvents(params: { pageNo: number; pageSize: number; actor?: string; action?: string }) {
|
||
return museAdminApi.get<{ total: number; list: BusinessAuditEvent[] }>('/audit/business-events', params);
|
||
}
|
||
|
||
export function getBusinessAuditDetail(eventId: string) {
|
||
return museAdminApi.get<BusinessAuditDetail>(`/audit/business-events/${eventId}`);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Step 3: Mock API 配置
|
||
|
||
- [ ] **Step 1: 配置 MSW 或 Vben 内置 Mock**
|
||
|
||
为每个 `/admin-api/muse/**` 接口提供 mock 数据,使管理端可独立开发。
|
||
|
||
---
|
||
|
||
## 完成标准
|
||
|
||
- [ ] 5 个功能域管理页面可交互
|
||
- [ ] 组件单元测试覆盖率 ≥70%(Vitest + Vue Test Utils)
|
||
- [ ] E2E 测试覆盖核心管理流程(Playwright)
|
||
- [ ] ESLint + Prettier + TypeScript 类型检查通过
|
||
- [ ] Vite build 成功 |