Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
projecttwo
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wangtao
projecttwo
Commits
91792091
Commit
91792091
authored
Jul 03, 2025
by
wangzhengwen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
学习进度优化
parent
f3000da9
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
165 additions
and
51 deletions
+165
-51
Test.php
app/api/controller/Test.php
+58
-0
CourseProgressService.php
app/api/service/CourseProgressService.php
+106
-50
CertOrder.php
app/model/CertOrder.php
+1
-1
No files found.
app/api/controller/Test.php
0 → 100644
View file @
91792091
<?php
namespace
app\api\controller
;
use
OSS\OssClient
;
class
Test
{
public
function
concurrentUpload
(
$object
,
$filePath
)
{
$ossClient
=
new
OssClient
(
/* 配置 */
);
$uploadId
=
$ossClient
->
initiateMultipartUpload
(
config
(
'oss.bucket'
),
$object
);
$partSize
=
10
*
1024
*
1024
;
$fileSize
=
filesize
(
$filePath
);
$partNum
=
ceil
(
$fileSize
/
$partSize
);
$parts
=
[];
$promises
=
[];
// 使用Guzzle或其他HTTP客户端并发请求
$client
=
new
\GuzzleHttp\Client
();
for
(
$i
=
1
;
$i
<=
$partNum
;
$i
++
)
{
$from
=
(
$i
-
1
)
*
$partSize
;
$to
=
min
(
$i
*
$partSize
,
$fileSize
);
$promises
[]
=
$client
->
postAsync
(
'您的上传处理接口'
,
[
'multipart'
=>
[
[
'name'
=>
'part_index'
,
'contents'
=>
$i
],
[
'name'
=>
'file'
,
'contents'
=>
fopen
(
$filePath
,
'r'
),
'headers'
=>
[
'Content-Range'
=>
'bytes '
.
$from
.
'-'
.
$to
.
'/'
.
$fileSize
]
]
]
])
->
then
(
function
(
$response
)
use
(
&
$parts
,
$i
)
{
$parts
[
$i
]
=
[
'PartNumber'
=>
$i
,
'ETag'
=>
$response
->
getHeader
(
'ETag'
)[
0
]
];
});
}
// 等待所有分片上传完成
\GuzzleHttp\Promise\Utils
::
all
(
$promises
)
->
wait
();
// 按分片顺序排序
ksort
(
$parts
);
// 完成上传
$ossClient
->
completeMultipartUpload
(
config
(
'oss.bucket'
),
$object
,
$uploadId
,
$parts
);
}
}
\ No newline at end of file
app/api/service/CourseProgressService.php
View file @
91792091
...
@@ -7,12 +7,13 @@ use app\model\CourseProgress;
...
@@ -7,12 +7,13 @@ use app\model\CourseProgress;
use
app\model\CourseUserWork
;
use
app\model\CourseUserWork
;
use
app\model\CourseWork
;
use
app\model\CourseWork
;
use
app\model\Payment
;
use
app\model\Payment
;
use
think\Exception
;
class
CourseProgressService
class
CourseProgressService
{
{
// 常量定义
// 常量定义
const
MIN_CONSIDERED_FINISHED_SECONDS
=
1
0
;
// 视为完成的最小观看秒数
const
MIN_CONSIDERED_FINISHED_SECONDS
=
0
;
// 视为完成的最小观看秒数
const
FINISHED_THRESHOLD_PERCENT
=
95
;
// 视为完成的进度百分比阈值
const
FINISHED_THRESHOLD_PERCENT
=
95
;
// 视为完成的进度百分比阈值
/**
/**
...
@@ -190,12 +191,15 @@ class CourseProgressService
...
@@ -190,12 +191,15 @@ class CourseProgressService
];
];
}
}
// /**
// * 获取用户课程进度统计
/**获取课程学习进度 7.3 老版本 记录5s 单课时
// * @param int $userId 用户ID
* @param $userId
// * @param int $courseId 课程ID
* @param $courseId
// * @return array
* @return array
// */
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
// public static function getCourseProgress($userId, $courseId)
// public static function getCourseProgress($userId, $courseId)
// {
// {
// // 获取所有课时进度
// // 获取所有课时进度
...
@@ -203,98 +207,150 @@ class CourseProgressService
...
@@ -203,98 +207,150 @@ class CourseProgressService
// ->where('user_id', $userId)
// ->where('user_id', $userId)
// ->where('course_id', $courseId)
// ->where('course_id', $courseId)
// ->select();
// ->select();
//
// // 统计计算
// // 统计计算
// $finishedCount = 0;
// $finishedCount = 0;
// $totalDuration = 0;
// $totalDuration = 0; // 课程总时长(所有课时的 tvtime 之和)
// $learnedDuration = 0;
// $learnedDuration = 0; // 有效学习时长(用于计算进度)
// $totalLearnedDuration = 0; // 总观看时长(实际观看时间,不经过优化逻辑)
//
//
// $classProgress = [];
// $classProgress = [];
// foreach ($progressList as $item) {
// foreach ($progressList as $item) {
// if ($item->is_wc_look) {
// // 计算当前课时的观看比例
// $currentProgress = ($item->tvtime > 0) ? ($item->look_tvtime / $item->tvtime) : 0;
//
// // 判断是否满足"视为完成"的条件(观看时间<10s 或 进度>95%)
// $isConsideredFinished = ($item->look_tvtime < self::MIN_CONSIDERED_FINISHED_SECONDS) || ($currentProgress > self::FINISHED_THRESHOLD_PERCENT);
//
// // 计算有效学习时长(用于进度计算)
// if ($item->is_wc_look || $isConsideredFinished) {
// $finishedCount++;
// $finishedCount++;
// $learnedDuration += $item->tvtime; // 视为完成则计入全部时长
// } else {
// $learnedDuration += min($item->look_tvtime, $item->tvtime);
// }
// }
//
// // 总观看时长(直接累加 look_tvtime,不经过优化逻辑)
// $totalLearnedDuration += $item->look_tvtime;
//
// // 课程总时长(所有课时的 tvtime 之和)
// $totalDuration += $item->tvtime;
// $totalDuration += $item->tvtime;
// $learnedDuration += min($item->look_tvtime, $item->tvtime);
//
//
// $classProgress[$item->class_id] = [
// $classProgress[$item->class_id] = [
// 'progress' => $item->look_tvtime,
// 'progress' => $item->look_tvtime,
// 'duration' => $item->tvtime,
// 'duration' => $item->tvtime,
// 'is_finished' => $item->is_wc_look,
// 'is_finished' => $item->is_wc_look
|| $isConsideredFinished
,
// 'last_learn' => $item->createtime,
// 'last_learn' => $item->createtime,
// 'class_id' => $item->class_id
//课时id,
// 'class_id' => $item->class_id
// ];
// ];
// }
// }
// $progress_percent = $totalDuration > 0 ? round($learnedDuration / $totalDuration * 100) : 0;
//
// // 计算总进度(如果满足条件则直接显示100%)
// $progress_percent = ($totalDuration > 0)
// ? round($learnedDuration / $totalDuration * 100)
// : 0;
//
// // 应用优化规则:如果观看进度<10s或已完成>95%,则显示100%
// if ($progress_percent > self::FINISHED_THRESHOLD_PERCENT || $learnedDuration < self::MIN_CONSIDERED_FINISHED_SECONDS) {
// $progress_percent = 100;
// }
//
//
// return [
// return [
// 'finished_count' => $finishedCount,
// 'finished_count' => $finishedCount,
// 'total_classes' => count($progressList),
// 'total_classes' => count($progressList),
// 'progress_percent' => $totalDuration > 0 ? round($learnedDuration / $totalDuration * 100) : 0,
// 'progress_percent' => min($progress_percent, 100), // 确保不超过100%
// 'total_duration' => $totalDuration, // 课程总时长(秒)
// 'total_learned_duration' => $totalLearnedDuration, // 用户实际观看总时长(秒)
// 'classes' => $classProgress
// 'classes' => $classProgress
// ];
// ];
// }
// }
public
static
function
getCourseProgress
(
$userId
,
$courseId
)
public
static
function
getCourseProgress
(
$userId
,
$courseId
)
{
{
// 获取所有课时进度
// 1. 获取课程及所有课时(通过关联查询)
$progressList
=
CourseProgress
::
with
([
'class'
])
$course
=
Course
::
with
([
'getCourseClass'
=>
function
(
$query
)
{
->
where
(
'user_id'
,
$userId
)
$query
->
where
(
'is_del'
,
0
)
->
where
(
'is_sell'
,
1
)
->
order
(
'sort'
,
'asc'
);
}])
->
where
(
'id'
,
$courseId
)
->
find
();
if
(
!
$course
)
{
throw
new
Exception
(
'课程不存在'
);
}
// 2. 获取用户的学习进度记录
$progressList
=
CourseProgress
::
where
(
'user_id'
,
$userId
)
->
where
(
'course_id'
,
$courseId
)
->
where
(
'course_id'
,
$courseId
)
->
select
();
->
select
()
->
toArray
();
// 转换为以class_id为键的数组
$progressMap
=
[];
foreach
(
$progressList
as
$progress
)
{
$progressMap
[
$progress
[
'class_id'
]]
=
$progress
;
}
// 统计计算
//
3.
统计计算
$finishedCount
=
0
;
$finishedCount
=
0
;
$totalDuration
=
0
;
// 课程总时长(所有课时的 tvtime 之和)
$totalDuration
=
0
;
$learnedDuration
=
0
;
// 有效学习时长(用于计算进度)
$learnedDuration
=
0
;
$totalLearnedDuration
=
0
;
// 总观看时长(实际观看时间,不经过优化逻辑)
$totalLearnedDuration
=
0
;
$classProgress
=
[];
$classProgress
=
[];
foreach
(
$progressList
as
$item
)
{
foreach
(
$course
->
getCourseClass
as
$class
)
{
$class
=
$class
->
toArray
();
$progress
=
$progressMap
[
$class
[
'id'
]]
??
null
;
// 使用数组访问方式
$lookTvtime
=
$progress
?
$progress
[
'look_tvtime'
]
:
0
;
$isWcLook
=
$progress
?
(
bool
)
$progress
[
'is_wc_look'
]
:
false
;
$tvtime
=
$class
[
'tvtime'
]
??
0
;
// 计算当前课时的观看比例
// 计算当前课时的观看比例
$currentProgress
=
(
$
item
->
tvtime
>
0
)
?
(
$item
->
look_tvtime
/
$item
->
tvtime
)
:
0
;
$currentProgress
=
(
$
tvtime
>
0
)
?
(
$lookTvtime
/
$
tvtime
)
:
0
;
// 判断是否满足"视为完成"的条件(观看时间<10s 或 进度>95%)
// 判断是否满足"视为完成"的条件
$isConsideredFinished
=
(
$item
->
look_tvtime
<
self
::
MIN_CONSIDERED_FINISHED_SECONDS
)
||
(
$currentProgress
>
self
::
FINISHED_THRESHOLD_PERCENT
);
$isConsideredFinished
=
(
$lookTvtime
<
self
::
MIN_CONSIDERED_FINISHED_SECONDS
)
||
(
$currentProgress
>
self
::
FINISHED_THRESHOLD_PERCENT
);
// 计算有效学习时长
(用于进度计算)
// 计算有效学习时长
if
(
$i
tem
->
is_wc_l
ook
||
$isConsideredFinished
)
{
if
(
$i
sWcL
ook
||
$isConsideredFinished
)
{
$finishedCount
++
;
$finishedCount
++
;
$learnedDuration
+=
$
item
->
tvtime
;
// 视为完成则计入全部时长
$learnedDuration
+=
$
tvtime
;
}
else
{
}
else
{
$learnedDuration
+=
min
(
$
item
->
look_tvtime
,
$item
->
tvtime
);
$learnedDuration
+=
min
(
$
lookTvtime
,
$
tvtime
);
}
}
// 总观看时长(直接累加 look_tvtime,不经过优化逻辑)
$totalLearnedDuration
+=
$lookTvtime
;
$totalLearnedDuration
+=
$item
->
look_tvtime
;
$totalDuration
+=
$tvtime
;
// 课程总时长(所有课时的 tvtime 之和)
$classProgress
[
$class
[
'id'
]]
=
[
$totalDuration
+=
$item
->
tvtime
;
'progress'
=>
$lookTvtime
,
'duration'
=>
$tvtime
,
$classProgress
[
$item
->
class_id
]
=
[
'is_finished'
=>
$isWcLook
||
$isConsideredFinished
,
'progress'
=>
$item
->
look_tvtime
,
'last_learn'
=>
$progress
?
$progress
[
'createtime'
]
:
null
,
'duration'
=>
$item
->
tvtime
,
'class_id'
=>
$class
[
'id'
],
'is_finished'
=>
$item
->
is_wc_look
||
$isConsideredFinished
,
'title'
=>
$class
[
'title'
],
'last_learn'
=>
$item
->
createtime
,
'type'
=>
$class
[
'type'
]
'class_id'
=>
$item
->
class_id
];
];
}
}
// 计算总进度
(如果满足条件则直接显示100%)
// 计算总进度
$progress_percent
=
(
$totalDuration
>
0
)
$progress_percent
=
(
$totalDuration
>
0
)
?
round
(
$learnedDuration
/
$totalDuration
*
100
)
?
round
(
$learnedDuration
/
$totalDuration
*
100
)
:
0
;
:
0
;
// 应用优化规则:如果观看进度<10s或已完成>95%,则显示100%
if
(
$progress_percent
>
self
::
FINISHED_THRESHOLD_PERCENT
if
(
$progress_percent
>
self
::
FINISHED_THRESHOLD_PERCENT
||
$learnedDuration
<
self
::
MIN_CONSIDERED_FINISHED_SECONDS
)
{
||
$learnedDuration
<
self
::
MIN_CONSIDERED_FINISHED_SECONDS
)
{
$progress_percent
=
100
;
$progress_percent
=
100
;
}
}
return
[
return
[
'course_id'
=>
$course
->
id
,
'course_title'
=>
$course
->
title
,
'finished_count'
=>
$finishedCount
,
'finished_count'
=>
$finishedCount
,
'total_classes'
=>
count
(
$
progressList
),
'total_classes'
=>
count
(
$
course
->
getCourseClass
),
'progress_percent'
=>
min
(
$progress_percent
,
100
),
// 确保不超过100%
'progress_percent'
=>
min
(
$progress_percent
,
100
),
'total_duration'
=>
$totalDuration
,
// 课程总时长(秒)
'total_duration'
=>
$totalDuration
,
'total_learned_duration'
=>
$totalLearnedDuration
,
// 用户实际观看总时长(秒)
'total_learned_duration'
=>
$totalLearnedDuration
,
'classes'
=>
$classProgress
'classes'
=>
$classProgress
];
];
}
}
...
...
app/model/CertOrder.php
View file @
91792091
...
@@ -73,7 +73,7 @@ class CertOrder extends Model
...
@@ -73,7 +73,7 @@ class CertOrder extends Model
$list
=
self
::
where
(
$where
)
$list
=
self
::
where
(
$where
)
->
with
([
'user'
=>
[
'headico'
],
'cert'
])
->
with
([
'user'
=>
[
'headico'
],
'cert'
])
->
order
(
'createtime'
,
'desc'
)
->
order
(
'createtime'
,
'desc'
)
->
field
(
'id,cert_id,user_id,name,updatetime'
)
->
field
(
'id,cert_id,user_id,name,updatetime
,createtime
'
)
->
append
([
'updatetime_text'
])
->
append
([
'updatetime_text'
])
->
paginate
([
->
paginate
([
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment