Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
95e25b1
docs: add DESIGN.md compliance fix specification
mikewoo May 23, 2026
cf39a4d
docs: move spec to docs/specs, enforce spec location convention in CL…
mikewoo May 23, 2026
9826ebd
docs: expand spec with visual verification strategy and 12-page check…
mikewoo May 23, 2026
f62e2a5
docs: add DESIGN.md compliance fix implementation plan
mikewoo May 23, 2026
8e20977
docs: add docs/plans/ directory convention to CLAUDE.md
mikewoo May 23, 2026
b3e0028
fix: replace hardcoded values with design tokens in LoginView
mikewoo May 23, 2026
b7bd8bc
fix: replace hardcoded values with design tokens in EifyFormDialog
mikewoo May 23, 2026
333c942
fix: replace hardcoded values with design tokens in EifyTable
mikewoo May 23, 2026
d4b5118
fix: replace hardcoded values with design tokens in EifySearch
mikewoo May 23, 2026
62a99b1
fix: replace hardcoded values with design tokens in DocumentPreview
mikewoo May 23, 2026
a79a8d5
fix: replace hardcoded values with design tokens in EifyHeader
mikewoo May 23, 2026
779a3de
fix: replace hardcoded colors with design tokens in ConfirmDialog
mikewoo May 23, 2026
3e6957e
fix: replace hardcoded values with design tokens in EifyListPage
mikewoo May 23, 2026
0544f1d
fix: replace hardcoded values with design tokens in ChatView
mikewoo May 23, 2026
d4ed69d
fix: replace hardcoded values with design tokens in AgentList
mikewoo May 23, 2026
0956c0c
fix: replace hardcoded values with design tokens in ProviderList
mikewoo May 23, 2026
3e65ffa
fix: replace hardcoded values with design tokens in McpServerList
mikewoo May 23, 2026
f286e93
fix: remove hardcoded font-sizes in ChatView, add text-* utility classes
mikewoo May 23, 2026
8ddedbe
fix: replace hardcoded values with design tokens in KnowledgeView
mikewoo May 23, 2026
691a889
fix: replace hardcoded values with design tokens in DocumentView
mikewoo May 23, 2026
3434d70
fix: replace hardcoded values with design tokens in WorkflowList
mikewoo May 23, 2026
0e7018d
fix: replace hardcoded values with design tokens in ProfileView
mikewoo May 23, 2026
75942dd
fix: remove hardcoded font-sizes in AgentList, add text-* utility cla…
mikewoo May 23, 2026
821af44
fix: remove hardcoded font-sizes in ProviderList
mikewoo May 23, 2026
e80f676
fix: remove hardcoded font-sizes in McpServerList
mikewoo May 23, 2026
a5ac888
fix: replace hardcoded values with design tokens in WorkflowEdit, Wor…
mikewoo May 23, 2026
a83dae4
fix: replace remaining hardcoded values in WorkflowEdit focus styles
mikewoo May 23, 2026
eb96c12
fix: resolve version display bug and add visual regression testing
mikewoo May 23, 2026
9489496
docs: restructure documentation and add Playwright for visual testing
mikewoo May 23, 2026
830ad4b
fix: update CI Node.js to 22 and sync package-lock.json
mikewoo May 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: npm
cache-dependency-path: eify-web/package-lock.json

Expand Down Expand Up @@ -98,7 +98,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: npm
cache-dependency-path: eify-web/package-lock.json

