aico-cli 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/simple-config.mjs +41 -15
- package/dist/cli.mjs +35 -31
- package/package.json +4 -1
- package/templates/agents/aico/plan/function-point-analyzer.md +219 -0
- package/templates/agents/aico/requirement/test-crossplatform.ps1 +0 -0
- package/templates/agents/aico/requirement/test-crossplatform.sh +456 -0
- package/templates/commands/base//344/273/243/347/240/201/345/256/241/346/237/245/346/231/272/350/203/275/344/275/223.md +2 -5
- package/templates/commands/base//345/212/237/350/203/275/347/202/271/346/265/213/347/256/227.md +469 -19
- package/templates/skills/slack-gif-creator/LICENSE.txt +202 -0
- package/templates/skills/slack-gif-creator/SKILL.md +646 -0
- package/templates/skills/slack-gif-creator/core/color_palettes.py +302 -0
- package/templates/skills/slack-gif-creator/core/easing.py +230 -0
- package/templates/skills/slack-gif-creator/core/frame_composer.py +469 -0
- package/templates/skills/slack-gif-creator/core/gif_builder.py +246 -0
- package/templates/skills/slack-gif-creator/core/typography.py +357 -0
- package/templates/skills/slack-gif-creator/core/validators.py +264 -0
- package/templates/skills/slack-gif-creator/core/visual_effects.py +494 -0
- package/templates/skills/slack-gif-creator/requirements.txt +4 -0
- package/templates/skills/slack-gif-creator/templates/bounce.py +106 -0
- package/templates/skills/slack-gif-creator/templates/explode.py +331 -0
- package/templates/skills/slack-gif-creator/templates/fade.py +329 -0
- package/templates/skills/slack-gif-creator/templates/flip.py +291 -0
- package/templates/skills/slack-gif-creator/templates/kaleidoscope.py +211 -0
- package/templates/skills/slack-gif-creator/templates/morph.py +329 -0
- package/templates/skills/slack-gif-creator/templates/move.py +293 -0
- package/templates/skills/slack-gif-creator/templates/pulse.py +268 -0
- package/templates/skills/slack-gif-creator/templates/shake.py +127 -0
- package/templates/skills/slack-gif-creator/templates/slide.py +291 -0
- package/templates/skills/slack-gif-creator/templates/spin.py +269 -0
- package/templates/skills/slack-gif-creator/templates/wiggle.py +300 -0
- package/templates/skills/slack-gif-creator/templates/zoom.py +312 -0
- package/templates/skills/swimlane-diagram/README.md +373 -0
- package/templates/skills/swimlane-diagram/SKILL.md +242 -0
- package/templates/skills/swimlane-diagram/examples.md +405 -0
- package/templates/skills/swimlane-diagram/generators.mjs +258 -0
- package/templates/skills/swimlane-diagram/package.json +126 -0
- package/templates/skills/swimlane-diagram/reference.md +368 -0
- package/templates/skills/swimlane-diagram/swimlane-diagram.mjs +215 -0
- package/templates/skills/swimlane-diagram/swimlane-diagram.test.mjs +358 -0
- package/templates/skills/swimlane-diagram/validators.mjs +291 -0
- package/templates/skills/theme-factory/LICENSE.txt +202 -0
- package/templates/skills/theme-factory/SKILL.md +59 -0
- package/templates/skills/theme-factory/theme-showcase.pdf +0 -0
- package/templates/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/templates/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/templates/skills/theme-factory/themes/desert-rose.md +19 -0
- package/templates/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/templates/skills/theme-factory/themes/golden-hour.md +19 -0
- package/templates/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/templates/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/templates/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/templates/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/templates/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/templates/code.md +0 -70
- package/templates/windows-bootstrap.ps1 +0 -390
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 泳道图生成器技能 - 测试文件
|
|
3
|
+
*
|
|
4
|
+
* 使用Node.js原生测试框架进行测试
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { dirname } from 'path';
|
|
9
|
+
import assert from 'assert';
|
|
10
|
+
|
|
11
|
+
// 导入要测试的模块
|
|
12
|
+
import { SwimlaneDiagramGenerator, utils } from './swimlane-diagram.mjs';
|
|
13
|
+
import { validators } from './validators.mjs';
|
|
14
|
+
import { generators } from './generators.mjs';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = dirname(__filename);
|
|
18
|
+
|
|
19
|
+
// 测试套件
|
|
20
|
+
console.log('🚀 开始泳道图生成器技能测试...\n');
|
|
21
|
+
|
|
22
|
+
// 测试数据
|
|
23
|
+
const testJourneyData = [
|
|
24
|
+
{
|
|
25
|
+
name: '准备阶段',
|
|
26
|
+
steps: [
|
|
27
|
+
{ description: '用户打开应用', score: 5, actor: '用户' },
|
|
28
|
+
{ description: '系统初始化', score: 4, actor: '系统' }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const testSequenceData = [
|
|
34
|
+
{ from: '用户', to: '系统', message: '提交请求', type: 'sync' }
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
// 测试用例集合
|
|
38
|
+
const testCases = {
|
|
39
|
+
// 验证器测试
|
|
40
|
+
validators: [
|
|
41
|
+
{
|
|
42
|
+
name: '验证图表类型 - 有效类型',
|
|
43
|
+
test: () => {
|
|
44
|
+
validators.diagramType('journey');
|
|
45
|
+
validators.diagramType('sequence');
|
|
46
|
+
console.log('✅ 有效类型验证通过');
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: '验证图表类型 - 无效类型',
|
|
51
|
+
test: () => {
|
|
52
|
+
try {
|
|
53
|
+
validators.diagramType('invalid');
|
|
54
|
+
throw new Error('应该抛出错误');
|
|
55
|
+
} catch (error) {
|
|
56
|
+
assert(error.message.includes('无效的图表类型'));
|
|
57
|
+
console.log('✅ 无效类型验证通过');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: '验证用户旅程图数据 - 有效数据',
|
|
63
|
+
test: () => {
|
|
64
|
+
validators.journeyData(testJourneyData);
|
|
65
|
+
console.log('✅ 有效旅程图数据验证通过');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
|
|
70
|
+
// 生成器测试
|
|
71
|
+
generators: [
|
|
72
|
+
{
|
|
73
|
+
name: '生成用户旅程图 - 基础功能',
|
|
74
|
+
test: () => {
|
|
75
|
+
const result = generators.journey({
|
|
76
|
+
title: '测试旅程图',
|
|
77
|
+
sections: testJourneyData
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
assert(result.includes('journey'));
|
|
81
|
+
assert(result.includes('title 测试旅程图'));
|
|
82
|
+
assert(result.includes('section 准备阶段'));
|
|
83
|
+
console.log('✅ 基础旅程图生成通过');
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: '生成时序图 - 基础功能',
|
|
88
|
+
test: () => {
|
|
89
|
+
const result = generators.sequence({
|
|
90
|
+
title: '测试时序图',
|
|
91
|
+
participants: ['用户', '系统'],
|
|
92
|
+
interactions: testSequenceData
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
assert(result.includes('sequenceDiagram'));
|
|
96
|
+
assert(result.includes('participant 用户'));
|
|
97
|
+
assert(result.includes('用户->>系统: 提交请求'));
|
|
98
|
+
console.log('✅ 基础时序图生成通过');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
|
|
103
|
+
// 核心功能测试
|
|
104
|
+
core: [
|
|
105
|
+
{
|
|
106
|
+
name: 'SwimlaneDiagramGenerator - 实例化',
|
|
107
|
+
test: () => {
|
|
108
|
+
const generator = new SwimlaneDiagramGenerator();
|
|
109
|
+
assert(generator instanceof SwimlaneDiagramGenerator);
|
|
110
|
+
console.log('✅ 实例化测试通过');
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'SwimlaneDiagramGenerator - 生成用户旅程图',
|
|
115
|
+
test: () => {
|
|
116
|
+
const generator = new SwimlaneDiagramGenerator();
|
|
117
|
+
const result = generator.generate({
|
|
118
|
+
type: 'journey',
|
|
119
|
+
title: '集成测试旅程图',
|
|
120
|
+
data: testJourneyData
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
assert(result.includes('journey'));
|
|
124
|
+
assert(result.includes('title 集成测试旅程图'));
|
|
125
|
+
console.log('✅ 集成旅程图生成通过');
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'SwimlaneDiagramGenerator - 生成时序图',
|
|
130
|
+
test: () => {
|
|
131
|
+
const generator = new SwimlaneDiagramGenerator();
|
|
132
|
+
const result = generator.generate({
|
|
133
|
+
type: 'sequence',
|
|
134
|
+
title: '集成测试时序图',
|
|
135
|
+
data: testSequenceData,
|
|
136
|
+
participants: ['用户', '系统']
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
assert(result.includes('sequenceDiagram'));
|
|
140
|
+
assert(result.includes('participant 用户'));
|
|
141
|
+
console.log('✅ 集成时序图生成通过');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
|
|
146
|
+
// 工具函数测试
|
|
147
|
+
utils: [
|
|
148
|
+
{
|
|
149
|
+
name: '工具函数 - 解析用户旅程图数据',
|
|
150
|
+
test: () => {
|
|
151
|
+
const rawData = [
|
|
152
|
+
{
|
|
153
|
+
name: '测试阶段',
|
|
154
|
+
steps: [
|
|
155
|
+
{ description: '测试步骤', score: 5, actor: '测试者' }
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
const result = utils.parseJourneyData(rawData);
|
|
161
|
+
assert(Array.isArray(result));
|
|
162
|
+
assert(result[0].name === '测试阶段');
|
|
163
|
+
assert(result[0].steps[0].description === '测试步骤');
|
|
164
|
+
console.log('✅ 旅程图数据解析通过');
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: '工具函数 - 美化Mermaid代码',
|
|
169
|
+
test: () => {
|
|
170
|
+
const rawCode = 'journey\n title 测试\n section 阶段\n 步骤: 5: 用户';
|
|
171
|
+
const result = utils.formatMermaidCode(rawCode);
|
|
172
|
+
|
|
173
|
+
assert(result.includes('journey'));
|
|
174
|
+
assert(result.includes(' title 测试'));
|
|
175
|
+
console.log('✅ Mermaid代码美化通过');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
|
|
180
|
+
// 边界测试
|
|
181
|
+
edgeCases: [
|
|
182
|
+
{
|
|
183
|
+
name: '边界测试 - 空数据验证',
|
|
184
|
+
test: () => {
|
|
185
|
+
try {
|
|
186
|
+
validators.journeyData([]);
|
|
187
|
+
throw new Error('应该抛出错误');
|
|
188
|
+
} catch (error) {
|
|
189
|
+
assert(error.message.includes('不能为空'));
|
|
190
|
+
console.log('✅ 空数据验证通过');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: '边界测试 - 无效评分处理',
|
|
196
|
+
test: () => {
|
|
197
|
+
const invalidData = [
|
|
198
|
+
{
|
|
199
|
+
name: '测试阶段',
|
|
200
|
+
steps: [
|
|
201
|
+
{ description: '测试步骤', score: 10, actor: '用户' }
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
validators.journeyData(invalidData);
|
|
208
|
+
throw new Error('应该抛出错误');
|
|
209
|
+
} catch (error) {
|
|
210
|
+
assert(error.message.includes('评分必须在1-5之间'));
|
|
211
|
+
console.log('✅ 无效评分验证通过');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// 测试运行器
|
|
219
|
+
async function runTests() {
|
|
220
|
+
let passed = 0;
|
|
221
|
+
let failed = 0;
|
|
222
|
+
const results = [];
|
|
223
|
+
|
|
224
|
+
// 按类别运行测试
|
|
225
|
+
for (const [category, tests] of Object.entries(testCases)) {
|
|
226
|
+
console.log(`\n📋 运行 ${category} 测试...`);
|
|
227
|
+
|
|
228
|
+
for (const testCase of tests) {
|
|
229
|
+
try {
|
|
230
|
+
await testCase.test();
|
|
231
|
+
passed++;
|
|
232
|
+
results.push({ name: testCase.name, status: '✅ 通过' });
|
|
233
|
+
} catch (error) {
|
|
234
|
+
failed++;
|
|
235
|
+
results.push({
|
|
236
|
+
name: testCase.name,
|
|
237
|
+
status: '❌ 失败',
|
|
238
|
+
error: error.message
|
|
239
|
+
});
|
|
240
|
+
console.error(`❌ ${testCase.name} 失败:`, error.message);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 输出测试结果
|
|
246
|
+
console.log('\n' + '='.repeat(50));
|
|
247
|
+
console.log('📊 测试结果汇总');
|
|
248
|
+
console.log('='.repeat(50));
|
|
249
|
+
|
|
250
|
+
results.forEach(result => {
|
|
251
|
+
console.log(`${result.status} ${result.name}`);
|
|
252
|
+
if (result.error) {
|
|
253
|
+
console.log(` 错误: ${result.error}`);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
console.log('\n' + '='.repeat(50));
|
|
258
|
+
console.log(`🎯 总计: ${passed + failed} 个测试`);
|
|
259
|
+
console.log(`✅ 通过: ${passed}`);
|
|
260
|
+
console.log(`❌ 失败: ${failed}`);
|
|
261
|
+
|
|
262
|
+
if (failed === 0) {
|
|
263
|
+
console.log('\n🎉 所有测试通过!泳道图生成器技能功能正常。');
|
|
264
|
+
} else {
|
|
265
|
+
console.log('\n⚠️ 存在测试失败,请检查相关功能。');
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 性能测试函数
|
|
271
|
+
function runPerformanceTest() {
|
|
272
|
+
console.log('\n⚡ 运行性能测试...');
|
|
273
|
+
|
|
274
|
+
const generator = new SwimlaneDiagramGenerator();
|
|
275
|
+
const startTime = Date.now();
|
|
276
|
+
|
|
277
|
+
// 生成100个简单的时序图
|
|
278
|
+
for (let i = 0; i < 100; i++) {
|
|
279
|
+
generator.generate({
|
|
280
|
+
type: 'sequence',
|
|
281
|
+
title: `性能测试 ${i}`,
|
|
282
|
+
data: [{ from: 'A', to: 'B', message: '测试消息', type: 'sync' }],
|
|
283
|
+
participants: ['A', 'B']
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const endTime = Date.now();
|
|
288
|
+
const duration = endTime - startTime;
|
|
289
|
+
|
|
290
|
+
console.log(`⏱️ 生成100个时序图耗时: ${duration}ms`);
|
|
291
|
+
console.log(`📈 平均每个图表: ${(duration / 100).toFixed(2)}ms`);
|
|
292
|
+
|
|
293
|
+
if (duration < 1000) {
|
|
294
|
+
console.log('✅ 性能测试通过 - 生成速度良好');
|
|
295
|
+
} else {
|
|
296
|
+
console.log('⚠️ 性能测试警告 - 生成速度较慢');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 错误处理测试
|
|
301
|
+
function runErrorHandlingTest() {
|
|
302
|
+
console.log('\n🛡️ 运行错误处理测试...');
|
|
303
|
+
|
|
304
|
+
const generator = new SwimlaneDiagramGenerator();
|
|
305
|
+
|
|
306
|
+
// 测试无效参数
|
|
307
|
+
try {
|
|
308
|
+
generator.generate(null);
|
|
309
|
+
console.log('❌ 应该抛出参数错误');
|
|
310
|
+
} catch (error) {
|
|
311
|
+
assert(error.message.includes('生成泳道图失败'));
|
|
312
|
+
console.log('✅ 空参数错误处理通过');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// 测试无效类型
|
|
316
|
+
try {
|
|
317
|
+
generator.generate({
|
|
318
|
+
type: 'invalid',
|
|
319
|
+
title: '测试',
|
|
320
|
+
data: []
|
|
321
|
+
});
|
|
322
|
+
console.log('❌ 应该抛出类型错误');
|
|
323
|
+
} catch (error) {
|
|
324
|
+
assert(error.message.includes('生成泳道图失败'));
|
|
325
|
+
console.log('✅ 无效类型错误处理通过');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
console.log('✅ 错误处理测试完成');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 主测试函数
|
|
332
|
+
async function main() {
|
|
333
|
+
try {
|
|
334
|
+
await runTests();
|
|
335
|
+
runPerformanceTest();
|
|
336
|
+
runErrorHandlingTest();
|
|
337
|
+
|
|
338
|
+
console.log('\n🎊 泳道图生成器技能测试全部完成!');
|
|
339
|
+
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error('💥 测试运行失败:', error);
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 如果直接运行此文件,则执行测试
|
|
347
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
348
|
+
main().catch(error => {
|
|
349
|
+
console.error('测试运行器错误:', error);
|
|
350
|
+
process.exit(1);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export default {
|
|
355
|
+
runTests,
|
|
356
|
+
runPerformanceTest,
|
|
357
|
+
runErrorHandlingTest
|
|
358
|
+
};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 泳道图生成器 - 验证器模块
|
|
3
|
+
*
|
|
4
|
+
* 负责参数验证、数据校验和错误处理
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 验证图表类型
|
|
9
|
+
* @param {string} type - 图表类型
|
|
10
|
+
* @throws {Error} 如果类型无效
|
|
11
|
+
*/
|
|
12
|
+
export function validateDiagramType(type) {
|
|
13
|
+
const validTypes = ['journey', 'sequence'];
|
|
14
|
+
|
|
15
|
+
if (!type) {
|
|
16
|
+
throw new Error('图表类型不能为空');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!validTypes.includes(type)) {
|
|
20
|
+
throw new Error(`无效的图表类型: ${type}。支持的类型: ${validTypes.join(', ')}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 验证生成参数
|
|
26
|
+
* @param {Object} params - 生成参数
|
|
27
|
+
* @throws {Error} 如果参数无效
|
|
28
|
+
*/
|
|
29
|
+
export function validateParameters(params) {
|
|
30
|
+
if (!params) {
|
|
31
|
+
throw new Error('生成参数不能为空');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!params.title || typeof params.title !== 'string') {
|
|
35
|
+
throw new Error('图表标题不能为空且必须是字符串');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!params.data || !Array.isArray(params.data)) {
|
|
39
|
+
throw new Error('图表数据不能为空且必须是数组');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 根据类型进行特定验证
|
|
43
|
+
switch (params.type) {
|
|
44
|
+
case 'journey':
|
|
45
|
+
validateJourneyData(params.data);
|
|
46
|
+
break;
|
|
47
|
+
case 'sequence':
|
|
48
|
+
validateSequenceData(params.data);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 验证用户旅程图数据
|
|
55
|
+
* @param {Array} data - 旅程图数据
|
|
56
|
+
* @throws {Error} 如果数据无效
|
|
57
|
+
*/
|
|
58
|
+
export function validateJourneyData(data) {
|
|
59
|
+
if (data.length === 0) {
|
|
60
|
+
throw new Error('用户旅程图数据不能为空');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
data.forEach((section, index) => {
|
|
64
|
+
if (!section.name) {
|
|
65
|
+
throw new Error(`第${index + 1}个阶段缺少名称`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!section.steps || !Array.isArray(section.steps)) {
|
|
69
|
+
throw new Error(`阶段"${section.name}"缺少步骤数据`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (section.steps.length === 0) {
|
|
73
|
+
throw new Error(`阶段"${section.name}"至少需要一个步骤`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
section.steps.forEach((step, stepIndex) => {
|
|
77
|
+
if (!step.description) {
|
|
78
|
+
throw new Error(`阶段"${section.name}"的第${stepIndex + 1}个步骤缺少描述`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof step.score !== 'number' || step.score < 1 || step.score > 5) {
|
|
82
|
+
throw new Error(`步骤"${step.description}"的评分必须在1-5之间`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!step.actor) {
|
|
86
|
+
throw new Error(`步骤"${step.description}"缺少参与者`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 验证时序图数据
|
|
94
|
+
* @param {Array} data - 时序图数据
|
|
95
|
+
* @throws {Error} 如果数据无效
|
|
96
|
+
*/
|
|
97
|
+
export function validateSequenceData(data) {
|
|
98
|
+
if (data.length === 0) {
|
|
99
|
+
throw new Error('时序图数据不能为空');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const participants = new Set();
|
|
103
|
+
|
|
104
|
+
data.forEach((interaction, index) => {
|
|
105
|
+
if (!interaction.from) {
|
|
106
|
+
throw new Error(`第${index + 1}个交互缺少发送者`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!interaction.to) {
|
|
110
|
+
throw new Error(`第${index + 1}个交互缺少接收者`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!interaction.message) {
|
|
114
|
+
throw new Error(`第${index + 1}个交互缺少消息内容`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 收集参与者
|
|
118
|
+
participants.add(interaction.from);
|
|
119
|
+
participants.add(interaction.to);
|
|
120
|
+
|
|
121
|
+
// 验证消息类型
|
|
122
|
+
if (interaction.type && !['sync', 'async'].includes(interaction.type)) {
|
|
123
|
+
throw new Error(`第${index + 1}个交互的消息类型无效,必须是'sync'或'async'`);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// 验证参与者数量(避免图表过于复杂)
|
|
128
|
+
if (participants.size > 10) {
|
|
129
|
+
throw new Error('时序图参与者数量过多(最多10个),建议拆分图表');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 验证参与者列表
|
|
135
|
+
* @param {Array} participants - 参与者列表
|
|
136
|
+
* @throws {Error} 如果参与者无效
|
|
137
|
+
*/
|
|
138
|
+
export function validateParticipants(participants) {
|
|
139
|
+
if (!participants || !Array.isArray(participants)) {
|
|
140
|
+
throw new Error('参与者列表不能为空且必须是数组');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (participants.length === 0) {
|
|
144
|
+
throw new Error('参与者列表不能为空');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
participants.forEach((participant, index) => {
|
|
148
|
+
if (typeof participant !== 'string') {
|
|
149
|
+
throw new Error(`第${index + 1}个参与者必须是字符串`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (participant.trim().length === 0) {
|
|
153
|
+
throw new Error(`第${index + 1}个参与者名称不能为空`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 验证名称长度
|
|
157
|
+
if (participant.length > 50) {
|
|
158
|
+
throw new Error(`参与者"${participant}"名称过长(最多50个字符)`);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 验证消息内容
|
|
165
|
+
* @param {string} message - 消息内容
|
|
166
|
+
* @throws {Error} 如果消息无效
|
|
167
|
+
*/
|
|
168
|
+
export function validateMessage(message) {
|
|
169
|
+
if (!message || typeof message !== 'string') {
|
|
170
|
+
throw new Error('消息内容不能为空且必须是字符串');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (message.trim().length === 0) {
|
|
174
|
+
throw new Error('消息内容不能为空');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (message.length > 200) {
|
|
178
|
+
throw new Error('消息内容过长(最多200个字符)');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 验证阶段名称
|
|
184
|
+
* @param {string} sectionName - 阶段名称
|
|
185
|
+
* @throws {Error} 如果名称无效
|
|
186
|
+
*/
|
|
187
|
+
export function validateSectionName(sectionName) {
|
|
188
|
+
if (!sectionName || typeof sectionName !== 'string') {
|
|
189
|
+
throw new Error('阶段名称不能为空且必须是字符串');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (sectionName.trim().length === 0) {
|
|
193
|
+
throw new Error('阶段名称不能为空');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (sectionName.length > 100) {
|
|
197
|
+
throw new Error('阶段名称过长(最多100个字符)');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 验证步骤描述
|
|
203
|
+
* @param {string} description - 步骤描述
|
|
204
|
+
* @throws {Error} 如果描述无效
|
|
205
|
+
*/
|
|
206
|
+
export function validateStepDescription(description) {
|
|
207
|
+
if (!description || typeof description !== 'string') {
|
|
208
|
+
throw new Error('步骤描述不能为空且必须是字符串');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (description.trim().length === 0) {
|
|
212
|
+
throw new Error('步骤描述不能为空');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (description.length > 150) {
|
|
216
|
+
throw new Error('步骤描述过长(最多150个字符)');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 验证评分
|
|
222
|
+
* @param {number} score - 评分值
|
|
223
|
+
* @throws {Error} 如果评分无效
|
|
224
|
+
*/
|
|
225
|
+
export function validateScore(score) {
|
|
226
|
+
if (typeof score !== 'number') {
|
|
227
|
+
throw new Error('评分必须是数字');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (score < 1 || score > 5) {
|
|
231
|
+
throw new Error('评分必须在1-5之间');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!Number.isInteger(score)) {
|
|
235
|
+
throw new Error('评分必须是整数');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 创建验证错误对象
|
|
241
|
+
* @param {string} message - 错误消息
|
|
242
|
+
* @param {string} field - 错误字段
|
|
243
|
+
* @param {any} value - 错误值
|
|
244
|
+
* @returns {Object} 验证错误对象
|
|
245
|
+
*/
|
|
246
|
+
export function createValidationError(message, field = null, value = null) {
|
|
247
|
+
return {
|
|
248
|
+
message,
|
|
249
|
+
field,
|
|
250
|
+
value,
|
|
251
|
+
timestamp: new Date().toISOString()
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 批量验证多个字段
|
|
257
|
+
* @param {Array} validations - 验证规则数组
|
|
258
|
+
* @returns {Array} 验证错误数组
|
|
259
|
+
*/
|
|
260
|
+
export function batchValidate(validations) {
|
|
261
|
+
const errors = [];
|
|
262
|
+
|
|
263
|
+
validations.forEach(({ value, validator, field, message }) => {
|
|
264
|
+
try {
|
|
265
|
+
validator(value);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
errors.push(createValidationError(
|
|
268
|
+
message || error.message,
|
|
269
|
+
field,
|
|
270
|
+
value
|
|
271
|
+
));
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return errors;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* 验证器集合
|
|
280
|
+
*/
|
|
281
|
+
export const validators = {
|
|
282
|
+
diagramType: validateDiagramType,
|
|
283
|
+
parameters: validateParameters,
|
|
284
|
+
journeyData: validateJourneyData,
|
|
285
|
+
sequenceData: validateSequenceData,
|
|
286
|
+
participants: validateParticipants,
|
|
287
|
+
message: validateMessage,
|
|
288
|
+
sectionName: validateSectionName,
|
|
289
|
+
stepDescription: validateStepDescription,
|
|
290
|
+
score: validateScore
|
|
291
|
+
};
|