diff --git a/plugins/itstudio-recruitment/includes/join-system.php b/plugins/itstudio-recruitment/includes/join-system.php index 469787d..e8d14eb 100644 --- a/plugins/itstudio-recruitment/includes/join-system.php +++ b/plugins/itstudio-recruitment/includes/join-system.php @@ -574,7 +574,7 @@ function itstudio_join_parse_result_rows_to_records($rows) { 'qq' => itstudio_join_normalize_lookup_value('qq', $qq), 'email' => itstudio_join_normalize_lookup_value('email', $email), 'student_id' => itstudio_join_normalize_lookup_value('student_id', $student_id), - 'phone' => preg_replace('/\D+/', '', $phone), + 'phone' => itstudio_join_normalize_lookup_value('phone', $phone), 'passed' => ($passed_raw === '1'), ); @@ -927,7 +927,7 @@ function itstudio_join_normalize_lookup_value($field, $value) { return ''; } - if ($field === 'qq') { + if ($field === 'qq' || $field === 'phone') { return preg_replace('/\D+/', '', $value); } @@ -972,6 +972,12 @@ function itstudio_join_detect_progress_query_identity($raw_identity) { $digits = $compacted; $len = strlen($digits); + if (preg_match('/^1[3-9]\d{9}$/', $digits)) { + $detected['field'] = 'phone'; + $detected['value'] = itstudio_join_normalize_lookup_value('phone', $digits); + return $detected; + } + if ($len >= 10 && $len <= 12) { $detected['field'] = 'student_id'; $detected['value'] = itstudio_join_normalize_lookup_value('student_id', $digits); @@ -1069,7 +1075,7 @@ function itstudio_join_record_matches_query($record, $query) { $query = is_array($query) ? $query : array(); $has_any_query = false; - foreach (array('name', 'qq', 'email', 'student_id') as $field) { + foreach (array('name', 'qq', 'email', 'student_id', 'phone') as $field) { $q = trim((string) ($query[$field] ?? '')); if ($q === '') { continue; @@ -1089,19 +1095,19 @@ function itstudio_join_find_record_in_stage_results($settings, $stage_key, $quer if (itstudio_join_is_file_result_mode($settings)) { $attachment_id = itstudio_join_get_result_file_attachment_id($settings, $stage_key); if ($attachment_id <= 0) { - return false; + return null; } $rows = itstudio_join_read_result_rows_from_attachment($attachment_id); $records = itstudio_join_parse_result_rows_to_records($rows); if (empty($records)) { - return false; + return null; } foreach ($records as $record) { - if (itstudio_join_record_matches_query($record, $query) && !empty($record['passed'])) { - return true; + if (itstudio_join_record_matches_query($record, $query)) { + return !empty($record['passed']); } } - return false; + return null; } if (itstudio_join_is_formidable_result_mode($settings)) { @@ -1115,7 +1121,7 @@ function itstudio_join_find_record_in_stage_results($settings, $stage_key, $quer } $entry_id = (int) $entry_cache[$cache_key]; if ($entry_id <= 0) { - return false; + return null; } return itstudio_join_formidable_entry_matches_stage($settings, $entry_id, $stage_key); } @@ -1123,17 +1129,17 @@ function itstudio_join_find_record_in_stage_results($settings, $stage_key, $quer $field_map = itstudio_join_get_result_field_map(); $field_key = isset($field_map[$stage_key]) ? $field_map[$stage_key] : ''; if ($field_key === '') { - return false; + return null; } $raw = (string) ($settings[$field_key] ?? ''); if (trim($raw) === '') { - return false; + return null; } $records = itstudio_join_parse_result_records($raw); if (empty($records)) { - return false; + return null; } foreach ($records as $record) { @@ -1142,7 +1148,15 @@ function itstudio_join_find_record_in_stage_results($settings, $stage_key, $quer } } - return false; + return null; +} + +function itstudio_join_progress_record_not_found_response($response) { + $response = is_array($response) ? $response : array(); + $response['message_cn'] = '未查询到该报名者,请检查输入信息是否正确。'; + $response['message_en'] = 'No matching applicant was found. Please check the information and try again.'; + $response['tone'] = 'warning'; + return $response; } function itstudio_join_get_stage_status_by_key($runtime, $stage_key) { @@ -1188,6 +1202,7 @@ function itstudio_join_resolve_progress_lookup($runtime = array(), $request_sour 'qq' => '', 'email' => '', 'student_id' => '', + 'phone' => '', ); if ($detected_identity['field'] !== '') { $query[$detected_identity['field']] = (string) $detected_identity['value']; @@ -1216,15 +1231,15 @@ function itstudio_join_resolve_progress_lookup($runtime = array(), $request_sour } if (!$has_input_value) { - $response['message_cn'] = '请输入姓名(中文)/ QQ / 邮箱 / 学号。'; - $response['message_en'] = 'Please enter Name (Chinese) / QQ / Email / Student ID.'; + $response['message_cn'] = '请输入姓名(中文)/ QQ / 邮箱 / 学号 / 手机号。'; + $response['message_en'] = 'Please enter Name (Chinese) / QQ / Email / Student ID / Phone.'; $response['tone'] = 'warning'; return $response; } if (!$has_query_value) { - $response['message_cn'] = '无法识别输入内容,请输入姓名(中文)/ QQ / 邮箱 / 学号。'; - $response['message_en'] = 'Input cannot be recognized. Please enter Name (Chinese) / QQ / Email / Student ID.'; + $response['message_cn'] = '无法识别输入内容,请输入姓名(中文)/ QQ / 邮箱 / 学号 / 手机号。'; + $response['message_en'] = 'Input cannot be recognized. Please enter Name (Chinese) / QQ / Email / Student ID / Phone.'; $response['tone'] = 'warning'; return $response; } @@ -1262,6 +1277,9 @@ function itstudio_join_resolve_progress_lookup($runtime = array(), $request_sour } $passed_first = itstudio_join_find_record_in_stage_results($settings, 'first_interview', $query); + if ($passed_first === null) { + return itstudio_join_progress_record_not_found_response($response); + } if (!$passed_first) { $response['message_cn'] = '未通过第一次面试,期待来年再次报名。'; $response['message_en'] = 'You did not pass Interview I. Welcome to apply again next year.'; @@ -1286,6 +1304,9 @@ function itstudio_join_resolve_progress_lookup($runtime = array(), $request_sour } $passed_assessment = itstudio_join_find_record_in_stage_results($settings, 'assessment', $query); + if ($passed_assessment === null) { + return itstudio_join_progress_record_not_found_response($response); + } if (!$passed_assessment) { $response['message_cn'] = '未通过国庆能力摸底,期待来年再次报名。'; $response['message_en'] = 'You did not pass the assessment stage. Welcome to apply again next year.'; @@ -1310,6 +1331,9 @@ function itstudio_join_resolve_progress_lookup($runtime = array(), $request_sour } $passed_second = itstudio_join_find_record_in_stage_results($settings, 'second_interview', $query); + if ($passed_second === null) { + return itstudio_join_progress_record_not_found_response($response); + } if (!$passed_second) { $response['message_cn'] = '未通过第二次面试,期待来年再次报名。'; $response['message_en'] = 'You did not pass Interview II. Welcome to apply again next year.'; @@ -1320,6 +1344,9 @@ function itstudio_join_resolve_progress_lookup($runtime = array(), $request_sour if ($status_notice === 'pending' || $status_notice === 'upcoming' || $status_notice === 'active') { if ($status_notice === 'active' && $uploaded_notice) { $admitted = itstudio_join_find_record_in_stage_results($settings, 'public_notice', $query); + if ($admitted === null) { + return itstudio_join_progress_record_not_found_response($response); + } if ($admitted) { $response['message_cn'] = '恭喜,您已被录取。'; $response['message_en'] = 'Congratulations! You have been admitted.'; @@ -1342,6 +1369,9 @@ function itstudio_join_resolve_progress_lookup($runtime = array(), $request_sour if (($status_notice === 'active' || $status_notice === 'completed') && $uploaded_notice) { $admitted = itstudio_join_find_record_in_stage_results($settings, 'public_notice', $query); + if ($admitted === null) { + return itstudio_join_progress_record_not_found_response($response); + } if ($admitted) { $response['message_cn'] = '恭喜,您已被录取。'; $response['message_en'] = 'Congratulations! You have been admitted.'; @@ -1952,6 +1982,15 @@ function itstudio_join_register_settings_page() { } add_action('admin_menu', 'itstudio_join_register_settings_page'); +function itstudio_join_enqueue_settings_page_media($hook_suffix) { + if ($hook_suffix !== 'settings_page_itstudio-join-settings') { + return; + } + + wp_enqueue_media(); +} +add_action('admin_enqueue_scripts', 'itstudio_join_enqueue_settings_page_media'); + /** * 统一跳回招新设置页并带上提示参数。 */ @@ -1970,11 +2009,50 @@ function itstudio_join_settings_error_message($code) { 'delete_last' => '至少要保留一个学期。', 'delete_failed' => '删除失败,学期不存在。', 'save_failed' => '保存失败,学期不存在。', + 'result_upload_failed' => '结果文件上传失败,请确认文件为 CSV 或 XLSX 后重试。', 'need_year' => '请填写学年或自定义名称。', ); return isset($map[$code]) ? $map[$code] : '操作失败。'; } +function itstudio_join_handle_result_file_uploads($settings) { + $settings = is_array($settings) ? $settings : array(); + + foreach (itstudio_join_get_result_file_field_map() as $field_key) { + $upload_key = 'itstudio_join_upload_' . $field_key; + if (!isset($_FILES[$upload_key]) || !is_array($_FILES[$upload_key])) { + continue; + } + + $upload_error = isset($_FILES[$upload_key]['error']) ? (int) $_FILES[$upload_key]['error'] : UPLOAD_ERR_NO_FILE; + if ($upload_error === UPLOAD_ERR_NO_FILE) { + continue; + } + if ($upload_error !== UPLOAD_ERR_OK) { + return new WP_Error('result_upload_failed'); + } + + $filename = isset($_FILES[$upload_key]['name']) ? sanitize_file_name(wp_unslash($_FILES[$upload_key]['name'])) : ''; + $extension = strtolower((string) pathinfo($filename, PATHINFO_EXTENSION)); + if (!in_array($extension, array('csv', 'xlsx'), true)) { + return new WP_Error('result_upload_failed'); + } + + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/media.php'; + require_once ABSPATH . 'wp-admin/includes/image.php'; + + $attachment_id = media_handle_upload($upload_key, 0); + if (is_wp_error($attachment_id)) { + return new WP_Error('result_upload_failed', $attachment_id->get_error_message()); + } + + $settings[$field_key] = absint($attachment_id); + } + + return $settings; +} + /** * 保存某学期的配置(含学期元信息)。 */ @@ -1988,6 +2066,11 @@ function itstudio_join_handle_save_term_settings() { $raw = (isset($_POST['itstudio_join_settings']) && is_array($_POST['itstudio_join_settings'])) ? wp_unslash($_POST['itstudio_join_settings']) : array(); $meta = (isset($_POST['itstudio_join_term_meta']) && is_array($_POST['itstudio_join_term_meta'])) ? wp_unslash($_POST['itstudio_join_term_meta']) : array(); + $raw = itstudio_join_handle_result_file_uploads($raw); + if (is_wp_error($raw)) { + itstudio_join_settings_redirect(array('term' => $term_id, 'error' => $raw->get_error_code())); + } + if (!empty($meta)) { itstudio_join_update_term_meta( $term_id, @@ -2133,15 +2216,58 @@ function itstudio_join_render_result_file_row($field_key, $label, $settings, $de $attachment_id = isset($settings[$field_key]) ? absint($settings[$field_key]) : 0; $file_url = $attachment_id > 0 ? wp_get_attachment_url($attachment_id) : ''; $filename = $attachment_id > 0 ? basename((string) get_attached_file($attachment_id)) : ''; + static $result_files = null; + if ($result_files === null) { + $result_files = get_posts(array( + 'post_type' => 'attachment', + 'post_status' => 'inherit', + 'post_mime_type' => array( + 'text/csv', + 'application/csv', + 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ), + 'posts_per_page' => 100, + 'orderby' => 'date', + 'order' => 'DESC', + )); + } + if ($attachment_id > 0 && !in_array($attachment_id, wp_list_pluck($result_files, 'ID'), true)) { + $current_file = get_post($attachment_id); + if ($current_file instanceof WP_Post && $current_file->post_type === 'attachment') { + $result_files[] = $current_file; + } + } ?>