Toggle navigation
Toggle navigation
此项目
正在载入...
Sign in
张艾宸
/
webScreen
转到一个项目
Toggle navigation
项目
群组
代码片段
帮助
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
huz1xuan
2026-08-12 16:12:52 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
720eb6b54ade3c5cf90e14ac1c6a98b444447635
720eb6b5
1 parent
01c1cc83
完善高光时刻V2接口
隐藏空白字符变更
内嵌
并排对比
正在显示
8 个修改的文件
包含
110 行增加
和
21 行删除
docs/FILE_EXISTS_V2.md
docs/HIGHLIGHT_DEPLOYMENT_USAGE_TEST.md
docs/RECORDING_API.md
release-packages/webScreen-recording-v2-20260812.tar.gz
routes/index.js
services/recordingTaskService.js
test/recordingRoutes.test.js
test/recordingTaskService.test.js
docs/FILE_EXISTS_V2.md
查看文件 @
720eb6b
...
...
@@ -4,7 +4,7 @@
查询指定课堂的录制文件是否已经生成,并在文件全部可用时返回访问地址。
接口
自动识别整课录制和仅高光录制,调用方不需要指定录制类型
。查询操作不会创建、重新执行或修改录制任务。
接口
通过
`onlyHighlight`
区分整课录制和仅高光录制
。查询操作不会创建、重新执行或修改录制任务。
| 项目 | 内容 |
|---|---|
...
...
@@ -23,6 +23,7 @@
|
`siteId`
| String | 是 | 站点 ID;仅支持字母、数字、下划线和短横线,最长 128 个字符 |
|
`classId`
| String | 是 | 课堂 ID;仅支持字母、数字、下划线和短横线,最长 128 个字符 |
|
`classStartTime` | String | 否 | 兼容历史整课文件查询时使用,格式为 `yyyyMMdd`
;V2 录制任务通常不需要传入 |
|
`onlyHighlight` | Integer | 否 | `0` 或不传表示整课录制,`1`
表示仅高光录制 |
### 请求示例
...
...
@@ -47,6 +48,18 @@ Content-Type: application/json; charset=utf-8
}
```
高光文件查询示例:
```
json
{
"siteId"
:
"doctest"
,
"classId"
:
"487012832"
,
"onlyHighlight"
:
1
}
```
高光查询会调用 SaaS
`getByClassPrivate.do`
获取该课堂的高光列表,再检查对应 MP4 是否已生成,不依赖 WebScreen 本地任务快照。
## 3. 响应参数
| 参数 | 类型 | 必定返回 | 说明 |
...
...
@@ -185,3 +198,4 @@ HTTP 200 && code == 0 && fileExists == true
| 版本 | 日期 | 说明 |
|---|---|---|
| V2 | 2026-08-10 | 支持统一查询整课录制和仅高光录制结果 |
| V2 | 2026-08-12 | 高光查询改为以
`onlyHighlight=1` 和 `getByClassPrivate.do`
为准,不再依赖本地任务快照 |
...
...
docs/HIGHLIGHT_DEPLOYMENT_USAGE_TEST.md
查看文件 @
720eb6b
...
...
@@ -32,7 +32,7 @@
## 3. 查询
-
`/fileExistsV2`
不触发录制
或 SaaS 查询
。
-
`/fileExistsV2` 不触发录制
;整课查询不访问 SaaS,高光查询通过 `getByClassPrivate.do`
获取高光列表
。
-
整课任务兼容返回
`classUrl`
。
-
高光任务返回全部高光 URL。
-
任一目标文件缺失时
`fileExists=false`
。
...
...
docs/RECORDING_API.md
查看文件 @
720eb6b
...
...
@@ -198,7 +198,8 @@ Content-Type: application/json
}
```
V2 根据
`/recordingTaskV2` 保存的任务快照判断文件类型,客户不需要再次传 `onlyHighlight`
。
V2 根据
`onlyHighlight` 判断文件类型:`0` 或不传查询整课文件,`1`
通过 SaaS
`getByClassPrivate.do`
获取高光列表并检查对应文件。本地任务快照不作为高光文件查询的前置条件。
整课成功响应:
...
...
release-packages/webScreen-recording-v2-20260812.tar.gz
0 → 100644
查看文件 @
720eb6b
不能预览此文件类型
routes/index.js
查看文件 @
720eb6b
...
...
@@ -379,10 +379,12 @@ router.post('/fileExistsV2', async (req, res) => {
const
result
=
await
recordingTaskService
.
getFileResult
(
req
.
body
||
{});
return
res
.
send
(
result
);
}
catch
(
err
)
{
if
(
err
instanceof
RecordingTaskValidationError
)
{
if
(
err
instanceof
RecordingTaskValidationError
||
err
instanceof
HighlightValidationError
)
{
return
res
.
status
(
400
).
send
({
code
:
err
.
apiCode
,
message
:
err
.
message
,
code
:
err
instanceof
RecordingTaskValidationError
?
err
.
apiCode
:
2
,
message
:
err
instanceof
RecordingTaskValidationError
?
err
.
message
:
'siteId或classId不正确'
,
fileExists
:
false
,
files
:
[]
});
...
...
services/recordingTaskService.js
查看文件 @
720eb6b
...
...
@@ -623,20 +623,36 @@ class RecordingTaskService {
const
key
=
buildManifestKey
({
siteId
,
classId
});
const
manifest
=
this
.
loadManifest
(
key
);
const
requestedPeriod
=
normalizeTaskPeriod
(
input
,
null
,
false
);
const
onlyHighlight
=
normalizeOnlyHighlight
(
input
.
onlyHighlight
);
if
(
onlyHighlight
===
1
)
{
const
statuses
=
await
this
.
highlightService
.
getClassFileStatuses
({
siteId
,
classId
});
const
fileExists
=
statuses
.
length
>
0
&&
statuses
.
every
(
item
=>
item
.
generated
&&
item
.
url
);
return
{
code
:
fileExists
?
0
:
1
,
message
:
fileExists
?
'文件已生成'
:
'文件未生成'
,
fileExists
,
onlyHighlight
:
1
,
files
:
fileExists
?
statuses
.
map
(
item
=>
({
type
:
'highlight'
,
highlightId
:
item
.
highlightId
,
url
:
item
.
url
}))
:
[]
};
}
if
(
manifest
&&
requestedPeriod
&&
requestedPeriod
.
beginTime
!=
null
&&
manifest
.
beginTime
!=
null
&&
(
requestedPeriod
.
beginTime
!==
manifest
.
beginTime
||
requestedPeriod
.
endTime
!==
manifest
.
endTime
))
{
return
{
code
:
1
,
message
:
'文件未生成'
,
fileExists
:
false
,
files
:
[]
};
return
{
code
:
1
,
message
:
'文件未生成'
,
fileExists
:
false
,
onlyHighlight
:
0
,
files
:
[]
};
}
let
expected
=
[];
let
onlyHighlight
;
if
(
manifest
)
{
onlyHighlight
=
manifest
.
onlyHighlight
;
expected
=
onlyHighlight
===
1
?
(
manifest
.
highlights
||
[])
:
[
this
.
buildFullFile
(
manifest
)];
expected
=
[
this
.
buildFullFile
(
manifest
)];
}
else
{
onlyHighlight
=
normalizeOnlyHighlight
(
input
.
onlyHighlight
);
if
(
onlyHighlight
===
1
||
!
requestedPeriod
)
{
return
{
code
:
1
,
message
:
'文件未生成'
,
fileExists
:
false
,
files
:
[]
};
if
(
!
requestedPeriod
)
{
return
{
code
:
1
,
message
:
'文件未生成'
,
fileExists
:
false
,
onlyHighlight
:
0
,
files
:
[]
};
}
expected
=
[
this
.
buildFullFile
({
siteId
,
classId
,
classDate
:
requestedPeriod
.
classDate
})];
}
...
...
test/recordingRoutes.test.js
查看文件 @
720eb6b
const
assert
=
require
(
'assert'
);
const
http
=
require
(
'http'
);
const
{
recordingTaskService
}
=
require
(
'../services/recordingTaskService'
);
const
{
HighlightValidationError
,
recordingTaskService
}
=
require
(
'../services/recordingTaskService'
);
const
app
=
require
(
'../app'
);
function
request
(
server
,
route
,
body
)
{
...
...
@@ -119,13 +122,27 @@ async function run() {
assert
.
strictEqual
(
fileCalls
,
0
,
'旧查询接口不得进入 V2 服务'
);
const
newFile
=
await
request
(
server
,
'/fileExistsV2'
,
{
siteId
:
'doctest'
,
classId
:
'1001'
siteId
:
'doctest'
,
classId
:
'1001'
,
onlyHighlight
:
1
});
assert
.
strictEqual
(
newFile
.
status
,
200
);
assert
.
strictEqual
(
newFile
.
body
.
fileExists
,
true
);
assert
.
strictEqual
(
newFile
.
body
.
onlyHighlight
,
1
);
assert
.
strictEqual
(
newFile
.
body
.
files
.
length
,
1
);
assert
.
strictEqual
(
fileCalls
,
1
);
recordingTaskService
.
getFileResult
=
async
()
=>
{
throw
new
HighlightValidationError
(
'高光录制功能未启用'
);
};
const
disabledHighlight
=
await
request
(
server
,
'/fileExistsV2'
,
{
siteId
:
'kiwikids'
,
classId
:
'258178512'
,
onlyHighlight
:
1
});
assert
.
strictEqual
(
disabledHighlight
.
status
,
400
);
assert
.
deepStrictEqual
(
disabledHighlight
.
body
,
{
code
:
2
,
message
:
'siteId或classId不正确'
,
fileExists
:
false
,
files
:
[]
});
}
finally
{
recordingTaskService
.
acceptTasks
=
originalAcceptTasks
;
recordingTaskService
.
getFileResult
=
originalGetFileResult
;
...
...
test/recordingTaskService.test.js
查看文件 @
720eb6b
...
...
@@ -99,8 +99,15 @@ async function run() {
siteId
:
'doctest'
,
beginTime
:
1785899498000
,
endTime
:
1785899543000
},
{
id
:
8
,
meetingNumber
:
'1007'
,
siteId
:
'doctest'
,
beginTime
:
1785895298000
,
endTime
:
1785895343000
}];
const
calls
=
{
fetch
:
0
,
enqueue
:
0
,
wait
:
0
};
const
calls
=
{
fetch
:
0
,
enqueue
:
0
,
wait
:
0
,
fileStatus
:
0
};
const
objects
=
new
Set
();
const
highlightService
=
{
fetchByClass
:
async
classId
=>
{
calls
.
fetch
+=
1
;
...
...
@@ -113,9 +120,24 @@ async function run() {
waitForTaskKeys
:
async
keys
=>
{
calls
.
wait
+=
1
;
return
keys
.
map
(
key
=>
({
key
,
status
:
'uploading'
}));
},
getClassFileStatuses
:
async
({
siteId
,
classId
})
=>
{
calls
.
fileStatus
+=
1
;
return
records
.
filter
(
item
=>
item
.
siteId
===
siteId
&&
item
.
meetingNumber
===
classId
)
.
map
(
item
=>
{
const
ossKey
=
`
oss
/
$
{
siteId
}
/20260805/
$
{
classId
}
_highlight_$
{
item
.
id
}.
mp4
`
;
const
generated
=
objects
.
has
(
ossKey
);
return
{
highlightId
:
item
.
id
,
classId
,
siteId
,
generated
,
status
:
generated
?
'generated'
:
'not_generated'
,
url
:
generated
?
`
https
:
//xdymp4.xuedianyun.com/${ossKey}` : null
};
});
}
};
const
objects
=
new
Set
();
const
statusUpdates
=
[];
const
service
=
new
RecordingTaskService
({
configPath
,
...
...
@@ -182,14 +204,29 @@ async function run() {
assert
.
deepStrictEqual
(
duplicate
,
{
accepted
:
0
,
duplicates
:
1
,
noMedia
:
0
});
assert
.
strictEqual
(
calls
.
fetch
,
1
);
let
highFiles
=
await
service
.
getFileResult
({
siteId
:
'doctest'
,
classId
:
'1002'
});
let
highFiles
=
await
service
.
getFileResult
({
siteId
:
'doctest'
,
classId
:
'1002'
,
onlyHighlight
:
1
});
assert
.
strictEqual
(
highFiles
.
fileExists
,
false
);
objects
.
add
(
'oss/doctest/20260805/1002_highlight_5.mp4'
);
objects
.
add
(
'oss/doctest/20260805/1002_highlight_6.mp4'
);
highFiles
=
await
service
.
getFileResult
({
siteId
:
'doctest'
,
classId
:
'1002'
});
objects
.
add
(
'oss/doctest/20260805/1002_highlight_7.mp4'
);
highFiles
=
await
service
.
getFileResult
({
siteId
:
'doctest'
,
classId
:
'1002'
,
onlyHighlight
:
1
});
assert
.
strictEqual
(
highFiles
.
fileExists
,
true
);
assert
.
strictEqual
(
highFiles
.
onlyHighlight
,
1
);
assert
.
deepStrictEqual
(
highFiles
.
files
.
map
(
item
=>
item
.
highlightId
),
[
5
,
6
]);
assert
.
deepStrictEqual
(
highFiles
.
files
.
map
(
item
=>
item
.
highlightId
),
[
5
,
6
,
7
]);
objects
.
add
(
'oss/doctest/20260805/1007_highlight_8.mp4'
);
const
manifestlessHighlight
=
await
service
.
getFileResult
({
siteId
:
'doctest'
,
classId
:
'1007'
,
classStartTime
:
'20260805'
,
onlyHighlight
:
1
});
assert
.
strictEqual
(
manifestlessHighlight
.
fileExists
,
true
,
'高光查询不得依赖本地 manifest'
);
assert
.
strictEqual
(
manifestlessHighlight
.
onlyHighlight
,
1
);
assert
.
deepStrictEqual
(
manifestlessHighlight
.
files
.
map
(
item
=>
item
.
highlightId
),
[
8
]);
assert
.
strictEqual
(
calls
.
fileStatus
,
3
,
'每次高光文件查询都必须读取 getByClass 文件状态'
);
const
noMedia
=
await
service
.
acceptTasks
({
list
:
[{
id
:
'task-high-empty'
,
...
...
@@ -200,7 +237,9 @@ async function run() {
}]
});
assert
.
deepStrictEqual
(
noMedia
,
{
accepted
:
1
,
duplicates
:
0
,
noMedia
:
1
});
assert
.
deepStrictEqual
(
statusUpdates
,
[
'1001:2'
,
'1002:2'
,
'1003:3'
]);
const
emptyResult
=
await
service
.
getFileResult
({
siteId
:
'doctest'
,
classId
:
'1003'
});
const
emptyResult
=
await
service
.
getFileResult
({
siteId
:
'doctest'
,
classId
:
'1003'
,
onlyHighlight
:
1
});
assert
.
strictEqual
(
emptyResult
.
fileExists
,
false
);
const
mappedNoMedia
=
await
service
.
acceptTasks
({
list
:
[{
...
...
请
注册
或
登录
后发表评论