Expand Down
236 changes: 18 additions & 218 deletions CLAUDE.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,13 +451,13 @@ class AgentServiceImplTest {

## 代码检查清单

> 完整提交检查清单已统一至 **[CLAUDE.md § 代码提交检查清单](../../CLAUDE.md#代码提交检查清单)**,涵盖结构合规、安全、数据库、API、前端、外部调用、代码质量等全部检查项。此处保留架构结构相关摘要。

- [ ] 包结构符合模块规范,类/方法命名符合规范
- [ ] Controller 只做参数校验和返回封装,业务逻辑在 Service 层
- [ ] Entity 继承 `BaseEntity`,使用 Lombok + MyBatis-Plus 注解
- [ ] DTO 使用 JSR-303 校验注解,Response 只包含必要字段
- [ ] 跨模块调用通过 Maven pom.xml 声明,不引入循环依赖
- [ ] 异常处理使用 `ErrorCode` 枚举
- [ ] 事务在 Service 层控制,外部调用使用线程池隔离
- [ ] 日志使用 SLF4J 占位符,级别正确(ERROR/WARN/INFO/DEBUG)
- [ ] 新 Entity 实现 `WorkspaceAware`,Service 层查询过滤 `workspace_id`
- [ ] 新模块有对应的单元测试(`src/test/java/.../{Service}Test.java`)
6 changes: 5 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ docs/
└── guides/ # HOW-TO 指南(体量大、持续更新)
├── AUTH-WORKSPACE.md # 用户认证与工作空间多租户
├── DATABASE.md # 数据库设计(MySQL + ClickHouse + 游标分页)
├── E2E-TESTING.md # Playwright 端到端测试指南
├── LOGGING.md # 日志系统完整指南(架构、格式、MQ、监控、性能)
├── SECURITY.md # 系统风险清单 + 安全审查
└── WORKFLOW.md # 工作流引擎设计(节点类型、变量系统、参考实现)
```

Expand Down Expand Up @@ -211,10 +213,12 @@ Eify 项目采用模块化文档管理,遵循以下原则:
| **数据库设计** | DATABASE.md(含 MySQL + ClickHouse + 游标分页) |
| **日志系统** | LOGGING.md |
| **LLM 调用 / SSE 流式** | ARCHITECTURE.md(线程池配置)、LOGGING.md |
| **安全审查** | SECURITY.md(系统风险清单) |
| **端到端测试** | E2E-TESTING.md(Playwright 模式) |
| **性能优化** | LOGGING.md#性能分析与瓶颈、DEPLOYMENT.md#扩容触发条件 |
| **部署运维** | DEPLOYMENT.md |
| **CI/CD 流水线** | DEPLOYMENT.md |
| **监控告警** | LOGGING.md#日志监控方案 |
| **监控告警** | LOGGING.md#日志监控方案、DATABASE.md#ClickHouse-监控查询 |

## 贡献指南

Expand Down
53 changes: 53 additions & 0 deletions docs/guides/DATABASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1047,4 +1047,57 @@ DROP TABLE IF EXISTS app_logs;
-- 从备份恢复
CREATE TABLE app_logs AS app_logs_backup ENGINE = MergeTree() ...;
INSERT INTO app_logs SELECT * FROM app_logs_backup;

---

## ClickHouse 监控查询

### 慢查询排查

```sql
-- 查询最近 1 小时耗时超过 1 秒的查询
SELECT
query_start_time,
query_duration_ms,
user,
query
FROM system.query_log
WHERE type = 'QueryFinish'
AND query_duration_ms > 1000
AND event_time > now() - INTERVAL 1 HOUR
ORDER BY query_duration_ms DESC
LIMIT 20;
```

### 存储统计

```sql
-- 按表查看磁盘占用和数据量
SELECT
table,
formatReadableSize(sum(bytes_on_disk)) AS disk_size,
sum(rows) AS total_rows,
max(modification_time) AS last_modified
FROM system.parts
WHERE active
GROUP BY table
ORDER BY sum(bytes_on_disk) DESC;
```

### 写入吞吐监控

```sql
-- 最近 1 小时内各表的写入行数
SELECT
table,
count() AS inserts,
sum(rows) AS total_rows_written,
formatReadableSize(sum(bytes)) AS total_written
FROM system.query_log
WHERE type = 'QueryFinish'
AND query_kind = 'Insert'
AND event_time > now() - INTERVAL 1 HOUR
GROUP BY table
ORDER BY inserts DESC;
```
```
214 changes: 214 additions & 0 deletions docs/guides/E2E-TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# 端到端测试指南

> Playwright 端到端测试模式,适用于 Eify 核心链路的 UI 验证。
> 参考:ECC e2e-testing skill。最后更新:2026-05-23。

---

## 目录结构

```
eify-web/
├── tests/
│ ├── e2e/
│ │ ├── auth/ # 登录/注册/工作空间切换
│ │ ├── features/ # Agent 对话、工作流执行、知识库
│ │ └── api/ # 直接 HTTP 接口验证
│ ├── fixtures/ # 共享的 test fixtures
│ └── playwright.config.ts
```

---

## Playwright 配置

```typescript
// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
fullyParallel: true,
workers: process.env.CI ? 1 : undefined,
retries: process.env.CI ? 2 : 0,
reporter: [
['html', { outputFolder: 'playwright-report' }],
['junit', { outputFile: 'junit.xml' }],
['json', { outputFile: 'results.json' }],
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:5173',
actionTimeout: 10000,
navigationTimeout: 30000,
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
});
```

---

## Page Object Model

每个页面封装为一个 Page 类,使用 `data-testid` 定位器:

```typescript
// tests/e2e/features/AgentChatPage.ts
import { Page, Locator } from '@playwright/test';

export class AgentChatPage {
readonly page: Page;
readonly chatInput: Locator;
readonly sendButton: Locator;
readonly messageList: Locator;
readonly loadingIndicator: Locator;

constructor(page: Page) {
this.page = page;
this.chatInput = page.locator('[data-testid="chat-input"]');
this.sendButton = page.locator('[data-testid="send-button"]');
this.messageList = page.locator('[data-testid="message-list"]');
this.loadingIndicator = page.locator('[data-testid="loading-indicator"]');
}

async goto() {
await this.page.goto('/chat');
await this.page.waitForLoadState('networkidle');
}

async sendMessage(text: string) {
await this.chatInput.fill(text);
await this.sendButton.click();
}

async getMessageCount() {
return this.messageList.locator('[data-testid="message-item"]').count();
}
}
```

---

## 测试结构

```typescript
// tests/e2e/features/agent-chat.spec.ts
import { test, expect } from '@playwright/test';
import { AgentChatPage } from './AgentChatPage';

test.describe('Agent Chat', () => {
let chatPage: AgentChatPage;

test.beforeEach(async ({ page }) => {
chatPage = new AgentChatPage(page);
await chatPage.goto();
});

test('send message and receive response', async ({ page }) => {
await chatPage.sendMessage('你好');

// 等待 SSE 响应到达
await expect(chatPage.messageList).toContainText('你好');
await expect(chatPage.loadingIndicator).not.toBeVisible({ timeout: 30000 });
await expect(chatPage.getMessageCount()).toBeGreaterThan(1);
});

test('empty message should show validation', async () => {
await chatPage.sendButton.click();
await expect(page.locator('[data-testid="input-error"]')).toBeVisible();
});
});
```

---

## SSE 流式响应测试

Eify 核心链路(Agent 对话、工作流执行)都涉及 SSE。测试模式:

```typescript
test('SSE streaming response', async ({ page }) => {
// 拦截 SSE 请求,等待响应完成
const responsePromise = page.waitForResponse(
resp => resp.url().includes('/api/v1/chat/stream') && resp.status() === 200,
{ timeout: 60000 }
);

await chatPage.sendMessage('写一段代码');

const response = await responsePromise;
expect(response.status()).toBe(200);
// 验证流式内容已渲染
await expect(chatPage.messageList.locator('[data-testid="message-item"]').last())
.toContainText('```', { timeout: 30000 });
});
```

---

## 防抖处理

### 跳过已知不稳定的测试

```typescript
test.fixme('flaky - SSE timeout on slow CI', async ({ page }) => {
// ...
});
```

### 复现偶发失败

```bash
npx playwright test --repeat-each=10 --retries=3 agent-chat.spec.ts
```

### 常见原因及修复

| 原因 | 错误做法 | 正确做法 |
|:---|:---|:---|
| 竞态条件 | `page.click()` 后立即断言 | 使用 `expect(locator).toBeVisible()` 等自动等待断言 |
| 网络时序 | `page.waitForTimeout(5000)` 盲等 | `page.waitForResponse(urlPattern)` 等具体响应 |
| 动画时序 | 动画中点击按钮 | `waitFor({ state: 'visible' })` 或 `waitForLoadState('networkidle')` |

---

## 产物管理

```typescript
// 截图
await page.screenshot({ path: 'artifacts/screenshots/full.png', fullPage: true });

// Trace(手动控制)
await context.tracing.start({ screenshots: true, snapshots: true });
// ... 测试步骤 ...
await context.tracing.stop({ path: 'artifacts/traces/test.zip' });
```

---

## Eify 优先测试目标

按系统风险清单的 P1 缺口,e2e 应优先覆盖:

| 测试对象 | 覆盖场景 | 优先级 |
|:---|:---|:---|
| `ChatServiceImpl` SSE 生命周期 | 发送消息 → 流式响应 → 完成/超时/报错 → UI 渲染 | P1 |
| `WorkflowEngine` 端到端 | 创建 → DAG 配置 → 执行 → 查看结果 | P1 |
| `McpClientServiceImpl` 生命周期 | 连接 → 工具列表 → 调用 → 缓存命中 → 断连降级 | P1 |

---

## 相关文档

- [SECURITY.md](SECURITY.md) — 系统风险清单(P0/P1/P2 测试优先级)
- [CLAUDE.md](../../CLAUDE.md) — 代码提交检查清单
- [ARCHITECTURE.md](../ARCHITECTURE.md) — 架构设计 + 模块依赖
Loading
Loading