|
|
|
@ -14,7 +14,7 @@ use app\service\core\upload\CoreImageService; |
|
|
|
use app\service\core\sys\CoreSysConfigService; |
|
|
|
use app\service\admin\performance\PerformanceService; |
|
|
|
|
|
|
|
// 应用公共文件 |
|
|
|
// 应用公共文件123 |
|
|
|
|
|
|
|
/** |
|
|
|
* 接口操作成功,返回信息 |
|
|
|
@ -1521,11 +1521,11 @@ function getValidCourseByStudentId($studentId) |
|
|
|
* @param int $campus_id 校区ID(必填) |
|
|
|
* @param array $dept_ids 部门ID数组,例如:[1, 2, 23] 对应市场、教务、教练部门 |
|
|
|
* @return array 人员列表,包含person_id、name、phone等信息 |
|
|
|
* |
|
|
|
* |
|
|
|
* 使用示例: |
|
|
|
* // 获取校区1的市场人员 |
|
|
|
* $market_staff = get_personnel_by_campus_dept(1, [1]); |
|
|
|
* |
|
|
|
* |
|
|
|
* // 获取校区2的教练和教务人员 |
|
|
|
* $coaches_and_educators = get_personnel_by_campus_dept(2, [24, 2]); |
|
|
|
*/ |
|
|
|
@ -1534,18 +1534,18 @@ function get_personnel_by_campus_dept($campus_id, $dept_ids = []) |
|
|
|
if (empty($campus_id) || empty($dept_ids)) { |
|
|
|
return []; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
// 1. 根据部门ID获取对应的角色ID,使用正确的表名 |
|
|
|
$role_ids = \think\facade\Db::table('school_sys_role')->where([ |
|
|
|
['dept_id', 'in', $dept_ids], |
|
|
|
['status', '=', 1] |
|
|
|
])->column('role_id'); |
|
|
|
|
|
|
|
|
|
|
|
if (empty($role_ids)) { |
|
|
|
return []; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2. 根据校区ID和角色ID获取人员 |
|
|
|
$personnel_list = \think\facade\Db::table('school_campus_person_role') |
|
|
|
->alias('cpr') |
|
|
|
@ -1559,9 +1559,9 @@ function get_personnel_by_campus_dept($campus_id, $dept_ids = []) |
|
|
|
->field('cpr.person_id, p.name, p.phone, cpr.role_id, cpr.dept_id') |
|
|
|
->select() |
|
|
|
->toArray(); |
|
|
|
|
|
|
|
|
|
|
|
return $personnel_list; |
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) { |
|
|
|
\think\facade\Log::write('获取校区部门人员失败:' . $e->getMessage()); |
|
|
|
return []; |
|
|
|
@ -1573,7 +1573,7 @@ function get_personnel_by_campus_dept($campus_id, $dept_ids = []) |
|
|
|
* @param int $campus_id 校区ID |
|
|
|
* @param array $role_ids 角色ID数组 |
|
|
|
* @return array 人员列表 |
|
|
|
* |
|
|
|
* |
|
|
|
* 使用示例: |
|
|
|
* // 获取指定校区的教练主管和普通教练 |
|
|
|
* $coaches = get_personnel_by_campus_role(1, [1, 5]); |
|
|
|
@ -1583,7 +1583,7 @@ function get_personnel_by_campus_role($campus_id, $role_ids = []) |
|
|
|
if (empty($campus_id) || empty($role_ids)) { |
|
|
|
return []; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
// 根据校区ID和角色ID直接获取人员 |
|
|
|
$personnel_list = \think\facade\Db::table('school_campus_person_role') |
|
|
|
@ -1598,9 +1598,9 @@ function get_personnel_by_campus_role($campus_id, $role_ids = []) |
|
|
|
->field('cpr.person_id, p.name, p.phone, cpr.role_id, cpr.dept_id') |
|
|
|
->select() |
|
|
|
->toArray(); |
|
|
|
|
|
|
|
|
|
|
|
return $personnel_list; |
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) { |
|
|
|
\think\facade\Log::write('获取校区角色人员失败:' . $e->getMessage()); |
|
|
|
return []; |
|
|
|
@ -1695,11 +1695,11 @@ function get_current_campus($campus_id = 0) |
|
|
|
// 这里需要根据实际的用户会话机制来获取 |
|
|
|
return '默认校区'; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$campus = \think\facade\Db::table('school_campus') |
|
|
|
->where('id', $campus_id) |
|
|
|
->value('campus_name'); |
|
|
|
|
|
|
|
|
|
|
|
return $campus ?: '未知校区'; |
|
|
|
} catch (\Exception $e) { |
|
|
|
return '获取校区失败'; |
|
|
|
@ -1826,7 +1826,7 @@ function get_class_schedule_rules($params = []) |
|
|
|
'advance_booking_days' => 7, // 提前预约天数 |
|
|
|
'cancel_deadline_hours' => 24, // 取消课程截止时间(小时) |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
// 合并自定义参数 |
|
|
|
return array_merge($default_rules, $params); |
|
|
|
} |
|
|
|
@ -1842,29 +1842,29 @@ function generate_class_schedule($start_date, $end_date, $rules = []) |
|
|
|
{ |
|
|
|
$rules = get_class_schedule_rules($rules); |
|
|
|
$schedule = []; |
|
|
|
|
|
|
|
|
|
|
|
$current_date = strtotime($start_date); |
|
|
|
$end_timestamp = strtotime($end_date); |
|
|
|
|
|
|
|
|
|
|
|
while ($current_date <= $end_timestamp) { |
|
|
|
$date_str = date('Y-m-d', $current_date); |
|
|
|
$day_of_week = date('w', $current_date); |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否为周末 |
|
|
|
if (!$rules['weekend_enabled'] && ($day_of_week == 0 || $day_of_week == 6)) { |
|
|
|
$current_date = strtotime('+1 day', $current_date); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 生成当天的课程时间段 |
|
|
|
$daily_schedule = generate_daily_time_slots($date_str, $rules); |
|
|
|
if (!empty($daily_schedule)) { |
|
|
|
$schedule[$date_str] = $daily_schedule; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$current_date = strtotime('+1 day', $current_date); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return $schedule; |
|
|
|
} |
|
|
|
|
|
|
|
@ -1881,18 +1881,18 @@ function generate_daily_time_slots($date, $rules) |
|
|
|
$end_time = strtotime($date . ' ' . $rules['end_time']); |
|
|
|
$lunch_start = strtotime($date . ' ' . $rules['lunch_break_start']); |
|
|
|
$lunch_end = strtotime($date . ' ' . $rules['lunch_break_end']); |
|
|
|
|
|
|
|
|
|
|
|
$slot_number = 1; |
|
|
|
|
|
|
|
|
|
|
|
while ($current_time < $end_time) { |
|
|
|
$slot_end = $current_time + ($rules['class_duration'] * 60); |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否与午休时间冲突 |
|
|
|
if ($current_time < $lunch_end && $slot_end > $lunch_start) { |
|
|
|
$current_time = $lunch_end; |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$slots[] = [ |
|
|
|
'slot_number' => $slot_number, |
|
|
|
'start_time' => date('H:i', $current_time), |
|
|
|
@ -1900,11 +1900,11 @@ function generate_daily_time_slots($date, $rules) |
|
|
|
'duration' => $rules['class_duration'], |
|
|
|
'available' => true |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
$current_time = $slot_end + ($rules['break_duration'] * 60); |
|
|
|
$slot_number++; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return $slots; |
|
|
|
} |
|
|
|
|
|
|
|
@ -1924,41 +1924,41 @@ function check_class_time_conflict($date, $start_time, $end_time, $teacher_id = |
|
|
|
['date', '=', $date], |
|
|
|
['status', '=', 1], // 正常状态 |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
// 时间冲突检查:新课程的开始时间在已有课程时间范围内,或新课程的结束时间在已有课程时间范围内 |
|
|
|
$time_conflict = [ |
|
|
|
['start_time', '<', $end_time], |
|
|
|
['end_time', '>', $start_time] |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
$where = array_merge($where, $time_conflict); |
|
|
|
|
|
|
|
|
|
|
|
// 检查教师冲突 |
|
|
|
if ($teacher_id > 0) { |
|
|
|
$teacher_conflict = \think\facade\Db::table('school_class_schedule') |
|
|
|
->where($where) |
|
|
|
->where('teacher_id', $teacher_id) |
|
|
|
->count(); |
|
|
|
|
|
|
|
|
|
|
|
if ($teacher_conflict > 0) { |
|
|
|
return true; // 有冲突 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查教室冲突 |
|
|
|
if ($classroom_id > 0) { |
|
|
|
$classroom_conflict = \think\facade\Db::table('school_class_schedule') |
|
|
|
->where($where) |
|
|
|
->where('classroom_id', $classroom_id) |
|
|
|
->count(); |
|
|
|
|
|
|
|
|
|
|
|
if ($classroom_conflict > 0) { |
|
|
|
return true; // 有冲突 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false; // 无冲突 |
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) { |
|
|
|
\think\facade\Log::write('检查上课时间冲突失败:' . $e->getMessage()); |
|
|
|
return true; // 异常情况下认为有冲突,确保安全 |
|
|
|
@ -1978,23 +1978,23 @@ function get_campus_seal_image($campus_id) |
|
|
|
if (empty($campus_id)) { |
|
|
|
return ''; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$seal_image = \think\facade\Db::table('school_campus') |
|
|
|
->where('id', $campus_id) |
|
|
|
->value('seal_image'); |
|
|
|
|
|
|
|
|
|
|
|
if (empty($seal_image)) { |
|
|
|
return ''; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 如果已经是完整URL,直接返回 |
|
|
|
if (str_contains($seal_image, 'http://') || str_contains($seal_image, 'https://')) { |
|
|
|
return $seal_image; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 转换为完整的URL路径 |
|
|
|
return get_file_url($seal_image); |
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) { |
|
|
|
\think\facade\Log::write('获取校区印章图片失败:' . $e->getMessage()); |
|
|
|
return ''; |
|
|
|
@ -2019,9 +2019,9 @@ function process_signature_image($file, $options = []) |
|
|
|
'max_width' => 800, // 最大宽度 |
|
|
|
'max_height' => 600, // 最大高度 |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
$config = array_merge($default_options, $options); |
|
|
|
|
|
|
|
|
|
|
|
// 验证文件大小 |
|
|
|
if ($file['size'] > $config['max_size']) { |
|
|
|
return [ |
|
|
|
@ -2029,7 +2029,7 @@ function process_signature_image($file, $options = []) |
|
|
|
'message' => '图片文件大小不能超过' . ($config['max_size'] / 1024 / 1024) . 'MB' |
|
|
|
]; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 验证文件类型 |
|
|
|
$file_ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); |
|
|
|
if (!in_array($file_ext, $config['allowed_types'])) { |
|
|
|
@ -2038,7 +2038,7 @@ function process_signature_image($file, $options = []) |
|
|
|
'message' => '只支持' . implode('、', $config['allowed_types']) . '格式的图片' |
|
|
|
]; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 创建保存目录 |
|
|
|
$save_dir = $config['save_path'] . date('Y/m/d') . '/'; |
|
|
|
if (!is_dir($save_dir) && !mkdir($save_dir, 0755, true)) { |
|
|
|
@ -2047,11 +2047,11 @@ function process_signature_image($file, $options = []) |
|
|
|
'message' => '创建保存目录失败' |
|
|
|
]; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 生成文件名 |
|
|
|
$filename = 'signature_' . date('YmdHis') . '_' . uniqid() . '.' . $file_ext; |
|
|
|
$save_path = $save_dir . $filename; |
|
|
|
|
|
|
|
|
|
|
|
// 移动文件 |
|
|
|
if (!move_uploaded_file($file['tmp_name'], $save_path)) { |
|
|
|
return [ |
|
|
|
@ -2059,13 +2059,13 @@ function process_signature_image($file, $options = []) |
|
|
|
'message' => '文件保存失败' |
|
|
|
]; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 压缩图片(如果需要) |
|
|
|
$compressed_path = compress_signature_image($save_path, $config); |
|
|
|
if ($compressed_path) { |
|
|
|
$save_path = $compressed_path; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return [ |
|
|
|
'success' => true, |
|
|
|
'message' => '签名图片上传成功', |
|
|
|
@ -2076,7 +2076,7 @@ function process_signature_image($file, $options = []) |
|
|
|
'original_name' => $file['name'] |
|
|
|
] |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) { |
|
|
|
\think\facade\Log::write('处理签名图片失败:' . $e->getMessage()); |
|
|
|
return [ |
|
|
|
@ -2100,19 +2100,19 @@ function compress_signature_image($source_path, $config) |
|
|
|
if (!$image_info) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[$width, $height, $type] = $image_info; |
|
|
|
|
|
|
|
|
|
|
|
// 如果图片尺寸已经符合要求,不需要压缩 |
|
|
|
if ($width <= $config['max_width'] && $height <= $config['max_height']) { |
|
|
|
return $source_path; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 计算新尺寸 |
|
|
|
$ratio = min($config['max_width'] / $width, $config['max_height'] / $height); |
|
|
|
$new_width = intval($width * $ratio); |
|
|
|
$new_height = intval($height * $ratio); |
|
|
|
|
|
|
|
|
|
|
|
// 创建源图像资源 |
|
|
|
switch ($type) { |
|
|
|
case IMAGETYPE_JPEG: |
|
|
|
@ -2127,14 +2127,14 @@ function compress_signature_image($source_path, $config) |
|
|
|
default: |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!$source_image) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 创建新图像 |
|
|
|
$new_image = imagecreatetruecolor($new_width, $new_height); |
|
|
|
|
|
|
|
|
|
|
|
// 保持透明度(PNG/GIF) |
|
|
|
if ($type == IMAGETYPE_PNG || $type == IMAGETYPE_GIF) { |
|
|
|
imagealphablending($new_image, false); |
|
|
|
@ -2142,14 +2142,14 @@ function compress_signature_image($source_path, $config) |
|
|
|
$transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127); |
|
|
|
imagefill($new_image, 0, 0, $transparent); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 重新采样 |
|
|
|
imagecopyresampled($new_image, $source_image, 0, 0, 0, 0, |
|
|
|
imagecopyresampled($new_image, $source_image, 0, 0, 0, 0, |
|
|
|
$new_width, $new_height, $width, $height); |
|
|
|
|
|
|
|
|
|
|
|
// 生成压缩后的文件路径 |
|
|
|
$compressed_path = str_replace('.', '_compressed.', $source_path); |
|
|
|
|
|
|
|
|
|
|
|
// 保存压缩后的图片 |
|
|
|
$saved = false; |
|
|
|
switch ($type) { |
|
|
|
@ -2163,19 +2163,19 @@ function compress_signature_image($source_path, $config) |
|
|
|
$saved = imagegif($new_image, $compressed_path); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 释放资源 |
|
|
|
imagedestroy($source_image); |
|
|
|
imagedestroy($new_image); |
|
|
|
|
|
|
|
|
|
|
|
if ($saved) { |
|
|
|
// 删除原文件 |
|
|
|
unlink($source_path); |
|
|
|
return $compressed_path; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) { |
|
|
|
\think\facade\Log::write('压缩签名图片失败:' . $e->getMessage()); |
|
|
|
return false; |
|
|
|
@ -2194,7 +2194,7 @@ function get_user_signature_records($user_id, $user_type = 'staff') |
|
|
|
if (empty($user_id)) { |
|
|
|
return []; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$signatures = \think\facade\Db::table('school_user_signatures') |
|
|
|
->where([ |
|
|
|
['user_id', '=', $user_id], |
|
|
|
@ -2205,16 +2205,16 @@ function get_user_signature_records($user_id, $user_type = 'staff') |
|
|
|
->order('created_at', 'desc') |
|
|
|
->select() |
|
|
|
->toArray(); |
|
|
|
|
|
|
|
|
|
|
|
// 处理签名图片URL |
|
|
|
foreach ($signatures as &$signature) { |
|
|
|
if (!empty($signature['signature_image'])) { |
|
|
|
$signature['signature_url'] = get_file_url($signature['signature_image']); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return $signatures; |
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) { |
|
|
|
\think\facade\Log::write('获取用户签名记录失败:' . $e->getMessage()); |
|
|
|
return []; |
|
|
|
@ -2235,7 +2235,7 @@ function save_user_signature($data) |
|
|
|
throw new \Exception("缺少必要字段:{$field}"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$insert_data = [ |
|
|
|
'user_id' => $data['user_id'], |
|
|
|
'user_type' => $data['user_type'], |
|
|
|
@ -2249,19 +2249,19 @@ function save_user_signature($data) |
|
|
|
'updated_at' => date('Y-m-d H:i:s'), |
|
|
|
'deleted_at' => 0 |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
$signature_id = \think\facade\Db::table('school_user_signatures') |
|
|
|
->insertGetId($insert_data); |
|
|
|
|
|
|
|
|
|
|
|
if ($signature_id) { |
|
|
|
\think\facade\Log::write('保存用户签名记录成功:' . json_encode($insert_data)); |
|
|
|
return $signature_id; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) { |
|
|
|
\think\facade\Log::write('保存用户签名记录失败:' . $e->getMessage()); |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|