提交
This commit is contained in:
@@ -11,7 +11,7 @@ class Upload extends Base
|
||||
{
|
||||
$scene = strtolower(trim((string)$this->request->post('scene', '')));
|
||||
$contactUserId = 0;
|
||||
if (in_array($scene, ['passport', 'id_card', 'evus_correction'], true)) {
|
||||
if (in_array($scene, ['passport', 'id_card', 'evus_correction', 'visa_application'], true)) {
|
||||
$login = $this->requireLogin();
|
||||
if (!is_int($login)) {
|
||||
return $login;
|
||||
@@ -40,13 +40,14 @@ class Upload extends Base
|
||||
$originalName = (string)($_FILES['file']['name'] ?? 'upload.jpg');
|
||||
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
|
||||
$ext = $ext === 'jpeg' ? 'jpg' : $ext;
|
||||
if (!in_array($ext, ['jpg', 'png', 'gif', 'webp', 'bmp'], true)) {
|
||||
return $this->uploadResponse(0, '请上传图片文件');
|
||||
$pdf = $scene === 'visa_application' && $ext === 'pdf';
|
||||
if (!in_array($ext, ['jpg', 'png', 'gif', 'webp', 'bmp'], true) && !$pdf) {
|
||||
return $this->uploadResponse(0, $scene === 'visa_application' ? '请上传图片或PDF文件' : '请上传图片文件');
|
||||
}
|
||||
|
||||
$mime = function_exists('mime_content_type') ? (string)mime_content_type($path) : '';
|
||||
if ($mime !== '' && strpos($mime, 'image/') !== 0) {
|
||||
return $this->uploadResponse(0, '请上传图片文件');
|
||||
if ($mime !== '' && ($pdf ? !in_array($mime, ['application/pdf', 'application/x-pdf'], true) : strpos($mime, 'image/') !== 0)) {
|
||||
return $this->uploadResponse(0, $scene === 'visa_application' ? '请上传图片或PDF文件' : '请上传图片文件');
|
||||
}
|
||||
|
||||
$content = file_get_contents($path);
|
||||
@@ -55,7 +56,9 @@ class Upload extends Base
|
||||
}
|
||||
|
||||
$fileName = date('YmdHis') . uniqid('', true) . '.' . $ext;
|
||||
if ($scene === 'evus_correction') {
|
||||
if ($scene === 'visa_application') {
|
||||
$objectKey = 'visa-application/' . $contactUserId . '/' . $fileName;
|
||||
} elseif ($scene === 'evus_correction') {
|
||||
$objectKey = 'evus-correction/' . $contactUserId . '/' . $fileName;
|
||||
} elseif ($contactUserId > 0) {
|
||||
$objectKey = 'contact/' . $contactUserId . '/' . $fileName;
|
||||
@@ -80,10 +83,12 @@ class Upload extends Base
|
||||
}
|
||||
|
||||
$url = OSS_URL . $objectKey;
|
||||
return $this->uploadResponse(1, '上传成功', [
|
||||
$data = [
|
||||
'path' => $url,
|
||||
'url' => $url,
|
||||
]);
|
||||
];
|
||||
if ($scene === 'visa_application') $data['upload_token'] = authcode((string)json_encode(['purpose' => 'visa_upload_receipt', 'user_id' => $contactUserId, 'url' => $url], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), 'ENCODE', MINIAPI_TOKEN_KEY, 2592000);
|
||||
return $this->uploadResponse(1, '上传成功', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->uploadResponse(0, '上传失败,请重试');
|
||||
}
|
||||
|
||||
29
app/miniapi/controller/VisaApplication.php
Normal file
29
app/miniapi/controller/VisaApplication.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\miniapi\controller;
|
||||
|
||||
use app\miniapi\service\VisaApplication\VisaApplicationService;
|
||||
|
||||
final class VisaApplication extends Base
|
||||
{
|
||||
public function options() { return $this->call(fn($service, $userId) => $service->options($userId, $this->orderSource())); }
|
||||
public function start() { return $this->call(fn($service, $userId) => $service->start($userId, $this->postData(), $this->orderSource())); }
|
||||
public function application() { return $this->call(fn($service, $userId) => $service->application($userId, trim((string)$this->request->param('public_id', '')))); }
|
||||
public function draft() { return $this->call(fn($service, $userId) => $service->saveDraft($userId, $this->postData()), '草稿已保存'); }
|
||||
public function submit() { return $this->call(fn($service, $userId) => $service->submit($userId, $this->postData()), '提交成功'); }
|
||||
public function dataSource()
|
||||
{
|
||||
$filters = (array)$this->request->get(); unset($filters['type'], $filters['s'], $filters['mini_app_code'], $filters['token']); $filters['source_code'] = $this->orderSource();
|
||||
$result = (new VisaApplicationService())->dataSource(trim((string)$this->request->param('type', '')), $filters);
|
||||
return (int)($result['code'] ?? 0) === 1 ? $this->ok((array)($result['data'] ?? [])) : $this->fail((string)($result['msg'] ?? '数据源暂不可用'));
|
||||
}
|
||||
private function call(callable $callback, string $message = '操作成功')
|
||||
{
|
||||
$userId = $this->requireLogin(); if (!is_int($userId)) return $userId;
|
||||
$result = $callback(new VisaApplicationService(), $userId); if ((int)($result['code'] ?? 0) !== 1) return $this->fail((string)($result['msg'] ?? '服务异常,请稍后重试'));
|
||||
return $this->ok((array)($result['data'] ?? []), $message);
|
||||
}
|
||||
public static function sourceForCode(string $code): string { return $code === 'evus_jn_mp' ? 'jnwxmini' : ($code === 'evus_bj_mp' ? 'qlwxmini' : ''); }
|
||||
private function orderSource(): string { $source = self::sourceForCode($this->currentMiniAppCode()); if ($source === '') throw new \InvalidArgumentException('当前应用不支持签证办理'); return $source; }
|
||||
}
|
||||
@@ -60,4 +60,10 @@ Route::group(function () {
|
||||
Route::post('evus/lookup', 'Evus/lookup');
|
||||
Route::post('evus/apply', 'Evus/apply');
|
||||
Route::post('esta/apply', 'Esta/apply');
|
||||
Route::get('visa/options', 'VisaApplication/options');
|
||||
Route::post('visa/start', 'VisaApplication/start');
|
||||
Route::get('visa/application', 'VisaApplication/application');
|
||||
Route::post('visa/draft', 'VisaApplication/draft');
|
||||
Route::post('visa/submit', 'VisaApplication/submit');
|
||||
Route::get('visa/data-source', 'VisaApplication/dataSource');
|
||||
})->namespace('app\\miniapi\\controller');
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\miniapi\service\VisaApplication;
|
||||
|
||||
use api\Httpcurl;
|
||||
use think\facade\Log;
|
||||
|
||||
final class VisaApplicationService
|
||||
{
|
||||
private const ACTIONS = ['options', 'start', 'application', 'saveDraft', 'submit', 'dataSource'];
|
||||
private const DATA_SOURCE_PARAMS = ['model', 'source_code', 'page', 'size', 'state_id', 'hotel_state', 'provinceCode', 'country', 'entry_mode', 'province', 'accommodation_province', 'accommodation_municipality', 'city', 'id', 'air_transport_type', 'state', 'japan_province', 'travel_country', 'hotel_city', 'districtCode', 'nationality_iso2', 'next_destination'];
|
||||
|
||||
public function options(int $userId, string $orderSource): array { return $this->request('options', $userId, ['order_source' => $orderSource]); }
|
||||
public function start(int $userId, array $data, string $orderSource): array { return $this->request('start', $userId, array_merge($data, ['order_source' => $orderSource])); }
|
||||
public function application(int $userId, string $publicId): array { return $this->request('application', $userId, ['public_id' => $publicId]); }
|
||||
public function saveDraft(int $userId, array $data): array { return $this->request('saveDraft', $userId, $data); }
|
||||
public function submit(int $userId, array $data): array { return self::decorateSubmitForCheck($this->request('submit', $userId, $data)); }
|
||||
public static function decorateSubmitForCheck(array $result): array { if ((int)($result['code'] ?? 0) === 1 && !empty($result['data']['requires_payment']) && !empty($result['data']['order_sn'])) $result['data']['pay_url'] = 'https://www.qlevisa.com/index/pay/h5pay.html?order_sn=' . rawurlencode((string)$result['data']['order_sn']); return $result; }
|
||||
public function payload(int $userId, array $params = []): array { $source = (string)($params['order_source'] ?? ''); unset($params['order_source']); return ['user_token' => authcode((string)json_encode(['purpose' => 'visa_api', 'user_id' => $userId, 'order_source' => $source], JSON_UNESCAPED_UNICODE), 'ENCODE', MINIAPI_TOKEN_KEY, 120)] + $params; }
|
||||
public function endpoint(string $userUrl, string $action): string
|
||||
{
|
||||
if (!in_array($action, self::ACTIONS, true)) return '';
|
||||
$endpoint = preg_replace('#/users/[^/?]+(?:\?.*)?$#i', '/visa_application/' . $action, rtrim(trim($userUrl), '/'));
|
||||
return is_string($endpoint) && $endpoint !== rtrim(trim($userUrl), '/') ? $endpoint : '';
|
||||
}
|
||||
public function dataSource(string $type, array $filters): array
|
||||
{
|
||||
try { $query = self::dataSourceQueryForCheck($type, $filters); } catch (\RuntimeException $e) { return ['code' => 0, 'msg' => '数据源参数错误', 'data' => []]; }
|
||||
$url = $this->endpoint((string)config('app.user_url'), 'dataSource'); $response = $url ? Httpcurl::request($url, 'post', http_build_query($query)) : false;
|
||||
if (!is_array($response) || !isset($response[0]) || !empty($response[3])) { Log::warning('miniapi visa data source request failed'); return ['code' => 0, 'msg' => '数据源暂不可用', 'data' => []]; }
|
||||
$result = json_decode($response[0], true); return is_array($result) ? $result : ['code' => 0, 'msg' => '数据源暂不可用', 'data' => []];
|
||||
}
|
||||
public static function dataSourceQueryForCheck(string $type, array $filters): array
|
||||
{
|
||||
if (!preg_match('/^[A-Za-z0-9_.-]+$/', $type)) throw new \RuntimeException('invalid');
|
||||
$query = ['type' => $type];
|
||||
foreach (self::DATA_SOURCE_PARAMS as $key) {
|
||||
$value = $filters[$key] ?? null;
|
||||
if (!is_scalar($value) || $value === '') continue;
|
||||
$value = (string)$value;
|
||||
if (strlen($value) > 128 || ($key === 'model' && !preg_match('/^[A-Za-z0-9_.-]+$/', $value)) || ($key === 'source_code' && !preg_match('/^[A-Za-z0-9._-]{1,20}$/', $value)) || (in_array($key, ['page', 'size'], true) && !preg_match('/^[1-9][0-9]*$/', $value))) continue;
|
||||
$query[$key] = $value;
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
private function request(string $action, int $userId, array $params): array
|
||||
{
|
||||
$url = $this->endpoint((string)config('app.user_url'), $action); if ($url === '') return ['code' => 0, 'msg' => '签证服务接口地址异常', 'data' => []];
|
||||
$response = Httpcurl::request($url, 'post', http_build_query($this->payload($userId, $params)));
|
||||
if (!is_array($response) || !isset($response[0]) || !empty($response[3])) { Log::warning('miniapi visa application request failed'); return ['code' => 0, 'msg' => '服务异常,请稍后重试', 'data' => []]; }
|
||||
$result = json_decode($response[0], true); return is_array($result) ? $result : ['code' => 0, 'msg' => '服务异常,请稍后重试', 'data' => []];
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
use think\facade\Route;
|
||||
|
||||
Route::group('miniapi', function () {
|
||||
Route::get('index/home', 'Index/home');
|
||||
Route::get('index/about', 'Index/about');
|
||||
Route::get('index/legal', 'Index/legal');
|
||||
Route::get('index/packages', 'Index/packages');
|
||||
Route::get('index/countries', 'Index/countries');
|
||||
|
||||
Route::post('auth/sendSms', 'Auth/sendSms');
|
||||
Route::post('auth/login', 'Auth/login');
|
||||
Route::post('auth/wxPhoneLogin', 'Auth/wxPhoneLogin');
|
||||
Route::get('auth/me', 'Auth/me');
|
||||
|
||||
Route::get('news/lists', 'News/lists');
|
||||
Route::get('news/detail', 'News/detail');
|
||||
|
||||
Route::get('help/faqs', 'Help/faqs');
|
||||
|
||||
Route::get('order/lists', 'Order/lists');
|
||||
Route::get('order/latest-evus', 'Order/latestEvus');
|
||||
Route::get('order/detail', 'Order/detail');
|
||||
Route::post('order/pay', 'Order/pay');
|
||||
Route::post('order/addInvoice', 'Order/addInvoice');
|
||||
|
||||
Route::get('customer-center/applicants', 'CustomerCenter/applicants');
|
||||
Route::get('customer-center/applications', 'CustomerCenter/applications');
|
||||
Route::get('customer-center/orders', 'CustomerCenter/orders');
|
||||
Route::post('customer-center/applicant/save', 'CustomerCenter/saveApplicant');
|
||||
Route::post('customer-center/applicant/remove', 'CustomerCenter/removeApplicant');
|
||||
Route::post('customer-center/applicant', 'CustomerCenter/applicant')->completeMatch();
|
||||
|
||||
Route::get('ocr/signature', 'Ocr/signature');
|
||||
Route::post('upload/image', 'Upload/image');
|
||||
|
||||
Route::get('evus/correction', 'EvusCorrection/detail');
|
||||
Route::post('evus/correction/submit', 'EvusCorrection/submit');
|
||||
Route::post('evus/lookup', 'Evus/lookup');
|
||||
Route::post('evus/apply', 'Evus/apply');
|
||||
Route::post('esta/apply', 'Esta/apply');
|
||||
})->namespace('app\\miniapi\\controller');
|
||||
23
tests/visa_application_facade_self_check.php
Normal file
23
tests/visa_application_facade_self_check.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
const MINIAPI_TOKEN_KEY = 'test';
|
||||
function authcode($value, $operation = 'DECODE', $key = '', $expiry = 0) { return $value; }
|
||||
|
||||
require dirname(__DIR__) . '/app/miniapi/service/VisaApplication/VisaApplicationService.php';
|
||||
|
||||
use app\miniapi\service\VisaApplication\VisaApplicationService;
|
||||
|
||||
$service = new VisaApplicationService();
|
||||
if ($service->endpoint('https://user.example.com/users/list', 'start') !== 'https://user.example.com/visa_application/start') throw new RuntimeException('endpoint construction failed');
|
||||
if ($service->endpoint('https://user.example.com/users/list', 'dataSource') !== 'https://user.example.com/visa_application/dataSource') throw new RuntimeException('data source forwarding endpoint failed');
|
||||
if ($service->endpoint('https://user.example.com/users/list', 'invalid') !== '') throw new RuntimeException('action allowlist failed');
|
||||
$payload = $service->payload(9, ['order_source' => 'jnwxmini']);
|
||||
if (isset($payload['user_id']) || (json_decode((string)($payload['user_token'] ?? ''), true) !== ['purpose' => 'visa_api', 'user_id' => 9, 'order_source' => 'jnwxmini'])) throw new RuntimeException('trusted token payload failed');
|
||||
if (str_contains((new ReflectionMethod($service, 'start'))->getFileName() ? file_get_contents((new ReflectionMethod($service, 'start'))->getFileName()) : '', '$data + [\'order_source\'')) throw new RuntimeException('start source override failed');
|
||||
if (!str_contains((string)file_get_contents(dirname(__DIR__) . '/app/miniapi/controller/VisaApplication.php'), "sourceForCode(string \$code)") || !str_contains((string)file_get_contents(dirname(__DIR__) . '/app/miniapi/controller/VisaApplication.php'), "\$code === 'evus_bj_mp'") || !str_contains((string)file_get_contents(dirname(__DIR__) . '/app/miniapi/controller/VisaApplication.php'), "\$filters['source_code'] = \$this->orderSource()")) throw new RuntimeException('visa source allowlist failed');
|
||||
$neutral = VisaApplicationService::decorateSubmitForCheck(['code' => 1, 'data' => ['order_sn' => 'V1', 'requires_payment' => false]]);
|
||||
$payable = VisaApplicationService::decorateSubmitForCheck(['code' => 1, 'data' => ['order_sn' => 'V 1', 'requires_payment' => true]]);
|
||||
if (isset($neutral['data']['pay_url']) || ($payable['data']['pay_url'] ?? '') !== 'https://www.qlevisa.com/index/pay/h5pay.html?order_sn=V%201') throw new RuntimeException('miniapi payment decoration failed');
|
||||
if (VisaApplicationService::dataSourceQueryForCheck('inac.city', ['model' => 'region', 'source_code' => 'jnwxmini', 'state_id' => '11', 'hotel_state' => '12', 'provinceCode' => '13', 'country' => 'CN', 'application_token' => 'secret', 'user_token' => 'secret']) !== ['type' => 'inac.city', 'model' => 'region', 'source_code' => 'jnwxmini', 'state_id' => '11', 'hotel_state' => '12', 'provinceCode' => '13', 'country' => 'CN']) throw new RuntimeException('miniapi data source filtering failed');
|
||||
echo "PASS visa_application_facade_self_check\n";
|
||||
Reference in New Issue
Block a user