Commit 91792091 authored by wangzhengwen's avatar wangzhengwen

学习进度优化

parent f3000da9
<?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
...@@ -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 = 10; // 视为完成的最小观看秒数 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 ($item->is_wc_look || $isConsideredFinished) { if ($isWcLook || $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
]; ];
} }
......
...@@ -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([
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment