智慧教务系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

242 lines
8.8 KiB

<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\job\schedule;
use app\model\course_schedule\CourseSchedule;
use core\base\BaseJob;
use think\facade\Db;
use think\facade\Log;
/**
* 队列异步调用定时任务
*/
class HandleCourseSchedule extends BaseJob
{
public function doJob()
{
// 添加执行锁,防止重复执行
$lockFile = runtime_path() . 'course_status_update.lock';
if (file_exists($lockFile) && (time() - filemtime($lockFile)) < 300) { // 5分钟锁定
Log::write('课程状态更新任务正在执行中,跳过');
return ['status' => 'skipped', 'reason' => 'locked'];
}
// 创建锁文件
file_put_contents($lockFile, time());
try {
Log::write('课程状态自动化任务开始' . date('Y-m-d H:i:s'));
$result = $this->handleCourseStatus();
Log::write('课程状态自动化任务完成' . date('Y-m-d H:i:s'));
return $result;
} finally {
// 删除锁文件
if (file_exists($lockFile)) {
unlink($lockFile);
}
}
}
private function handleCourseStatus()
{
try {
Db::startTrans();
$currentDate = date('Y-m-d');
$currentTime = date('H:i:s');
$currentDateTime = date('Y-m-d H:i:s');
// 先处理time_slot,解析并更新start_time和end_time字段
$this->updateTimeFields();
$completedCount = 0;
$upcomingCount = 0;
$ongoingCount = 0;
$pendingCount = 0;
// 1. 更新已完成课程:course_date < 当天的课程
$completedRows = CourseSchedule::where('course_date', '<', $currentDate)
->where('status', '<>', 'completed')
->update([
'status' => 'completed',
'updated_at' => time()
]);
$completedCount = $completedRows;
// 2. 处理今天的课程,需要根据时间段判断状态
$todaySchedules = CourseSchedule::where('course_date', '=', $currentDate)
->whereIn('status', ['pending', 'upcoming', 'ongoing'])
->select();
foreach ($todaySchedules as $schedule) {
$startTime = $schedule['start_time'];
$endTime = $schedule['end_time'];
if (empty($startTime) || empty($endTime)) {
// 如果没有解析出时间,尝试从time_slot解析
$timeData = $this->parseTimeSlot($schedule['time_slot']);
if ($timeData) {
$startTime = $timeData['start_time'];
$endTime = $timeData['end_time'];
// 更新数据库中的时间字段
CourseSchedule::where('id', $schedule['id'])->update([
'start_time' => $startTime,
'end_time' => $endTime
]);
} else {
continue; // 无法解析时间,跳过
}
}
// 判断课程状态
$newStatus = $this->determineStatus($currentTime, $startTime, $endTime);
if ($newStatus !== $schedule['status']) {
CourseSchedule::where('id', $schedule['id'])->update([
'status' => $newStatus,
'updated_at' => time()
]);
switch ($newStatus) {
case 'upcoming':
$upcomingCount++;
break;
case 'ongoing':
$ongoingCount++;
break;
case 'completed':
$completedCount++;
break;
default:
$pendingCount++;
break;
}
}
}
// 3. 重置未来日期的课程为pending状态
$futureRows = CourseSchedule::where('course_date', '>', $currentDate)
->where('status', '<>', 'pending')
->update([
'status' => 'pending',
'updated_at' => time()
]);
$pendingCount += $futureRows;
Log::write("课程状态更新完成 - 已完成: {$completedCount}个, 即将开始: {$upcomingCount}个, 进行中: {$ongoingCount}个, 待安排: {$pendingCount}");
Db::commit();
return [
'status' => 'success',
'completed_count' => $completedCount,
'upcoming_count' => $upcomingCount,
'ongoing_count' => $ongoingCount,
'pending_count' => $pendingCount
];
} catch (\Exception $e) {
Db::rollback();
Log::write('更新课程状态失败:' . $e->getMessage());
return [
'status' => 'failed',
'total_count' => 0,
'updated_count' => 0,
'error' => $e->getMessage()
];
}
}
/**
* 更新课程安排表的start_time和end_time字段
*/
private function updateTimeFields()
{
try {
// 查询所有没有start_time或end_time的记录
$schedules = CourseSchedule::where(function($query) {
$query->whereNull('start_time')
->whereOr('end_time', null)
->whereOr('start_time', '')
->whereOr('end_time', '');
})->select();
foreach ($schedules as $schedule) {
$timeData = $this->parseTimeSlot($schedule['time_slot']);
if ($timeData) {
CourseSchedule::where('id', $schedule['id'])->update([
'start_time' => $timeData['start_time'],
'end_time' => $timeData['end_time']
]);
}
}
} catch (\Exception $e) {
Log::write('更新时间字段失败:' . $e->getMessage());
}
}
/**
* 解析time_slot字符串,提取开始和结束时间
* @param string $timeSlot 格式如 "09:00-10:30"
* @return array|null
*/
private function parseTimeSlot($timeSlot)
{
if (empty($timeSlot)) {
return null;
}
// 支持多种格式:09:00-10:30, 09:00~10:30, 9:00-10:30
if (preg_match('/(\d{1,2}:\d{2})\s*[-~~]\s*(\d{1,2}:\d{2})/', $timeSlot, $matches)) {
return [
'start_time' => $matches[1],
'end_time' => $matches[2]
];
}
return null;
}
/**
* 根据当前时间和课程时间段判断课程状态
* @param string $currentTime 当前时间 H:i:s
* @param string $startTime 开始时间 H:i:s
* @param string $endTime 结束时间 H:i:s
* @return string
*/
private function determineStatus($currentTime, $startTime, $endTime)
{
$currentTimestamp = strtotime($currentTime);
$startTimestamp = strtotime($startTime);
$endTimestamp = strtotime($endTime);
// 如果当前时间在课程时间段内,状态为进行中
if ($currentTimestamp >= $startTimestamp && $currentTimestamp <= $endTimestamp) {
return 'ongoing';
}
// 如果课程已结束,状态为已完成
if ($currentTimestamp > $endTimestamp) {
return 'completed';
}
// 如果距离开始时间不足6小时,状态为即将开始
$timeDiff = $startTimestamp - $currentTimestamp;
if ($timeDiff <= 6 * 3600 && $timeDiff > 0) { // 6小时 = 6 * 60 * 60 秒
return 'upcoming';
}
// 其他情况为待安排
return 'pending';
}
}