Browse Source

临时提交

master
王泽彦 8 months ago
parent
commit
96e5edd4a7
  1. 107
      niucloud/app/job/transfer/schedule/CourseScheduleJob.php
  2. 673
      niucloud/app/service/api/apiService/CourseScheduleService.php

107
niucloud/app/job/transfer/schedule/CourseScheduleJob.php

@ -9,80 +9,46 @@ use think\facade\Log;
class CourseScheduleJob extends BaseJob class CourseScheduleJob extends BaseJob
{ {
/** /**
* 执行任务,将今天的自动排课复制到未来30天 * 执行任务,将最新的自动排课复制到未来7天
* @param mixed ...$data 任务参数(BaseJob要求)
* @return array 处理结果 * @return array 处理结果
*/ */
public function doJob() public function doJob(...$data)
{ {
// 添加执行锁,防止重复执行 Log::write('开始执行自动排课任务');
$lockFile = runtime_path() . 'course_schedule.lock';
if (file_exists($lockFile) && (time() - filemtime($lockFile)) < 600) { // 10分钟锁定
Log::write('自动排课任务正在执行中,跳过');
return ['status' => 'skipped', 'reason' => 'locked'];
}
// 创建锁文件
file_put_contents($lockFile, time());
try { try {
Log::write('开始执行自动排课任务'); // 直接执行排课任务,生成未来7天的可预约时间段
$result = $this->copyCoursesToFutureDays(7);
// 检查今天是否已经执行过
$today = date('Y-m-d');
$executionFlag = runtime_path() . 'course_schedule_' . $today . '.flag';
if (file_exists($executionFlag)) {
Log::write('今天已经执行过自动排课,跳过');
return ['status' => 'skipped', 'reason' => 'already_executed_today'];
}
// 执行排课任务
$result = $this->copyCoursesToFutureDays(30);
// 创建执行标记文件 Log::write('自动排课任务执行完成,插入:' . $result['inserted'] . ',跳过:' . $result['skipped']);
file_put_contents($executionFlag, time());
Log::write('自动排课任务执行完成');
return $result; return $result;
} finally {
// 删除锁文件 } catch (\Exception $e) {
if (file_exists($lockFile)) { Log::write('自动排课任务执行失败:' . $e->getMessage());
unlink($lockFile); throw $e;
}
} }
} }
/** /**
* 将今天的自动排课复制到未来指定天数 * 将今天的自动排课复制到未来指定天数,生成可预约的课程时间段
* @param int $days 未来天数 * @param int $days 未来天数,默认7天
* @return array 处理结果 * @return array 处理结果
*/ */
public function copyCoursesToFutureDays($days = 30) public function copyCoursesToFutureDays($days = 7)
{ {
// 获取基准日期 - 使用最近一个工作日的自动排课作为模板 $today = date('Y-m-d');
$baseDate = $this->getLatestAutoScheduleDate();
if (empty($baseDate)) {
Log::write('未找到自动排课模板数据');
return [
'status' => 'failed',
'reason' => 'no_template_data',
'total' => 0,
'inserted' => 0,
'skipped' => 0
];
}
// 获取基准日期的所有auto_schedule=1的课程 // 获取今天所有auto_schedule=1的课程
$autoSchedules = CourseSchedule::where('auto_schedule', 1) $autoSchedules = CourseSchedule::where('auto_schedule', 1)
->where('course_date', $baseDate) ->where('course_date', $today)
->select(); ->select();
Log::write('找到' . count($autoSchedules) . '个基于日期 ' . $baseDate . ' 的自动排课模板'); Log::write('找到' . count($autoSchedules) . '个今日自动排课模板');
$results = [ $results = [
'status' => 'success', 'status' => 'success',
'base_date' => $baseDate, 'base_date' => $today,
'total' => count($autoSchedules), 'total' => count($autoSchedules),
'inserted' => 0, 'inserted' => 0,
'skipped' => 0, 'skipped' => 0,
@ -90,11 +56,11 @@ class CourseScheduleJob extends BaseJob
]; ];
if (count($autoSchedules) == 0) { if (count($autoSchedules) == 0) {
Log::write('没有找到自动排课模板,跳过执行'); Log::write('今日没有找到自动排课,跳过执行');
return $results; return $results;
} }
// 遍历每个课程,复制到未来30天 // 遍历每个自动排课计划,为未来指定天数生成可预约时间段
foreach ($autoSchedules as $schedule) { foreach ($autoSchedules as $schedule) {
$courseResults = $this->copyCourseToFutureDays($schedule, $days); $courseResults = $this->copyCourseToFutureDays($schedule, $days);
$results['inserted'] += $courseResults['inserted']; $results['inserted'] += $courseResults['inserted'];
@ -254,35 +220,4 @@ class CourseScheduleJob extends BaseJob
return $newSchedule; return $newSchedule;
} }
/**
* 获取最近一个有自动排课数据的日期
* @return string|null 最近的自动排课日期
*/
protected function getLatestAutoScheduleDate()
{
// 查找最近7天内有自动排课的日期
$latestDate = CourseSchedule::where('auto_schedule', 1)
->where('course_date', '>=', date('Y-m-d', strtotime('-7 days')))
->where('course_date', '<=', date('Y-m-d'))
->order('course_date', 'desc')
->value('course_date');
if ($latestDate) {
Log::write('使用日期 ' . $latestDate . ' 作为自动排课模板');
return $latestDate;
}
// 如果最近7天没有,则查找历史数据中最新的
$latestDate = CourseSchedule::where('auto_schedule', 1)
->order('course_date', 'desc')
->value('course_date');
if ($latestDate) {
Log::write('使用历史日期 ' . $latestDate . ' 作为自动排课模板');
return $latestDate;
}
return null;
}
} }

673
niucloud/app/service/api/apiService/CourseScheduleService.php

@ -915,6 +915,255 @@ class CourseScheduleService extends BaseApiService
} }
} }
/**
* 更新课程安排
* @param array $data 更新数据
* @return array 更新结果
*/
public function updateSchedule(array $data)
{
try {
// 验证必填字段
if (empty($data['schedule_id'])) {
return [
'code' => 0,
'msg' => '课程安排ID不能为空'
];
}
// 开启事务
Db::startTrans();
$scheduleId = $data['schedule_id'];
// 查询当前课程安排信息
$currentSchedule = Db::name('course_schedule')
->where('id', $scheduleId)
->where('deleted_at', 0)
->find();
if (empty($currentSchedule)) {
Db::rollback();
return [
'code' => 0,
'msg' => '课程安排不存在或已被删除'
];
}
// 准备更新数据
$updateData = [
'updated_at' => date('Y-m-d H:i:s')
];
// 如果修改了场地或时间,需要检查冲突
$needCheckConflict = false;
if (isset($data['venue_id']) && $data['venue_id'] != $currentSchedule['venue_id']) {
$updateData['venue_id'] = $data['venue_id'];
$needCheckConflict = true;
}
if (isset($data['course_date']) && $data['course_date'] != $currentSchedule['course_date']) {
$updateData['course_date'] = $data['course_date'];
$needCheckConflict = true;
}
if (isset($data['time_slot']) && $data['time_slot'] != $currentSchedule['time_slot']) {
$updateData['time_slot'] = $data['time_slot'];
$needCheckConflict = true;
}
// 如果修改了教练,需要检查教练时间冲突
$needCheckCoachConflict = false;
if (isset($data['coach_id']) && $data['coach_id'] != $currentSchedule['coach_id']) {
$updateData['coach_id'] = $data['coach_id'];
$needCheckCoachConflict = true;
}
// 检查场地时间冲突
if ($needCheckConflict) {
$venueId = $updateData['venue_id'] ?? $currentSchedule['venue_id'];
$courseDate = $updateData['course_date'] ?? $currentSchedule['course_date'];
$timeSlot = $updateData['time_slot'] ?? $currentSchedule['time_slot'];
$conflictCheck = $this->checkVenueConflictForUpdate($venueId, $courseDate, $timeSlot, $scheduleId);
if (!$conflictCheck['code']) {
Db::rollback();
return $conflictCheck;
}
}
// 检查教练时间冲突
if ($needCheckCoachConflict || $needCheckConflict) {
$coachId = $updateData['coach_id'] ?? $currentSchedule['coach_id'];
$courseDate = $updateData['course_date'] ?? $currentSchedule['course_date'];
$timeSlot = $updateData['time_slot'] ?? $currentSchedule['time_slot'];
$coachConflictCheck = $this->checkCoachConflictForUpdate($coachId, $courseDate, $timeSlot, $scheduleId);
if (!$coachConflictCheck['code']) {
Db::rollback();
return $coachConflictCheck;
}
}
// 其他可更新字段
if (isset($data['available_capacity'])) {
$updateData['available_capacity'] = $data['available_capacity'];
}
if (isset($data['campus_id'])) {
$updateData['campus_id'] = $data['campus_id'];
}
if (isset($data['course_id'])) {
$updateData['course_id'] = $data['course_id'];
}
if (isset($data['education_id'])) {
$updateData['education_id'] = $data['education_id'];
}
if (isset($data['remarks'])) {
$updateData['remarks'] = $data['remarks'];
}
if (isset($data['status'])) {
$updateData['status'] = $data['status'];
}
// 如果没有实际的更新数据,返回成功
if (count($updateData) <= 1) { // 只有 updated_at
Db::commit();
return [
'code' => 1,
'msg' => '更新成功',
'data' => ['schedule_id' => $scheduleId]
];
}
// 执行更新
$result = Db::name('course_schedule')
->where('id', $scheduleId)
->update($updateData);
if ($result === false) {
Db::rollback();
return [
'code' => 0,
'msg' => '更新失败'
];
}
// 记录变更历史(可选)
$this->recordScheduleChange($scheduleId, $currentSchedule, $updateData);
// 提交事务
Db::commit();
return [
'code' => 1,
'msg' => '更新成功',
'data' => ['schedule_id' => $scheduleId]
];
} catch (\Exception $e) {
Db::rollback();
return [
'code' => 0,
'msg' => '更新课程安排失败:' . $e->getMessage()
];
}
}
/**
* 检查场地时间冲突(更新时排除自身)
* @param int $venueId 场地ID
* @param string $date 日期
* @param string $timeSlot 时间段
* @param int $excludeScheduleId 排除的课程安排ID
* @return array
*/
private function checkVenueConflictForUpdate($venueId, $date, $timeSlot, $excludeScheduleId)
{
$conflict = Db::name('course_schedule')
->where('venue_id', $venueId)
->where('course_date', $date)
->where('time_slot', $timeSlot)
->where('id', '<>', $excludeScheduleId)
->where('deleted_at', 0)
->find();
if ($conflict) {
return [
'code' => 0,
'msg' => '该场地在该时间段已有其他课程安排'
];
}
return ['code' => 1];
}
/**
* 检查教练时间冲突(更新时排除自身)
* @param int $coachId 教练ID
* @param string $date 日期
* @param string $timeSlot 时间段
* @param int $excludeScheduleId 排除的课程安排ID
* @return array
*/
private function checkCoachConflictForUpdate($coachId, $date, $timeSlot, $excludeScheduleId)
{
$conflict = Db::name('course_schedule')
->where('coach_id', $coachId)
->where('course_date', $date)
->where('time_slot', $timeSlot)
->where('id', '<>', $excludeScheduleId)
->where('deleted_at', 0)
->find();
if ($conflict) {
return [
'code' => 0,
'msg' => '该教练在该时间段已有其他课程安排'
];
}
return ['code' => 1];
}
/**
* 记录课程安排变更历史
* @param int $scheduleId 课程安排ID
* @param array $oldData 原始数据
* @param array $newData 新数据
* @return void
*/
private function recordScheduleChange($scheduleId, $oldData, $newData)
{
try {
$changes = [];
// 比较字段变化
foreach ($newData as $field => $newValue) {
if ($field == 'updated_at') continue;
$oldValue = $oldData[$field] ?? null;
if ($oldValue != $newValue) {
$changes[] = [
'field' => $field,
'old_value' => $oldValue,
'new_value' => $newValue
];
}
}
if (!empty($changes)) {
Db::name('course_schedule_changes')->insert([
'schedule_id' => $scheduleId,
'changes' => json_encode($changes),
'changed_by' => $this->user_id ?? 0,
'changed_at' => date('Y-m-d H:i:s'),
'created_at' => date('Y-m-d H:i:s')
]);
}
} catch (\Exception $e) {
// 记录日志,但不影响主流程
trace($e->getMessage());
}
}
/** /**
* 根据场地生成可用时间选项 * 根据场地生成可用时间选项
* @param array $venue 场地信息 * @param array $venue 场地信息
@ -982,4 +1231,428 @@ class CourseScheduleService extends BaseApiService
return $timeOptions; return $timeOptions;
} }
/**
* 批量创建课程安排
* @param array $data 批量创建数据
* @return array 创建结果
*/
public function batchCreateSchedule(array $data)
{
try {
// 验证必填字段
if (empty($data['schedules']) || !is_array($data['schedules'])) {
return [
'code' => 0,
'msg' => '课程安排数据不能为空'
];
}
// 开启事务
Db::startTrans();
$successCount = 0;
$failedCount = 0;
$errors = [];
foreach ($data['schedules'] as $index => $schedule) {
$result = $this->createCourseSchedule($schedule);
if ($result['code']) {
$successCount++;
} else {
$failedCount++;
$errors[] = "第" . ($index + 1) . "条: " . $result['msg'];
}
}
if ($failedCount > 0) {
Db::rollback();
return [
'code' => 0,
'msg' => "批量创建失败,成功:{$successCount}条,失败:{$failedCount}条",
'data' => [
'success_count' => $successCount,
'failed_count' => $failedCount,
'errors' => $errors
]
];
}
// 提交事务
Db::commit();
return [
'code' => 1,
'msg' => "批量创建成功,共创建{$successCount}条课程安排",
'data' => [
'success_count' => $successCount,
'failed_count' => $failedCount
]
];
} catch (\Exception $e) {
Db::rollback();
return [
'code' => 0,
'msg' => '批量创建课程安排失败:' . $e->getMessage()
];
}
}
/**
* 删除课程安排
* @param int $scheduleId 课程安排ID
* @return array 删除结果
*/
public function deleteSchedule($scheduleId)
{
try {
if (empty($scheduleId)) {
return [
'code' => 0,
'msg' => '课程安排ID不能为空'
];
}
// 开启事务
Db::startTrans();
// 查询课程安排是否存在
$schedule = Db::name('course_schedule')
->where('id', $scheduleId)
->where('deleted_at', 0)
->find();
if (empty($schedule)) {
Db::rollback();
return [
'code' => 0,
'msg' => '课程安排不存在或已被删除'
];
}
// 检查是否有学员已报名
$hasStudents = Db::name('person_course_schedule')
->where('schedule_id', $scheduleId)
->where('deleted_at', 0)
->count();
if ($hasStudents > 0) {
Db::rollback();
return [
'code' => 0,
'msg' => '该课程安排已有学员报名,无法删除'
];
}
// 软删除课程安排
$result = Db::name('course_schedule')
->where('id', $scheduleId)
->update([
'deleted_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
if ($result === false) {
Db::rollback();
return [
'code' => 0,
'msg' => '删除失败'
];
}
// 提交事务
Db::commit();
return [
'code' => 1,
'msg' => '删除成功'
];
} catch (\Exception $e) {
Db::rollback();
return [
'code' => 0,
'msg' => '删除课程安排失败:' . $e->getMessage()
];
}
}
/**
* 获取课程安排统计
* @param array $data 统计参数
* @return array 统计结果
*/
public function getScheduleStatistics($data = [])
{
try {
$where = $this->buildScheduleWhere($data);
// 总课程安排数
$totalSchedules = Db::name('course_schedule')
->alias('cs')
->where($where)
->where('cs.deleted_at', 0)
->count();
// 按状态统计
$statusStats = Db::name('course_schedule')
->alias('cs')
->where($where)
->where('cs.deleted_at', 0)
->field('status, COUNT(*) as count')
->group('status')
->select()
->toArray();
// 按教练统计
$coachStats = Db::name('course_schedule')
->alias('cs')
->leftJoin('school_personnel p', 'cs.coach_id = p.id')
->where($where)
->where('cs.deleted_at', 0)
->field('cs.coach_id, p.name as coach_name, COUNT(*) as count')
->group('cs.coach_id')
->order('count DESC')
->limit(10)
->select()
->toArray();
// 按场地统计
$venueStats = Db::name('course_schedule')
->alias('cs')
->leftJoin('school_venue v', 'cs.venue_id = v.id')
->where($where)
->where('cs.deleted_at', 0)
->field('cs.venue_id, v.venue_name, COUNT(*) as count')
->group('cs.venue_id')
->order('count DESC')
->limit(10)
->select()
->toArray();
// 按日期统计(最近7天)
$dateStats = [];
for ($i = 6; $i >= 0; $i--) {
$date = date('Y-m-d', strtotime("-{$i} days"));
$count = Db::name('course_schedule')
->alias('cs')
->where($where)
->where('cs.course_date', $date)
->where('cs.deleted_at', 0)
->count();
$dateStats[] = [
'date' => $date,
'count' => $count
];
}
return [
'total_schedules' => $totalSchedules,
'status_stats' => $statusStats,
'coach_stats' => $coachStats,
'venue_stats' => $venueStats,
'date_stats' => $dateStats
];
} catch (\Exception $e) {
return [
'total_schedules' => 0,
'status_stats' => [],
'coach_stats' => [],
'venue_stats' => [],
'date_stats' => [],
'error' => $e->getMessage()
];
}
}
/**
* 学员加入课程安排
* @param array $data 加入数据
* @return array 加入结果
*/
public function joinSchedule(array $data)
{
try {
// 验证必填字段
if (empty($data['schedule_id']) || empty($data['student_id'])) {
return [
'code' => 0,
'msg' => '课程安排ID和学员ID不能为空'
];
}
// 开启事务
Db::startTrans();
$scheduleId = $data['schedule_id'];
$studentId = $data['student_id'];
// 查询课程安排信息
$schedule = Db::name('course_schedule')
->where('id', $scheduleId)
->where('deleted_at', 0)
->find();
if (empty($schedule)) {
Db::rollback();
return [
'code' => 0,
'msg' => '课程安排不存在或已被删除'
];
}
// 检查学员是否已经报名
$exists = Db::name('person_course_schedule')
->where('schedule_id', $scheduleId)
->where('student_id', $studentId)
->where('deleted_at', 0)
->find();
if ($exists) {
Db::rollback();
return [
'code' => 0,
'msg' => '学员已经报名该课程安排'
];
}
// 检查课程容量
$enrolledCount = Db::name('person_course_schedule')
->where('schedule_id', $scheduleId)
->where('deleted_at', 0)
->count();
if ($enrolledCount >= $schedule['available_capacity']) {
Db::rollback();
return [
'code' => 0,
'msg' => '课程容量已满,无法报名'
];
}
// 插入报名记录
$insertData = [
'schedule_id' => $scheduleId,
'student_id' => $studentId,
'person_type' => 'student',
'course_type' => $data['course_type'] ?? 0,
'resources_id' => $data['resources_id'] ?? 0,
'status' => 0, // 待上课
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'deleted_at' => 0
];
$result = Db::name('person_course_schedule')->insert($insertData);
if (!$result) {
Db::rollback();
return [
'code' => 0,
'msg' => '报名失败'
];
}
// 提交事务
Db::commit();
return [
'code' => 1,
'msg' => '报名成功',
'data' => [
'schedule_id' => $scheduleId,
'student_id' => $studentId
]
];
} catch (\Exception $e) {
Db::rollback();
return [
'code' => 0,
'msg' => '学员加入课程安排失败:' . $e->getMessage()
];
}
}
/**
* 学员退出课程安排
* @param array $data 退出数据
* @return array 退出结果
*/
public function leaveSchedule(array $data)
{
try {
// 验证必填字段
if (empty($data['schedule_id']) || empty($data['student_id'])) {
return [
'code' => 0,
'msg' => '课程安排ID和学员ID不能为空'
];
}
// 开启事务
Db::startTrans();
$scheduleId = $data['schedule_id'];
$studentId = $data['student_id'];
// 查询报名记录
$enrollment = Db::name('person_course_schedule')
->where('schedule_id', $scheduleId)
->where('student_id', $studentId)
->where('deleted_at', 0)
->find();
if (empty($enrollment)) {
Db::rollback();
return [
'code' => 0,
'msg' => '学员未报名该课程安排'
];
}
// 检查是否已经上课
if ($enrollment['status'] == 1) {
Db::rollback();
return [
'code' => 0,
'msg' => '学员已经上课,无法退出'
];
}
// 软删除报名记录
$result = Db::name('person_course_schedule')
->where('id', $enrollment['id'])
->update([
'deleted_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'remark' => $data['remark'] ?? '学员主动退出'
]);
if ($result === false) {
Db::rollback();
return [
'code' => 0,
'msg' => '退出失败'
];
}
// 提交事务
Db::commit();
return [
'code' => 1,
'msg' => '退出成功'
];
} catch (\Exception $e) {
Db::rollback();
return [
'code' => 0,
'msg' => '学员退出课程安排失败:' . $e->getMessage()
];
}
}
} }
Loading…
Cancel
Save