@sokeai/cli 1.0.63 → 1.0.65

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.
@@ -1,513 +1,119 @@
1
1
  ---
2
2
  name: soke-lesson
3
- summary: 授客课件管理(创建课件、查询课件),支持视频/音频/文档等多种类型
4
3
  version: 1.0.0
5
- description: "授客课件管理:课件生命周期管理,包括基于素材创建课件、查询课件列表和详情、管理课件排序。支持视频、音频、文档等多种课件类型。当用户需要添加课件、管理课件、查询课件信息时使用。"
6
- metadata:
7
- requires:
8
- bins: ["soke-cli"]
9
- cliHelp: "soke-cli course --help"
4
+ description: 课件管理 — 创建/查询/排序课程章节课件,所有参数来自素材上传返回结果
5
+ tags: [soke, lesson, courseware, chapter, video, audio, document]
10
6
  ---
11
7
 
12
- # 课件管理 (lesson)
8
+ # 课件管理
13
9
 
14
- **CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../soke-shared/SKILL.md`](../soke-shared/SKILL.md),其中包含认证、配置、权限处理**
10
+ ## 触发描述
15
11
 
16
- ## ⚠️ 使用前提
12
+ 用户已有课程(course)且完成素材上传后,需要将素材添加为课程的章节课件时触发本技能。支持创建课件、列表查询、详情查询和排序,仅支持 `video | audio | document` 三种类型的课件。
17
13
 
18
- skill 基于 **soke-cli** 命令行工具,使用前必须完成以下步骤:
14
+ ## 输入要求
19
15
 
20
- 1. **安装 soke-cli**:确保已安装 `@sokeai/cli` NPM 包或 soke-cli 二进制文件
21
- 2. **用户登录授权**:运行 `soke-cli auth login` 完成 OAuth 设备码授权流程,获取用户访问令牌
16
+ 所有课件创建参数**必须来自 `soke-material` 上传返回的结果对象**,不允许手动构造。传入参数:
22
17
 
23
- **快速开始**:
24
- ```bash
25
-
26
- # 1. 用户登录授权(必须)
27
- soke-cli auth login
28
-
29
- # 2. 验证登录状态
30
- soke-cli config show
18
+ ```json
19
+ {
20
+ "uuid": "素材上传返回的 uuid",
21
+ "filename": "素材上传返回的 filename",
22
+ "type": "素材上传返回的 type(仅 video/audio/document)",
23
+ "ext": "素材上传返回的 ext",
24
+ "filesize": "素材上传返回的 filesize",
25
+ "object": "素材上传返回的 object",
26
+ "duration": "默认时长或自定义时长(秒)",
27
+ "courseId": "目标课程 ID",
28
+ "title": "课件标题(用户提供或从 filename 推断)"
29
+ }
31
30
  ```
32
31
 
33
- 未完成登录授权将无法调用任何课件管理相关的 API。详细的认证配置说明请参考 [soke-shared/SKILL.md](../soke-shared/SKILL.md)。
34
-
35
- ## 核心概念
36
-
37
- - **Lesson(课件)**: 课程下的学习单元,包含视频、音频、文档等类型的学习内容
38
- - **Material(素材)**: 课件关联的素材文件,必须先上传到素材库才能创建课件
39
- - **Course(课程)**: 课件所属的课程,通过 `course_id` 关联
40
- - **Sort Order(排序)**: 课件在课程中的显示顺序,按创建时间自动排序
41
-
42
- ## 课件类型
43
-
44
- | 类型 | 说明 | 对应素材类型 |
45
- |------|------|-------------|
46
- | video | 视频课件 | video |
47
- | audio | 音频课件 | audio |
48
- | document | 文档课件 | document |
49
-
50
- ## 课件创建流程
51
-
52
- 完整的课件创建流程:
53
-
54
- 1. **上传素材** - 使用 `file +upload` 上传文件到素材库,获取 `media_id`
55
- 2. **创建课件** - 使用 `course +lesson-create` 基于素材创建课件
56
- 3. **发布课程** - 课件添加完成后,使用 `course +publish` 发布课程
57
-
58
- ## 可用命令
59
-
60
- ### 课件创建
32
+ > ⚠️ `uuid` `filename` `type` `ext` `filesize` `object` 六个字段必须严格从素材上传返回结果中取值,不可自行构造或编造。
61
33
 
