function extract_shipment_data(string $text): array { $text = normalize_text($text); $isFbsAct = preg_match('/Акт\s+приема-передачи\s+заказов/u', $text) === 1; $fbsAct = $isFbsAct ? extract_fbs_act_data($text) : null; $deliveryTime = $fbsAct['delivery_time'] ?? parse_delivery_time($text); $targetPoint = detect_target_point($text); $shipmentType = detect_shipment_type($text, $targetPoint); $items = $isFbsAct ? [] : extract_items($text); $confidence = 0.1; if ($deliveryTime) { $confidence += 0.25; } if ($targetPoint) { $confidence += 0.2; } if (count($items) > 0) { $confidence += min(0.45, count($items) * 0.1); } if ($fbsAct && ($fbsAct['document_number'] || $fbsAct['order_number'])) { $confidence += 0.25; } $result = [ 'delivery_time' => $deliveryTime, 'target_point' => $targetPoint, 'shipment_type' => $shipmentType, 'items' => $items, 'raw_text' => $text, 'confidence' => round(min(0.99, $confidence), 2), 'document_kind' => $isFbsAct ? 'fbs_act' : 'generic', ]; if ($fbsAct) { $result['fbs_act'] = $fbsAct; if (empty($result['target_point']) && !empty($fbsAct['place_of_supply'])) { $result['target_point'] = $fbsAct['place_of_supply']; } if (empty($result['shipment_type'])) { $result['shipment_type'] = 'FBS'; } if (empty($result['delivery_time']) && !empty($fbsAct['delivery_window_start'])) { $result['delivery_time'] = $fbsAct['delivery_window_start']; } } return $result; } function store_upload(array $file, string $prefix): array { if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { throw new RuntimeException('文件上传失败:' . ($file['error'] ?? '未知错误')); } if (!is_uploaded_file($file['tmp_name'])) { throw new RuntimeException('无效的上传文件'); } if ((int) ($file['size'] ?? 0) > MAX_UPLOAD_SIZE) { throw new RuntimeException('文件过大,单文件不能超过 20MB'); } $original = (string) ($file['name'] ?? 'file'); $ext = strtolower(pathinfo($original, PATHINFO_EXTENSION)); $allowed = ['pdf', 'png', 'jpg', 'jpeg']; if (!in_array($ext, $allowed, true)) { throw new RuntimeException('只允许上传 PDF、PNG、JPG、JPEG 文件'); } $folder = storage_path('uploads/' . date('Y/m')); if (!is_dir($folder) && !@mkdir($folder, 0777, true) && !is_dir($folder)) { throw new RuntimeException('无法创建上传目录'); } $safe = $prefix . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $ext; $destination = $folder . DIRECTORY_SEPARATOR . $safe; if (!move_uploaded_file($file['tmp_name'], $destination)) { throw new RuntimeException('保存上传文件失败'); } $root = rtrim(storage_path(), "\\/"); $relative = ltrim(str_replace($root, '', $destination), "\\/"); return [ 'original_name' => $original, 'file_path' => $relative, 'disk_path' => $destination, 'extension' => $ext, 'size' => (int) $file['size'], ]; } function store_upload_batch(?array $field, string $prefix): array { $stored = []; foreach (file_input_list($field ?? []) as $file) { if (($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) { continue; } $stored[] = store_upload($file, $prefix); } return $stored; } function create_notification(int $shipmentId, string $type, string $title, string $content): void { db_exec( 'INSERT INTO notifications (shipment_id, notify_type, title, content, is_read, created_at) VALUES (?, ?, ?, ?, 0, NOW())', 'isss', [$shipmentId, $type, $title, $content] ); } function render_header(string $title): void { $user = app_schema_ready() ? current_user() : null; $flash = flash_get(); $countSoon = 0; if ($user && table_exists('shipments')) { $rows = db_all( "SELECT COUNT(*) AS total FROM shipments WHERE status NOT IN ('done','canceled') AND delivery_time IS NOT NULL AND delivery_time <= DATE_ADD(NOW(), INTERVAL 24 HOUR)" ); $countSoon = (int) ($rows[0]['total'] ?? 0); } $lang = current_lang(); $htmlLang = $lang === 'ru' ? 'ru-RU' : ($lang === 'uz' ? 'uz-UZ' : 'zh-CN'); echo ''; echo ''; echo '' . h($title) . ' - ' . h(APP_NAME) . ''; echo ''; echo '
'; echo '
'; foreach (supported_langs() as $candidate) { $active = $candidate === $lang ? 'active' : ''; echo '' . h(lang_short_label($candidate)) . ''; } echo '
'; echo '
' . h(APP_NAME) . '
FBO / FBS 上传、识别、下载、提醒一体化后台
'; if ($user) { echo '