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
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';
|
|
}
|
|
}
|
|
|