62
- #### `course +lesson-create` - 创建课件
34
+ ## 执行流程
63
35
 
64
- 基于素材库文件创建课件并关联到课程。
36
+ ### 1. 创建课件 `course +lesson-create`
65
37
 
66
- **参数:**
67
- - `--course-id` (必填): 课程 UUID
68
- - `--title` (必填): 课件标题
69
- - `--media-id` (必填): 素材库文件 UUID(通过 `file +upload` 获取)
70
- - `--type` (必填): 课件类型 (video/audio/document)
71
- - `--length` (必填): 媒体时长(秒,文档类型可填 0)
72
- - `--object` (必填): OSS 对象路径
73
- - `--filename` (必填): 文件名
74
- - `--ext` (必填): 文件扩展名
75
- - `--filesize` (必填): 文件大小(字节)
76
- - `--storage` (可选): 存储类型,默认 "aliyun"
77
- - `--description` (可选): 课件描述
78
-
79
- **示例:**
80
38
  ```bash
81
- # 创建视频课件
82
- soke-cli course +lesson-create \
83
- --course-id "COURSE-UUID" \
84
- --title "第一课:入门介绍" \
85
- --media-id "MATERIAL-UUID" \
86
- --type "video" \
87
- --length 600 \
88
- --object "ding123/course/video/lesson1.mp4" \
89
- --filename "lesson1.mp4" \
90
- --ext "mp4" \
91
- --filesize 10485760
92
-
93
- # 创建音频课件
94
- soke-cli course +lesson-create \
95
- --course-id "COURSE-UUID" \
96
- --title "第二课:音频讲解" \
97
- --media-id "MATERIAL-UUID" \
98
- --type "audio" \
99
- --length 236 \
100
- --object "ding123/course/audio/lesson2.mp3" \
101
- --filename "lesson2.mp3" \
102
- --ext "mp3" \
103
- --filesize 9453464
104
- ```
105
-
106
- **返回:**
107
- - 课件 UUID
108
- - 课件标题
109
- - 课件类型
110
- - 关联的素材信息
111
-
112
- ### 课件查询
113
-
114
- #### `course +lesson-list` - 列出课程的课件列表
115
-
116
- 查询指定课程下的所有课件。
39
+ soke course +lesson-create \
40
+ --course-id <courseId> \
41
+ --title "<课件标题>" \
42
+ --type <video|audio|document> \
43
+ --uuid <素材uuid> \
44
+ --filename "<文件名>" \
45
+ --ext <扩展名> \
46
+ --filesize <字节数> \
47
+ --object "<存储对象>" \
48
+ --duration <时长秒数>
49
+ ```
50
+
51
+ **参数对照(全部来自素材上传返回):**
52
+
53
+ | 参数 | 来源 | 说明 |
54
+ |------|------|------|
55
+ | `--course-id` | 当前课程上下文 | 目标课程 ID |
56
+ | `--title` | 用户指定或 filename 推断 | 课件展示名称 |
57
+ | `--type` | 返回 `.type` | video / audio / document |
58
+ | `--uuid` | 返回 `.uuid` | 素材唯一标识 |
59
+ | `--filename` | 返回 `.filename` | 原始文件名 |
60
+ | `--ext` | 返回 `.ext` | 文件扩展名 |
61
+ | `--filesize` | 返回 `.filesize` | 文件字节数 |
62
+ | `--object` | 返回 `.object` | 存储对象键 |
63
+ | `--duration` | 默认规则或用户指定 | 学习时长(秒) |
64
+
65
+ ### 2. 课件列表 `course +lesson-list`
117
66
 
118
- **参数:**
119
- - `--course-id` (必填): 课程 UUID
120
-
121
- **示例:**
122
67
  ```bash
123
- soke-cli course +lesson-list --course-id "COURSE-UUID"
68
+ soke course +lesson-list --course-id <courseId>
124
69
  ```
125
70
 
126
- **返回:**
127
- - 课件列表(包含 UUID、标题、类型、时长、排序等信息)
128
-
129
- #### `course +lesson-get` - 获取课件详情
71
+ 返回指定课程下所有课件的列表,包含课件 ID、标题、类型、排序序号。
130
72
 
131
- 获取单个课件的详细信息。
73
+ ### 3. 课件详情 `course +lesson-get`
132
74
 
133
- **参数:**
134
- - `--lesson-id` (必填): 课件 UUID
135
-
136
- **示例:**
137
75
  ```bash
138
- soke-cli course +lesson-get --lesson-id "LESSON-UUID"
76
+ soke course +lesson-get --lesson-id <lessonId>
139
77
  ```
140
78
 
141
- **返回:**
142
- - 课件详细信息(包含素材信息、转换状态等)
143
-
144
- ### 课件排序
79
+ 返回单个课件的完整信息(标题、类型、素材引用、时长、转码状态等)。
145
80
 
146
- #### `course +lesson-sort` - 调整课程课件顺序
81
+ ### 4. 课件排序 `course +lesson-sort`
147
82
 
148
- 按指定顺序重新排列课程下的课件。
149
-
150
- **参数:**
151
- - `--course-id` (必填): 课程 UUID
152
- - `--sort-list` (必填): 课件 ID 有序列表,多个 ID 用逗号分隔,顺序即为最终显示顺序
153
-
154
- **示例:**
155
83
  ```bash
156
- # 将两个课件按指定顺序排列
157
- soke-cli course +lesson-sort \
158
- --course-id "F23F32C5-8586-45E5-B98D-5D005FD596B1" \
159
- --sort-list "29F629AB-3FF6-4EC0-A7C5-CC6D7124518E,058B89B2-95CA-45EF-9F52-231E58AF2E91"
160
- ```
161
-
162
- **返回:**
163
- - 调整成功提示及课件数量
164
-
165
- **说明:**
166
- - `--sort-list` 中课件 ID 的顺序即为调整后的最终排列顺序
167
- - 先使用 `course +lesson-list` 查出所有课件 UUID,再按需排列后传入
168
-
169
-
170
- ## 使用场景
171
-
172
- ### 场景1:为课程添加单个课件
173
-
174
- **步骤说明**:
175
- 1. 上传素材到素材库
176
- 2. 从上传响应中提取完整文件信息
177
- 3. 使用提取的信息创建课件
178
-
179
- **步骤1:上传素材**
180
- ```
181
- soke-cli file +upload --file ./lesson1.mp4 --format json
182
- ```
183
-
184
- **响应示例**:
185
- ```json
186
- {
187
- "success": true,
188
- "data": {
189
- "uuid": "media-123456",
190
- "filename": "lesson1.mp4",
191
- "filesize": 10485760,
192
- "object": "uploads/2024/01/abc123.mp4",
193
- "ext": "mp4",
194
- "type": "video"
195
- }
196
- }
197
- ```
198
-
199
- **提取字段**:
200
- - `data.uuid` → `media_id`
201
- - `data.filename` → `filename`
202
- - `data.filesize` → `filesize`
203
- - `data.object` → `object`
204
- - `data.ext` → `ext`
205
- - `data.type` → `type`
206
-
207
- **步骤2:创建课件**(使用步骤1提取的信息)
208
- ```
209
- soke-cli course +lesson-create \
210
- --course-id "{course_id}" \
211
- --title "第一课" \
212
- --media-id "media-123456" \
213
- --type "video" \
214
- --length 600 \
215
- --object "uploads/2024/01/abc123.mp4" \
216
- --filename "lesson1.mp4" \
217
- --ext "mp4" \
218
- --filesize 10485760
219
- ```
220
-
221
- ### 场景2:批量添加课件
222
-
223
- **流程说明**:
224
- 1. 对每个文件重复"上传素材 → 创建课件"流程
225
- 2. 保持 `course_id` 不变
226
- 3. 每个课件使用不同的 `title`
227
-
228
- **命令序列**:
229
- ```
230
- # 对每个文件重复以下步骤:
231
-
232
- # 文件1:
233
- # 1. 上传素材
234
- soke-cli file +upload --file "lesson1.mp4" --format json
235
- # 提取: media_id, filename, filesize, object, ext, type
236
-
237
- # 2. 创建课件
238
- soke-cli course +lesson-create \
239
- --course-id "{course_id}" \
240
- --title "第1课" \
241
- --media-id "{media_id}" \
242
- --type "{type}" \
243
- --length 600 \
244
- --object "{object}" \
245
- --filename "{filename}" \
246
- --ext "{ext}" \
247
- --filesize {filesize}
248
-
249
- # 文件2:
250
- # 1. 上传素材
251
- soke-cli file +upload --file "lesson2.mp4" --format json
252
- # 提取: media_id, filename, filesize, object, ext, type
253
-
254
- # 2. 创建课件
255
- soke-cli course +lesson-create \
256
- --course-id "{course_id}" \
257
- --title "第2课" \
258
- --media-id "{media_id}" \
259
- --type "{type}" \
260
- --length 600 \
261
- --object "{object}" \
262
- --filename "{filename}" \
263
- --ext "{ext}" \
264
- --filesize {filesize}
265
-
266
- # ... 重复上述步骤
267
- ```
268
-
269
- ### 场景3:管理课件
270
-
271
- **查询课程的所有课件**:
272
- ```
273
- soke-cli course +lesson-list --course-id "{course_id}" --format json
274
- ```
275
-
276
- **获取课件详情**:
277
- ```
278
- soke-cli course +lesson-get --lesson-id "{lesson_id}" --format json
84
+ soke course +lesson-sort --course-id <courseId> --order "<lessonId1>,<lessonId2>,<lessonId3>"
279
85
  ```
280
86
 
281
- **调整课件顺序**:
282
- ```
283
- # 先查询出所有课件 UUID
284
- soke-cli course +lesson-list --course-id "{course_id}" --format json
87
+ 调整课程内课件的展示顺序,`--order` 为逗号分隔的课件 ID 列表,按先后顺序排列。
285
88
 
286
- # 按期望顺序传入 UUID 列表(逗号分隔,第一个排在最前)
287
- soke-cli course +lesson-sort \
288
- --course-id "{course_id}" \
289
- --sort-list "{lesson_id_1},{lesson_id_2},{lesson_id_3}"
290
- ```
89
+ ## 输出标准
291
90
 
292
- ## API 接口映射
293
-
294
- | CLI 命令 | API 路径 | HTTP 方法 | 说明 |
295
- |----------|----------|-----------|------|
296
- | `course +lesson-create` | `/skills/course/lesson/create` | POST | 创建课件 |
297
- | `course +lesson-list` | `/skills/course/lesson/list` | GET | 列出课件列表 |
298
- | `course +lesson-get` | `/skills/course/lesson/{id}` | GET | 获取课件详情 |
299
- | `course +lesson-sort` | `/skills/course/lesson/sort/{course_id}` | PUT | 调整课件顺序 |
300
- | `course +lesson-update` | `/skills/course/lesson/{id}` | PUT | 更新课件信息 |
301
- ## 课件与素材的关系
91
+ 创建课件成功后输出摘要:
302
92
 
303
93
  ```
304
- 素材库 (Material)
305
- (通过 media_id 关联)
306
- 课件 (Lesson)
307
- (通过 course_id 关联)
308
- 课程 (Course)
94
+ 课件创建成功
95
+ - 课程: {courseTitle} (ID: {courseId})
96
+ - 课件: {title}
97
+ - 类型: {type}
98
+ - 时长: {duration}s
99
+ - 课件 ID: {lessonId}
309
100
  ```
310
101
 
311
- **重要说明:**
312
- 1. 课件必须关联素材库中的文件(通过 `media_id`)
313
- 2. 一个素材可以被多个课件使用
314
- 4. 删除素材会影响关联的课件
315
-
316
- ## 课件参数说明
317
-
318
- ### 必填参数详解
319
-
320
- | 参数 | 说明 | 如何获取 |
321
- |------|------|----------|
322
- | course_id | 课程UUID | 通过 `course +create` 创建课程时返回 |
323
- | media_id | 素材UUID | 通过 `file +upload` 上传素材时返回 |
324
- | title | 课件标题 | 用户自定义 |
325
- | type | 课件类型 | 根据素材类型设置 (video/audio/document) |
326
- | length | 媒体时长 | 视频/音频的实际时长(秒),文档填 0 |
327
- | object | OSS路径 | 从素材信息中获取 |
328
- | filename | 文件名 | 从素材信息中获取 |
329
- | ext | 文件扩展名 | 从素材信息中获取 |
330
- | filesize | 文件大小 | 从素材信息中获取(字节) |
331
-
332
- ### 从素材信息获取参数
333
-
334
- **推荐方式**:直接从 `file +upload` 响应中提取所有参数
335
-
336
- **上传素材命令**:
337
- ```
338
- soke-cli file +upload --file "lesson.mp4" --format json
339
- ```
340
-
341
- **响应包含所有必需字段**:
342
- ```json
343
- {
344
- "success": true,
345
- "data": {
346
- "uuid": "media-123456",
347
- "filename": "lesson.mp4",
348
- "filesize": 10485760,
349
- "object": "uploads/2024/01/abc123.mp4",
350
- "ext": "mp4",
351
- "type": "video",
352
- "length": 600
353
- }
354
- }
355
- ```
356
-
357
- **字段映射**:
358
-
359
- | 课件参数 | 来源字段 | 说明 |
360
- |---------|---------|------|
361
- | media-id | data.uuid | 素材 UUID |
362
- | type | data.type | 文件类型(video/audio/document) |
363
- | length | data.length 或默认值 | 媒体时长(秒) |
364
- | object | data.object | OSS 对象路径 |
365
- | filename | data.filename | 文件名 |
366
- | ext | data.ext | 文件扩展名 |
367
- | filesize | data.filesize | 文件大小(字节) |
368
-
369
- **时长设置**:
370
- - 如果响应中有 `length` 字段,直接使用
371
- - 如果没有,根据类型设置默认值:
372
- - video → 600秒
373
- - audio → 300秒
374
- - document → 0秒
375
-
376
- **不推荐的方式**:
377
- - ❌ 手动填写 `--object` 路径
378
- - ❌ 本地计算 `--filesize`
379
- - ❌ 额外调用 `file +get-info` 查询素材信息
380
-
381
- ## 最佳实践
382
-
383
- ### 1. 素材上传与信息提取
384
-
385
- **关键原则**:素材上传响应包含创建课件所需的所有信息,直接提取使用。
386
-
387
- **正确流程**:
388
- 1. 调用 `file +upload` 上传文件
389
- 2. 从响应的 `data` 字段提取所有必需信息
390
- 3. 使用提取的信息调用 `course +lesson-create`
391
-
392
- **错误做法**:
393
- - ❌ 手动构造 OSS 路径
394
- - ❌ 猜测文件大小或扩展名
395
- - ❌ 上传后再查询素材详情
396
-
397
- ### 2. 课件类型匹配
398
-
399
- 课件类型必须与素材类型一致:
400
-
401
- | 素材类型 | 课件类型 | 说明 |
402
- |---------|---------|------|
403
- | video | video | 视频课件 |
404
- | audio | audio | 音频课件 |
405
- | document | document | 文档课件 |
406
-
407
- **建议**:直接使用素材上传响应中的 `type` 字段
408
-
409
- ### 3. 时长设置规则
410
-
411
- | 课件类型 | 时长要求 | 默认值 |
412
- |---------|---------|--------|
413
- | video | 必填,单位秒 | 600秒(10分钟) |
414
- | audio | 必填,单位秒 | 300秒(5分钟) |
415
- | document | 填 0 | 0秒 |
416
-
417
- ### 4. 批量创建建议
418
-
419
- **批量创建课件**:
420
- - 对每个文件重复"上传 → 创建"流程
421
- - 保持 `course_id` 不变
422
- - 使用不同的 `title` 区分课件
423
- - 可以并行上传素材以提高效率
424
-
425
- **命名建议**:
426
- - 使用序号:第1课、第2课、第3课
427
- - 使用主题:入门介绍、基础语法、高级特性
428
- - 使用文件名:从文件名中提取标题
429
-
430
- ### 5. 错误处理
431
-
432
- **检查点**:
433
- - 素材上传成功(`success: true`)
434
- - 提取的 `media_id` 不为空
435
- - 课件创建成功(`success: true`)
436
- - 课件类型与素材类型匹配
437
-
438
- **常见错误**:
439
- - `course_id` 无效 → 确认课程已创建
440
- - `media_id` 无效 → 确认素材上传成功
441
- - 类型不匹配 → 检查 `type` 参数
442
- - 参数缺失 → 检查所有必填参数
443
-
444
- ### 6. 数据流转
445
-
446
- **完整的数据流转链路**:
447
-
448
- ```
449
- 1. 上传素材
450
- 输入: 文件路径
451
- 输出: media_id, filename, filesize, object, ext, type, length
452
-
453
- 2. 创建课件
454
- 输入: course_id + 步骤1的所有输出 + title
455
- 输出: lesson_id
456
-
457
- 3. 查询验证
458
- 输入: lesson_id 或 course_id
459
- 输出: 课件详情或课件列表
460
- ```
461
-
462
- ### 7. 跨平台兼容性
463
-
464
- **文件路径**:
465
- - 支持相对路径和绝对路径
466
- - Windows 支持 `/` 和 `\` 两种分隔符
467
- - 建议使用 `/` 保持跨平台兼容
468
-
469
- **JSON 解析**:
470
- - 使用 `--format json` 获取 JSON 输出
471
- - 使用各平台的 JSON 解析工具:
472
- - 命令行: `jq`
473
- - PowerShell: `ConvertFrom-Json`
474
- - Python: `json.loads()`
475
- - Node.js: `JSON.parse()`
476
-
477
- **命令执行**:
478
- - 所有命令在 Windows、macOS、Linux 上通用
479
- - 只需根据平台选择合适的 JSON 解析方法
102
+ 排序完成后输出更新后的课件列表。
480
103
 
481
104
  ## 注意事项
482
105
 
483
- 1. **素材必须先上传**: 创建课件前必须先上传素材到素材库
484
- 2. **类型匹配**: 课件类型必须与素材类型匹配
485
- 3. **时长设置**: 视频和音频必须设置正确的时长,文档类型填 0
486
- 4. **排序规则**: 课件按创建时间自动排序,可通过 `course +lesson-sort` 手动调整顺序
487
- 5. **排序需覆盖全部课件**: 调用 `+lesson-sort` 时建议传入课程下所有课件的 UUID,避免遗漏
488
- 6. **发布要求**: 课程至少需要一个课件才能发布
489
-
490
- ## 权限要求
491
-
492
- - **写操作 (创建)): `course:courseware:write`
493
- - **读操作** (查询): `course:courseware:readonly`
494
-
495
- ## 错误处理
496
-
497
- 常见错误及解决方案:
498
-
499
- | 错误码 | 错误信息 | 解决方案 |
500
- |--------|----------|----------|
501
- | 1001 | 参数错误 | 检查必填参数是否完整 |
502
- | 2001 | 课程不存在 | 确认 course_id 正确 |
503
- | 2002 | 素材不存在 | 确认 media_id 正确,素材已上传 |
504
- | 2003 | 课件类型不匹配 | 确保课件类型与素材类型一致 |
505
- | 3001 | 课件不存在 | 确认 lesson_id 正确 |
506
- | 3002 | 课件数量超限 | 课程课件数量达到上限 |
106
+ 1. **仅支持三种类型**:`video` `audio` `document`,图片等其他类型不支持作为课件
107
+ 2. 所有素材引用参数必须来自 `soke-material` 上传返回,严禁手动编造 uuid / object 等字段
108
+ 3. 课件创建后自动关联到指定课程,无需额外绑定步骤
109
+ 4. 排序使用课件 ID 列表,未在列表中的课件保持在末尾,不丢失
110
+ 5. 课件删除、批量导入等操作不在 CLI 范围,如有需求引导至后台操作
507
111
 
508
- ## 相关文档
112
+ ## 检查清单
509
113
 
510
- - [课程管理 Skill](../soke-course/SKILL.md) - 课程创建和发布
511
- - [素材库管理 Skill](../soke-material/SKILL.md) - 素材上传和管理
512
- - [共享配置 Skill](../soke-shared/SKILL.md) - 认证和配置管理
513
- - [API 接口文档](../../docs/API_PROXY_INTERFACES.md) - 完整的 API 接口说明
114
+ - [ ] 素材已完成上传,持有完整返回对象
115
+ - [ ] courseId 已确认存在
116
+ - [ ] type 仅限 video / audio / document
117
+ - [ ] uuid / filename / type / ext / filesize / object 六个字段均来自上传返回
118
+ - [ ] duration 已按类型默认规则或用户指定设定
119
+ - [ ] 课件创建完成后能通过 lesson-list 验证